Struct druid::EventCtx

source ·
pub struct EventCtx<'a, 'b> { /* private fields */ }
Expand description

A mutable context provided to event handling methods of widgets.

Widgets should call request_paint whenever an event causes a change in the widget’s appearance, to schedule a repaint.

Implementations§

source§

impl EventCtx<'_, '_>

source

pub fn widget_id(&self) -> WidgetId

get the WidgetId of the current widget.

source

pub fn window(&self) -> &WindowHandle

Returns a reference to the current WindowHandle.

Examples found in repository?
examples/transparency.rs (line 43)
34
35
36
37
38
39
40
41
42
43
44
45
    fn event(
        &mut self,
        _child: &mut W,
        ctx: &mut EventCtx,
        event: &Event,
        _data: &mut T,
        _env: &Env,
    ) {
        if let Event::MouseMove(_) = event {
            ctx.window().handle_titlebar(true);
        }
    }
More examples
Hide additional examples
examples/input_region.rs (line 64)
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
    pub fn new() -> Self {
        let info_label = Label::new(INFO_TEXT)
            .with_line_break_mode(LineBreaking::WordWrap)
            .padding(20.0)
            .background(Color::rgba(0.2, 0.2, 0.2, 1.0));
        let toggle_input_region = Button::new("Toggle Input Region")
            .on_click(|ctx, data: &mut bool, _: &Env| {
                *data = !*data;
                tracing::debug!("Setting input region toggle to: {}", *data);
                ctx.request_layout();
            })
            .lens(AppState::limit_input_region);
        let toggle_titlebar = Button::new("Toggle TitleBar")
            .on_click(|ctx, data: &mut bool, _: &Env| {
                *data = !*data;
                tracing::debug!("Setting titlebar visibility to: {}", *data);
                ctx.window().show_titlebar(*data);
                ctx.request_layout();
            })
            .lens(AppState::show_titlebar);
        let toggle_always_on_top = Button::new("Toggle Always On Top")
            .on_click(|ctx, data: &mut bool, _: &Env| {
                *data = !*data;
                tracing::debug!("Setting always on top to: {}", *data);
                ctx.window().set_always_on_top(*data);
            })
            .lens(AppState::always_on_top);
        let controls_flex = Flex::row()
            .with_child(toggle_input_region)
            .with_child(toggle_titlebar)
            .with_child(toggle_always_on_top);
        Self {
            info_label: WidgetPod::new(info_label),
            controls: WidgetPod::new(controls_flex),
        }
    }
examples/sub_window.rs (line 153)
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
    fn event(&mut self, child: &mut W, ctx: &mut EventCtx, event: &Event, data: &mut T, env: &Env) {
        let wait_duration = Duration::from_millis(500);
        let resched_dur = Duration::from_millis(50);
        let cursor_size = Size::new(15., 15.);
        let now = Instant::now();
        let new_state = match &self.state {
            TooltipState::Fresh => match event {
                Event::MouseMove(me) if ctx.is_hot() => Some(TooltipState::Waiting {
                    last_move: now,
                    timer_expire: now + wait_duration,
                    token: ctx.request_timer(wait_duration),
                    position_in_window_coordinates: me.window_pos,
                }),
                _ => None,
            },
            TooltipState::Waiting {
                last_move,
                timer_expire,
                token,
                position_in_window_coordinates,
            } => match event {
                Event::MouseMove(me) if ctx.is_hot() => {
                    let (cur_token, cur_expire) = if *timer_expire - now < resched_dur {
                        (ctx.request_timer(wait_duration), now + wait_duration)
                    } else {
                        (*token, *timer_expire)
                    };
                    Some(TooltipState::Waiting {
                        last_move: now,
                        timer_expire: cur_expire,
                        token: cur_token,
                        position_in_window_coordinates: me.window_pos,
                    })
                }
                Event::Timer(tok) if tok == token => {
                    let deadline = *last_move + wait_duration;
                    ctx.set_handled();
                    if deadline > now {
                        let wait_for = deadline - now;
                        tracing::info!("Waiting another {:?}", wait_for);
                        Some(TooltipState::Waiting {
                            last_move: *last_move,
                            timer_expire: deadline,
                            token: ctx.request_timer(wait_for),
                            position_in_window_coordinates: *position_in_window_coordinates,
                        })
                    } else {
                        let tooltip_position_in_window_coordinates =
                            (position_in_window_coordinates.to_vec2() + cursor_size.to_vec2())
                                .to_point();
                        let win_id = ctx.new_sub_window(
                            WindowConfig::default()
                                .show_titlebar(false)
                                .window_size_policy(WindowSizePolicy::Content)
                                .set_level(WindowLevel::Tooltip(ctx.window().clone()))
                                .set_position(tooltip_position_in_window_coordinates),
                            Label::<()>::new(self.tip.clone()),
                            (),
                            env.clone(),
                        );
                        Some(TooltipState::Showing(win_id))
                    }
                }
                _ => None,
            },
            TooltipState::Showing(win_id) => {
                match event {
                    Event::MouseMove(me) if !ctx.is_hot() => {
                        // TODO another timer on leaving
                        tracing::info!("Sending close window for {:?}", win_id);
                        ctx.submit_command(CLOSE_WINDOW.to(*win_id));
                        Some(TooltipState::Waiting {
                            last_move: now,
                            timer_expire: now + wait_duration,
                            token: ctx.request_timer(wait_duration),
                            position_in_window_coordinates: me.window_pos,
                        })
                    }
                    _ => None,
                }
            }
        };

        if let Some(state) = new_state {
            self.state = state;
        }

        if !ctx.is_handled() {
            child.event(ctx, event, data, env);
        }
    }

    fn lifecycle(
        &mut self,
        child: &mut W,
        ctx: &mut LifeCycleCtx,
        event: &LifeCycle,
        data: &T,
        env: &Env,
    ) {
        if let LifeCycle::HotChanged(false) = event {
            if let TooltipState::Showing(win_id) = self.state {
                ctx.submit_command(CLOSE_WINDOW.to(win_id));
            }
            self.state = TooltipState::Fresh;
        }
        child.lifecycle(ctx, event, data, env)
    }
}

struct DragWindowController {
    init_pos: Option<Point>,
    //dragging: bool
}

impl DragWindowController {
    pub fn new() -> Self {
        DragWindowController { init_pos: None }
    }
}

