15
15
//! A stepper widget.
16
16
17
17
use crate :: {
18
- BaseState , BoxConstraints , Data , Env , Event , EventCtx , LayoutCtx , PaintCtx , Size , UpdateCtx ,
19
- Widget ,
18
+ BaseState , BoxConstraints , Data , Env , Event , EventCtx , LayoutCtx , PaintCtx , Size , TimerToken ,
19
+ UpdateCtx , Widget ,
20
20
} ;
21
+ use std:: time:: { Duration , Instant } ;
21
22
22
23
use crate :: kurbo:: { BezPath , Rect , RoundedRect } ;
23
24
use crate :: piet:: {
@@ -28,8 +29,6 @@ use crate::theme;
28
29
use crate :: widget:: { Align , Label , LabelText , SizedBox } ;
29
30
use crate :: Point ;
30
31
31
- const STEPPER_WIDTH : f64 = 15. ;
32
-
33
32
/// A stepper.
34
33
pub struct Stepper {
35
34
max : f64 ,
@@ -38,15 +37,18 @@ pub struct Stepper {
38
37
wrap : bool ,
39
38
/// A closure that will be invoked when the value changed.
40
39
value_changed : Box < dyn Fn ( & mut EventCtx , & mut f64 , & Env ) > ,
40
+ /// Keeps track of which button is currently triggered.
41
41
increase_active : bool ,
42
42
decrease_active : bool ,
43
+ timer_id : TimerToken ,
43
44
}
44
45
45
46
impl Stepper {
46
47
pub fn new (
47
48
max : f64 ,
48
49
min : f64 ,
49
50
step : f64 ,
51
+ wrap : bool ,
50
52
value_changed : impl Fn ( & mut EventCtx , & mut f64 , & Env ) + ' static ,
51
53
) -> impl Widget < f64 > {
52
54
Align :: vertical (
@@ -55,27 +57,38 @@ impl Stepper {
55
57
max,
56
58
min,
57
59
step,
58
- wrap : false ,
60
+ wrap,
59
61
value_changed : Box :: new ( value_changed) ,
60
62
increase_active : false ,
61
63
decrease_active : false ,
64
+ timer_id : TimerToken :: INVALID ,
62
65
} ,
63
66
)
64
67
}
65
68
66
- pub fn min ( mut self , min : f64 ) -> Self {
67
- self . min = min;
68
- self
69
- }
69
+ fn change_value ( & mut self , ctx : & mut EventCtx , data : & mut f64 , env : & Env ) {
70
+ let delta = if self . increase_active {
71
+ self . step
72
+ } else if self . decrease_active {
73
+ -1. * self . step
74
+ } else {
75
+ 0.0
76
+ } ;
70
77
71
- pub fn max ( mut self , max : f64 ) -> Self {
72
- self . max = max;
73
- self
74
- }
78
+ let old_data = * data;
79
+ * data = ( * data + delta) . min ( self . min ) . max ( self . max ) ;
75
80
76
- pub fn wrap ( mut self , wrap : bool ) -> Self {
77
- self . wrap = wrap;
78
- self
81
+ if old_data != * data {
82
+ ( self . value_changed ) ( ctx, data, env) ;
83
+ } else {
84
+ if self . wrap {
85
+ if * data == self . min {
86
+ * data = self . max
87
+ } else {
88
+ * data = self . min
89
+ }
90
+ }
91
+ }
79
92
}
80
93
}
81
94
@@ -85,8 +98,13 @@ impl Widget<f64> for Stepper {
85
98
RoundedRect :: from_origin_size ( Point :: ORIGIN , base_state. size ( ) . to_vec2 ( ) , 4. ) ;
86
99
87
100
let height = base_state. size ( ) . height ;
88
- let button_size = Size :: new ( STEPPER_WIDTH , height / 2. ) ;
101
+ let width = env. get ( theme:: BASIC_WIDGET_HEIGHT ) ;
102
+ let button_size = Size :: new ( width, height / 2. ) ;
89
103
104
+ paint_ctx. stroke ( rounded_rect, & env. get ( theme:: BORDER ) , 2.0 ) ;
105
+ paint_ctx. clip ( rounded_rect) ;
106
+
107
+ // draw buttons for increase/decrease
90
108
let mut increase_button_origin = Point :: ORIGIN ;
91
109
let mut decrease_button_origin = Point :: ORIGIN ;
92
110
decrease_button_origin. y += height / 2. ;
@@ -106,9 +124,7 @@ impl Widget<f64> for Stepper {
106
124
( env. get ( theme:: BUTTON_DARK ) , env. get ( theme:: BUTTON_LIGHT ) ) ,
107
125
) ;
108
126
109
- paint_ctx. stroke ( rounded_rect, & env. get ( theme:: BORDER ) , 2.0 ) ;
110
- paint_ctx. clip ( rounded_rect) ;
111
-
127
+ // draw buttons that are currently triggered as active
112
128
if self . increase_active {
113
129
paint_ctx. fill ( increase_rect, & active_gradient) ;
114
130
} else {
@@ -121,17 +137,18 @@ impl Widget<f64> for Stepper {
121
137
paint_ctx. fill ( decrease_rect, & inactive_gradient) ;
122
138
} ;
123
139
140
+ // draw up and down triangles
124
141
let mut increase_arrow = BezPath :: new ( ) ;
125
142
increase_arrow. move_to ( Point :: new ( 4. , height / 2. - 4. ) ) ;
126
- increase_arrow. line_to ( Point :: new ( STEPPER_WIDTH - 4. , height / 2. - 4. ) ) ;
127
- increase_arrow. line_to ( Point :: new ( STEPPER_WIDTH / 2. , 4. ) ) ;
143
+ increase_arrow. line_to ( Point :: new ( width - 4. , height / 2. - 4. ) ) ;
144
+ increase_arrow. line_to ( Point :: new ( width / 2. , 4. ) ) ;
128
145
increase_arrow. close_path ( ) ;
129
146
paint_ctx. fill ( increase_arrow, & env. get ( theme:: LABEL_COLOR ) ) ;
130
147
131
148
let mut decrease_arrow = BezPath :: new ( ) ;
132
149
decrease_arrow. move_to ( Point :: new ( 4. , height / 2. + 4. ) ) ;
133
- decrease_arrow. line_to ( Point :: new ( STEPPER_WIDTH - 4. , height / 2. + 4. ) ) ;
134
- decrease_arrow. line_to ( Point :: new ( STEPPER_WIDTH / 2. , height - 4. ) ) ;
150
+ decrease_arrow. line_to ( Point :: new ( width - 4. , height / 2. + 4. ) ) ;
151
+ decrease_arrow. line_to ( Point :: new ( width / 2. , height - 4. ) ) ;
135
152
decrease_arrow. close_path ( ) ;
136
153
paint_ctx. fill ( decrease_arrow, & env. get ( theme:: LABEL_COLOR ) ) ;
137
154
}
@@ -144,7 +161,7 @@ impl Widget<f64> for Stepper {
144
161
env : & Env ,
145
162
) -> Size {
146
163
bc. constrain ( Size :: new (
147
- STEPPER_WIDTH ,
164
+ env . get ( theme :: BASIC_WIDGET_HEIGHT ) ,
148
165
env. get ( theme:: BORDERED_WIDGET_HEIGHT ) ,
149
166
) )
150
167
}
@@ -162,7 +179,10 @@ impl Widget<f64> for Stepper {
162
179
self . increase_active = true ;
163
180
}
164
181
165
- // todo: increase/decrease value
182
+ self . change_value ( ctx, data, env) ;
183
+
184
+ let delay = Instant :: now ( ) + Duration :: from_millis ( 500 ) ;
185
+ self . timer_id = ctx. request_timer ( delay) ;
166
186
167
187
ctx. invalidate ( ) ;
168
188
}
@@ -171,9 +191,15 @@ impl Widget<f64> for Stepper {
171
191
172
192
self . decrease_active = false ;
173
193
self . increase_active = false ;
194
+ self . timer_id = TimerToken :: INVALID ;
174
195
175
196
ctx. invalidate ( ) ;
176
197
}
198
+ Event :: Timer ( id) if * id == self . timer_id => {
199
+ self . change_value ( ctx, data, env) ;
200
+ let delay = Instant :: now ( ) + Duration :: from_millis ( 200 ) ;
201
+ self . timer_id = ctx. request_timer ( delay) ;
202
+ }
177
203
_ => ( ) ,
178
204
}
179
205
}
0 commit comments