Skip to content
106 changes: 42 additions & 64 deletions Chapters/gettingStarted/buildingAWidget.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,9 @@ space root addChild: anInput.
space show.
```

The main widget is composed of four elements: two buttons, a label, and a value.
Let us detail these elements:
The main widget is composed of four elements: two buttons (the circle with a "+" and the circle with the "-"), a label (that displays "input"), and a value (that displays "20"). Let us detail these elements:
- the label and the value are text elements,
- the two buttons are, in fact, a composite element composed of a circle element with a text element inside.
- the two buttons are, in fact, a composite element composed of an element with a circle geometry, holding a text element inside.


### Getting started
Expand All @@ -37,28 +36,15 @@ BlElement << #BlIntegerInputElement
package: 'ABlocPackage'
```

We start by defining the shape of the main element. Notice that visual properties such the background, the border could be stored differently to be customized afterwards.This is what we will show in a following chapter using stylesheet and skins as done in Toplo.


```
BlIntegerInputElement >> widgetExtent

^ 300@120
```

```
BlIntegerInputElement >> backgroundPaint

^ Color black
```
We start by defining the shape of the main element. Notice that visual properties such as the background or the border could be stored differently to be customized afterwards. This is what we will show in a following chapter using stylesheet and skins as done in Toplo.


```
BlIntegerInputElement >> initialize

super initialize.
self size: self widgetExtent.
self background: self backgroundPaint.
self size: 300@120.
self background: Color black.
self geometry: (BlRoundedRectangleGeometry cornerRadius: 20).
self layout: BlFrameLayout new.
self border: (BlBorder paint: Color pink).
Expand All @@ -80,13 +66,13 @@ BlIntegerInputElement >> configuredString: aString
^ aString asRopedText attributes: { (BlTextForegroundAttribute paint: Color white) }
```

The `label:` method creates a `BlTextElement`, sets its text using properties and translates it to place it above the center.
The `initializeInputLabel` method creates a `BlTextElement`, sets its text using properties and translates it to place it above the center.

```
BlIntegerInputElement >> label: aString
BlIntegerInputElement >> initializeInputLabel

inputLabel := BlTextElement new.
inputLabel text: (self configuredString: aString).
inputLabel text: (self configuredString: 'Input').
inputLabel text fontSize: 25.
inputLabel constraintsDo: [ :c |
c frame horizontal alignCenter.
Expand All @@ -98,17 +84,17 @@ Note that we use `addChild:` method to add the text element in the composite (th

![With a label. % anchor=input1&width=50](figures/Input1.png)

We modify the initialize method to invoke the `label` method.
We modify the initialize method to invoke the `initializeInputLabel` method.
```
BlIntegerInputElement >> initialize

super initialize.
self size: self widgetExtent.
self background: self backgroundPaint.
self size: 300@120.
self background: Color black.
self geometry: (BlRoundedRectangleGeometry cornerRadius: 20).
self layout: BlFrameLayout new.
self border: (BlBorder paint: Color pink).
self label: 'Input'
self initializeInputlabel
```

We should get now a widget similar to the one shown in Fig *@input1@*.
Expand All @@ -128,8 +114,7 @@ BlIntegerInputElement >> initializeInputValue: aValue
self addChild: inputValue
```

We define a little helper method `changeValueTo:` that we will expose as pubic API for example
to start the input with a specific value.
We define a little helper method `changeValueTo:` that we will expose as public API but we will also use this method internally later.
Note again that we add the input value element in the composite one.

```
Expand All @@ -147,13 +132,14 @@ Then we change the `initialize` method to invoke `initializeInputValue:`.
BlIntegerInputElement >> initialize

super initialize.
self size: self widgetExtent.
self background: self backgroundPaint.
self size: 300@120.
self background: Color black.
self geometry: (BlRoundedRectangleGeometry cornerRadius: 20).
self layout: BlFrameLayout new.
self border: (BlBorder paint: Color pink).
self initializeInputValue: 20.
self label: 'Input'
self initializeInputLabel.
self initializeInputValue: 20

```