impl<T, W: Widget<T>> Controller<T, W> for DragWindowController {
    fn event(&mut self, child: &mut W, ctx: &mut EventCtx, event: &Event, data: &mut T, env: &Env) {
        match event {
            Event::MouseDown(me) if me.buttons.has_left() => {
                ctx.set_active(true);
                self.init_pos = Some(me.window_pos)
            }
            Event::MouseMove(me) if ctx.is_active() && me.buttons.has_left() => {
                if let Some(init_pos) = self.init_pos {
                    let within_window_change = me.window_pos.to_vec2() - init_pos.to_vec2();
                    let old_pos = ctx.window().get_position();
                    let new_pos = old_pos + within_window_change;
                    tracing::info!(
                        "Drag {:?} ",
                        (
                            init_pos,
                            me.window_pos,
                            within_window_change,
                            old_pos,
                            new_pos
                        )
                    );
                    ctx.window().set_position(new_pos)
                }
            }
            Event::MouseUp(_me) if ctx.is_active() => {
                self.init_pos = None;
                ctx.set_active(false)
            }
            _ => (),
        }
        child.event(ctx, event, data, env)
    }
source

pub fn window_id(&self) -> WindowId

Get the WindowId of the current window.

source

pub fn text(&mut self) -> &mut PietText

Get an object which can create text layouts.

source

pub fn scale(&self) -> Scale

The current window’s Scale.

The returned Scale is a copy and thus its information will be stale after the platform changes the window’s scale. This means you can only rely on it until the next Event::WindowScale event happens.

source§

impl EventCtx<'_, '_>

source

pub fn size(&self) -> Size

The layout size.

This is the layout size as ultimately determined by the parent container, on the previous layout pass.

Generally it will be the same as the size returned by the child widget’s layout method.

Examples found in repository?
examples/timer.rs (line 59)
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
    fn event(&mut self, ctx: &mut EventCtx, event: &Event, data: &mut u32, env: &Env) {
        match event {
            Event::WindowConnected => {
                // Start the timer when the application launches
                self.timer_id = ctx.request_timer(TIMER_INTERVAL);
            }
            Event::Timer(id) => {
                if *id == self.timer_id {
                    self.adjust_box_pos(ctx.size());
                    ctx.request_layout();
                    self.timer_id = ctx.request_timer(TIMER_INTERVAL);
                }
            }
            _ => (),
        }
        self.simple_box.event(ctx, event, data, env);
    }
source

pub fn window_origin(&self) -> Point

The origin of the widget in window coordinates, relative to the top left corner of the content area.

source

pub fn to_window(&self, widget_point: Point) -> Point

Convert a point from the widget’s coordinate space to the window’s.

The returned point is relative to the content area; it excludes window chrome.

source

pub fn to_screen(&self, widget_point: Point) -> Point

Convert a point from the widget’s coordinate space to the screen’s. See the Screen module

source

pub fn is_hot(&self) -> bool

Query the “hot” state of the widget.

See WidgetPod::is_hot for additional information.

Examples found in repository?
examples/sub_window.rs (line 106)
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
    fn event(&mut self, child: &mut W, ctx: &mut EventCtx, event: &Event, data: &mut T, env: &Env) {
        let wait_duration = Duration::from_millis(500);
        let resched_dur = Duration::from_millis(50);
        let cursor_size = Size::new(15., 15.);
        let now = Instant::now();
        let new_state = match &self.state {
            TooltipState::Fresh => match event {
                Event::MouseMove(me) if ctx.is_hot() => Some(TooltipState::Waiting {
                    last_move: now,
                    timer_expire: now + wait_duration,
                    token: ctx.request_timer(wait_duration),
                    position_in_window_coordinates: me.window_pos,
                }),
                _ => None,
            },
            TooltipState::Waiting {
                last_move,
                timer_expire,
                token,
                position_in_window_coordinates,
            } => match event {
                Event::MouseMove(me) if ctx.is_hot() => {
                    let (cur_token, cur_expire) = if *timer_expire - now < resched_dur {
                        (ctx.request_timer(wait_duration), now + wait_duration)
                    } else {
                        (*token, *timer_expire)
                    };
                    Some(TooltipState::Waiting {
                        last_move: now,
                        timer_expire: cur_expire,
                        token: cur_token,
                        position_in_window_coordinates: me.window_pos,
                    })
                }
                Event::Timer(tok) if tok == token => {
                    let deadline = *last_move + wait_duration;
                    ctx.set_handled();
                    if deadline > now {
                        let wait_for = deadline - now;
                        tracing::info!("Waiting another {:?}", wait_for);
                        Some(TooltipState::Waiting {
                            last_move: *last_move,
                            timer_expire: deadline,
                            token: ctx.request_timer(wait_for),
                            position_in_window_coordinates: *position_in_window_coordinates,
                        })
                    } else {
                        let tooltip_position_in_window_coordinates =
                            (position_in_window_coordinates.to_vec2() + cursor_size.to_vec2())
                                .to_point();
                        let win_id = ctx.new_sub_window(
                            WindowConfig::default()
                                .show_titlebar(false)
                                .window_size_policy(WindowSizePolicy::Content)
                                .set_level(WindowLevel::Tooltip(ctx.window().clone()))
                                .set_position(tooltip_position_in_window_coordinates),
                            Label::<()>::new(self.tip.clone()),
                            (),
                            env.clone(),
                        );
                        Some(TooltipState::Showing(win_id))
                    }
                }
                _ => None,
            },
            TooltipState::Showing(win_id) => {
                match event {
                    Event::MouseMove(me) if !ctx.is_hot() => {
                        // TODO another timer on leaving
                        tracing::info!("Sending close window for {:?}", win_id);
                        ctx.submit_command(CLOSE_WINDOW.to(*win_id));
                        Some(TooltipState::Waiting {
                            last_move: now,
                            timer_expire: now + wait_duration,
                            token: ctx.request_timer(wait_duration),
                            position_in_window_coordinates: me.window_pos,
                        })
                    }
                    _ => None,
                }
            }
        };

        if let Some(state) = new_state {
            self.state = state;
        }

        if !ctx.is_handled() {
            child.event(ctx, event, data, env);
        }
    }
source

pub fn is_active(&self) -> bool

Query the “active” state of the widget.

See WidgetPod::is_active for additional information.

Examples found in repository?
examples/sub_window.rs (line 227)
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
    fn event(&mut self, child: &mut W, ctx: &mut EventCtx, event: &Event, data: &mut T, env: &Env) {
        match event {
            Event::MouseDown(me) if me.buttons.has_left() => {
                ctx.set_active(true);
                self.init_pos = Some(me.window_pos)
            }
            Event::MouseMove(me) if ctx.is_active() && me.buttons.has_left() => {
                if let Some(init_pos) = self.init_pos {
                    let within_window_change = me.window_pos.to_vec2() - init_pos.to_vec2();
                    let old_pos = ctx.window().get_position();
                    let new_pos = old_pos + within_window_change;
                    tracing::info!(
                        "Drag {:?} ",
                        (
                            init_pos,
                            me.window_pos,
                            within_window_change,
                            old_pos,
                            new_pos
                        )
                    );
                    ctx.window().set_position(new_pos)
                }
            }
            Event::MouseUp(_me) if ctx.is_active() => {
                self.init_pos = None;
                ctx.set_active(false)
            }
            _ => (),
        }
        child.event(ctx, event, data, env)
    }
source

pub fn is_focused(&self) -> bool

The focus status of a widget.

Returns true if this specific widget is focused. To check if any descendants are focused use has_focus.

Focus means that the widget receives keyboard events.

A widget can request focus using the request_focus method. It’s also possible to register for automatic focus via register_for_focus.

If a widget gains or loses focus it will get a LifeCycle::FocusChanged event.

Only one widget at a time is focused. However due to the way events are routed, all ancestors of that widget will also receive keyboard events.

source

pub fn has_focus(&self) -> bool

The (tree) focus status of a widget.

Returns true if either this specific widget or any one of its descendants is focused. To check if only this specific widget is focused use is_focused,

source

pub fn is_disabled(&self) -> bool

The disabled state of a widget.

Returns true if this widget or any of its ancestors is explicitly disabled. To make this widget explicitly disabled use set_disabled.

Disabled means that this widget should not change the state of the application. What that means is not entirely clear but in any it should not change its data. Therefore others can use this as a safety mechanism to prevent the application from entering an illegal state. For an example the decrease button of a counter of type usize should be disabled if the value is 0.

source§

impl EventCtx<'_, '_>

source

pub fn set_cursor(&mut self, cursor: &Cursor)

Set the cursor icon.

This setting will be retained until clear_cursor is called, but it will only take effect when this widget is either hot or active. If a child widget also sets a cursor, the child widget’s cursor will take precedence. (If that isn’t what you want, use override_cursor instead.)

source

pub fn override_cursor(&mut self, cursor: &Cursor)

Override the cursor icon.

This setting will be retained until clear_cursor is called, but it will only take effect when this widget is either hot or active. This will override the cursor preferences of a child widget. (If that isn’t what you want, use set_cursor instead.)

source

pub fn clear_cursor(&mut self)

Clear the cursor icon.

This undoes the effect of set_cursor and override_cursor.

source§

impl EventCtx<'_, '_>

source

pub fn view_context_changed(&mut self)

Indicate that your ViewContext has changed.

This event is sent after layout is done and before paint is called. Note that you can still receive this event even if there was no prior call to layout.

Widgets must call this method after changing the clip region of their children. Changes to the other parts of ViewContext (cursor position and global origin) are tracked internally.

source§

impl EventCtx<'_, '_>

source

pub fn request_paint(&mut self)

Request a paint pass. This is equivalent to calling request_paint_rect for the widget’s paint_rect.

Examples found in repository?
examples/anim.rs (line 45)
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
    fn event(&mut self, ctx: &mut EventCtx, event: &Event, _data: &mut (), _env: &Env) {
        match event {
            Event::MouseDown(_) => {
                self.t = 0.0;
                ctx.request_anim_frame();
            }
            Event::AnimFrame(interval) => {
                ctx.request_paint();
                self.t += (*interval as f64) * 1e-9;
                if self.t < 1.0 {
                    ctx.request_anim_frame();
                } else {
                    // We might have t>1.0 at the end of the animation,
                    // we want to make sure the line points up at the
                    // end of the animation.
                    self.t = 0.0;
                }
            }
            _ => (),
        }
    }
More examples
Hide additional examples
examples/game_of_life.rs (line 220)
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
    fn event(&mut self, ctx: &mut EventCtx, event: &Event, data: &mut AppData, _env: &Env) {
        match event {
            Event::WindowConnected => {
                ctx.request_paint();
                let deadline = Duration::from_millis(data.iter_interval());
                self.last_update = Instant::now();
                self.timer_id = ctx.request_timer(deadline);
            }
            Event::Timer(id) => {
                if *id == self.timer_id {
                    if !data.paused {
                        data.grid.evolve();
                        ctx.request_paint();
                    }
                    let deadline = Duration::from_millis(data.iter_interval());
                    self.last_update = Instant::now();
                    self.timer_id = ctx.request_timer(deadline);
                }
            }
            Event::MouseDown(e) => {
                if e.button == MouseButton::Left {
                    data.drawing = true;
                    let grid_pos_opt = self.grid_pos(e.pos);
                    grid_pos_opt
                        .iter()
                        .for_each(|pos| data.grid[*pos] = !data.grid[*pos]);
                }
            }
            Event::MouseUp(e) => {
                if e.button == MouseButton::Left {
                    data.drawing = false;
                }
            }
            Event::MouseMove(e) => {
                if data.drawing {
                    if let Some(grid_pos_opt) = self.grid_pos(e.pos) {
                        data.grid[grid_pos_opt] = true
                    }
                }
            }
            _ => {}
        }
    }

