[go: up one dir, main page]

Size

Struct Size 

Source
pub struct Size {
    pub width: f64,
    pub height: f64,
}
Expand description

A 2D size.

Fields§

§width: f64

The width.

§height: f64

The height.

Implementations§

Source§

impl Size

Source

pub const ZERO: Size

A size with zero width or height.

Source

pub const fn new(width: f64, height: f64) -> Size

Create a new Size with the provided width and height.

Examples found in repository?
examples/scroll.rs (line 58)
56    fn layout(&mut self, ctx: &mut LayoutCtx, bc: &BoxConstraints, _: &T, _: &Env) -> Size {
57        ctx.set_paint_insets(INSETS);
58        bc.constrain(Size::new(100., 100.))
59    }
More examples
Hide additional examples
examples/custom_widget.rs (line 69)
48    fn layout(
49        &mut self,
50        _layout_ctx: &mut LayoutCtx,
51        bc: &BoxConstraints,
52        _data: &String,
53        _env: &Env,
54    ) -> Size {
55        // BoxConstraints are passed by the parent widget.
56        // This method can return any Size within those constraints:
57        // bc.constrain(my_size)
58        //
59        // To check if a dimension is infinite or not (e.g. scrolling):
60        // bc.is_width_bounded() / bc.is_height_bounded()
61        //
62        // bx.max() returns the maximum size of the widget. Be careful
63        // using this, since always make sure the widget is bounded.
64        // If bx.max() is used in a scrolling widget things will probably
65        // not work correctly.
66        if bc.is_width_bounded() && bc.is_height_bounded() {
67            bc.max()
68        } else {
69            let size = Size::new(100.0, 100.0);
70            bc.constrain(size)
71        }
72    }
examples/input_region.rs (line 129)
120    fn layout(
121        &mut self,
122        ctx: &mut druid::LayoutCtx,
123        bc: &druid::BoxConstraints,
124        data: &AppState,
125        env: &druid::Env,
126    ) -> druid::Size {
127        let mut interactable_area = Region::EMPTY;
128        let smaller_bc = BoxConstraints::new(
129            Size::new(0.0, 0.0),
130            Size::new(bc.max().width - 100.0, bc.max().height - 100.0),
131        );
132        let full_bc = BoxConstraints::new(Size::new(0.0, 0.0), bc.max());
133        let _label_size = self.info_label.layout(ctx, &smaller_bc, data, env);
134        let controls_size = self.controls.layout(ctx, &full_bc, data, env);
135
136        let text_origin_point = Point::new(50.0, 50.0 + controls_size.height);
137        self.info_label.set_origin(ctx, text_origin_point);
138        let controls_origin_point = Point::new(EXAMPLE_BORDER_SIZE, EXAMPLE_BORDER_SIZE);
139        self.controls.set_origin(ctx, controls_origin_point);
140
141        // Add side rects to clarify the dimensions of the window.
142        let left_rect = Rect::new(0.0, 0.0, EXAMPLE_BORDER_SIZE, bc.max().height);
143        let right_rect = Rect::new(
144            bc.max().width - EXAMPLE_BORDER_SIZE,
145            0.0,
146            bc.max().width,
147            bc.max().height,
148        );
149        let bottom_rect = Rect::new(
150            0.0,
151            bc.max().height - EXAMPLE_BORDER_SIZE,
152            bc.max().width,
153            bc.max().height,
154        );
155        interactable_area.add_rect(left_rect);
156        interactable_area.add_rect(right_rect);
157        interactable_area.add_rect(bottom_rect);
158        interactable_area.add_rect(self.info_label.layout_rect());
159        interactable_area.add_rect(self.controls.layout_rect());
160
161        if data.limit_input_region {
162            ctx.window().set_input_region(Some(interactable_area));
163        } else {
164            ctx.window().set_input_region(None);
165        }
166
167        bc.max()
168    }
examples/sub_window.rs (line 102)
99    fn event(&mut self, child: &mut W, ctx: &mut EventCtx, event: &Event, data: &mut T, env: &Env) {
100        let wait_duration = Duration::from_millis(500);
101        let resched_dur = Duration::from_millis(50);
102        let cursor_size = Size::new(15., 15.);
103        let now = Instant::now();
104        let new_state = match &self.state {
105            TooltipState::Fresh => match event {
106                Event::MouseMove(me) if ctx.is_hot() => Some(TooltipState::Waiting {
107                    last_move: now,
108                    timer_expire: now + wait_duration,
109                    token: ctx.request_timer(wait_duration),
110                    position_in_window_coordinates: me.window_pos,
111                }),
112                _ => None,
113            },
114            TooltipState::Waiting {
115                last_move,
116                timer_expire,
117                token,
118                position_in_window_coordinates,
119            } => match event {
120                Event::MouseMove(me) if ctx.is_hot() => {
121                    let (cur_token, cur_expire) = if *timer_expire - now < resched_dur {
122                        (ctx.request_timer(wait_duration), now + wait_duration)
123                    } else {
124                        (*token, *timer_expire)
125                    };
126                    Some(TooltipState::Waiting {
127                        last_move: now,
128                        timer_expire: cur_expire,
129                        token: cur_token,
130                        position_in_window_coordinates: me.window_pos,
131                    })
132                }
133                Event::Timer(tok) if tok == token => {
134                    let deadline = *last_move + wait_duration;
135                    ctx.set_handled();
136                    if deadline > now {
137                        let wait_for = deadline - now;
138                        tracing::info!("Waiting another {:?}", wait_for);
139                        Some(TooltipState::Waiting {
140                            last_move: *last_move,
141                            timer_expire: deadline,
142                            token: ctx.request_timer(wait_for),
143                            position_in_window_coordinates: *position_in_window_coordinates,
144                        })
145                    } else {
146                        let tooltip_position_in_window_coordinates =
147                            (position_in_window_coordinates.to_vec2() + cursor_size.to_vec2())
148                                .to_point();
149                        let win_id = ctx.new_sub_window(
150                            WindowConfig::default()
151                                .show_titlebar(false)
152                                .window_size_policy(WindowSizePolicy::Content)
153                                .set_level(WindowLevel::Tooltip(ctx.window().clone()))
154                                .set_position(tooltip_position_in_window_coordinates),
155                            Label::<()>::new(self.tip.clone()),
156                            (),
157                            env.clone(),
158                        );
159                        Some(TooltipState::Showing(win_id))
160                    }
161                }
162                _ => None,
163            },
164            TooltipState::Showing(win_id) => {
165                match event {
166                    Event::MouseMove(me) if !ctx.is_hot() => {
167                        // TODO another timer on leaving
168                        tracing::info!("Sending close window for {:?}", win_id);
169                        ctx.submit_command(CLOSE_WINDOW.to(*win_id));
170                        Some(TooltipState::Waiting {
171                            last_move: now,
172                            timer_expire: now + wait_duration,
173                            token: ctx.request_timer(wait_duration),
174                            position_in_window_coordinates: me.window_pos,
175                        })
176                    }
177                    _ => None,
178                }
179            }
180        };
181
182        if let Some(state) = new_state {
183            self.state = state;
184        }
185
186        if !ctx.is_handled() {
187            child.event(ctx, event, data, env);
188        }
189    }
190
191    fn lifecycle(
192        &mut self,
193        child: &mut W,
194        ctx: &mut LifeCycleCtx,
195        event: &LifeCycle,
196        data: &T,
197        env: &Env,
198    ) {
199        if let LifeCycle::HotChanged(false) = event {
200            if let TooltipState::Showing(win_id) = self.state {
201                ctx.submit_command(CLOSE_WINDOW.to(win_id));
202            }
203            self.state = TooltipState::Fresh;
204        }
205        child.lifecycle(ctx, event, data, env)
206    }
207}
208
209struct DragWindowController {
210    init_pos: Option<Point>,
211    //dragging: bool
212}
213
214impl DragWindowController {
215    pub fn new() -> Self {
216        DragWindowController { init_pos: None }
217    }
218}
219
220impl<T, W: Widget<T>> Controller<T, W> for DragWindowController {
221    fn event(&mut self, child: &mut W, ctx: &mut EventCtx, event: &Event, data: &mut T, env: &Env) {
222        match event {
223            Event::MouseDown(me) if me.buttons.has_left() => {
224                ctx.set_active(true);
225                self.init_pos = Some(me.window_pos)
226            }
227            Event::MouseMove(me) if ctx.is_active() && me.buttons.has_left() => {
228                if let Some(init_pos) = self.init_pos {
229                    let within_window_change = me.window_pos.to_vec2() - init_pos.to_vec2();
230                    let old_pos = ctx.window().get_position();
231                    let new_pos = old_pos + within_window_change;
232                    tracing::info!(
233                        "Drag {:?} ",
234                        (
235                            init_pos,
236                            me.window_pos,
237                            within_window_change,
238                            old_pos,
239                            new_pos
240                        )
241                    );
242                    ctx.window().set_position(new_pos)
243                }
244            }
245            Event::MouseUp(_me) if ctx.is_active() => {
246                self.init_pos = None;
247                ctx.set_active(false)
248            }
249            _ => (),
250        }
251        child.event(ctx, event, data, env)
252    }
253}
254
255struct ScreenThing;
256
257impl Widget<()> for ScreenThing {
258    fn event(&mut self, _ctx: &mut EventCtx, _event: &Event, _data: &mut (), _env: &Env) {}
259
260    fn lifecycle(&mut self, _ctx: &mut LifeCycleCtx, _event: &LifeCycle, _data: &(), _env: &Env) {}
261
262    fn update(&mut self, _ctx: &mut UpdateCtx, _old_data: &(), _data: &(), _env: &Env) {}
263
264    fn layout(
265        &mut self,
266        _ctx: &mut LayoutCtx,
267        bc: &BoxConstraints,
268        _data: &(),
269        _env: &Env,
270    ) -> Size {
271        bc.constrain(Size::new(800.0, 600.0))
272    }
273
274    fn paint(&mut self, ctx: &mut PaintCtx, _data: &(), env: &Env) {
275        let sz = ctx.size();
276
277        let monitors = Screen::get_monitors();
278        let all = monitors
279            .iter()
280            .map(|x| x.virtual_rect())
281            .fold(Rect::ZERO, |s, r| r.union(s));
282        if all.width() > 0. && all.height() > 0. {
283            let trans = Affine::scale(f64::min(sz.width / all.width(), sz.height / all.height()))
284                * Affine::translate(all.origin().to_vec2()).inverse();
285            let font = env.get(theme::UI_FONT).family;
286
287            for (i, mon) in monitors.iter().enumerate() {
288                let vr = mon.virtual_rect();
289                let tr = trans.transform_rect_bbox(vr);
290                ctx.stroke(tr, &Color::WHITE, 1.0);
291
292                if let Ok(tl) = ctx
293                    .text()
294                    .new_text_layout(format!(
295                        "{}:{}x{}@{},{}",
296                        i,
297                        vr.width(),
298                        vr.height(),
299                        vr.x0,
300                        vr.y0
301                    ))
302                    .max_width(tr.width() - 5.)
303                    .font(font.clone(), env.get(theme::TEXT_SIZE_NORMAL))
304                    .text_color(Color::WHITE)
305                    .build()
306                {
307                    ctx.draw_text(&tl, tr.center() - tl.size().to_vec2() * 0.5);
308                }
309            }
310        }
311    }
312}
313
314struct CancelClose;
315
316impl<W: Widget<bool>> Controller<bool, W> for CancelClose {
317    fn event(
318        &mut self,
319        w: &mut W,
320        ctx: &mut EventCtx<'_, '_>,
321        event: &Event,
322        data: &mut bool,
323        env: &Env,
324    ) {
325        match (&data, event) {
326            (false, Event::WindowCloseRequested) => ctx.set_handled(),
327            _ => w.event(ctx, event, data, env),
328        }
329    }
330}
331
332fn build_root_widget() -> impl Widget<HelloState> {
333    let label = EnvScope::new(
334        |env, _t| env.set(theme::TEXT_COLOR, env.get(theme::PRIMARY_LIGHT)),
335        ControllerHost::new(
336            Label::new(|data: &HelloState, _env: &Env| {
337                format!("Hello {}! {} ", data.name, data.sub.my_stuff)
338            }),
339            TooltipController::new("Tips! Are good"),
340        ),
341    );
342    // a textbox that modifies `name`.
343    let textbox = TextBox::new()
344        .with_placeholder("Who are we greeting?")
345        .fix_width(TEXT_BOX_WIDTH)
346        .lens(HelloState::sub.then(SubState::my_stuff));
347
348    let button = Button::new("Make sub window")
349        .on_click(|ctx, data: &mut SubState, env| {
350            let tb = TextBox::new().lens(SubState::my_stuff);
351            let drag_thing = Label::new("Drag me").controller(DragWindowController::new());
352            let col = Flex::column().with_child(drag_thing).with_child(tb);
353
354            ctx.new_sub_window(
355                WindowConfig::default()
356                    .show_titlebar(false)
357                    .window_size(Size::new(100., 100.))
358                    .set_level(WindowLevel::AppWindow),
359                col,
360                data.clone(),
361                env.clone(),
362            );
363        })
364        .center()
365        .lens(HelloState::sub);
366
367    let check_box =
368        ControllerHost::new(Checkbox::new("Closeable?"), CancelClose).lens(HelloState::closeable);
369    // arrange the two widgets vertically, with some padding
370    let layout = Flex::column()
371        .with_child(label)
372        .with_flex_child(ScreenThing.lens(Unit::default()).padding(5.), 1.)
373        .with_spacer(VERTICAL_WIDGET_SPACING)
374        .with_child(textbox)
375        .with_child(button)
376        .with_child(check_box);
377
378    // center the two widgets in the available space
379    Align::centered(layout)
380}
Source

