Playing with label aptitudes for buttons

When using a label aptitude, the label aptitude embeds the label element directly in the widget element as a child. In the example below, the aptitude is added directly to the button element:

buttonWithDirectlyEmbeddedLabel
	<gtExample>
	| aButton aLabelLook |
	
	aButton := self buttonElement.
	aLabelLook := self buttonLabelLookWithConstraints.

	aButton viewModel: self allModels.
	aButton aptitude: aLabelLook.
	
	self assert: aLabelLook label text asString equals: self labelText.
	self assert: aButton children asArray equals: { aLabelLook label }.

	^ aButton
    

If the button has a more complex structure, this behaviour can cause problems. In the example below, the button defines a content element in which the label should be added. However, because the aptitude is added to the button element, the label will be emdedd in the button element instead of the content element:

buttonWithModelAndWrongContentPlacement
	<gtExample>
	| aButton aContent aLabelLook |
	
	aButton := self buttonElement.
	aContent := self buttonContentElementWithFrameLayout.
	aLabelLook := self buttonLabelLookWithConstraints.

	aButton addChild: aContent.

	aButton viewModel: self allModels.
	aButton aptitude: aLabelLook.

	self assert: aButton children asArray equals: { aContent . aLabelLook label }.
	self assert: aContent children asArray equals: { }.

	^ aButton
    

To change this behavior, the label aptitude provides more control over where the label is inserted into the button element: to identify the element in the button element where the label should be placed, the aptitude looks for an element named #content. There shoud be only one element named #content within the button. To fix the problem from the above example, we can name the aContent element with #content when adding it to the button element:

buttonWithModelAndLookWithoutConstraints
	<gtExample>
	| aButton aContent aLabelLook |
	
	aButton := self buttonElement.
	aContent := self buttonContentElementWithLinearLayout.
	aLabelLook := self buttonLabelLookWithoutConstraints.
	
	aButton addChild: aContent as: #content.

	aButton viewModel: self allModels.
	aButton aptitude: aLabelLook.
	
	self assert: aLabelLook label text asString equals: self labelText.

	self assert: aButton children asArray equals: { aContent }.
	self assert: aContent children asArray equals: { aLabelLook label }.

	^ aButton
    

The label aptitude can directly change properties of the label element. This can be usefull when one wants to change layout properties or update graphical properties of the label:

buttonWithChangedConstraintsInLook
	<gtExample>
	| aButton aContent aLabelLook |
	
	aButton := self buttonElement.
	aContent := self buttonContentElementWithLinearLayout.
	aLabelLook := self buttonLabelLookWithoutConstraints
		labelDo: [ :aLabelElement | aLabelElement constraintsDo: [ :c | c linear vertical alignBottom ] ];
		fontSize: 30;
		bold.
	
	aButton addChild: aContent as: #content.

	aButton viewModel: self buttonModel.
	aButton aptitude: aLabelLook.
	
	^ aButton
    

After linking the button element with the label aptitude and the button model, changing the value in the button model updates the label in the button element:

buttonWithModelAndLookAndChangedModelLabel
	<gtExample>
	| aButton newLabel |
	
	newLabel := self labelText, ' World'.
	
	aButton := self buttonWithModelAndLookWithoutConstraints.
	
	aButton viewModel widgetModels first text: newLabel.

	self assert: aButton aptitude text asString equals: newLabel.
	self assert: aButton aptitude label text asString equals: newLabel.

	^ aButton
    

When changing the label in the model, a BrLabelChanged BrChangeEvent subclass: #BrLabelChanged instanceVariableNames: 'text' classVariableNames: '' package: 'Brick-! Core - Events' event is triggered that reaches the label aptitude, through the button widget.

labelChangedEvent
	<gtExample>
	| aButton newLabel aLabelChangedEvent |

	aLabelChangedEvent := nil.
	newLabel := self labelText, ' World'.

	aButton := self buttonWithModelAndLookWithoutConstraints.
	aButton aptitude when: BrLabelChanged do: [ :anEvent | aLabelChangedEvent := anEvent copy ].
	aButton viewModel widgetModels first text: newLabel.

	self assert: aButton viewModel widgetModels first text asString equals: newLabel.

	self assert: aLabelChangedEvent notNil.
	self assert: aLabelChangedEvent isConsumed not.
	self assert: aLabelChangedEvent target equals: aButton.
	self assert: aLabelChangedEvent currentTarget equals: aButton aptitude.
	self assert: aLabelChangedEvent source equals: aButton viewModel widgetModels first.
	self assert: aLabelChangedEvent text asString equals: newLabel.
	self assert: aLabelChangedEvent timestamp isNil.

	^ aLabelChangedEvent
    

In case the button widget does not have a label aptitude attached, changing the value of the text in the model does not update the visual representation of the label in the button widget:

buttonWithOnlyModelAndChangedModelLabel
	<gtExample>
	| aButton aLabel |
	
	aButton := self buttonWithOnlyModel.
	
	"we know the structure from the sub-example"
	aLabel := aButton children first children first.
	
	"changing label in the view model does not propagate to the label element"
	self flag: 'Do we need the following line?'.
	self labelModel text: self labelText, ' world'.
	self assert: aLabel text asString equals: self labelText.

	^ aButton
    

When attaching a label aptitude and a button model to a button widget the order in which they are attached does not matter. If the model is set first then the text value set in the label aptitude is not used; the model has priority over the label aptitude:

buttonWithModelFirstThenLook
	<gtExample>
	| aButton aModel aLook modelLabel lookLabel |
	
	modelLabel := 'Hello world'.
	lookLabel := 'Hello cruel world'.
	
	aButton := self buttonElement.

	aModel := self labelModel + self buttonModel.
	aModel text: modelLabel.
	
	aLook := self buttonLabelLookWithConstraints.
	aLook text: lookLabel.
	
	aButton viewModel: aModel.
	aButton aptitude: aLook.
	
	self assert: aLook text asString equals: modelLabel.
	self assert: aModel text asString equals: modelLabel.
	
	^ aButton