    fn lifecycle(
        &mut self,
        _ctx: &mut LifeCycleCtx,
        _event: &LifeCycle,
        _data: &AppData,
        _env: &Env,
    ) {
    }

    fn update(&mut self, ctx: &mut UpdateCtx, old_data: &AppData, data: &AppData, _env: &Env) {
        if (data.updates_per_second - old_data.updates_per_second).abs() > 0.001 {
            let deadline = Duration::from_millis(data.iter_interval())
                .checked_sub(Instant::now().duration_since(self.last_update))
                .unwrap_or_else(|| Duration::from_secs(0));
            self.timer_id = ctx.request_timer(deadline);
        }
        if data.grid != old_data.grid {
            ctx.request_paint();
        }
    }

    fn layout(
        &mut self,
        _layout_ctx: &mut LayoutCtx,
        bc: &BoxConstraints,
        _data: &AppData,
        _env: &Env,
    ) -> Size {
        let max_size = bc.max();
        let min_side = max_size.height.min(max_size.width);
        Size {
            width: min_side,
            height: min_side,
        }
    }

    fn paint(&mut self, ctx: &mut PaintCtx, data: &AppData, _env: &Env) {
        let size: Size = ctx.size();
        let w0 = size.width / GRID_SIZE as f64;
        let h0 = size.height / GRID_SIZE as f64;
        let cell_size = Size {
            width: w0,
            height: h0,
        };
        self.cell_size = cell_size;
        for row in 0..GRID_SIZE {
            for col in 0..GRID_SIZE {
                let pos = GridPos { row, col };
                if data.grid[pos] {
                    let point = Point {
                        x: w0 * row as f64,
                        y: h0 * col as f64,
                    };
                    let rect = Rect::from_origin_size(point, cell_size);

                    // We divide by 2 so that the colour changes every 2 positions instead of every 1
                    ctx.fill(
                        rect,
                        &COLOURS[((pos.row * GRID_SIZE + pos.col) / 2) % COLOURS.len()],
                    );
                }
            }
        }
    }
}

// gives back positions of a glider pattern
// _____
// |  *|
// |* *|
// | **|
// ‾‾‾‾‾
fn glider(left_most: GridPos) -> Option<[GridPos; 5]> {
    let center = left_most.right()?;
    Some([
        left_most,
        center.below()?.right()?,
        center.below()?,
        center.right()?,
        center.above()?.right()?,
    ])
}

// gives back positions of a blinker pattern
// ___
// |*|
// |*|
// |*|
// ‾‾‾
fn blinker(top: GridPos) -> Option<[GridPos; 3]> {
    let center = top.below()?;
    Some([top, center, center.below()?])
}

fn make_widget() -> impl Widget<AppData> {
    Flex::column()
        .with_flex_child(
            GameOfLifeWidget {
                timer_id: TimerToken::INVALID,
                cell_size: Size {
                    width: 0.0,
                    height: 0.0,
                },
                last_update: Instant::now(),
            },
            1.0,
        )
        .with_child(
            Flex::column()
                .with_child(
                    // a row with two buttons
                    Flex::row()
                        .with_flex_child(
                            // pause / resume button
                            Button::new(|data: &bool, _: &Env| match data {
                                true => "Resume",
                                false => "Pause",
                            })
                            .on_click(|ctx, data: &mut bool, _: &Env| {
                                *data = !*data;
                                ctx.request_layout();
                            })
                            .lens(AppData::paused)
                            .padding((5., 5.)),
                            1.0,
                        )
                        .with_flex_child(
                            // clear button
                            Button::new("Clear")
                                .on_click(|ctx, data: &mut Grid, _: &Env| {
                                    data.clear();
                                    ctx.request_paint();
                                })
                                .lens(AppData::grid)
                                .padding((5., 5.)),
                            1.0,
                        )
                        .padding(8.0),
                )
                .with_child(
                    Flex::row()
                        .with_child(
                            Label::new(|data: &AppData, _env: &_| {
                                format!("{:.2}updates/s", data.updates_per_second)
                            })
                            .padding(3.0),
                        )
                        .with_flex_child(
                            Slider::new()
                                .with_range(0.2, 20.0)
                                .expand_width()
                                .lens(AppData::updates_per_second),
                            1.,
                        )
                        .padding(8.0),
                )
                .background(BACKGROUND),
        )
}
source

pub fn request_paint_rect(&mut self, rect: Rect)

Request a paint pass for redrawing a rectangle, which is given relative to our layout rectangle.

Examples found in repository?
examples/invalidation.rs (line 82)
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
    fn event(&mut self, ctx: &mut EventCtx, ev: &Event, data: &mut Vector<Circle>, _env: &Env) {
        if let Event::MouseDown(ev) = ev {
            if ev.mods.shift() {
                data.push_back(Circle {
                    pos: ev.pos,
                    time: Instant::now(),
                });
            } else if ev.mods.ctrl() {
                data.retain(|c| {
                    if (c.pos - ev.pos).hypot() > RADIUS {
                        true
                    } else {
                        ctx.request_paint_rect(kurbo::Circle::new(c.pos, RADIUS).bounding_box());
                        false
                    }
                });
            } else {
                // Move the circle to a new location, invalidating the old locations. The new location
                // will be invalidated during AnimFrame.
                for c in data.iter() {
                    ctx.request_paint_rect(kurbo::Circle::new(c.pos, RADIUS).bounding_box());
                }
                data.clear();
                data.push_back(Circle {
                    pos: ev.pos,
                    time: Instant::now(),
                });
            }
            ctx.request_anim_frame();
        } else if let Event::AnimFrame(_) = ev {
            for c in &*data {
                ctx.request_paint_rect(kurbo::Circle::new(c.pos, RADIUS).bounding_box());
            }
            if !data.is_empty() {
                ctx.request_anim_frame();
            }
        }
    }
source

pub fn request_layout(&mut self)

Request a layout pass.

A Widget’s layout method is always called when the widget tree changes, or the window is resized.

If your widget would like to have layout called at any other time, (such as if it would like to change the layout of children in response to some event) it must call this method.

Examples found in repository?
examples/timer.rs (line 60)
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
    fn event(&mut self, ctx: &mut EventCtx, event: &Event, data: &mut u32, env: &Env) {
        match event {
            Event::WindowConnected => {
                // Start the timer when the application launches
                self.timer_id = ctx.request_timer(TIMER_INTERVAL);
            }
            Event::Timer(id) => {
                if *id == self.timer_id {
                    self.adjust_box_pos(ctx.size());
                    ctx.request_layout();
                    self.timer_id = ctx.request_timer(TIMER_INTERVAL);
                }
            }
            _ => (),
        }
        self.simple_box.event(ctx, event, data, env);
    }
More examples
Hide additional examples
examples/input_region.rs (line 57)
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
    pub fn new() -> Self {
        let info_label = Label::new(INFO_TEXT)
            .with_line_break_mode(LineBreaking::WordWrap)
            .padding(20.0)
            .background(Color::rgba(0.2, 0.2, 0.2, 1.0));
        let toggle_input_region = Button::new("Toggle Input Region")
            .on_click(|ctx, data: &mut bool, _: &Env| {
                *data = !*data;
                tracing::debug!("Setting input region toggle to: {}", *data);
                ctx.request_layout();
            })
            .lens(AppState::limit_input_region);
        let toggle_titlebar = Button::new("Toggle TitleBar")
            .on_click(|ctx, data: &mut bool, _: &Env| {
                *data = !*data;
                tracing::debug!("Setting titlebar visibility to: {}", *data);
                ctx.window().show_titlebar(*data);
                ctx.request_layout();
            })
            .lens(AppState::show_titlebar);
        let toggle_always_on_top = Button::new("Toggle Always On Top")
            .on_click(|ctx, data: &mut bool, _: &Env| {
                *data = !*data;
                tracing::debug!("Setting always on top to: {}", *data);
                ctx.window().set_always_on_top(*data);
            })
            .lens(AppState::always_on_top);
        let controls_flex = Flex::row()
            .with_child(toggle_input_region)
            .with_child(toggle_titlebar)
            .with_child(toggle_always_on_top);
        Self {
            info_label: WidgetPod::new(info_label),
            controls: WidgetPod::new(controls_flex),
        }
    }
