15
15
//! A toggle switch widget.
16
16
17
17
use crate :: kurbo:: { Circle , Point , Rect , RoundedRect , Size } ;
18
+ use crate :: piet:: { FontBuilder , Text , TextLayout , TextLayoutBuilder } ;
18
19
use crate :: piet:: { LinearGradient , RenderContext , UnitPoint } ;
19
20
use crate :: theme;
20
21
use crate :: widget:: Align ;
21
22
use crate :: {
22
23
BaseState , BoxConstraints , Env , Event , EventCtx , LayoutCtx , PaintCtx , UpdateCtx , Widget ,
23
24
} ;
24
- use crate :: piet:: {
25
- FontBuilder , PietText , PietTextLayout , Text , TextLayout , TextLayoutBuilder ,
26
- } ;
27
25
28
26
#[ derive( Debug , Clone ) ]
29
27
pub struct Switch ;
30
28
31
29
impl Switch {
32
- pub fn new ( ) -> impl Widget < bool > { Align :: vertical ( UnitPoint :: CENTER , SwitchRaw :: default ( ) ) }
30
+ pub fn new ( ) -> impl Widget < bool > {
31
+ Align :: vertical ( UnitPoint :: CENTER , SwitchRaw :: default ( ) )
32
+ }
33
33
}
34
34
35
35
#[ derive( Debug , Clone , Default ) ]
@@ -46,56 +46,34 @@ impl SwitchRaw {
46
46
}
47
47
false
48
48
}
49
- }
50
49
51
- impl Widget < bool > for SwitchRaw {
52
- fn paint ( & mut self , paint_ctx : & mut PaintCtx , base_state : & BaseState , data : & bool , env : & Env ) {
53
- let knob_size = env. get ( theme:: BASIC_WIDGET_HEIGHT ) ;
54
- let size = env. get ( theme:: BASIC_WIDGET_HEIGHT ) ;
55
- let switch_thickness = 8. + knob_size;
50
+ fn paint_label (
51
+ & mut self ,
52
+ paint_ctx : & mut PaintCtx ,
53
+ base_state : & BaseState ,
54
+ data : & bool ,
55
+ env : & Env ,
56
+ switch_width : f64 ,
57
+ switch_padding : f64 ,
58
+ ) {
56
59
let font_name = env. get ( theme:: FONT_NAME ) ;
57
60
let font_size = env. get ( theme:: TEXT_SIZE_NORMAL ) ;
58
61
59
- let background_rect =
60
- RoundedRect :: from_origin_size ( Point :: ORIGIN , Size :: new ( switch_thickness * 2.5 , switch_thickness) . to_vec2 ( ) , switch_thickness / 2. ) ;
62
+ let label = if * data { "ON" } else { "OFF" } ;
61
63
62
- let background_gradient = if * data {
63
- LinearGradient :: new (
64
- UnitPoint :: TOP ,
65
- UnitPoint :: BOTTOM ,
66
- (
67
- env. get ( theme:: PRIMARY_LIGHT ) ,
68
- env. get ( theme:: PRIMARY_DARK ) ,
69
- ) ,
70
- )
71
- } else {
72
- LinearGradient :: new (
73
- UnitPoint :: TOP ,
74
- UnitPoint :: BOTTOM ,
75
- (
76
- env. get ( theme:: BACKGROUND_LIGHT ) ,
77
- env. get ( theme:: BACKGROUND_DARK ) ,
78
- ) ,
79
- )
80
- } ;
81
-
82
- paint_ctx. stroke ( background_rect, & env. get ( theme:: BORDER ) , 2.0 ) ;
83
-
84
- paint_ctx. fill ( background_rect, & background_gradient) ;
85
-
86
- let label = if * data {
87
- "ON"
88
- } else {
89
- "OFF"
90
- } ;
91
-
92
- let font = paint_ctx. text ( )
64
+ let font = paint_ctx
65
+ . text ( )
93
66
. new_font_by_name ( font_name, font_size)
94
67
. unwrap ( )
95
68
. build ( )
96
69
. unwrap ( ) ;
97
70
98
- let text_layout = paint_ctx. text ( ) . new_text_layout ( & font, label) . unwrap ( ) . build ( ) . unwrap ( ) ;
71
+ let text_layout = paint_ctx
72
+ . text ( )
73
+ . new_text_layout ( & font, label)
74
+ . unwrap ( )
75
+ . build ( )
76
+ . unwrap ( ) ;
99
77
100
78
let mut origin = UnitPoint :: LEFT . resolve ( Rect :: from_origin_size (
101
79
Point :: ORIGIN ,
@@ -105,28 +83,64 @@ impl Widget<bool> for SwitchRaw {
105
83
) ,
106
84
) ) ;
107
85
108
- //Make sure we don't draw the text too low
109
- origin. y = origin. y . min ( base_state. size ( ) . height ) + 4. ;
86
+ // adjust label position
87
+ origin. y = origin. y . min ( base_state. size ( ) . height ) ;
110
88
111
89
if * data {
112
- origin. x = 8 .
90
+ origin. x = switch_padding * 2 .
113
91
} else {
114
- origin. x = switch_thickness * 2.5 - text_layout. width ( ) - 8 .
92
+ origin. x = switch_width - text_layout. width ( ) - switch_padding * 2 .
115
93
}
116
94
117
95
paint_ctx. draw_text ( & text_layout, origin, & env. get ( theme:: LABEL_COLOR ) ) ;
96
+ }
97
+ }
98
+
99
+ impl Widget < bool > for SwitchRaw {
100
+ fn paint ( & mut self , paint_ctx : & mut PaintCtx , base_state : & BaseState , data : & bool , env : & Env ) {
101
+ let switch_padding = 3. ;
102
+ let switch_height = env. get ( theme:: BORDERED_WIDGET_HEIGHT ) ;
103
+ let switch_width = switch_height * 2.75 ;
104
+ let knob_size = switch_height - 2. * switch_padding;
105
+
106
+ let background_rect = RoundedRect :: from_origin_size (
107
+ Point :: ORIGIN ,
108
+ Size :: new ( switch_width, switch_height) . to_vec2 ( ) ,
109
+ switch_height / 2. ,
110
+ ) ;
111
+
112
+ // paint different background for on and off state
113
+ let background_gradient = if * data {
114
+ LinearGradient :: new (
115
+ UnitPoint :: TOP ,
116
+ UnitPoint :: BOTTOM ,
117
+ ( env. get ( theme:: PRIMARY_LIGHT ) , env. get ( theme:: PRIMARY_DARK ) ) ,
118
+ )
119
+ } else {
120
+ LinearGradient :: new (
121
+ UnitPoint :: TOP ,
122
+ UnitPoint :: BOTTOM ,
123
+ (
124
+ env. get ( theme:: BACKGROUND_LIGHT ) ,
125
+ env. get ( theme:: BACKGROUND_DARK ) ,
126
+ ) ,
127
+ )
128
+ } ;
118
129
130
+ paint_ctx. stroke ( background_rect, & env. get ( theme:: BORDER ) , 2.0 ) ;
131
+ paint_ctx. fill ( background_rect, & background_gradient) ;
119
132
133
+ // paint the knob
120
134
let is_active = base_state. is_active ( ) ;
121
135
let is_hovered = self . knob_hovered ;
122
136
123
137
let knob_position = if * data {
124
- switch_thickness * 2.5 - knob_size / 2. - 4.
138
+ switch_width - knob_size / 2. - switch_padding
125
139
} else {
126
140
knob_size / 2. + 4.
127
141
} ;
128
142
129
- self . knob_pos = Point :: new ( knob_position, knob_size / 2. + 4. ) ;
143
+ self . knob_pos = Point :: new ( knob_position, knob_size / 2. + switch_padding ) ;
130
144
let knob_circle = Circle :: new ( self . knob_pos , knob_size / 2. ) ;
131
145
132
146
let normal_knob_gradient = LinearGradient :: new (
@@ -152,16 +166,25 @@ impl Widget<bool> for SwitchRaw {
152
166
normal_knob_gradient
153
167
} ;
154
168
155
- //Paint the border
169
+ // paint the border
156
170
let border_color = if is_hovered || is_active {
157
171
env. get ( theme:: FOREGROUND_LIGHT )
158
172
} else {
159
173
env. get ( theme:: FOREGROUND_DARK )
160
174
} ;
161
175
162
176
paint_ctx. stroke ( knob_circle, & border_color, 2. ) ;
163
-
164
177
paint_ctx. fill ( knob_circle, & knob_gradient) ;
178
+
179
+ // paint on/off label
180
+ self . paint_label (
181
+ paint_ctx,
182
+ base_state,
183
+ data,
184
+ env,
185
+ switch_width,
186
+ switch_padding,
187
+ ) ;
165
188
}
166
189
167
190
fn layout (
@@ -171,15 +194,12 @@ impl Widget<bool> for SwitchRaw {
171
194
_data : & bool ,
172
195
env : & Env ,
173
196
) -> Size {
174
- let width = ( 8. + env. get ( theme:: BASIC_WIDGET_HEIGHT ) ) * 2.5 ;
175
- bc. constrain ( Size :: new (
176
- width,
177
- env. get ( theme:: BASIC_WIDGET_HEIGHT ) ,
178
- ) )
197
+ let width = ( 6. + env. get ( theme:: BORDERED_WIDGET_HEIGHT ) ) * 2.75 ;
198
+ bc. constrain ( Size :: new ( width, env. get ( theme:: BORDERED_WIDGET_HEIGHT ) ) )
179
199
}
180
200
181
201
fn event ( & mut self , event : & Event , ctx : & mut EventCtx , data : & mut bool , env : & Env ) {
182
- let knob_size = env. get ( theme:: BASIC_WIDGET_HEIGHT ) ;
202
+ let knob_size = env. get ( theme:: BORDERED_WIDGET_HEIGHT ) ;
183
203
184
204
match event {
185
205
Event :: MouseDown ( _) => {
@@ -201,7 +221,7 @@ impl Widget<bool> for SwitchRaw {
201
221
}
202
222
Event :: MouseMoved ( mouse) => {
203
223
if ctx. is_active ( ) {
204
- // todo
224
+ // todo: animate dragging of knob
205
225
}
206
226
if ctx. is_hot ( ) {
207
227
if self . knob_hit_test ( knob_size, mouse. pos ) {
@@ -219,4 +239,4 @@ impl Widget<bool> for SwitchRaw {
219
239
fn update ( & mut self , ctx : & mut UpdateCtx , _old_data : Option < & bool > , _data : & bool , _env : & Env ) {
220
240
ctx. invalidate ( ) ;
221
241
}
222
- }
242
+ }
0 commit comments