We should obtain now a widget similar to the one show in Fig *@input2@*.
Expand Down Expand Up @@ -221,14 +207,15 @@ the circle element to react to mouse-down events.
BlIntegerInputElement >> initialize

super initialize.
self size: self widgetExtent.
self background: self backgroundPaint.
self size: 300@120.
self background: Color black.
self geometry: (BlRoundedRectangleGeometry cornerRadius: 20).
self layout: BlFrameLayout new.
self border: (BlBorder paint: Color pink).
self initializePlusButton.
self initializeInputLabel.
self initializeInputValue: 20.
self label: 'Input'
self initializePlusButton.

```

We should now get a widget similar to the one shown in Fig *@input3@*.
Expand Down Expand Up @@ -278,15 +265,15 @@ FInally we update the initialize method to call the minus creation.
BlIntegerInputElement >> initialize

super initialize.
self size: self widgetExtent.
self background: self backgroundPaint.
self size: 300@120.
self background: Color black.
self geometry: (BlRoundedRectangleGeometry cornerRadius: 20).
self layout: BlFrameLayout new.
self border: (BlBorder paint: Color pink).
self initializePlusButton.
self initializeMinusButton.
self initializeInputLabel.
self initializeInputValue: 20.
self label: 'Input'
self initializePlusButton.
self initializeMinusButton
```

Now we should have the full widget.
Expand All @@ -299,15 +286,19 @@ Any time the `inputValue` is changed we will value a block with the new value of
First we initialize the callback block.

```
BlIntegerInputElement >> initializeInputValue: aValue
BlIntegerInputElement >> initialize.

super initialize.
self size: 300@120.
self background: Color black.
self geometry: (BlRoundedRectangleGeometry cornerRadius: 20).
self layout: BlFrameLayout new.
self border: (BlBorder paint: Color pink).
self initializeInputLabel.
self initializeInputValue: 20.
self initializePlusButton.
self initializeMinusButton
callbackBlock := [ :newInputValue | ].
inputValue := BlTextElement new.
inputValue constraintsDo: [ :c |
c frame horizontal alignCenter.
c frame vertical alignCenter ].
self changeValueTo: aValue.
self addChild: inputValue
```

Then anytime we changed the state, we update the value.
Expand All @@ -321,10 +312,10 @@ BlIntegerInputElement >> changeValueTo: aValue
callbackBlock value: aValue
```

Finnaly we create a mutator for the `callbackBlock` variable.
Finnaly we create a mutator for the `callbackBlock` variable called `whenInputValueChangedDo:`.

```
BlIntegerInputElement >> callbackBlock: aBlock
BlIntegerInputElement >> whenInputValueChangedDo: aBlock

callbackBlock := aBlock
```
Expand All @@ -345,19 +336,6 @@ BlNumberInputElementTest >> testChildrenAreSet
self assert: inputElem children size equals: 4
```

Let us see a more interesting test.
The following test shows that we can effectively changes the label.

```
BlNumberInputElementTest >> testCanChangeLabel

| inputElem |
inputElem := BlNumberInputElement new.
self assert: inputElem label text asString equals: 'Input'.
inputElem label: 'Volume'.
self assert: inputElem label text asString equals: 'Volume'.
```

