Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: button local variables #3314

Draft
wants to merge 37 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
9972f23
wip
Julusian Feb 23, 2025
b403740
wip: boilerplate
Julusian Feb 23, 2025
2847553
wip: refactor and prep as entity type
Julusian Feb 23, 2025
fc8c375
wip: finish structure for local var entities
Julusian Feb 23, 2025
2b193c6
wip
Julusian Feb 23, 2025
520a67d
wip: ui propogation
Julusian Feb 23, 2025
a2e32dc
wip
Julusian Feb 23, 2025
9ac35d3
wip
Julusian Feb 23, 2025
584178d
wip: sets
Julusian Feb 23, 2025
3a810b6
wip: cleanup
Julusian Feb 23, 2025
50bda94
wip: constant value
Julusian Feb 23, 2025
5be3fe3
wip: pass local vars, but missing reactivity
Julusian Feb 23, 2025
a3c96a7
wip: refactor
Julusian Feb 23, 2025
7281cd4
wip: this didn't help reactivity. is this code any good or is it rubbish
Julusian Feb 23, 2025
f157a05
wip: fix local variable value propogation
Julusian Feb 24, 2025
b5d26a5
wip: reactive feedback values
Julusian Feb 24, 2025
0e527db
fix: reactivity from feedback variables
Julusian Feb 24, 2025
7b84bc8
wip: fix tests
Julusian Feb 24, 2025
30d0c25
fix: improve add variable component
Julusian Feb 24, 2025
6a7e67a
wip: support local variables when parsing previews
Julusian Feb 24, 2025
3b3eaa7
wip: fix button reference preview parsing
Julusian Feb 24, 2025
71fcf74
wip: todos
Julusian Feb 24, 2025
b798049
wip: make feedback subType enum
Julusian Feb 27, 2025
647ace5
fix: hide empty connections in add entity modal
Julusian Feb 27, 2025
8d828cf
wip
Julusian Feb 27, 2025
a67f61f
wip: remove local variable entity type
Julusian Feb 27, 2025
de53662
wip
Julusian Feb 27, 2025
6f28150
wip
Julusian Feb 27, 2025
c1c6077
wip
Julusian Feb 27, 2025
4af5f9e
fix
Julusian Feb 27, 2025
485f3c4
wip: recursive variableName
Julusian Feb 27, 2025
0242f4a
fix
Julusian Feb 27, 2025
d3ae257
wip: user value
Julusian Feb 28, 2025
762e49d
wip
Julusian Feb 28, 2025
bb84cf8
wip
Julusian Feb 28, 2025
01741f5
wip
Julusian Feb 28, 2025
bb41b70
wip
Julusian Feb 28, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion companion/lib/Controls/ControlTypes/Button/Base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,8 @@ export abstract class ButtonControlBase<TJson, TOptions extends Record<string, a
{
controlId,
commitChange: this.commitChange.bind(this),
triggerRedraw: this.triggerRedraw.bind(this),
invalidateControl: this.triggerRedraw.bind(this),
localVariablesChanged: this.#onLocalVariablesChanged.bind(this),
instanceDefinitions: deps.instance.definitions,
internalModule: deps.internalModule,
moduleHost: deps.instance.moduleHost,
Expand Down Expand Up @@ -207,6 +208,15 @@ export abstract class ButtonControlBase<TJson, TOptions extends Record<string, a
return result
}

#onLocalVariablesChanged(allChangedVariables: Set<string>): void {
// Trigger the change after a short debounce, just in case we end up in an infinite loop
setImmediate(() => {
this.deps.variables.values.emit('local_variables_changed', allChangedVariables, this.controlId)
})
}

abstract onVariablesChanged(allChangedVariables: Set<string>): void

/**
* Update an option field of this control
*/
Expand Down
28 changes: 16 additions & 12 deletions companion/lib/Controls/ControlTypes/Button/Normal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ import type { ButtonStyleProperties, DrawStyleButtonModel } from '@companion-app
import type { ControlDependencies } from '../../ControlDependencies.js'
import { EntityModelType } from '@companion-app/shared/Model/EntityModel.js'
import type { ControlActionSetAndStepsManager } from '../../Entities/ControlActionSetAndStepsManager.js'
import type { CompanionVariableValues } from '@companion-module/base'
import { GetButtonBitmapSize } from '../../../Resources/Util.js'
import { CompanionVariableValues } from '@companion-module/base'

