-
Notifications
You must be signed in to change notification settings - Fork 0
3. Update shapes properties (Colors, Transformations, ...)
This section will present the differents "basics" properties of a CadeShape
. We will see:
- How to change the color of a shape.
- How to change the stroke (border).
- How to move a shape.
- How to rotate a shape.
- How to apply a scale transformation.
- How to hide / show a shape.
- How to allow / disallow interactions.
- How to bufferised a shape.
To change the color that fill the shape you have to use the method fillColor:
. We will see different way to use this method:
- with
Color
- with
AthensCairoGradientPaint
We can directly use the default color object of Pharo.
view := CadeView new open.
"Create shapes and add them to the view."
shape1 := CadeShapeLeaf2D shape: (0@0 extent: 200@200).
view addShape: shape1.
shape2 := CadeShapeLeaf2D shape: (50@50 extent: 200@200).
view addShape: shape2.
shape3 := CadeShapeLeaf2D shape: (100@100 extent: 200@200).
view addShape: shape3.
shape4 := CadeShapeLeaf2D shape: (150@150 extent: 200@200).
view addShape: shape4.
"Simple blue color"
shape1 fillColor: (Color blue).
"From a hexadecimal value"
shape2 fillColor: (Color fromHexString: 'B00778').
"From RGB value"
shape3 fillColor: (Color r: 0.1 g: 0.2 b: 0.3).
"With transparency added"
shape4 fillColor: (Color green alpha: 0.3).
We use AthensCairoGradientPaint
to create a soft transition between multiple Color in Pharo. There is two type of gradiant we can make: linear or radial.
Example with linear gradient:
view := CadeView new open.
shape := CadeShapeLeaf2D shape: (0@0 extent: 400@400).
view addShape: shape.
color := AthensCairoGradientPaint
createLinearGradient: {
(0 -> Color red).
(0.2 -> Color pink).
(0.4 -> Color white).
(0.6 -> Color cyan).
(0.8 -> Color blue).
(1 -> Color yellow)}
start: 0@0
stop: 0@400.
shape fillColor: color.
Example with radial gradient:
view := CadeView new open.
shape := CadeShapeLeaf2D shape: (0@0 extent: 400@400).
view addShape: shape.
color := AthensCairoGradientPaint
radialBetween: 200 @ 150
extending: 250
and: 0 @ 0
extending: 0
withColorRamp: {
(0 -> Color black).
(0.1 -> Color blue).
(0.5 -> Color red).
(1 -> Color yellow) }.
shape fillColor: color.
There are 2 methods to change the stroke arround a shape:
-
strokeColor:
to just add a simple stroke arround the shape with a default size of 1 pixel. -
strokeColor:width:
to add a stroke and to precise it's size.
You can add any color you want from Color
or AthensCairoGradientPaint
. Important to note, the interraction of a stroked shape is arround the shape and not arround the shape and the stroke.
view := CadeView new open.
default := CadeShapeLeaf2D shape: (CadeCircle center: 100@200 radius: 25).
default fillColor: (Color blue).
view addShape: default.
shape1 := CadeShapeLeaf2D shape: (CadeCircle center: 200@100 radius: 50).
shape1 fillColor: (Color red).
view addShape: shape1.
shape2 := CadeShapeLeaf2D shape: (CadeCircle center: 200@300 radius: 50).
shape2 fillColor: (Color red).
view addShape: shape2.
"We stroke the shapes"
default strokeColor: Color black.
shape1 strokeColor: Color black width: 40.
shape2 strokeColor: Color black translucent width: 40.
"A bit of interraction on shape2"
shape2 onEvent: #mouseEnter executeAction: [ shape2 fillColor: Color yellow ].
shape2 onEvent: #mouseExit executeAction: [ shape2 fillColor: Color red ].
It' possible to change the position of a shape in the model with different methods:
-
x:
to change the horizontal position of a shape. -
y:
to change the vertical position of a shape. -
coordinates:
to change the horizontal and the vertical position of a shape.
For example, lets start by creating a simple hierachy of shapes. In a playground execute the following code:
view := CadeView new open.
"A container"
container1 := CadeShapeContainer new.
view addShape: container1.
bg1 := CadeShapeLeaf2D shape: (0@0 extent: 200@200).
bg1 fillColor: (Color blue).
container1 addShape: bg1.
"A container inside the first container"
container2 := CadeShapeContainer new.
container1 addShape: container2.
bg2 := CadeShapeLeaf2D shape: (0@0 extent: 100@100).
bg2 fillColor: (Color red).
container2 addShape: bg2.
shape := CadeShapeLeaf2D shape: (0@0 extent: 20@20).
shape fillColor: (Color yellow).
container2 addShape: shape.
The container1 with a blue background contains a container2 with the red background. The container2 contains a shape in yellow. All poistion start at 0@0
.
We will use the methods coordinates to move the container1
in the center of the view. In the same playground execute the following code:
container1 coordinates: 100@100.
All the shapes inside the container1
moved. Then we will move the yellow shape
a bit to the right.
shape x: 60.
The shape moved 60 pixel to the right. In fact if we inspect the result of the method shape coordinates.
we can see that the coordinate of the shape is now 60.0@0
. Note that it didn't added the first translation of the container1
of 100 pixel. The coordinates are always relatives to the direct parent of a shape. To prove it, we will move the container 2 on the horizontal axis.
container2 y: -50.
If we inspect the different coordinates of every shapes we now have:
-
container1
→100.0@100.0
-
bg1
→0 @ 0
-
container2
→0@ -50.0
-
bg2
→0 @ 0
-
shape
→60.0 @ 0
In this part, we will see 2 methods. angleInDegree:
and rotationPoint:
. To illustrate this methods we will use this example:
view := CadeView new open.
"A container"
container1 := CadeShapeContainer new.
container1 coordinates: 100@150.
view addShape: container1.
bg1 := CadeShapeLeaf2D shape: (0@0 extent: 200@100).
bg1 fillColor: (Color blue).
container1 addShape: bg1.
"A container inside the first container"
container2 := CadeShapeContainer new.
container2 coordinates: 50@25.
container1 addShape: container2.
bg2 := CadeShapeLeaf2D shape: (0@0 extent: 100@50).
bg2 fillColor: (Color red).
container2 addShape: bg2.
shape := CadeShapeLeaf2D shape: (0@0 extent: 20@10).
shape fillColor: (Color yellow).
shape coordinates: 40@20.
container2 addShape: shape.
First let's see angleInDegree:
on container1
:
container1 angleInDegree: 25.
The shape rotate, and all it's children, from the origin point (0 @ 0)
of the shape.
We can change the origin of the rotation with the method: rotationPoint:
.
"We change the rotation angle to something more pronounced."
container1 angleInDegree: 90.
"We will put the rotation point on the center of the shape."
container1 rotationPoint: 100@50.
--- Before changing the rotation point ---
--- After changing the rotation point ---
Let's see what happen when we change the rotation angle inside multiple containers. With the previous example:
container1 angleInDegree: 45.
container2 angleInDegree: -90.
shape angleInDegree: 45.
The blue square should be in diagonal (angle of 45°). The red square should be perpendicular to the blue square (angle of -90°). The yellow square should look flat (45° + -90° + 45° = 0°).
Cade allow to change the scale of a shape. we have 2 methods to influe on the scale: scale:
and scaleCenter:
. Let's see them in action with the previous example.
view := CadeView new open.
"A container"
container1 := CadeShapeContainer new.
container1 coordinates: 100@150.
view addShape: container1.
bg1 := CadeShapeLeaf2D shape: (0@0 extent: 200@100).
bg1 fillColor: (Color blue).
container1 addShape: bg1.
"A container inside the first container"
container2 := CadeShapeContainer new.
container2 coordinates: 50@25.
container1 addShape: container2.
bg2 := CadeShapeLeaf2D shape: (0@0 extent: 100@50).
bg2 fillColor: (Color red).
container2 addShape: bg2.
shape := CadeShapeLeaf2D shape: (0@0 extent: 20@10).
shape fillColor: (Color yellow).
shape coordinates: 40@20.
container2 addShape: shape.
"We change the scale center. We will see after what it does."
container1 scaleCenter: 100@50.
Lets modify the scale of the first container. By default the scale is 1@1
.
"This should do nothing."
container1 scale: 1@1.
"This should double the width."
container1 scale: 2@1.
"This should divide the height by 2."
container1 scale: 1@0.5.
--- Normal scale (1 @ 1)
---
--- Double width (2 @ 1)
---
--- Divide the height by 2 (1 @ 0.5)
---
The method scaleCenter:
allow us to change the origin of the growth of a shape. For example if we set the scale center in the bottom left of a shape it should grow toward the top right. If we sttle it in the center (like in the previous example it should grow in all direction.
--- Top left ---
"Top left"
container1 scaleCenter: 0 @ 0.
container1 scale: 4.0 @ 4.0.
--- Top right---
"Top right"
container1 scaleCenter: 200 @ 0.
container1 scale: 4.0 @ 4.0.
--- Bottom right ---
"Bottom right"
container1 scaleCenter: 200 @ 100.
container1 scale: 4.0 @ 4.0.
--- Etc. ---
The scale is also reported to the children shapes. We will see an example that combine multiple transformations.
"First we double the height of the yellow shape."
shape scale: 1@2.
"Secondly we reduce by half the width of the container with the red shape and the yellow shape."
container2 scale: 0.5@1.
"Finally we double the height of the contianer that contain all the shapes."
"Pay attention to all shapes. The blue one will change but also the red and the yellow one."
container1 scale: 1@2.
It's possible to hide a shape by setting the isVisible
variable to false
. By default this value is equal to true. We will see an example:
view := CadeView new open.
"Adding a background"
shape1 := CadeShapeLeaf2D shape: (0@0 extent: 100@100).
shape1 fillColor: (Color red).
"Adding a shape to display"
shape2 := CadeShapeContainer new
addShape: (CadeCircle center: 50@20 radius: 10) asCadeShape;
addShape: (45@20 extent: 10@45) asCadeShape;
addShape: (25@35 extent: 50@10) asCadeShape;
addShape: (35@60 extent: 10@30) asCadeShape;
addShape: (55@60 extent: 10@30) asCadeShape.
view addShape: shape1.
view addShape: shape2.
Execute the following code in a playground then in the same playground we can change the isVisble
value of one shape and see the result.
"select then ctrl+d"
shape2 isVisible: false
It is possible to allow or dissalow interractions on a shape by setting the value isEnabled
of a shape to false
.
view := CadeView new open.
"Adding a basic shape to the view"
bg := CadeShapeLeaf2D shape: (0@0 extent: 300@300).
bg fillColor: (Color blue).
button := CadeShapeLeaf2D shape: (0@0 extent: 200@50).
button fillColor: Color gray.
button coordinates: 50@125.
"button interractions"
button action: [ Transcript show: 'click ' ].
button onEvent: #mouseEnter executeAction: [ button fillColor: Color gray muchLighter ].
button onEvent: #mouseExit executeAction: [ button fillColor: Color gray ].
container := CadeShapeContainer new
addShape: bg;
addShape: button.
view addShape: container.
If you want to dissalow the interraction on the button you can set the isEnabled
value to false
on the button or any parent shape of the button.
button isEnabled: false.
"or"
container isEnabled: false.
It is possible to create a buffer for a shape.
If a shape is NOT in a buffer and a part of it need to be redraw. Cade will redraw every visible pixel of the shape. If a shape is inside a buffer and a part of it need to be redraw, it will redraw only the part that needed.
Putting the shape inside a buffer decrease the number of opération needed to redraw the shape and increase significativly the performance of the application. To increase even more the performance you can disble the event of the shape by setting the isEnebled
value to false.
Next an example of a buffered shape:
view := CadeView new open.
"Creating a container containning 10 000 shapes"
random := Random new.
bg := CadeShapeContainer new.
1 to: 10000 do: [ :i | | _shape |
_shape := (-5 @ -5 extent: 10 @ 10) asCadeShape.
_shape fillColor: Color random.
_shape x: random next * 400.
_shape y: random next * 400.
bg addShape: _shape.
].
bg isEnabled: false.
bg isBuffered: true.
overlay := CadeShapeLeaf2D shape: (CadeCircle center: 0@0 radius: 30).
overlay fillColor: Color black.
overlay coordinates: 200@200.
overlay isDraggable: true.
view addShape: bg.
view addShape: overlay.
The buffer is created by the engine. Currently the only engine working with Cade is the
CairoEngine
. A buffered shape by the CairoEngine will first create newAthensCairoSurface
and draw itself on it. Then the surface will be saved as aRessource
in the shape. The engine will then display every shapes. If a shape is inside a buffer it will simply draw the pre-drawn surface inside theRessource
.When a buffered shape need to be redraw, a special signal is send to the shape and the
Ressource
is redraw. When the child of a buffered shape need to be redraw, it will trigger an event to its parent.