examples/game_of_life.rs (line 381)
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
fn make_widget() -> impl Widget<AppData> {
    Flex::column()
        .with_flex_child(
            GameOfLifeWidget {
                timer_id: TimerToken::INVALID,
                cell_size: Size {
                    width: 0.0,
                    height: 0.0,
                },
                last_update: Instant::now(),
            },
            1.0,
        )
        .with_child(
            Flex::column()
                .with_child(
                    // a row with two buttons
                    Flex::row()
                        .with_flex_child(
                            // pause / resume button
                            Button::new(|data: &bool, _: &Env| match data {
                                true => "Resume",
                                false => "Pause",
                            })
                            .on_click(|ctx, data: &mut bool, _: &Env| {
                                *data = !*data;
                                ctx.request_layout();
                            })
                            .lens(AppData::paused)
                            .padding((5., 5.)),
                            1.0,
                        )
                        .with_flex_child(
                            // clear button
                            Button::new("Clear")
                                .on_click(|ctx, data: &mut Grid, _: &Env| {
                                    data.clear();
                                    ctx.request_paint();
                                })
                                .lens(AppData::grid)
                                .padding((5., 5.)),
                            1.0,
                        )
                        .padding(8.0),
                )
                .with_child(
                    Flex::row()
                        .with_child(
                            Label::new(|data: &AppData, _env: &_| {
                                format!("{:.2}updates/s", data.updates_per_second)
                            })
                            .padding(3.0),
                        )
                        .with_flex_child(
                            Slider::new()
                                .with_range(0.2, 20.0)
                                .expand_width()
                                .lens(AppData::updates_per_second),
                            1.,
                        )
                        .padding(8.0),
                )
                .background(BACKGROUND),
        )
}
source

pub fn request_anim_frame(&mut self)

Request an AnimFrame event.

Receiving AnimFrame does not inherently mean a paint invocation will follow. If you want something actually painted you need to explicitly call request_paint or request_paint_rect when handling the AnimFrame event.

Note that not requesting paint when handling the AnimFrame event and then recursively requesting another AnimFrame can lead to rapid event fire, which is probably not what you want and would most likely be wasted compute cycles.

Examples found in repository?
examples/anim.rs (line 42)
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
    fn event(&mut self, ctx: &mut EventCtx, event: &Event, _data: &mut (), _env: &Env) {
        match event {
            Event::MouseDown(_) => {
                self.t = 0.0;
                ctx.request_anim_frame();
            }
            Event::AnimFrame(interval) => {
                ctx.request_paint();
                self.t += (*interval as f64) * 1e-9;
                if self.t < 1.0 {
                    ctx.request_anim_frame();
                } else {
                    // We might have t>1.0 at the end of the animation,
                    // we want to make sure the line points up at the
                    // end of the animation.
                    self.t = 0.0;
                }
            }
            _ => (),
        }
    }
More examples
Hide additional examples
examples/invalidation.rs (line 98)
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
    fn event(&mut self, ctx: &mut EventCtx, ev: &Event, data: &mut Vector<Circle>, _env: &Env) {
        if let Event::MouseDown(ev) = ev {
            if ev.mods.shift() {
                data.push_back(Circle {
                    pos: ev.pos,
                    time: Instant::now(),
                });
            } else if ev.mods.ctrl() {
                data.retain(|c| {
                    if (c.pos - ev.pos).hypot() > RADIUS {
                        true
                    } else {
                        ctx.request_paint_rect(kurbo::Circle::new(c.pos, RADIUS).bounding_box());
                        false
                    }
                });
            } else {
                // Move the circle to a new location, invalidating the old locations. The new location
                // will be invalidated during AnimFrame.
                for c in data.iter() {
                    ctx.request_paint_rect(kurbo::Circle::new(c.pos, RADIUS).bounding_box());
                }
                data.clear();
                data.push_back(Circle {
                    pos: ev.pos,
                    time: Instant::now(),
                });
            }
            ctx.request_anim_frame();
        } else if let Event::AnimFrame(_) = ev {
            for c in &*data {
                ctx.request_paint_rect(kurbo::Circle::new(c.pos, RADIUS).bounding_box());
            }
            if !data.is_empty() {
                ctx.request_anim_frame();
            }
        }
    }
source

pub fn children_changed(&mut self)

Indicate that your children have changed.

Widgets must call this method after adding a new child, removing a child or changing which children are hidden (see should_propagate_to_hidden).

source

pub fn set_disabled(&mut self, disabled: bool)

Set the disabled state for this widget.

Setting this to false does not mean a widget is not still disabled; for instance it may still be disabled by an ancestor. See is_disabled for more information.

Calling this method during LifeCycle::DisabledChanged has no effect.

source

pub fn invalidate_text_input(&mut self, event: ImeInvalidation)

Indicate that text input state has changed.

A widget that accepts text input should call this anytime input state (such as the text or the selection) changes as a result of a non text-input event.

source

pub fn new_sub_window<W: Widget<U> + 'static, U: Data>( &mut self, window_config: WindowConfig, widget: W, data: U, env: Env ) -> WindowId

Create a new sub-window.

The sub-window will have its app data synchronised with caller’s nearest ancestor WidgetPod. ‘U’ must be the type of the nearest surrounding WidgetPod. The ‘data’ argument should be the current value of data for that widget.

Examples found in repository?
examples/sub_window.rs (lines 149-158)
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
    fn event(&mut self, child: &mut W, ctx: &mut EventCtx, event: &Event, data: &mut T, env: &Env) {
        let wait_duration = Duration::from_millis(500);
        let resched_dur = Duration::from_millis(50);
        let cursor_size = Size::new(15., 15.);
        let now = Instant::now();
        let new_state = match &self.state {
            TooltipState::Fresh => match event {
                Event::MouseMove(me) if ctx.is_hot() => Some(TooltipState::Waiting {
                    last_move: now,
                    timer_expire: now + wait_duration,
                    token: ctx.request_timer(wait_duration),
                    position_in_window_coordinates: me.window_pos,
                }),
                _ => None,
            },
            TooltipState::Waiting {
                last_move,
                timer_expire,
                token,
                position_in_window_coordinates,
            } => match event {
                Event::MouseMove(me) if ctx.is_hot() => {
                    let (cur_token, cur_expire) = if *timer_expire - now < resched_dur {
                        (ctx.request_timer(wait_duration), now + wait_duration)
                    } else {
                        (*token, *timer_expire)
                    };
                    Some(TooltipState::Waiting {
                        last_move: now,
                        timer_expire: cur_expire,
                        token: cur_token,
                        position_in_window_coordinates: me.window_pos,
                    })
                }
                Event::Timer(tok) if tok == token => {
                    let deadline = *last_move + wait_duration;
                    ctx.set_handled();
                    if deadline > now {
                        let wait_for = deadline - now;
                        tracing::info!("Waiting another {:?}", wait_for);
                        Some(TooltipState::Waiting {
                            last_move: *last_move,
                            timer_expire: deadline,
                            token: ctx.request_timer(wait_for),
                            position_in_window_coordinates: *position_in_window_coordinates,
                        })
                    } else {
                        let tooltip_position_in_window_coordinates =
                            (position_in_window_coordinates.to_vec2() + cursor_size.to_vec2())
                                .to_point();
                        let win_id = ctx.new_sub_window(
                            WindowConfig::default()
                                .show_titlebar(false)
                                .window_size_policy(WindowSizePolicy::Content)
                                .set_level(WindowLevel::Tooltip(ctx.window().clone()))
                                .set_position(tooltip_position_in_window_coordinates),
                            Label::<()>::new(self.tip.clone()),
                            (),
                            env.clone(),
                        );
                        Some(TooltipState::Showing(win_id))
                    }
                }
                _ => None,
            },
            TooltipState::Showing(win_id) => {
                match event {
                    Event::MouseMove(me) if !ctx.is_hot() => {
                        // TODO another timer on leaving
                        tracing::info!("Sending close window for {:?}", win_id);
                        ctx.submit_command(CLOSE_WINDOW.to(*win_id));
                        Some(TooltipState::Waiting {
                            last_move: now,
                            timer_expire: now + wait_duration,
                            token: ctx.request_timer(wait_duration),
                            position_in_window_coordinates: me.window_pos,
                        })
                    }
                    _ => None,
                }
            }
        };

        if let Some(state) = new_state {
            self.state = state;
        }

        if !ctx.is_handled() {
            child.event(ctx, event, data, env);
        }
    }

    fn lifecycle(
        &mut self,
        child: &mut W,
        ctx: &mut LifeCycleCtx,
        event: &LifeCycle,
        data: &T,
        env: &Env,
    ) {
        if let LifeCycle::HotChanged(false) = event {
            if let TooltipState::Showing(win_id) = self.state {
                ctx.submit_command(CLOSE_WINDOW.to(win_id));
            }
            self.state = TooltipState::Fresh;
        }
        child.lifecycle(ctx, event, data, env)
    }
}

struct DragWindowController {
    init_pos: Option<Point>,
    //dragging: bool
}

impl DragWindowController {
    pub fn new() -> Self {
        DragWindowController { init_pos: None }
    }
}

impl<T, W: Widget<T>> Controller<T, W> for DragWindowController {
    fn event(&mut self, child: &mut W, ctx: &mut EventCtx, event: &Event, data: &mut T, env: &Env) {
        match event {
            Event::MouseDown(me) if me.buttons.has_left() => {
                ctx.set_active(true);
                self.init_pos = Some(me.window_pos)
            }
            Event::MouseMove(me) if ctx.is_active() && me.buttons.has_left() => {
                if let Some(init_pos) = self.init_pos {
                    let within_window_change = me.window_pos.to_vec2() - init_pos.to_vec2();
                    let old_pos = ctx.window().get_position();
                    let new_pos = old_pos + within_window_change;
                    tracing::info!(
                        "Drag {:?} ",
                        (
                            init_pos,
                            me.window_pos,
                            within_window_change,
                            old_pos,
                            new_pos
                        )
                    );
                    ctx.window().set_position(new_pos)
                }
            }
            Event::MouseUp(_me) if ctx.is_active() => {
                self.init_pos = None;
                ctx.set_active(false)
            }
            _ => (),
        }
        child.event(ctx, event, data, env)
    }
}