/**
* Class for the stepped button control.
Expand Down Expand Up @@ -132,21 +132,23 @@ export class ControlButtonNormal

if (style.text) {
// Block out the button text
const injectedVariableValues: CompanionVariableValues = {}
const overrideVariableValues: CompanionVariableValues = {}

const location = this.deps.page.getLocationOfControlId(this.controlId)
if (location) {
// Ensure we don't enter into an infinite loop
// TODO - legacy location variables?
injectedVariableValues[`$(internal:b_text_${location.pageNumber}_${location.row}_${location.column})`] = '$RE'
overrideVariableValues[`$(internal:b_text_${location.pageNumber}_${location.row}_${location.column})`] = '$RE'
}

// Setup the parser
const parser = this.deps.variables.values.createVariablesAndExpressionParser(
location,
this.entities.getLocalVariableEntities(),
overrideVariableValues
)

if (style.textExpression) {
const parseResult = this.deps.variables.values.executeExpression(
style.text,
location,
undefined,
injectedVariableValues
)
const parseResult = parser.executeExpression(style.text, undefined)
if (parseResult.ok) {
style.text = parseResult.value + ''
} else {
Expand All @@ -155,9 +157,9 @@ export class ControlButtonNormal
}
this.#last_draw_variables = parseResult.variableIds.size > 0 ? parseResult.variableIds : null
} else {
const parseResult = this.deps.variables.values.parseVariables(style.text, location, injectedVariableValues)
const parseResult = parser.parseVariables(style.text)
style.text = parseResult.text
this.#last_draw_variables = parseResult.variableIds.length > 0 ? new Set(parseResult.variableIds) : null
this.#last_draw_variables = parseResult.variableIds.size > 0 ? parseResult.variableIds : null
}
}

Expand Down Expand Up @@ -218,6 +220,7 @@ export class ControlButtonNormal
* @param allChangedVariables - variables with changes
*/
onVariablesChanged(allChangedVariables: Set<string>): void {
// console.log('change', allChangedVariables)
if (this.#last_draw_variables) {
for (const variable of allChangedVariables.values()) {
if (this.#last_draw_variables.has(variable)) {
Expand Down Expand Up @@ -276,6 +279,7 @@ export class ControlButtonNormal
options: this.options,
feedbacks: this.entities.getFeedbackEntities(),
steps: this.entities.asNormalButtonSteps(),
localVariables: this.entities.getLocalVariableEntities().map((ent) => ent.asEntityModel(true)),
}

return clone ? cloneDeep(obj) : obj
Expand Down
11 changes: 10 additions & 1 deletion companion/lib/Controls/ControlTypes/Triggers/Events/Variable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ export class TriggersEventVariables {
*/
readonly #eventBus: TriggerEvents

/**
* The control id of the parent trigger
*/
readonly #controlId: string

/**
* Execute the actions of the parent trigger
*/
Expand All @@ -62,6 +67,7 @@ export class TriggersEventVariables {
this.#logger = LogController.createLogger(`Controls/Triggers/Events/Timer/${controlId}`)

this.#eventBus = eventBus
this.#controlId = controlId
this.#executeActions = executeActions

this.#eventBus.on('variables_changed', this.#onVariablesChanged)
Expand All @@ -85,7 +91,10 @@ export class TriggersEventVariables {
* Handler for the variable_changed event
* @param allChangedVariables Set of all the variables that have changed
*/
#onVariablesChanged = (allChangedVariables: Set<string>): void => {
#onVariablesChanged = (allChangedVariables: Set<string>, fromControlId: string | null): void => {
// If the event is from a control, but not the same control, ignore it
if (fromControlId && fromControlId !== this.#controlId) return

if (this.#enabled) {
let execute = false

Expand Down
3 changes: 2 additions & 1 deletion companion/lib/Controls/ControlTypes/Triggers/Trigger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,8 @@ export class ControlTrigger
this.entities = new ControlEntityListPoolTrigger({
controlId,
commitChange: this.commitChange.bind(this),
triggerRedraw: this.triggerRedraw.bind(this),
invalidateControl: this.triggerRedraw.bind(this),
localVariablesChanged: null,
instanceDefinitions: deps.instance.definitions,
internalModule: deps.internalModule,
moduleHost: deps.instance.moduleHost,
Expand Down
52 changes: 50 additions & 2 deletions companion/lib/Controls/Controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ import type { ControlLocation } from '@companion-app/shared/Model/Common.js'
import { EventEmitter } from 'events'
import type { ControlCommonEvents, ControlDependencies } from './ControlDependencies.js'
import { TriggerExecutionSource } from './ControlTypes/Triggers/TriggerExecutionSource.js'
import { CompanionVariableValues } from '@companion-module/base'
import { VariablesAndExpressionParser } from '../Variables/VariablesAndExpressionParser.js'

export const TriggersListRoom = 'triggers:list'
const ActiveLearnRoom = 'learn:active'
Expand Down Expand Up @@ -428,6 +430,24 @@ export class ControlsController extends CoreBase {
return control.entities.entitySetInverted(entityLocation, id, isInverted)
})

client.onPromise('controls:entity:set-variable-name', (controlId, entityLocation, id, name) => {
const control = this.getControl(controlId)
if (!control) return false

if (!control.supportsEntities) throw new Error(`Control "${controlId}" does not support entities`)

return control.entities.entitySetVariableName(entityLocation, id, name)
})

client.onPromise('controls:entity:set-variable-value', (controlId, entityLocation, id, value) => {
const control = this.getControl(controlId)
if (!control) return false

if (!control.supportsEntities) throw new Error(`Control "${controlId}" does not support entities`)

return control.entities.entitySetVariableValue(entityLocation, id, value)
})

client.onPromise(
'controls:entity:move',
(controlId, moveEntityLocation, moveEntityId, newOwnerId, newEntityLocation, newIndex) => {
Expand Down Expand Up @@ -792,6 +812,15 @@ export class ControlsController extends CoreBase {
client.onPromise('controls:unsubscribe:learn', async () => {
client.leave(ActiveLearnRoom)
})

client.onPromise('controls:local-variables-values', (controlId) => {
const control = this.getControl(controlId)
if (!control) return {}

if (!control.supportsEntities) throw new Error(`Control "${controlId}" does not support entities`)

return control.entities.getLocalVariableValues()
})
}

/**
Expand Down Expand Up @@ -955,12 +984,15 @@ export class ControlsController extends CoreBase {
/**
* Propagate variable changes to the controls
*/
onVariablesChanged(allChangedVariablesSet: Set<string>): void {
onVariablesChanged(allChangedVariablesSet: Set<string>, fromControlId: string | null): void {
// Inform triggers of the change
this.triggers.emit('variables_changed', allChangedVariablesSet)
this.triggers.emit('variables_changed', allChangedVariablesSet, fromControlId)

if (allChangedVariablesSet.size > 0) {
for (const control of this.#controls.values()) {
// If the changes are local variables and from another control, ignore them
if (fromControlId && fromControlId !== control.controlId) continue

if (control.supportsStyle) {
control.onVariablesChanged(allChangedVariablesSet)
}
Expand Down Expand Up @@ -1137,6 +1169,22 @@ export class ControlsController extends CoreBase {
control.entities.verifyConnectionIds(knownConnectionIds)
}
}

createVariablesAndExpressionParser(
controlLocation: ControlLocation | null | undefined,
overrideVariableValues: CompanionVariableValues | null
): VariablesAndExpressionParser {
const controlId = controlLocation && this.page.getControlIdAt(controlLocation)
const control = controlId && this.getControl(controlId)

const variableEntities = control && control.supportsEntities ? control.entities.getLocalVariableEntities() : []

return this.variablesController.values.createVariablesAndExpressionParser(
controlLocation,
variableEntities,
overrideVariableValues
)
}
}

export interface NewFeedbackValue {
Expand Down
Loading