Struct druid::Selector

source ·
pub struct Selector<T = ()>(_, _);
Expand description

An identifier for a particular command.

This should be a unique string identifier. Having multiple selectors with the same identifier but different payload types is not allowed and can cause Command::get and get_unchecked to panic.

The type parameter T specifies the command’s payload type. See Command for more information.

Certain Selectors are defined by Druid, and have special meaning to the framework; these are listed in the druid::commands module.

Implementations§

source§

impl Selector<()>

source

pub const NOOP: Selector = _

A selector that does nothing.

source

pub fn to(self, target: impl Into<Target>) -> Command

Turns this into a command with the specified Target.

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/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
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
    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)
    }
source§

impl<T> Selector<T>

source

pub const fn new(s: &'static str) -> Selector<T>

Create a new Selector with the given string.

Examples found in repository?
examples/identity.rs (line 37)
37
const INCREMENT: Selector = Selector::new("identity-example.increment");
More examples
Hide additional examples
examples/markdown_preview.rs (line 44)
44
const OPEN_LINK: Selector<String> = Selector::new("druid-example.open-link");
examples/blocking_function.rs (line 31)
31
const FINISH_SLOW_FUNCTION: Selector<u32> = Selector::new("finish_slow_function");
source§

impl<T: Any> Selector<T>

source

pub fn with(self, payload: T) -> Command

Convenience method for Command::new with this selector.

If the payload is () there is no need to call this, as Selector<()> implements Into<Command>.

By default, the command will have Target::Auto. The Selector::to method can be used to override this.

Examples found in repository?
examples/markdown_preview.rs (line 228)
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
fn add_attribute_for_tag(tag: &Tag, mut attrs: AttributesAdder) {
    match tag {
        Tag::Heading(lvl) => {
            let font_size = match lvl {
                1 => 38.,
                2 => 32.0,
                3 => 26.0,
                4 => 20.0,
                5 => 16.0,
                _ => 12.0,
            };
            attrs.size(font_size).weight(FontWeight::BOLD);
        }
        Tag::BlockQuote => {
            attrs.style(FontStyle::Italic).text_color(BLOCKQUOTE_COLOR);
        }
        Tag::CodeBlock(_) => {
            attrs.font_family(FontFamily::MONOSPACE);
        }
        Tag::Emphasis => {
            attrs.style(FontStyle::Italic);
        }
        Tag::Strong => {
            attrs.weight(FontWeight::BOLD);
        }
        Tag::Strikethrough => {
            attrs.strikethrough(true);
        }
        Tag::Link(_link_ty, target, _title) => {
            attrs
                .underline(true)
                .text_color(LINK_COLOR)
                .link(OPEN_LINK.with(target.to_string()));
        }
        // ignore other tags for now
        _ => (),
    }
}
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)
}

Trait Implementations§

source§

impl<T> Clone for Selector<T>

source§

fn clone(&self) -> Self

Returns a copy 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<T: Debug> Debug for Selector<T>

source§

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

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

impl<T> Display for Selector<T>

source§

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

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

impl From<Selector<()>> for Command

source§

fn from(selector: Selector) -> Command

Converts to this type from the input type.
source§

impl<T: PartialEq> PartialEq<Selector<T>> for Selector<T>

source§

fn eq(&self, other: &Selector<T>) -> bool

This method tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

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

This method tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
source§

impl<T> Copy for Selector<T>

source§

impl<T: Eq> Eq for Selector<T>

source§

impl<T> StructuralEq for Selector<T>

source§

impl<T> StructuralPartialEq for Selector<T>

Auto Trait Implementations§

§

impl<T> RefUnwindSafe for Selector<T>where T: RefUnwindSafe,

§

impl<T> Send for Selector<T>where T: Send,

§

impl<T> Sync for Selector<T>where T: Sync,

§

impl<T> Unpin for Selector<T>where T: Unpin,

§

impl<T> UnwindSafe for Selector<T>where T: UnwindSafe,

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> AnyEq for Twhere T: Any + PartialEq<T>,

source§

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

source§

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

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> ToOwned for Twhere T: Clone,

§

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 Twhere T: Display + ?Sized,

source§

default fn to_string(&self) -> String

Converts the given value to a String. Read more
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