struct ScreenThing;

impl Widget<()> for ScreenThing {
    fn event(&mut self, _ctx: &mut EventCtx, _event: &Event, _data: &mut (), _env: &Env) {}

    fn lifecycle(&mut self, _ctx: &mut LifeCycleCtx, _event: &LifeCycle, _data: &(), _env: &Env) {}

    fn update(&mut self, _ctx: &mut UpdateCtx, _old_data: &(), _data: &(), _env: &Env) {}

    fn layout(
        &mut self,
        _ctx: &mut LayoutCtx,
        bc: &BoxConstraints,
        _data: &(),
        _env: &Env,
    ) -> Size {
        bc.constrain(Size::new(800.0, 600.0))
    }

    fn paint(&mut self, ctx: &mut PaintCtx, _data: &(), env: &Env) {
        let sz = ctx.size();

        let monitors = Screen::get_monitors();
        let all = monitors
            .iter()
            .map(|x| x.virtual_rect())
            .fold(Rect::ZERO, |s, r| r.union(s));
        if all.width() > 0. && all.height() > 0. {
            let trans = Affine::scale(f64::min(sz.width / all.width(), sz.height / all.height()))
                * Affine::translate(all.origin().to_vec2()).inverse();
            let font = env.get(theme::UI_FONT).family;

            for (i, mon) in monitors.iter().enumerate() {
                let vr = mon.virtual_rect();
                let tr = trans.transform_rect_bbox(vr);
                ctx.stroke(tr, &Color::WHITE, 1.0);

                if let Ok(tl) = ctx
                    .text()
                    .new_text_layout(format!(
                        "{}:{}x{}@{},{}",
                        i,
                        vr.width(),
                        vr.height(),
                        vr.x0,
                        vr.y0
                    ))
                    .max_width(tr.width() - 5.)
                    .font(font.clone(), env.get(theme::TEXT_SIZE_NORMAL))
                    .text_color(Color::WHITE)
                    .build()
                {
                    ctx.draw_text(&tl, tr.center() - tl.size().to_vec2() * 0.5);
                }
            }
        }
    }
}

struct CancelClose;

impl<W: Widget<bool>> Controller<bool, W> for CancelClose {
    fn event(
        &mut self,
        w: &mut W,
        ctx: &mut EventCtx<'_, '_>,
        event: &Event,
        data: &mut bool,
        env: &Env,
    ) {
        match (&data, event) {
            (false, Event::WindowCloseRequested) => ctx.set_handled(),
            _ => w.event(ctx, event, data, env),
        }
    }
}

fn build_root_widget() -> impl Widget<HelloState> {
    let label = EnvScope::new(
        |env, _t| env.set(theme::TEXT_COLOR, env.get(theme::PRIMARY_LIGHT)),
        ControllerHost::new(
            Label::new(|data: &HelloState, _env: &Env| {
                format!("Hello {}! {} ", data.name, data.sub.my_stuff)
            }),
            TooltipController::new("Tips! Are good"),
        ),
    );
    // a textbox that modifies `name`.
    let textbox = TextBox::new()
        .with_placeholder("Who are we greeting?")
        .fix_width(TEXT_BOX_WIDTH)
        .lens(HelloState::sub.then(SubState::my_stuff));

    let button = Button::new("Make sub window")
        .on_click(|ctx, data: &mut SubState, env| {
            let tb = TextBox::new().lens(SubState::my_stuff);
            let drag_thing = Label::new("Drag me").controller(DragWindowController::new());
            let col = Flex::column().with_child(drag_thing).with_child(tb);

            ctx.new_sub_window(
                WindowConfig::default()
                    .show_titlebar(false)
                    .window_size(Size::new(100., 100.))
                    .set_level(WindowLevel::AppWindow),
                col,
                data.clone(),
                env.clone(),
            );
        })
        .center()
        .lens(HelloState::sub);

    let check_box =
        ControllerHost::new(Checkbox::new("Closeable?"), CancelClose).lens(HelloState::closeable);
    // arrange the two widgets vertically, with some padding
    let layout = Flex::column()
        .with_child(label)
        .with_flex_child(ScreenThing.lens(Unit::default()).padding(5.), 1.)
        .with_spacer(VERTICAL_WIDGET_SPACING)
        .with_child(textbox)
        .with_child(button)
        .with_child(check_box);

    // center the two widgets in the available space
    Align::centered(layout)
}
source

pub fn scroll_to_view(&mut self)

Scrolls this widget into view.

If this widget is only partially visible or not visible at all because of Scrolls it is wrapped in, they will do the minimum amount of scrolling necessary to bring this widget fully into view.

If the widget is hidden, this method has no effect.

This functionality is achieved by sending a SCROLL_TO_VIEW notification.

source§

impl EventCtx<'_, '_>

source

pub fn submit_command(&mut self, cmd: impl Into<Command>)

Submit a Command to be run after this event is handled.

Commands are run in the order they are submitted; all commands submitted during the handling of an event are executed before the update method is called; events submitted during update are handled after painting.

Target::Auto commands will be sent to the window containing the widget.

