Skip to content

Commit 64bc9b2

Browse files
committed
Add stylesheet example to widget styling and remove clock example, as I feel those are redundant with widget creation. The content is still there, as some parts already written could be reused in remaining chapters.
1 parent a0eae20 commit 64bc9b2

File tree

2 files changed

+186
-3
lines changed

2 files changed

+186
-3
lines changed

Chapters/toplo/skinningAWidget.md

Lines changed: 184 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -292,4 +292,187 @@ space show.
292292

293293
### Using a stylesheet
294294

295-
TODO
295+
Stylesheet is another way to style an element. With stylesheet, we select on which element we want to apply our style using dedicated selector
296+
297+
A style rule is an association between a selector and one or more writers. A rule can only be applied to a graphic element if the rule's selector corresponds to the graphic element. There are 5 types of selector, and they can be combined with different operators. The five selector types are:
298+
299+
* **Any:** Selects any element.
300+
* **Action:** Evaluates a *block closure* with the graphic element as argument. If the block returns true, then the element is selected, otherwise it is not.
301+
* **Type:** Used to test whether the graphic element is an object of a given class. For example, it can check that the graphic element is a **ToButton**.
302+
* **Id:** Selects graphic elements according to their *id*. The id is a BlElement's property.
303+
* **Stamp:** Checks that the graphic element has a stamp with a given key.
304+
305+
Selectors can be combined together using different operators. There are 6 different operators:
306+
307+
* **Not:** Negates the selection.
308+
* **And:** Boolean combination of *and* between two selectors.
309+
* **Or:** Boolean *or* combination between two selectors.
310+
* **Child:** Used to apply a selector to the graphic element's children. It is possible to set a child depth level.
311+
* **Parent:** Allows you to apply a selector to the graphic element's parent. It is possible to set a parent depth level.
312+
* **Sibling:** Allows you to apply a selector to graphic elements having the same parent as the graphic element.
313+
314+
All our element needs to be ToElement. Let's update our element creation
315+
accordingly:
316+
317+
```smalltalk
318+
ToIntegerInputElement >> createCircle
319+
320+
| circle |
321+
circle := ToElement new
322+
background: Color black;
323+
border: (BlBorder paint: Color pink width: 2);
324+
layout: BlFrameLayout new;
325+
geometry: BlCircleGeometry new.
326+
^ circle
327+
```
328+
329+
Let's adapt our input widget to ease sub-element selection, by adding an *id* to relevant parts:
330+
331+
```smalltalk
332+
ToIntegerInputElement >> initialize
333+
334+
super initialize.
335+
self layout: BlFrameLayout new.
336+
self id: #coreInput.
337+
self initializePlusButton.
338+
self initializeMinusButton.
339+
self initializeInputValue: 20.
340+
self label: 'Input'
341+
```
342+
343+
```smalltalk
344+
ToIntegerInputElement >> initializeInputValue: aValue
345+
346+
inputValue := BlTextElement new.
347+
inputValue id: #input.
348+
inputValue constraintsDo: [ :c |
349+
c frame horizontal alignCenter.
350+
c frame vertical alignCenter ].
351+
self changeValueTo: aValue.
352+
self addChild: inputValue
353+
```
354+
355+
```smalltalk
356+
ToIntegerInputElement >> initializeMinusButton
357+
358+
| textElt |
359+
minus := self createCircle.
360+
minus id: #minus.
361+
minus constraintsDo: [ :c |
362+
c frame horizontal alignLeft.
363+
c frame vertical alignCenter.
364+
c margin: (BlInsets all: 10) ].
365+
textElt := BlTextElement new text: (self configuredString: '-').
366+
textElt text fontSize: 80.
367+
textElt constraintsDo: [ :c |
368+
c frame horizontal alignCenter.
369+
c frame vertical alignCenter ].
370+
minus
371+
addEventHandlerOn: BlMouseDownEvent
372+
do: [ :e | self decreaseInput ].
373+
minus addChild: textElt.
374+
self addChild: minus
375+
```
376+
377+
```smalltalk
378+
ToIntegerInputElement >> initializePlusButton
379+
380+
| textElt |
381+
plus := self createCircle.
382+
plus id: #plus.
383+
plus constraintsDo: [ :c |
384+
c frame horizontal alignRight.
385+
c frame vertical alignCenter ].
386+
plus transformDo: [ :t | t translateBy: -15 @ 0 ].
387+
textElt := BlTextElement new text: (self configuredString: '+').
388+
textElt text fontSize: 55.
389+
textElt constraintsDo: [ :c |
390+
c frame horizontal alignCenter.
391+
c frame vertical alignCenter ].
392+
plus
393+
addEventHandlerOn: BlMouseDownEvent
394+
do: [ :e | self increaseInput ].
395+
plus addChild: textElt.
396+
self addChild: plus
397+
```
398+
399+
The method `newRawSkin` is not used with StyleSheet, and can be safely removed. It's time now to define our stylesheet
400+
401+
First, create your stylesheet class:
402+
403+
```smalltalk
404+
ToStyleSheetTheme << #ToInputElementStyleSheet
405+
slots: {};
406+
tag: 'Input';
407+
package: 'Bloc-Book'
408+
```
409+
410+
Now, let's define the rule matching our element with custom appearance:
411+
412+
```smalltalk
413+
ToInputElementStyleSheet >> initializeStyleRules
414+
415+
super initializeStyleRules.
416+
self select: #coreInput asIdSelector style: [
417+
self
418+
write: (self property: #geometry)
419+
with: [ :e | BlRoundedRectangleGeometry cornerRadius: 20 ];
420+
write: (self property: #size) with: [ :e | 200 @ 100 ];
421+
write: (self property: #background) with: [ :e | Color blue ];
422+
write: (self property: #border)
423+
with: [ :e | BlBorder paint: Color black width: 3 ] ].
424+
425+
self select: #minus asIdSelector style: [
426+
self
427+
write: (self property: #background) with: [ :e | Color brown ];
428+
write: (self property: #border) with: [ :e |
429+
BlBorder
430+
paint: (e valueOfTokenNamed: #'color-border')
431+
width: (e valueOfTokenNamed: #'line-width') ] ].
432+
433+
self select: #plus asIdSelector style: [
434+
self
435+
write: (self property: #background) with: [ :e | Color brown ];
436+
write: (self property: #border) with: [ :e |
437+
BlBorder
438+
paint: (e valueOfTokenNamed: #'color-border')
439+
width: (e valueOfTokenNamed: #'line-width') ] ]
440+
```
441+
442+
You can noticed we use custom properties for specific element.
443+
That's a good idea, as we can reuse those value to keep theme consistent.
444+
445+
```smalltalk
446+
ToInputElementStyleSheet class >> defaultTokenProperties
447+
^ super defaultTokenProperties , {
448+
(ToTokenProperty name: #'color-border' value: Color lightOrange).
449+
(ToTokenProperty name: #'line-width' value: 2) }
450+
```
451+
452+
Last, if you want some element to react to mouse event, like mouse hover,
453+
you can define it like:
454+
455+
```smalltalk
456+
self
457+
when: ToHoveredSkinEvent
458+
write: (self property: #background)
459+
with: [ :e | BlBackground paint:
460+
(e valueOfTokenNamed: #'color-background-hover') ] ]
461+
```
462+
463+
Let's now open our new element. We now specify which theme we use on startup, referencing our stylesheet:
464+
465+
```smalltalk
466+
| input space |
467+
space := BlSpace new.
468+
space toTheme: ToInputElementStyleSheet new.
469+
input := self new.
470+
space root addChild: input.
471+
space show.
472+
```
473+
474+
You may have noticed, we mixed element appearance definition in
475+
our code and in our stylesheet. With styling, your scene could be
476+
reduced to a tree of *ToElement* with custom *stamp* or *id*, and
477+
all their styling defined in stylesheet, with custom rules or ad-hoc
478+
definition.

index.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,15 @@
1414
<!inputFile|path=Chapters/bloc/blocArchitecture.md!>
1515
<!inputFile|path=Chapters/bloc/text.md!>
1616
<!inputFile|path=Chapters/bloc/animation.md!>
17-
<!inputFile|path=Chapters/bloc/complexElement.md!>
17+
1818
<!inputFile|path=Chapters/bloc/dragAndDropExample.md!>
1919

2020
# Toplo: Bloc widget set
2121

2222
<!inputFile|path=Chapters/toplo/toplo.md!>
2323
<!inputFile|path=Chapters/toplo/existingWidgets.md!>
2424
<!inputFile|path=Chapters/toplo/widgetList.md!>
25-
<!inputFile|path=Chapters/toplo/widget_creation.md!>
25+
2626
<!inputFile|path=Chapters/toplo/skinningAWidget.md!>
2727
<!inputFile|path=Chapters/toplo/definingATheme.md!>
2828
<!inputFile|path=Chapters/toplo/stylesheet.md!>

0 commit comments

Comments
 (0)