pub fn max_side(self) -> f64

Returns the max of width and height.

§Examples
use kurbo::Size;
let size = Size::new(-10.5, 42.0);
assert_eq!(size.max_side(), 42.0);
Source

pub fn min_side(self) -> f64

Returns the min of width and height.

§Examples
use kurbo::Size;
let size = Size::new(-10.5, 42.0);
assert_eq!(size.min_side(), -10.5);
Source

pub fn area(self) -> f64

The area covered by this size.

Source

pub fn is_empty(self) -> bool

Whether this size has zero area.

Note: a size with negative area is not considered empty.

Source

pub fn clamp(self, min: Size, max: Size) -> Size

Returns a new size bounded by min and max.

§Examples
use kurbo::Size;

let this = Size::new(0., 100.);
let min = Size::new(10., 10.,);
let max = Size::new(50., 50.);
assert_eq!(this.clamp(min, max), Size::new(10., 50.))
Source

pub const fn to_vec2(self) -> Vec2

Convert this size into a Vec2, with width mapped to x and height mapped to y.

Examples found in repository?
examples/sub_window.rs (line 147)
99    fn event(&mut self, child: &mut W, ctx: &mut EventCtx, event: &Event, data: &mut T, env: &Env) {
100        let wait_duration = Duration::from_millis(500);
101        let resched_dur = Duration::from_millis(50);
102        let cursor_size = Size::new(15., 15.);
103        let now = Instant::now();
104        let new_state = match &self.state {
105            TooltipState::Fresh => match event {
106                Event::MouseMove(me) if ctx.is_hot() => Some(TooltipState::Waiting {
107                    last_move: now,
108                    timer_expire: now + wait_duration,
109                    token: ctx.request_timer(wait_duration),
110                    position_in_window_coordinates: me.window_pos,
111                }),
112                _ => None,
113            },
114            TooltipState::Waiting {
115                last_move,
116                timer_expire,
117                token,
118                position_in_window_coordinates,
119            } => match event {
120                Event::MouseMove(me) if ctx.is_hot() => {
121                    let (cur_token, cur_expire) = if *timer_expire - now < resched_dur {
122                        (ctx.request_timer(wait_duration), now + wait_duration)
123                    } else {
124                        (*token, *timer_expire)
125                    };
126                    Some(TooltipState::Waiting {
127                        last_move: now,
128                        timer_expire: cur_expire,
129                        token: cur_token,
130                        position_in_window_coordinates: me.window_pos,
131                    })
132                }
133                Event::Timer(tok) if tok == token => {
134                    let deadline = *last_move + wait_duration;
135                    ctx.set_handled();
136                    if deadline > now {
137                        let wait_for = deadline - now;
138                        tracing::info!("Waiting another {:?}", wait_for);
139                        Some(TooltipState::Waiting {
140                            last_move: *last_move,
141                            timer_expire: deadline,
142                            token: ctx.request_timer(wait_for),
143                            position_in_window_coordinates: *position_in_window_coordinates,
144                        })
145                    } else {
146                        let tooltip_position_in_window_coordinates =
147                            (position_in_window_coordinates.to_vec2() + cursor_size.to_vec2())
148                                .to_point();
149                        let win_id = ctx.new_sub_window(
150                            WindowConfig::default()
151                                .show_titlebar(false)
152                                .window_size_policy(WindowSizePolicy::Content)
153                                .set_level(WindowLevel::Tooltip(ctx.window().clone()))
154                                .set_position(tooltip_position_in_window_coordinates),
155                            Label::<()>::new(self.tip.clone()),
156                            (),
157                            env.clone(),
158                        );
159                        Some(TooltipState::Showing(win_id))
160                    }
161                }
162                _ => None,
163            },
164            TooltipState::Showing(win_id) => {
165                match event {
166                    Event::MouseMove(me) if !ctx.is_hot() => {
167                        // TODO another timer on leaving
168                        tracing::info!("Sending close window for {:?}", win_id);
169                        ctx.submit_command(CLOSE_WINDOW.to(*win_id));
170                        Some(TooltipState::Waiting {
171                            last_move: now,
172                            timer_expire: now + wait_duration,
173                            token: ctx.request_timer(wait_duration),
174                            position_in_window_coordinates: me.window_pos,
175                        })
176                    }
177                    _ => None,
178                }
179            }
180        };
181
182        if let Some(state) = new_state {
183            self.state = state;
184        }
185
186        if !ctx.is_handled() {
187            child.event(ctx, event, data, env);
188        }
189    }
190
191    fn lifecycle(
192        &mut self,
193        child: &mut W,
194        ctx: &mut LifeCycleCtx,
195        event: &LifeCycle,
196        data: &T,
197        env: &Env,
198    ) {
199        if let LifeCycle::HotChanged(false) = event {
200            if let TooltipState::Showing(win_id) = self.state {
201                ctx.submit_command(CLOSE_WINDOW.to(win_id));
202            }
203            self.state = TooltipState::Fresh;
204        }
205        child.lifecycle(ctx, event, data, env)
206    }
207}
208
209struct DragWindowController {
210    init_pos: Option<Point>,
211    //dragging: bool
212}
213
214impl DragWindowController {
215    pub fn new() -> Self {
216        DragWindowController { init_pos: None }
217    }
218}
219
220impl<T, W: Widget<T>> Controller<T, W> for DragWindowController {
221    fn event(&mut self, child: &mut W, ctx: &mut EventCtx, event: &Event, data: &mut T, env: &Env) {
222        match event {
223            Event::MouseDown(me) if me.buttons.has_left() => {
224                ctx.set_active(true);
225                self.init_pos = Some(me.window_pos)
226            }
227            Event::MouseMove(me) if ctx.is_active() && me.buttons.has_left() => {
228                if let Some(init_pos) = self.init_pos {
229                    let within_window_change = me.window_pos.to_vec2() - init_pos.to_vec2();
230                    let old_pos = ctx.window().get_position();
231                    let new_pos = old_pos + within_window_change;
232                    tracing::info!(
233                        "Drag {:?} ",
234                        (
235                            init_pos,
236                            me.window_pos,
237                            within_window_change,
238                            old_pos,
239                            new_pos
240                        )
241                    );
242                    ctx.window().set_position(new_pos)
243                }
244            }
245            Event::MouseUp(_me) if ctx.is_active() => {
246                self.init_pos = None;
247                ctx.set_active(false)
248            }
249            _ => (),
250        }
251        child.event(ctx, event, data, env)
252    }
253}
254
255struct ScreenThing;
256
257impl Widget<()> for ScreenThing {
258    fn event(&mut self, _ctx: &mut EventCtx, _event: &Event, _data: &mut (), _env: &Env) {}
259
260    fn lifecycle(&mut self, _ctx: &mut LifeCycleCtx, _event: &LifeCycle, _data: &(), _env: &Env) {}
261
262    fn update(&mut self, _ctx: &mut UpdateCtx, _old_data: &(), _data: &(), _env: &Env) {}
263
264    fn layout(
265        &mut self,
266        _ctx: &mut LayoutCtx,
267        bc: &BoxConstraints,
268        _data: &(),
269        _env: &Env,
270    ) -> Size {
271        bc.constrain(Size::new(800.0, 600.0))
272    }
273
274    fn paint(&mut self, ctx: &mut PaintCtx, _data: &(), env: &Env) {
275        let sz = ctx.size();
276
277        let monitors = Screen::get_monitors();
278        let all = monitors
279            .iter()
280            .map(|x| x.virtual_rect())
281            .fold(Rect::ZERO, |s, r| r.union(s));
282        if all.width() > 0. && all.height() > 0. {
283            let trans = Affine::scale(f64::min(sz.width / all.width(), sz.height / all.height()))
284                * Affine::translate(all.origin().to_vec2()).inverse();
285            let font = env.get(theme::UI_FONT).family;
286
287            for (i, mon) in monitors.iter().enumerate() {
288                let vr = mon.virtual_rect();
289                let tr = trans.transform_rect_bbox(vr);
290                ctx.stroke(tr, &Color::WHITE, 1.0);
291
292                if let Ok(tl) = ctx
293                    .text()
294                    .new_text_layout(format!(
295                        "{}:{}x{}@{},{}",
296                        i,
297                        vr.width(),
298                        vr.height(),
299                        vr.x0,
300                        vr.y0
301                    ))
302                    .max_width(tr.width() - 5.)
303                    .font(font.clone(), env.get(theme::TEXT_SIZE_NORMAL))
304                    .text_color(Color::WHITE)
305                    .build()
306                {
307                    ctx.draw_text(&tl, tr.center() - tl.size().to_vec2() * 0.5);
308                }
309            }
310        }
311    }
Source

pub fn round(self) -> Size

Returns a new Size, with width and height rounded to the nearest integer.

§Examples
use kurbo::Size;
let size_pos = Size::new(3.3, 3.6).round();
assert_eq!(size_pos.width, 3.0);
assert_eq!(size_pos.height, 4.0);
let size_neg = Size::new(-3.3, -3.6).round();
assert_eq!(size_neg.width, -3.0);
assert_eq!(size_neg.height, -4.0);
Source

pub fn ceil(self) -> Size

Returns a new Size, with width and height rounded up to the nearest integer, unless they are already an integer.

§Examples
use kurbo::Size;
let size_pos = Size::new(3.3, 3.6).ceil();
assert_eq!(size_pos.width, 4.0);
assert_eq!(size_pos.height, 4.0);
let size_neg = Size::new(-3.3, -3.6).ceil();
assert_eq!(size_neg.width, -3.0);
assert_eq!(size_neg.height, -3.0);
Source

pub fn floor(self) -> Size

Returns a new Size, with width and height rounded down to the nearest integer, unless they are already an integer.

§Examples
use kurbo::Size;
let size_pos = Size::new(3.3, 3.6).floor();
assert_eq!(size_pos.width, 3.0);
assert_eq!(size_pos.height, 3.0);
let size_neg = Size::new(-3.3, -3.6).floor();
assert_eq!(size_neg.width, -4.0);
assert_eq!(size_neg.height, -4.0);
Source

pub fn expand(self) -> Size

Returns a new Size, with width and height rounded away from zero to the nearest integer, unless they are already an integer.

§Examples
use kurbo::Size;
let size_pos = Size::new(3.3, 3.6).expand();
assert_eq!(size_pos.width, 4.0);
assert_eq!(size_pos.height, 4.0);
let size_neg = Size::new(-3.3, -3.6).expand();
assert_eq!(size_neg.width, -4.0);
assert_eq!(size_neg.height, -4.0);
Source

pub fn trunc(self) -> Size

Returns a new Size, with width and height rounded down towards zero the nearest integer, unless they are already an integer.

§Examples
use kurbo::Size;
let size_pos = Size::new(3.3, 3.6).trunc();
assert_eq!(size_pos.width, 3.0);
assert_eq!(size_pos.height, 3.0);
let size_neg = Size::new(-3.3, -3.6).trunc();
assert_eq!(size_neg.width, -3.0);
assert_eq!(size_neg.height, -3.0);
Source

pub fn aspect_ratio(self) -> f64

Returns the aspect ratio of a rectangle with the given size.

If the width is 0, the output will be sign(self.height) * infinity. If The width and height are 0, then the output will be NaN.

Source

pub const fn to_rect(self) -> Rect

Convert this Size into a Rect with origin (0.0, 0.0).

Examples found in repository?
examples/invalidation.rs (line 139)
137    fn paint(&mut self, ctx: &mut PaintCtx, data: &Vector<Circle>, _env: &Env) {
138        ctx.with_save(|ctx| {
139            let rect = ctx.size().to_rect();
140            ctx.clip(rect);
141            ctx.fill(rect, &Color::WHITE);
142            let now = Instant::now();
143            for c in data {
144                let color =
145                    Color::BLACK.with_alpha(now.duration_since(c.time).as_secs_f64().cos().abs());
146                ctx.fill(kurbo::Circle::new(c.pos, RADIUS), &color);
147            }
148        });
149    }
More examples
Hide additional examples
examples/calc.rs (line 123)
121fn op_button_label(op: char, label: String) -> impl Widget<CalcState> {
122    let painter = Painter::new(|ctx, _, env| {
123        let bounds = ctx.size().to_rect();
124
125        ctx.fill(bounds, &env.get(theme::PRIMARY_DARK));
126
127        if ctx.is_hot() {
128            ctx.stroke(bounds.inset(-0.5), &Color::WHITE, 1.0);
129        }
130
131        if ctx.is_active() {
132            ctx.fill(bounds, &env.get(theme::PRIMARY_LIGHT));
133        }
134    });
135
136    Label::new(label)
137        .with_text_size(24.)
138        .center()
139        .background(painter)
140        .expand()
141        .on_click(move |_ctx, data: &mut CalcState, _env| data.op(op))
142}
143
144fn op_button(op: char) -> impl Widget<CalcState> {
145    op_button_label(op, op.to_string())
146}
147
148fn digit_button(digit: u8) -> impl Widget<CalcState> {
149    let painter = Painter::new(|ctx, _, env| {
150        let bounds = ctx.size().to_rect();
151
152        ctx.fill(bounds, &env.get(theme::BACKGROUND_LIGHT));
153
154        if ctx.is_hot() {
155            ctx.stroke(bounds.inset(-0.5), &Color::WHITE, 1.0);
156        }
157
158        if ctx.is_active() {
159            ctx.fill(bounds, &Color::rgb8(0x71, 0x71, 0x71));
160        }
161    });
162
163    Label::new(format!("{digit}"))
164        .with_text_size(24.)
165        .center()
166        .background(painter)
167        .expand()
168        .on_click(move |_ctx, data: &mut CalcState, _env| data.digit(digit))
169}
examples/transparency.rs (line 65)
62fn build_root_widget() -> impl Widget<HelloState> {
63    // Draw red circle, and two semi-transparent rectangles
64    let circle_and_rects = Painter::new(|ctx, _data, _env| {
65        let boundaries = ctx.size().to_rect();
66        let center = (boundaries.width() / 2., boundaries.height() / 2.);
67        let circle = Circle::new(center, center.0.min(center.1));
68        ctx.fill(circle, &Color::RED);
69
70        let rect1 = Rect::new(0., 0., boundaries.width() / 2., boundaries.height() / 2.);
71        ctx.fill(rect1, &Color::rgba8(0x0, 0xff, 0, 125));
72
73        let rect2 = Rect::new(
74            boundaries.width() / 2.,
75            boundaries.height() / 2.,
76            boundaries.width(),
77            boundaries.height(),
78        );
79        ctx.fill(rect2, &Color::rgba8(0x0, 0x0, 0xff, 125));
80    });
81
82    // This textbox modifies the label, idea here is to test that the background
83    // invalidation works when you type to the textbox
84    let textbox = TextBox::new()
85        .with_placeholder("Type to test clearing")
86        .with_text_size(18.0)
87        .lens(HelloState::name)
88        .fix_width(250.);
89
90    let label = Label::new(|data: &HelloState, _env: &Env| {
91        if data.name.is_empty() {
92            "Text: ".to_string()
93        } else {
94            format!("Text: {}!", data.name)
95        }
96    })
97    .with_text_color(Color::RED)
98    .with_text_size(32.0);
99
100    Flex::column()
101        .with_flex_child(circle_and_rects.expand().controller(DragController), 10.0)
102        .with_spacer(4.0)
103        .with_child(textbox)
104        .with_spacer(4.0)
105        .with_child(label)
106}
examples/panels.rs (line 40)
31fn build_app() -> impl Widget<()> {
32    let gradient = LinearGradient::new(
33        UnitPoint::TOP_LEFT,
34        UnitPoint::BOTTOM_RIGHT,
35        (DARKER_GREY, LIGHTER_GREY),
36    );
37
38    // a custom background
39    let polka_dots = Painter::new(|ctx, _, _| {
40        let bounds = ctx.size().to_rect();
41        let dot_diam = bounds.width().max(bounds.height()) / 20.;
42        let dot_spacing = dot_diam * 1.8;
43        for y in 0..((bounds.height() / dot_diam).ceil() as usize) {
44            for x in 0..((bounds.width() / dot_diam).ceil() as usize) {
45                let x_offset = (y % 2) as f64 * (dot_spacing / 2.0);
46                let x = x as f64 * dot_spacing + x_offset;
47                let y = y as f64 * dot_spacing;
48                let circ = Circle::new((x, y), dot_diam / 2.0);
49                let purp = Color::rgb(1.0, 0.22, 0.76);
50                ctx.fill(circ, &purp);
51            }
52        }
53    });
54
55    Flex::column()
56        .with_flex_child(
57            Flex::row()
58                .with_flex_child(
59                    Label::new("top left")
60                        .center()
61                        .border(DARK_GREY, 4.0)
62                        .padding(10.0),
63                    1.0,
64                )
65                .with_flex_child(
66                    Label::new("top right")
67                        .center()
68                        .background(DARK_GREY)
69                        .padding(10.0),
70                    1.0,
71                ),
72            1.0,
73        )
74        .with_flex_child(
75            Flex::row()
76                .with_flex_child(
77                    Label::new("bottom left")
78                        .center()
79                        .background(gradient)
80                        .rounded(10.0)
81                        .padding(10.0),
82                    1.0,
83                )
84                .with_flex_child(
85                    Label::new("bottom right")
86                        .center()
87                        .border(LIGHTER_GREY, 4.0)
88                        .background(polka_dots)
89                        .rounded(10.0)
90                        .padding(10.0),
91                    1.0,
92                ),
93            1.0,
94        )
95}
examples/styled_text.rs (line 74)
72fn ui_builder() -> impl Widget<AppData> {
73    let my_painter = Painter::new(|ctx, _, _| {
74        let bounds = ctx.size().to_rect();
75        if ctx.is_hot() {
76            ctx.fill(bounds, &Color::rgba8(0, 0, 0, 128));
77        }
78
79        if ctx.is_active() {
80            ctx.stroke(bounds, &Color::WHITE, 2.0);
81        }
82    });
83
84    // This is Druid's default text style.
85    // It's set by theme::LABEL_COLOR and theme::UI_FONT
86    let label =
87        Label::new(|data: &String, _env: &_| format!("Default: {data}")).lens(AppData::text);
88
89    // The text_color, text_size, and font builder methods can override the
90    // defaults provided by the theme by passing in a Key or a concrete value.
91    //
92    // In this example, text_color receives a Key from the theme, text_size
93    // gets a custom key which we set with the env_scope wrapper, and the
94    // default font key (theme::FONT_NAME) is overridden in the env_scope
95    // wrapper. (Like text_color and text_size, the font can be set using the
96    // with_font builder method, but overriding here makes it easy to fall back
97    // to the default font)
98    let styled_label = Label::new(|data: &AppData, _env: &_| format!("{data}"))
99        .with_text_color(theme::PRIMARY_LIGHT)
100        .with_font(MY_CUSTOM_FONT)
101        .background(my_painter)
102        .on_click(|_, data, _| {
103            data.size *= 1.1;
104        })
105        .env_scope(|env: &mut druid::Env, data: &AppData| {
106            let new_font = if data.mono {
107                FontDescriptor::new(FontFamily::MONOSPACE)
108            } else {
109                FontDescriptor::new(FontFamily::SYSTEM_UI)
110            }
111            .with_size(data.size);
112            env.set(MY_CUSTOM_FONT, new_font);
113        });
114
115    let labels = Scroll::new(
116        Flex::column()
117            .cross_axis_alignment(CrossAxisAlignment::Start)
118            .with_child(label)
119            .with_default_spacer()
120            .with_child(styled_label),
121    )
122    .expand_height()
123    .fix_width(COLUMN_WIDTH);
124
125    let stepper = Stepper::new()
126        .with_range(0.0, 100.0)
127        .with_step(1.0)
128        .with_wraparound(false)
129        .lens(AppData::size);
130
131    // TODO: Replace Parse usage with TextBox::with_formatter
132    #[allow(deprecated)]
133    let stepper_textbox = LensWrap::new(
134        Parse::new(TextBox::new()),
135        AppData::size.map(|x| Some(*x), |x, y| *x = y.unwrap_or(24.0)),
136    );
137
138    let mono_checkbox = Checkbox::new("Monospace").lens(AppData::mono);
139    let stepper_row = Flex::row()
140        .with_child(stepper_textbox)
141        .with_child(stepper)
142        .with_default_spacer()
143        .with_child(mono_checkbox);
144
145    let input = TextBox::multiline()
146        .with_placeholder("Your sample text here :)")
147        .fix_width(COLUMN_WIDTH)
148        .fix_height(140.0)
149        .lens(AppData::text);
150
151    Flex::column()
152        .main_axis_alignment(MainAxisAlignment::Center)
153        .with_default_spacer()
154        .with_flex_child(labels, 1.0)
155        .with_default_spacer()
156        .with_child(input)
157        .with_default_spacer()
158        .with_child(stepper_row)
159        .with_default_spacer()
160}
examples/custom_widget.rs (line 83)
77    fn paint(&mut self, ctx: &mut PaintCtx, data: &String, env: &Env) {
78        // Clear the whole widget with the color of your choice
79        // (ctx.size() returns the size of the layout rect we're painting in)
80        // Note: ctx also has a `clear` method, but that clears the whole context,
81        // and we only want to clear this widget's area.
82        let size = ctx.size();
83        let rect = size.to_rect();
84        ctx.fill(rect, &Color::WHITE);
85
86        // We can paint with a Z index, this indicates that this code will be run
87        // after the rest of the painting. Painting with z-index is done in order,
88        // so first everything with z-index 1 is painted and then with z-index 2 etc.
89        // As you can see this(red) curve is drawn on top of the green curve
90        ctx.paint_with_z_index(1, move |ctx| {
91            let mut path = BezPath::new();
92            path.move_to((0.0, size.height));
93            path.quad_to((40.0, 50.0), (size.width, 0.0));
94            // Create a color
95            let stroke_color = Color::rgb8(128, 0, 0);
96            // Stroke the path with thickness 1.0
97            ctx.stroke(path, &stroke_color, 5.0);
98        });
99
100        // Create an arbitrary bezier path
101        let mut path = BezPath::new();
102        path.move_to(Point::ORIGIN);
103        path.quad_to((40.0, 50.0), (size.width, size.height));
104        // Create a color
105        let stroke_color = Color::rgb8(0, 128, 0);
106        // Stroke the path with thickness 5.0
107        ctx.stroke(path, &stroke_color, 5.0);
108
109        // Rectangles: the path for practical people
110        let rect = Rect::from_origin_size((10.0, 10.0), (100.0, 100.0));
111        // Note the Color:rgba8 which includes an alpha channel (7F in this case)
112        let fill_color = Color::rgba8(0x00, 0x00, 0x00, 0x7F);
113        ctx.fill(rect, &fill_color);
114
115        // Text is easy; in real use TextLayout should either be stored in the
116        // widget and reused, or a label child widget to manage it all.
117        // This is one way of doing it, you can also use a builder-style way.
118        let mut layout = TextLayout::<String>::from_text(data);
119        layout.set_font(FontDescriptor::new(FontFamily::SERIF).with_size(24.0));
120        layout.set_text_color(fill_color);
121        layout.rebuild_if_needed(ctx.text(), env);
122
123        // Let's rotate our text slightly. First we save our current (default) context:
124        ctx.with_save(|ctx| {
125            // Now we can rotate the context (or set a clip path, for instance):
126            // This makes it so that anything drawn after this (in the closure) is
127            // transformed.
128            // The transformation is in radians, but be aware it transforms the canvas,
129            // not just the part you are drawing. So we draw at (80.0, 40.0) on the rotated
130            // canvas, this is NOT the same position as (80.0, 40.0) on the original canvas.
131            ctx.transform(Affine::rotate(std::f64::consts::FRAC_PI_4));
132            layout.draw(ctx, (80.0, 40.0));
133        });
134        // When we exit with_save, the original context's rotation is restored
135
136        // This is the builder-style way of drawing text.
137        let text = ctx.text();
138        let layout = text
139            .new_text_layout(data.clone())
140            .font(FontFamily::SERIF, 24.0)
141            .text_color(Color::rgb8(128, 0, 0))
142            .build()
143            .unwrap();
144        ctx.draw_text(&layout, (100.0, 25.0));
145
146        // Let's burn some CPU to make a (partially transparent) image buffer
147        let image_data = make_image_data(256, 256);
148        let image = ctx
149            .make_image(256, 256, &image_data, ImageFormat::RgbaSeparate)
150            .unwrap();
151        // The image is automatically scaled to fit the rect you pass to draw_image
152        ctx.draw_image(&image, size.to_rect(), InterpolationMode::Bilinear);
153    }
Source

pub fn to_rounded_rect(self, radii: impl Into<RoundedRectRadii>) -> RoundedRect

Convert this Size into a RoundedRect with origin (0.0, 0.0) and the provided corner radius.

Examples found in repository?
examples/async_event.rs (line 83)
81fn make_ui() -> impl Widget<Color> {
82    Painter::new(|ctx, data, _env| {
83        let rect = ctx.size().to_rounded_rect(5.0);
84        ctx.fill(rect, data);
85    })
86    .fix_width(300.0)
87    .fix_height(300.0)
88    .padding(10.0)
89    .center()
90}
Source

pub fn is_finite(self) -> bool

Is this size finite?

Source

pub fn is_nan(self) -> bool

Is this size NaN?

Trait Implementations§

Source§

impl Add for Size

Source§

type Output = Size

The resulting type after applying the + operator.
Source§

fn add(self, other: Size) -> Size

Performs the + operation. Read more
Source§

impl AddAssign for Size

Source§

fn add_assign(&mut self, other: Size)

Performs the += operation. Read more
Source§

impl Clone for Size

Source§

fn clone(&self) -> Size

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Data for Size

Source§

fn same(&self, other: &Self) -> bool

Determine whether two values are the same. Read more
Source§

impl Debug for Size

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Default for Size

Source§

fn default() -> Size

Returns the “default value” for a type. Read more
Source§

impl Display for Size

Source§

fn fmt(&self, formatter: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Div<f64> for Size

Source§

type Output = Size

The resulting type after applying the / operator.
Source§

fn div(self, other: f64) -> Size

Performs the / operation. Read more
Source§

impl DivAssign<f64> for Size

Source§

fn div_assign(&mut self, other: f64)

Performs the /= operation. Read more
Source§

impl From<(f64, f64)> for Size

Source§

fn from(v: (f64, f64)) -> Size

Converts to this type from the input type.
Source§

impl From<Size> for Value

Source§

fn from(val: Size) -> Value

Converts to this type from the input type.
Source§

impl Mul<f64> for Size

Source§

type Output = Size

The resulting type after applying the * operator.
Source§

fn mul(self, other: f64) -> Size

Performs the * operation. Read more
Source§

impl MulAssign<f64> for Size

Source§

fn mul_assign(&mut self, other: f64)

Performs the *= operation. Read more
Source§

impl PartialEq for Size

Source§

fn eq(&self, other: &Size) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
Source§

impl Scalable for Size

Source§

fn to_px(&self, scale: Scale) -> Size

Converts a Size from display points into pixels, using the x axis scale factor for width and the y axis scale factor for height.

Source§

fn to_dp(&self, scale: Scale) -> Size

Converts a Size from pixels into points, using the x axis scale factor for width and the y axis scale factor for height.

Source§

impl Sub for Size

Source§

type Output = Size

The resulting type after applying the - operator.
Source§

fn sub(self, other: Size) -> Size

Performs the - operation. Read more
Source§

impl SubAssign for Size

Source§

fn sub_assign(&mut self, other: Size)

Performs the -= operation. Read more
Source§

impl ValueType for Size

Source§

fn try_from_value(value: &Value) -> Result<Self, ValueTypeError>

Attempt to convert the generic Value into this type.
Source§

impl Copy for Size

Source§

impl StructuralPartialEq for Size

Auto Trait Implementations§

§

impl Freeze for Size

§

impl RefUnwindSafe for Size

§

impl Send for Size

§

impl Sync for Size

§

impl Unpin for Size

§

impl UnwindSafe for Size

Blanket Implementations§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> AnyEq for T
where T: Any + PartialEq,

Source§

fn equals(&self, other: &(dyn Any + 'static)) -> bool

Source§

fn as_any(&self) -> &(dyn Any + 'static)

Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

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

Source§

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

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

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 T
where U: From<T>,

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.

Source§

impl<T> IsDefault for T
where T: Default + PartialEq + Copy,

Source§

fn is_default(&self) -> bool

Checks that type has a default value.
Source§

impl<T> RoundFrom<T> for T

Source§

fn round_from(x: T) -> T

Performs the conversion.
Source§

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

Source§

fn round_into(self) -> U

Performs the conversion.
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> ToString for T
where T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

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

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

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

Performs the conversion.
Source§

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

Source§

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

The type returned in the event of a conversion error.
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