Examples found in repository?
examples/multiwin.rs (line 60)
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
fn ui_builder() -> impl Widget<State> {
    let text = LocalizedString::new("hello-counter")
        .with_arg("count", |data: &State, _env| data.menu_count.into());
    let label = Label::new(text);
    let inc_button =
        Button::<State>::new("Add menu item").on_click(|_ctx, data, _env| data.menu_count += 1);
    let dec_button = Button::<State>::new("Remove menu item")
        .on_click(|_ctx, data, _env| data.menu_count = data.menu_count.saturating_sub(1));
    let new_button = Button::<State>::new("New window").on_click(|ctx, _data, _env| {
        ctx.submit_command(sys_cmds::NEW_FILE.to(Global));
    });
    let quit_button = Button::<State>::new("Quit app").on_click(|_ctx, _data, _env| {
        Application::global().quit();
    });

    let mut col = Flex::column();
    col.add_flex_child(Align::centered(Padding::new(5.0, label)), 1.0);
    let mut row = Flex::row();
    row.add_child(Padding::new(5.0, inc_button));
    row.add_child(Padding::new(5.0, dec_button));
    col.add_flex_child(Align::centered(row), 1.0);
    let mut row = Flex::row();
    row.add_child(Padding::new(5.0, new_button));
    row.add_child(Padding::new(5.0, quit_button));
    col.add_flex_child(Align::centered(row), 1.0);
    let content = ControllerHost::new(col, ContextMenuController);
    Glow::new(content)
}
More examples
Hide additional examples
examples/open_save.rs (line 62)
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
fn ui_builder() -> impl Widget<String> {
    let rs = FileSpec::new("Rust source", &["rs"]);
    let txt = FileSpec::new("Text file", &["txt"]);
    let other = FileSpec::new("Bogus file", &["foo", "bar", "baz"]);
    // The options can also be generated at runtime,
    // so to show that off we create a String for the default save name.
    let default_save_name = String::from("MyFile.txt");
    let save_dialog_options = FileDialogOptions::new()
        .allowed_types(vec![rs, txt, other])
        .default_type(txt)
        .default_name(default_save_name)
        .name_label("Target")
        .title("Choose a target for this lovely file")
        .button_text("Export");
    let open_dialog_options = save_dialog_options
        .clone()
        .default_name("MySavedFile.txt")
        .name_label("Source")
        .title("Where did you put that file?")
        .button_text("Import");

    let input = TextBox::new();
    let save = Button::new("Save").on_click(move |ctx, _, _| {
        ctx.submit_command(druid::commands::SHOW_SAVE_PANEL.with(save_dialog_options.clone()))
    });
    let open = Button::new("Open").on_click(move |ctx, _, _| {
        ctx.submit_command(druid::commands::SHOW_OPEN_PANEL.with(open_dialog_options.clone()))
    });

    let mut col = Flex::column();
    col.add_child(input);
    col.add_spacer(8.0);
    col.add_child(save);
    col.add_child(open);
    Align::centered(col)
}
examples/identity.rs (line 88)
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
fn make_ui() -> impl Widget<OurData> {
    // We can also generate these dynamically whenever we need it.
    let id_two = WidgetId::next();
    // We have a column with 2 labels and 2 buttons.
    // Each of the 2 labels only has access to its own counter and is given a `WidgetId`.
    // Both labels have a controller, this handles commands send to children.
    // The 2 buttons send a command when clicked. Both send the exact same command.
    // The key diference is that they both give a different `WidgetId` as target.
    // This means that only the corresponding controller gets the command, and increments their counter.
    Flex::column()
        .with_child(
            Label::dynamic(|data, _| format!("One: {data}"))
                .controller(LabelControler)
                .with_id(ID_ONE)
                .lens(OurData::counter_one)
                .padding(2.0),
        )
        .with_child(
            Label::dynamic(|data, _| format!("Two: {data}"))
                .controller(LabelControler)
                .with_id(id_two)
                .lens(OurData::counter_two)
                .padding(2.0),
        )
        .with_child(
            Button::<OurData>::new("Increment one")
                .on_click(|ctx, _data, _env| ctx.submit_command(INCREMENT.to(ID_ONE)))
                .padding(2.0),
        )
        .with_child(
            Button::<OurData>::new("Increment two")
                .on_click(move |ctx, _data, _env| ctx.submit_command(INCREMENT.to(id_two)))
                .padding(2.0),
        )
        .padding(10.0)
}
examples/sub_window.rs (line 169)
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
    fn event(&mut self, child: &mut W, ctx: &mut EventCtx, event: &Event, data: &mut T, env: &Env) {
        let wait_duration = Duration::from_millis(500);
        let resched_dur = Duration::from_millis(50);
        let cursor_size = Size::new(15., 15.);
        let now = Instant::now();
        let new_state = match &self.state {
            TooltipState::Fresh => match event {
                Event::MouseMove(me) if ctx.is_hot() => Some(TooltipState::Waiting {
                    last_move: now,
                    timer_expire: now + wait_duration,
                    token: ctx.request_timer(wait_duration),
                    position_in_window_coordinates: me.window_pos,
                }),
                _ => None,
            },
            TooltipState::Waiting {
                last_move,
                timer_expire,
                token,
                position_in_window_coordinates,
            } => match event {
                Event::MouseMove(me) if ctx.is_hot() => {
                    let (cur_token, cur_expire) = if *timer_expire - now < resched_dur {
                        (ctx.request_timer(wait_duration), now + wait_duration)
                    } else {
                        (*token, *timer_expire)
                    };
                    Some(TooltipState::Waiting {
                        last_move: now,
                        timer_expire: cur_expire,
                        token: cur_token,
                        position_in_window_coordinates: me.window_pos,
                    })
                }
                Event::Timer(tok) if tok == token => {
                    let deadline = *last_move + wait_duration;
                    ctx.set_handled();
                    if deadline > now {
                        let wait_for = deadline - now;
                        tracing::info!("Waiting another {:?}", wait_for);
                        Some(TooltipState::Waiting {
                            last_move: *last_move,
                            timer_expire: deadline,
                            token: ctx.request_timer(wait_for),
                            position_in_window_coordinates: *position_in_window_coordinates,
                        })
                    } else {
                        let tooltip_position_in_window_coordinates =
                            (position_in_window_coordinates.to_vec2() + cursor_size.to_vec2())
                                .to_point();
                        let win_id = ctx.new_sub_window(
                            WindowConfig::default()
                                .show_titlebar(false)
                                .window_size_policy(WindowSizePolicy::Content)
                                .set_level(WindowLevel::Tooltip(ctx.window().clone()))
                                .set_position(tooltip_position_in_window_coordinates),
                            Label::<()>::new(self.tip.clone()),
                            (),
                            env.clone(),
                        );
                        Some(TooltipState::Showing(win_id))
                    }
                }
                _ => None,
            },
            TooltipState::Showing(win_id) => {
                match event {
                    Event::MouseMove(me) if !ctx.is_hot() => {
                        // TODO another timer on leaving
                        tracing::info!("Sending close window for {:?}", win_id);
                        ctx.submit_command(CLOSE_WINDOW.to(*win_id));
                        Some(TooltipState::Waiting {
                            last_move: now,
                            timer_expire: now + wait_duration,
                            token: ctx.request_timer(wait_duration),
                            position_in_window_coordinates: me.window_pos,
                        })
                    }
                    _ => None,
                }
            }
        };

        if let Some(state) = new_state {
            self.state = state;
        }

        if !ctx.is_handled() {
            child.event(ctx, event, data, env);
        }
    }
source

pub fn get_external_handle(&self) -> ExtEventSink

Returns an ExtEventSink that can be moved between threads, and can be used to submit commands back to the application.

Examples found in repository?
examples/blocking_function.rs (line 46)
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
fn ui_builder() -> impl Widget<AppState> {
    let button = Button::new("Start slow increment")
        .on_click(|ctx, data: &mut AppState, _env| {
            data.processing = true;
            // In order to make sure that the other thread can communicate with the main thread we
            // have to pass an external handle to the second thread.
            // Using this handle we can send commands back to the main thread.
            wrapped_slow_function(ctx.get_external_handle(), data.value);
        })
        .padding(5.0);

    let button_placeholder = Flex::column()
        .with_child(Label::new(LocalizedString::new("Processing...")).padding(5.0))
        .with_child(Spinner::new());

    // Hello-counter is defined in the built-in localisation file. This maps to "Current value is {count}"
    // localised in english, french, or german. Every time the value is updated it shows the new value.
    let text = LocalizedString::new("hello-counter")
        .with_arg("count", |data: &AppState, _env| (data.value).into());
    let label = Label::new(text).padding(5.0).center();

    let either = Either::new(|data, _env| data.processing, button_placeholder, button);

    Flex::column().with_child(label).with_child(either)
}
source

pub fn request_timer(&mut self, deadline: Duration) -> TimerToken

Request a timer event.

The return value is a token, which can be used to associate the request with the event.

Examples found in repository?
examples/timer.rs (line 55)
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
    fn event(&mut self, ctx: &mut EventCtx, event: &Event, data: &mut u32, env: &Env) {
        match event {
            Event::WindowConnected => {
                // Start the timer when the application launches
                self.timer_id = ctx.request_timer(TIMER_INTERVAL);
            }
            Event::Timer(id) => {
                if *id == self.timer_id {
                    self.adjust_box_pos(ctx.size());
                    ctx.request_layout();
                    self.timer_id = ctx.request_timer(TIMER_INTERVAL);
                }
            }
            _ => (),
        }
        self.simple_box.event(ctx, event, data, env);
    }
More examples
Hide additional examples
examples/game_of_life.rs (line 223)
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
    fn event(&mut self, ctx: &mut EventCtx, event: &Event, data: &mut AppData, _env: &Env) {
        match event {
            Event::WindowConnected => {
                ctx.request_paint();
                let deadline = Duration::from_millis(data.iter_interval());
                self.last_update = Instant::now();
                self.timer_id = ctx.request_timer(deadline);
            }
            Event::Timer(id) => {
                if *id == self.timer_id {
                    if !data.paused {
                        data.grid.evolve();
                        ctx.request_paint();
                    }
                    let deadline = Duration::from_millis(data.iter_interval());
                    self.last_update = Instant::now();
                    self.timer_id = ctx.request_timer(deadline);
                }
            }
            Event::MouseDown(e) => {
                if e.button == MouseButton::Left {
                    data.drawing = true;
                    let grid_pos_opt = self.grid_pos(e.pos);
                    grid_pos_opt
                        .iter()
                        .for_each(|pos| data.grid[*pos] = !data.grid[*pos]);
                }
            }
            Event::MouseUp(e) => {
                if e.button == MouseButton::Left {
                    data.drawing = false;
                }
            }
            Event::MouseMove(e) => {
                if data.drawing {
                    if let Some(grid_pos_opt) = self.grid_pos(e.pos) {
                        data.grid[grid_pos_opt] = true
                    }
                }
            }
            _ => {}
        }
    }