The following tests are checking that the interaction on the buttons is working correctly.
```
BlNumberInputElementTest >> testValueUpdatedOnClick
Expand Down Expand Up @@ -391,7 +369,7 @@ BlNumberInputElementTest >> testCallbackCallOnClick
testNumberOfCall := 0.
testValue := -1.
inputElem := BlNumberInputElement new.
inputElem callbackBlock: [ :val |
inputElem whenInputValueChangedDo: [ :val |
testNumberOfCall := testNumberOfCall + 1.
testValue := val.
]
Expand Down
36 changes: 11 additions & 25 deletions Chapters/toplo/skinningAWidget.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,11 @@

In this chapter we show how we can take the simple input widget developed in a previous chapter and make it skinnable.

In a first period, we will focus on what is called raw skins. The idea behind raw skins are that we define classes with states and methods to define the behavior of a skin in a given theme. At the end of this chapter we will see that we can also skin widgets
using stylesheets following the spirit of CSS.
In a first period, we will focus on what is called raw skins. The idea behind raw skins are that we define classes with states and methods to define the behavior of a skin in a given theme. At the end of this chapter we will see that we can also skin widgets using stylesheets following the spirit of CSS.

The outline of this chapter is then: first we show that we can extend a theme and define a skin.
Then in a second time we show that we can define an autonomous theme.
Finally we will show that we can use stylesheet
Finally we will show that we can use stylesheet.

Remember that we want to create a widget as shown in Figure *@inputFinalSkin@*.

Expand All @@ -16,13 +15,12 @@ Remember that we want to create a widget as shown in Figure *@inputFinalSkin@*.
### Getting started

If you implemented the widget as presented earlier, just copy the class giving it a new name for example `ToIntegerInputElement`.
The definition of the `BlIntegerInputElement` is available SD:DefineAPlace.

The first thing that we should do is to make `ToIntegerInputElement` inherit from `ToElement` as follows:

```
ToElement << #ToIntegerInputElement
slots: { #plus . #minus . #inputValue . #value . #inputLabel };
slots: { #plus . #minus . #inputValue . #value . #inputLabel. #callbackBlock };
tag: 'Input';
package: 'Bloc-Book'
```
Expand All @@ -32,7 +30,7 @@ Our widget will then inherit the behavior to install a skin when instantiated, w
### Define a skin

We define a skin by inheriting from `ToRawSkin`, this class defines methods reacting to some events.
The class `ToRawSkin` is th default skin class when using the `ToRawTheme` which is the default theme but themes are detailed later.
The class `ToRawSkin` is the default skin class when using the `ToRawTheme` which is the default theme but themes are detailed later.
In fact, skins in Toplo are EventHandlers we simply add to our elements, changing their visual properties according to incoming events

```
Expand All @@ -50,7 +48,7 @@ can simply use values specific to this skin.

```
ToInputElementSkin >> installSkinEvent: anEvent
"when installing the skin, changes the properties of widget mentionned down here"
"when installing the skin, changes the properties of widget mentioned down here"

super installSkinEvent: anEvent.
anEvent elementDo: [ :e |
Expand Down Expand Up @@ -84,11 +82,6 @@ ToIntegerInputElement >> minus
Here we redefine the background of the element and its 'plus' and 'minus' sub-elements, but we also define a border to our element using tokens from our theme.
We accessed our element through the event received, this can be done in both following ways


##### Remark
Notice that the two following forms are equivalent.
This is important if you want to maximize

```
anEvent elementDo: [ :e |
e border: (e valueOfTokenNamed: #'color-border-checkable’).
Expand All @@ -98,8 +91,11 @@ target := anEvent currentTarget.
target border: target valueOfTokenNamed: #'color-border-checkable’)
```

##### Remark
Notice that the two following forms are equivalent.


Now that we defined our skin, we only need to tell our element to install this skins during initialization
Now that we defined our skin, we only need to tell our element to install this skin during initialization
In the `ToNumberInputElement` we define the method

### Declaring the skin
Expand Down Expand Up @@ -133,7 +129,7 @@ ToIntegerInputElement >> initialize

We can now open our element and see our new skin installed.

![An integer input widget with a new skin installed. % anchor=inputFinalSkin&width=50](figures/inputWithSkin.png )
![An integer input widget with a new skin installed. %width=50](figures/inputWithSkin.png )


### Define a theme that extends an existing one
Expand Down Expand Up @@ -190,7 +186,7 @@ space root addChild: anInput.
space show.
```

![An integer input widget with a new skin and new theme installed. % anchor=inputFinalSkin&width=50](figures/inputWithTheme.png )
![An integer input widget with a new skin and new theme installed. %width=50](figures/inputWithTheme.png )


### Decorating a BlElement to get a ToElement
Expand All @@ -216,16 +212,6 @@ ToNumberInputElement >> initialize
self initializeForToplo
```


SD: we should check if the following is necessary
```
ToNumberInputElement >> onAddedToSceneGraph

super onAddedToSceneGraph.
self ensuredSkinManager requestInstallSkinIn: self.
self addEventHandler: ToSkinStateGenerator new
```

### Autonome theme

We should now how we can define a full new theme.
Expand Down