pub struct Selector<T = ()>(/* private fields */);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<()>
impl Selector<()>
Sourcepub fn to(self, target: impl Into<Target>) -> Command
pub fn to(self, target: impl Into<Target>) -> Command
Turns this into a command with the specified Target.
Examples found in repository?
51fn ui_builder() -> impl Widget<State> {
52 let text = LocalizedString::new("hello-counter")
53 .with_arg("count", |data: &State, _env| data.menu_count.into());
54 let label = Label::new(text);
55 let inc_button =
56 Button::<State>::new("Add menu item").on_click(|_ctx, data, _env| data.menu_count += 1);
57 let dec_button = Button::<State>::new("Remove menu item")
58 .on_click(|_ctx, data, _env| data.menu_count = data.menu_count.saturating_sub(1));
59 let new_button = Button::<State>::new("New window").on_click(|ctx, _data, _env| {
60 ctx.submit_command(sys_cmds::NEW_FILE.to(Global));
61 });
62 let quit_button = Button::<State>::new("Quit app").on_click(|_ctx, _data, _env| {
63 Application::global().quit();
64 });
65
66 let mut col = Flex::column();
67 col.add_flex_child(Align::centered(Padding::new(5.0, label)), 1.0);
68 let mut row = Flex::row();
69 row.add_child(Padding::new(5.0, inc_button));
70 row.add_child(Padding::new(5.0, dec_button));
71 col.add_flex_child(Align::centered(row), 1.0);
72 let mut row = Flex::row();
73 row.add_child(Padding::new(5.0, new_button));
74 row.add_child(Padding::new(5.0, quit_button));
75 col.add_flex_child(Align::centered(row), 1.0);
76 let content = ControllerHost::new(col, ContextMenuController);
77 Glow::new(content)
78}More examples
62fn make_ui() -> impl Widget<OurData> {
63 // We can also generate these dynamically whenever we need it.
64 let id_two = WidgetId::next();
65 // We have a column with 2 labels and 2 buttons.
66 // Each of the 2 labels only has access to its own counter and is given a `WidgetId`.
67 // Both labels have a controller, this handles commands send to children.
68 // The 2 buttons send a command when clicked. Both send the exact same command.
69 // The key diference is that they both give a different `WidgetId` as target.
70 // This means that only the corresponding controller gets the command, and increments their counter.
71 Flex::column()
72 .with_child(
73 Label::dynamic(|data, _| format!("One: {data}"))
74 .controller(LabelControler)
75 .with_id(ID_ONE)
76 .lens(OurData::counter_one)
77 .padding(2.0),
78 )
79 .with_child(
80 Label::dynamic(|data, _| format!("Two: {data}"))
81 .controller(LabelControler)
82 .with_id(id_two)
83 .lens(OurData::counter_two)
84 .padding(2.0),
85 )
86 .with_child(
87 Button::<OurData>::new("Increment one")
88 .on_click(|ctx, _data, _env| ctx.submit_command(INCREMENT.to(ID_ONE)))
89 .padding(2.0),
90 )
91 .with_child(
92 Button::<OurData>::new("Increment two")
93 .on_click(move |ctx, _data, _env| ctx.submit_command(INCREMENT.to(id_two)))
94 .padding(2.0),
95 )
96 .padding(10.0)
97}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 }Source§impl<T: Any> Selector<T>
impl<T: Any> Selector<T>
Sourcepub fn with(self, payload: T) -> Command
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?
196fn add_attribute_for_tag(tag: &Tag, mut attrs: AttributesAdder) {
197 match tag {
198 Tag::Heading(lvl) => {
199 let font_size = match lvl {
200 1 => 38.,
201 2 => 32.0,
202 3 => 26.0,
203 4 => 20.0,
204 5 => 16.0,
205 _ => 12.0,
206 };
207 attrs.size(font_size).weight(FontWeight::BOLD);
208 }
209 Tag::BlockQuote => {
210 attrs.style(FontStyle::Italic).text_color(BLOCKQUOTE_COLOR);
211 }
212 Tag::CodeBlock(_) => {
213 attrs.font_family(FontFamily::MONOSPACE);
214 }
215 Tag::Emphasis => {
216 attrs.style(FontStyle::Italic);
217 }
218 Tag::Strong => {
219 attrs.weight(FontWeight::BOLD);
220 }
221 Tag::Strikethrough => {
222 attrs.strikethrough(true);
223 }
224 Tag::Link(_link_ty, target, _title) => {
225 attrs
226 .underline(true)
227 .text_color(LINK_COLOR)
228 .link(OPEN_LINK.with(target.to_string()));
229 }
230 // ignore other tags for now
231 _ => (),
232 }
233}More examples
39fn ui_builder() -> impl Widget<String> {
40 let rs = FileSpec::new("Rust source", &["rs"]);
41 let txt = FileSpec::new("Text file", &["txt"]);
42 let other = FileSpec::new("Bogus file", &["foo", "bar", "baz"]);
43 // The options can also be generated at runtime,
44 // so to show that off we create a String for the default save name.
45 let default_save_name = String::from("MyFile.txt");
46 let save_dialog_options = FileDialogOptions::new()
47 .allowed_types(vec![rs, txt, other])
48 .default_type(txt)
49 .default_name(default_save_name)
50 .name_label("Target")
51 .title("Choose a target for this lovely file")
52 .button_text("Export");
53 let open_dialog_options = save_dialog_options
54 .clone()
55 .default_name("MySavedFile.txt")
56 .name_label("Source")
57 .title("Where did you put that file?")
58 .button_text("Import");
59
60 let input = TextBox::new();
61 let save = Button::new("Save").on_click(move |ctx, _, _| {
62 ctx.submit_command(druid::commands::SHOW_SAVE_PANEL.with(save_dialog_options.clone()))
63 });
64 let open = Button::new("Open").on_click(move |ctx, _, _| {
65 ctx.submit_command(druid::commands::SHOW_OPEN_PANEL.with(open_dialog_options.clone()))
66 });
67
68 let mut col = Flex::column();
69 col.add_child(input);
70 col.add_spacer(8.0);
71 col.add_child(save);
72 col.add_child(open);
73 Align::centered(col)
74}