examples/sub_window.rs (line 109)
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
    fn event(&mut self, child: &mut W, ctx: &mut EventCtx, event: &Event, data: &mut T, env: &Env) {
        let wait_duration = Duration::from_millis(500);
        let resched_dur = Duration::from_millis(50);
        let cursor_size = Size::new(15., 15.);
        let now = Instant::now();
        let new_state = match &self.state {
            TooltipState::Fresh => match event {
                Event::MouseMove(me) if ctx.is_hot() => Some(TooltipState::Waiting {
                    last_move: now,
                    timer_expire: now + wait_duration,
                    token: ctx.request_timer(wait_duration),
                    position_in_window_coordinates: me.window_pos,
                }),
                _ => None,
            },
            TooltipState::Waiting {
                last_move,
                timer_expire,
                token,
                position_in_window_coordinates,
            } => match event {
                Event::MouseMove(me) if ctx.is_hot() => {
                    let (cur_token, cur_expire) = if *timer_expire - now < resched_dur {
                        (ctx.request_timer(wait_duration), now + wait_duration)
                    } else {
                        (*token, *timer_expire)
                    };
                    Some(TooltipState::Waiting {
                        last_move: now,
                        timer_expire: cur_expire,
                        token: cur_token,
                        position_in_window_coordinates: me.window_pos,
                    })
                }
                Event::Timer(tok) if tok == token => {
                    let deadline = *last_move + wait_duration;
                    ctx.set_handled();
                    if deadline > now {
                        let wait_for = deadline - now;
                        tracing::info!("Waiting another {:?}", wait_for);
                        Some(TooltipState::Waiting {
                            last_move: *last_move,
                            timer_expire: deadline,
                            token: ctx.request_timer(wait_for),
                            position_in_window_coordinates: *position_in_window_coordinates,
                        })
                    } else {
                        let tooltip_position_in_window_coordinates =
                            (position_in_window_coordinates.to_vec2() + cursor_size.to_vec2())
                                .to_point();
                        let win_id = ctx.new_sub_window(
                            WindowConfig::default()
                                .show_titlebar(false)
                                .window_size_policy(WindowSizePolicy::Content)
                                .set_level(WindowLevel::Tooltip(ctx.window().clone()))
                                .set_position(tooltip_position_in_window_coordinates),
                            Label::<()>::new(self.tip.clone()),
                            (),
                            env.clone(),
                        );
                        Some(TooltipState::Showing(win_id))
                    }
                }
                _ => None,
            },
            TooltipState::Showing(win_id) => {
                match event {
                    Event::MouseMove(me) if !ctx.is_hot() => {
                        // TODO another timer on leaving
                        tracing::info!("Sending close window for {:?}", win_id);
                        ctx.submit_command(CLOSE_WINDOW.to(*win_id));
                        Some(TooltipState::Waiting {
                            last_move: now,
                            timer_expire: now + wait_duration,
                            token: ctx.request_timer(wait_duration),
                            position_in_window_coordinates: me.window_pos,
                        })
                    }
                    _ => None,
                }
            }
        };

        if let Some(state) = new_state {
            self.state = state;
        }

        if !ctx.is_handled() {
            child.event(ctx, event, data, env);
        }
    }
source§

impl EventCtx<'_, '_>

source

pub fn submit_notification(&mut self, note: impl Into<Command>)

Submit a Notification.

The provided argument can be a Selector or a Command; this lets us work with the existing API for adding a payload to a Selector.

If the argument is a Command, the command’s target will be ignored.

Examples
const IMPORTANT_EVENT: Selector<String> = Selector::new("druid-example.important-event");

fn check_event(ctx: &mut EventCtx, event: &Event) {
    if is_this_the_event_we_were_looking_for(event) {
        ctx.submit_notification(IMPORTANT_EVENT.with("That's the one".to_string()))
    }
}
source

pub fn submit_notification_without_warning(&mut self, note: impl Into<Command>)

Submit a Notification without warning.

In contrast to submit_notification, calling this method will not result in an “unhandled notification” warning.

source

pub fn set_active(&mut self, active: bool)

Set the “active” state of the widget.

See EventCtx::is_active.

Examples found in repository?
examples/sub_window.rs (line 224)
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
    fn event(&mut self, child: &mut W, ctx: &mut EventCtx, event: &Event, data: &mut T, env: &Env) {
        match event {
            Event::MouseDown(me) if me.buttons.has_left() => {
                ctx.set_active(true);
                self.init_pos = Some(me.window_pos)
            }
            Event::MouseMove(me) if ctx.is_active() && me.buttons.has_left() => {
                if let Some(init_pos) = self.init_pos {
                    let within_window_change = me.window_pos.to_vec2() - init_pos.to_vec2();
                    let old_pos = ctx.window().get_position();
                    let new_pos = old_pos + within_window_change;
                    tracing::info!(
                        "Drag {:?} ",
                        (
                            init_pos,
                            me.window_pos,
                            within_window_change,
                            old_pos,
                            new_pos
                        )
                    );
                    ctx.window().set_position(new_pos)
                }
            }
            Event::MouseUp(_me) if ctx.is_active() => {
                self.init_pos = None;
                ctx.set_active(false)
            }
            _ => (),
        }
        child.event(ctx, event, data, env)
    }
source

pub fn new_window<T: Any>(&mut self, desc: WindowDesc<T>)

Create a new window. T must be the application’s root Data type (the type provided to AppLauncher::launch).

source

pub fn show_context_menu<T: Any>(&mut self, menu: Menu<T>, location: Point)

Show the context menu in the window containing the current widget. T must be the application’s root Data type (the type provided to AppLauncher::launch).

Examples found in repository?
examples/multiwin.rs (line 143)
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
    fn event(
        &mut self,
        child: &mut W,
        ctx: &mut EventCtx,
        event: &Event,
        data: &mut State,
        env: &Env,
    ) {
        match event {
            Event::MouseDown(ref mouse) if mouse.button.is_right() => {
                ctx.show_context_menu(make_context_menu(), mouse.pos);
            }
            _ => child.event(ctx, event, data, env),
        }
    }
source

pub fn set_handled(&mut self)

Set the event as “handled”, which stops its propagation to other widgets.

Examples found in repository?
examples/sub_window.rs (line 135)
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
    fn event(&mut self, child: &mut W, ctx: &mut EventCtx, event: &Event, data: &mut T, env: &Env) {
        let wait_duration = Duration::from_millis(500);
        let resched_dur = Duration::from_millis(50);
        let cursor_size = Size::new(15., 15.);
        let now = Instant::now();
        let new_state = match &self.state {
            TooltipState::Fresh => match event {
                Event::MouseMove(me) if ctx.is_hot() => Some(TooltipState::Waiting {
                    last_move: now,
                    timer_expire: now + wait_duration,
                    token: ctx.request_timer(wait_duration),
                    position_in_window_coordinates: me.window_pos,
                }),
                _ => None,
            },
            TooltipState::Waiting {
                last_move,
                timer_expire,
                token,
                position_in_window_coordinates,
            } => match event {
                Event::MouseMove(me) if ctx.is_hot() => {
                    let (cur_token, cur_expire) = if *timer_expire - now < resched_dur {
                        (ctx.request_timer(wait_duration), now + wait_duration)
                    } else {
                        (*token, *timer_expire)
                    };
                    Some(TooltipState::Waiting {
                        last_move: now,
                        timer_expire: cur_expire,
                        token: cur_token,
                        position_in_window_coordinates: me.window_pos,
                    })
                }
                Event::Timer(tok) if tok == token => {
                    let deadline = *last_move + wait_duration;
                    ctx.set_handled();
                    if deadline > now {
                        let wait_for = deadline - now;
                        tracing::info!("Waiting another {:?}", wait_for);
                        Some(TooltipState::Waiting {
                            last_move: *last_move,
                            timer_expire: deadline,
                            token: ctx.request_timer(wait_for),
                            position_in_window_coordinates: *position_in_window_coordinates,
                        })
                    } else {
                        let tooltip_position_in_window_coordinates =
                            (position_in_window_coordinates.to_vec2() + cursor_size.to_vec2())
                                .to_point();
                        let win_id = ctx.new_sub_window(
                            WindowConfig::default()
                                .show_titlebar(false)
                                .window_size_policy(WindowSizePolicy::Content)
                                .set_level(WindowLevel::Tooltip(ctx.window().clone()))
                                .set_position(tooltip_position_in_window_coordinates),
                            Label::<()>::new(self.tip.clone()),
                            (),
                            env.clone(),
                        );
                        Some(TooltipState::Showing(win_id))
                    }
                }
                _ => None,
            },
            TooltipState::Showing(win_id) => {
                match event {
                    Event::MouseMove(me) if !ctx.is_hot() => {
                        // TODO another timer on leaving
                        tracing::info!("Sending close window for {:?}", win_id);
                        ctx.submit_command(CLOSE_WINDOW.to(*win_id));
                        Some(TooltipState::Waiting {
                            last_move: now,
                            timer_expire: now + wait_duration,
                            token: ctx.request_timer(wait_duration),
                            position_in_window_coordinates: me.window_pos,
                        })
                    }
                    _ => None,
                }
            }
        };

        if let Some(state) = new_state {
            self.state = state;
        }

        if !ctx.is_handled() {
            child.event(ctx, event, data, env);
        }
    }

    fn lifecycle(
        &mut self,
        child: &mut W,
        ctx: &mut LifeCycleCtx,
        event: &LifeCycle,
        data: &T,
        env: &Env,
    ) {
        if let LifeCycle::HotChanged(false) = event {
            if let TooltipState::Showing(win_id) = self.state {
                ctx.submit_command(CLOSE_WINDOW.to(win_id));
            }
            self.state = TooltipState::Fresh;
        }
        child.lifecycle(ctx, event, data, env)
    }
}

struct DragWindowController {
    init_pos: Option<Point>,
    //dragging: bool
}

impl DragWindowController {
    pub fn new() -> Self {
        DragWindowController { init_pos: None }
    }
}

impl<T, W: Widget<T>> Controller<T, W> for DragWindowController {
    fn event(&mut self, child: &mut W, ctx: &mut EventCtx, event: &Event, data: &mut T, env: &Env) {
        match event {
            Event::MouseDown(me) if me.buttons.has_left() => {
                ctx.set_active(true);
                self.init_pos = Some(me.window_pos)
            }
            Event::MouseMove(me) if ctx.is_active() && me.buttons.has_left() => {
                if let Some(init_pos) = self.init_pos {
                    let within_window_change = me.window_pos.to_vec2() - init_pos.to_vec2();
                    let old_pos = ctx.window().get_position();
                    let new_pos = old_pos + within_window_change;
                    tracing::info!(
                        "Drag {:?} ",
                        (
                            init_pos,
                            me.window_pos,
                            within_window_change,
                            old_pos,
                            new_pos
                        )
                    );
                    ctx.window().set_position(new_pos)
                }
            }
            Event::MouseUp(_me) if ctx.is_active() => {
                self.init_pos = None;
                ctx.set_active(false)
            }
            _ => (),
        }
        child.event(ctx, event, data, env)
    }
}

struct ScreenThing;

impl Widget<()> for ScreenThing {
    fn event(&mut self, _ctx: &mut EventCtx, _event: &Event, _data: &mut (), _env: &Env) {}

    fn lifecycle(&mut self, _ctx: &mut LifeCycleCtx, _event: &LifeCycle, _data: &(), _env: &Env) {}

    fn update(&mut self, _ctx: &mut UpdateCtx, _old_data: &(), _data: &(), _env: &Env) {}

    fn layout(
        &mut self,
        _ctx: &mut LayoutCtx,
        bc: &BoxConstraints,
        _data: &(),
        _env: &Env,
    ) -> Size {
        bc.constrain(Size::new(800.0, 600.0))
    }

    fn paint(&mut self, ctx: &mut PaintCtx, _data: &(), env: &Env) {
        let sz = ctx.size();

        let monitors = Screen::get_monitors();
        let all = monitors
            .iter()
            .map(|x| x.virtual_rect())
            .fold(Rect::ZERO, |s, r| r.union(s));
        if all.width() > 0. && all.height() > 0. {
            let trans = Affine::scale(f64::min(sz.width / all.width(), sz.height / all.height()))
                * Affine::translate(all.origin().to_vec2()).inverse();
            let font = env.get(theme::UI_FONT).family;

            for (i, mon) in monitors.iter().enumerate() {
                let vr = mon.virtual_rect();
                let tr = trans.transform_rect_bbox(vr);
                ctx.stroke(tr, &Color::WHITE, 1.0);

                if let Ok(tl) = ctx
                    .text()
                    .new_text_layout(format!(
                        "{}:{}x{}@{},{}",
                        i,
                        vr.width(),
                        vr.height(),
                        vr.x0,
                        vr.y0
                    ))
                    .max_width(tr.width() - 5.)
                    .font(font.clone(), env.get(theme::TEXT_SIZE_NORMAL))
                    .text_color(Color::WHITE)
                    .build()
                {
                    ctx.draw_text(&tl, tr.center() - tl.size().to_vec2() * 0.5);
                }
            }
        }
    }
}

struct CancelClose;

impl<W: Widget<bool>> Controller<bool, W> for CancelClose {
    fn event(
        &mut self,
        w: &mut W,
        ctx: &mut EventCtx<'_, '_>,
        event: &Event,
        data: &mut bool,
        env: &Env,
    ) {
        match (&data, event) {
            (false, Event::WindowCloseRequested) => ctx.set_handled(),
            _ => w.event(ctx, event, data, env),
        }
    }
source

pub fn is_handled(&self) -> bool

Determine whether the event has been handled by some other widget.

Examples found in repository?
examples/sub_window.rs (line 186)
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
    fn event(&mut self, child: &mut W, ctx: &mut EventCtx, event: &Event, data: &mut T, env: &Env) {
        let wait_duration = Duration::from_millis(500);
        let resched_dur = Duration::from_millis(50);
        let cursor_size = Size::new(15., 15.);
        let now = Instant::now();
        let new_state = match &self.state {
            TooltipState::Fresh => match event {
                Event::MouseMove(me) if ctx.is_hot() => Some(TooltipState::Waiting {
                    last_move: now,
                    timer_expire: now + wait_duration,
                    token: ctx.request_timer(wait_duration),
                    position_in_window_coordinates: me.window_pos,
                }),
                _ => None,
            },
            TooltipState::Waiting {
                last_move,
                timer_expire,
                token,
                position_in_window_coordinates,
            } => match event {
                Event::MouseMove(me) if ctx.is_hot() => {
                    let (cur_token, cur_expire) = if *timer_expire - now < resched_dur {
                        (ctx.request_timer(wait_duration), now + wait_duration)
                    } else {
                        (*token, *timer_expire)
                    };
                    Some(TooltipState::Waiting {
                        last_move: now,
                        timer_expire: cur_expire,
                        token: cur_token,
                        position_in_window_coordinates: me.window_pos,
                    })
                }
                Event::Timer(tok) if tok == token => {
                    let deadline = *last_move + wait_duration;
                    ctx.set_handled();
                    if deadline > now {
                        let wait_for = deadline - now;
                        tracing::info!("Waiting another {:?}", wait_for);
                        Some(TooltipState::Waiting {
                            last_move: *last_move,
                            timer_expire: deadline,
                            token: ctx.request_timer(wait_for),
                            position_in_window_coordinates: *position_in_window_coordinates,
                        })
                    } else {
                        let tooltip_position_in_window_coordinates =
                            (position_in_window_coordinates.to_vec2() + cursor_size.to_vec2())
                                .to_point();
                        let win_id = ctx.new_sub_window(
                            WindowConfig::default()
                                .show_titlebar(false)
                                .window_size_policy(WindowSizePolicy::Content)
                                .set_level(WindowLevel::Tooltip(ctx.window().clone()))
                                .set_position(tooltip_position_in_window_coordinates),
                            Label::<()>::new(self.tip.clone()),
                            (),
                            env.clone(),
                        );
                        Some(TooltipState::Showing(win_id))
                    }
                }
                _ => None,
            },
            TooltipState::Showing(win_id) => {
                match event {
                    Event::MouseMove(me) if !ctx.is_hot() => {
                        // TODO another timer on leaving
                        tracing::info!("Sending close window for {:?}", win_id);
                        ctx.submit_command(CLOSE_WINDOW.to(*win_id));
                        Some(TooltipState::Waiting {
                            last_move: now,
                            timer_expire: now + wait_duration,
                            token: ctx.request_timer(wait_duration),
                            position_in_window_coordinates: me.window_pos,
                        })
                    }
                    _ => None,
                }
            }
        };

        if let Some(state) = new_state {
            self.state = state;
        }

        if !ctx.is_handled() {
            child.event(ctx, event, data, env);
        }
    }
source

pub fn request_focus(&mut self)

Request keyboard focus.

Because only one widget can be focused at a time, multiple focus requests from different widgets during a single event cycle means that the last widget that requests focus will override the previous requests.

See is_focused for more information about focus.

source

pub fn set_focus(&mut self, target: WidgetId)

Transfer focus to the widget with the given WidgetId.

See is_focused for more information about focus.

source

pub fn focus_next(&mut self)

Transfer focus to the next focusable widget.

This should only be called by a widget that currently has focus.

See is_focused for more information about focus.

source

pub fn focus_prev(&mut self)

Transfer focus to the previous focusable widget.

This should only be called by a widget that currently has focus.

See is_focused for more information about focus.

source

pub fn resign_focus(&mut self)

Give up focus.

This should only be called by a widget that currently has focus.

See is_focused for more information about focus.

source

pub fn request_update(&mut self)

Request an update cycle.

After this, update will be called on the widget in the next update cycle, even if there’s not a data change.

The use case for this method is when a container widget synthesizes data for its children. This is appropriate in specialized cases, but before reaching for this method, consider whether it might be better to refactor to be more idiomatic, in particular to make that data available in the app state.

source

pub fn scroll_area_to_view(&mut self, area: Rect)

Scrolls the area into view.

If the area is only partially visible or not visible at all because of Scrolls this widget is wrapped in, they will do the minimum amount of scrolling necessary to bring the area fully into view.

If the widget is hidden, this method has no effect.

Auto Trait Implementations§

§

impl<'a, 'b> !RefUnwindSafe for EventCtx<'a, 'b>

§

impl<'a, 'b> !Send for EventCtx<'a, 'b>

§

impl<'a, 'b> !Sync for EventCtx<'a, 'b>

§

impl<'a, 'b> Unpin for EventCtx<'a, 'b>where 'b: 'a,

§

impl<'a, 'b> !UnwindSafe for EventCtx<'a, 'b>

Blanket Implementations§

source§

impl<T> Any for Twhere T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere T: ?Sized,

const: unstable · source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere T: ?Sized,

const: unstable · source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

const: unstable · source§

fn from(t: T) -> T

Returns the argument unchanged.

source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for Twhere U: From<T>,

const: unstable · source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

§

impl<T> RoundFrom<T> for T

§

fn round_from(x: T) -> T

Performs the conversion.
§

impl<T, U> RoundInto<U> for Twhere U: RoundFrom<T>,

§

fn round_into(self) -> U

Performs the conversion.
source§

impl<T> Same<T> for T

§

type Output = T

Should always be Self
source§

impl<T, U> TryFrom<U> for Twhere U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
const: unstable · source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
const: unstable · source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more