slt/context/widgets_interactive/events.rs
1use super::*;
2
3impl Context {
4 /// Render a help bar showing keybinding hints.
5 ///
6 /// `bindings` is a slice of `(key, action)` pairs. Keys are rendered in the
7 /// theme's primary color; actions in the dim text color. Pairs are separated
8 /// by a `·` character.
9 pub fn help(&mut self, bindings: &[(&str, &str)]) -> Response {
10 if bindings.is_empty() {
11 return Response::none();
12 }
13
14 self.skip_interaction_slot();
15 let help_gap = self.theme.spacing.sm();
16 self.commands
17 .push(Command::BeginContainer(Box::new(BeginContainerArgs {
18 direction: Direction::Row,
19 gap: help_gap as i32,
20 align: Align::Start,
21 align_self: None,
22 justify: Justify::Start,
23 border: None,
24 border_sides: BorderSides::all(),
25 border_style: Style::new().fg(self.theme.border),
26 bg_color: None,
27 padding: Padding::default(),
28 margin: Margin::default(),
29 constraints: Constraints::default(),
30 title: None,
31 grow: 0,
32 group_name: None,
33 })));
34 for (idx, (key, action)) in bindings.iter().enumerate() {
35 if idx > 0 {
36 self.styled("·", Style::new().fg(self.theme.text_dim));
37 }
38 self.styled(*key, Style::new().bold().fg(self.theme.primary));
39 self.styled(*action, Style::new().fg(self.theme.text_dim));
40 }
41 self.commands.push(Command::EndContainer);
42 self.rollback.last_text_idx = None;
43
44 Response::none()
45 }
46
47 /// Render a help bar with custom key/description colors.
48 pub fn help_colored(
49 &mut self,
50 bindings: &[(&str, &str)],
51 key_color: Color,
52 text_color: Color,
53 ) -> Response {
54 if bindings.is_empty() {
55 return Response::none();
56 }
57
58 self.skip_interaction_slot();
59 let help_gap = self.theme.spacing.sm();
60 self.commands
61 .push(Command::BeginContainer(Box::new(BeginContainerArgs {
62 direction: Direction::Row,
63 gap: help_gap as i32,
64 align: Align::Start,
65 align_self: None,
66 justify: Justify::Start,
67 border: None,
68 border_sides: BorderSides::all(),
69 border_style: Style::new().fg(self.theme.border),
70 bg_color: None,
71 padding: Padding::default(),
72 margin: Margin::default(),
73 constraints: Constraints::default(),
74 title: None,
75 grow: 0,
76 group_name: None,
77 })));
78 for (idx, (key, action)) in bindings.iter().enumerate() {
79 if idx > 0 {
80 self.styled("·", Style::new().fg(text_color));
81 }
82 self.styled(*key, Style::new().bold().fg(key_color));
83 self.styled(*action, Style::new().fg(text_color));
84 }
85 self.commands.push(Command::EndContainer);
86 self.rollback.last_text_idx = None;
87
88 Response::none()
89 }
90
91 // ── events ───────────────────────────────────────────────────────
92
93 /// Check if a character key was pressed this frame.
94 ///
95 /// Returns `true` if the key event has not been consumed by another widget.
96 pub fn key(&self, c: char) -> bool {
97 if (self.rollback.modal_active || self.prev_modal_active)
98 && self.rollback.overlay_depth == 0
99 {
100 return false;
101 }
102 self.events.iter().enumerate().any(|(i, e)| {
103 !self.consumed[i]
104 && matches!(e, Event::Key(k) if k.kind == KeyEventKind::Press && k.code == KeyCode::Char(c))
105 })
106 }
107
108 /// Check if a specific key code was pressed this frame.
109 ///
110 /// Returns `true` if the key event has not been consumed by another widget.
111 /// Blocked when a modal/overlay is active and the caller is outside the overlay.
112 /// Use [`raw_key_code`](Self::raw_key_code) for global shortcuts that must work
113 /// regardless of modal/overlay state.
114 pub fn key_code(&self, code: KeyCode) -> bool {
115 if (self.rollback.modal_active || self.prev_modal_active)
116 && self.rollback.overlay_depth == 0
117 {
118 return false;
119 }
120 self.events.iter().enumerate().any(|(i, e)| {
121 !self.consumed[i]
122 && matches!(e, Event::Key(k) if k.kind == KeyEventKind::Press && k.code == code)
123 })
124 }
125
126 /// Check if a specific key code was pressed this frame, ignoring modal/overlay state.
127 ///
128 /// Unlike [`key_code`](Self::key_code), this method bypasses the modal/overlay guard
129 /// so it works even when a modal or overlay is active. Use this for global shortcuts
130 /// (e.g. Esc to close a modal, Ctrl+Q to quit) that must always be reachable.
131 ///
132 /// Returns `true` if the key event has not been consumed by another widget.
133 pub fn raw_key_code(&self, code: KeyCode) -> bool {
134 self.events.iter().enumerate().any(|(i, e)| {
135 !self.consumed[i]
136 && matches!(e, Event::Key(k) if k.kind == KeyEventKind::Press && k.code == code)
137 })
138 }
139
140 /// Check if a character key was released this frame.
141 ///
142 /// Returns `true` if the key release event has not been consumed by another widget.
143 pub fn key_release(&self, c: char) -> bool {
144 if (self.rollback.modal_active || self.prev_modal_active)
145 && self.rollback.overlay_depth == 0
146 {
147 return false;
148 }
149 self.events.iter().enumerate().any(|(i, e)| {
150 !self.consumed[i]
151 && matches!(e, Event::Key(k) if k.kind == KeyEventKind::Release && k.code == KeyCode::Char(c))
152 })
153 }
154
155 /// Check if a specific key code was released this frame.
156 ///
157 /// Returns `true` if the key release event has not been consumed by another widget.
158 pub fn key_code_release(&self, code: KeyCode) -> bool {
159 if (self.rollback.modal_active || self.prev_modal_active)
160 && self.rollback.overlay_depth == 0
161 {
162 return false;
163 }
164 self.events.iter().enumerate().any(|(i, e)| {
165 !self.consumed[i]
166 && matches!(e, Event::Key(k) if k.kind == KeyEventKind::Release && k.code == code)
167 })
168 }
169
170 /// Check for a character key press and consume the event, preventing other
171 /// handlers from seeing it.
172 ///
173 /// Returns `true` if the key was found unconsumed and is now consumed.
174 /// Unlike [`key()`](Self::key) which peeks without consuming, this claims
175 /// exclusive ownership of the event.
176 ///
177 /// Call **after** widgets if you want widgets to have priority over your
178 /// handler, or **before** widgets to intercept first.
179 pub fn consume_key(&mut self, c: char) -> bool {
180 if (self.rollback.modal_active || self.prev_modal_active)
181 && self.rollback.overlay_depth == 0
182 {
183 return false;
184 }
185 let index = self.available_key_presses().find_map(|(i, key)| {
186 if key.code == KeyCode::Char(c) {
187 Some(i)
188 } else {
189 None
190 }
191 });
192 if let Some(index) = index {
193 self.consume_indices([index]);
194 true
195 } else {
196 false
197 }
198 }
199
200 /// Check for a special key press and consume the event, preventing other
201 /// handlers from seeing it.
202 ///
203 /// Returns `true` if the key was found unconsumed and is now consumed.
204 /// Unlike [`key_code()`](Self::key_code) which peeks without consuming,
205 /// this claims exclusive ownership of the event.
206 ///
207 /// Call **after** widgets if you want widgets to have priority over your
208 /// handler, or **before** widgets to intercept first.
209 pub fn consume_key_code(&mut self, code: KeyCode) -> bool {
210 if (self.rollback.modal_active || self.prev_modal_active)
211 && self.rollback.overlay_depth == 0
212 {
213 return false;
214 }
215 let index = self
216 .available_key_presses()
217 .find_map(|(i, key)| if key.code == code { Some(i) } else { None });
218 if let Some(index) = index {
219 self.consume_indices([index]);
220 true
221 } else {
222 false
223 }
224 }
225
226 /// Check if a character key with specific modifiers was pressed this frame.
227 ///
228 /// Returns `true` if the key event has not been consumed by another widget.
229 pub fn key_mod(&self, c: char, modifiers: KeyModifiers) -> bool {
230 if (self.rollback.modal_active || self.prev_modal_active)
231 && self.rollback.overlay_depth == 0
232 {
233 return false;
234 }
235 self.events.iter().enumerate().any(|(i, e)| {
236 !self.consumed[i]
237 && matches!(e, Event::Key(k) if k.kind == KeyEventKind::Press && k.code == KeyCode::Char(c) && k.modifiers.contains(modifiers))
238 })
239 }
240
241 /// Like [`key_mod`](Self::key_mod) but bypasses the modal/overlay guard.
242 pub fn raw_key_mod(&self, c: char, modifiers: KeyModifiers) -> bool {
243 self.events.iter().enumerate().any(|(i, e)| {
244 !self.consumed[i]
245 && matches!(e, Event::Key(k) if k.kind == KeyEventKind::Press && k.code == KeyCode::Char(c) && k.modifiers.contains(modifiers))
246 })
247 }
248
249 /// Return the position of a left mouse button down event this frame, if any.
250 ///
251 /// Returns `None` if no unconsumed mouse-down event occurred.
252 pub fn mouse_down(&self) -> Option<(u32, u32)> {
253 if (self.rollback.modal_active || self.prev_modal_active)
254 && self.rollback.overlay_depth == 0
255 {
256 return None;
257 }
258 self.events.iter().enumerate().find_map(|(i, event)| {
259 if self.consumed[i] {
260 return None;
261 }
262 if let Event::Mouse(mouse) = event
263 && matches!(mouse.kind, MouseKind::Down(MouseButton::Left))
264 {
265 return Some((mouse.x, mouse.y));
266 }
267 None
268 })
269 }
270
271 /// Return the position of a left mouse button drag event this frame, if any.
272 ///
273 /// Returns `None` if no unconsumed drag event occurred. Drag events fire
274 /// while the left button is held and the cursor moves.
275 pub fn mouse_drag(&self) -> Option<(u32, u32)> {
276 if (self.rollback.modal_active || self.prev_modal_active)
277 && self.rollback.overlay_depth == 0
278 {
279 return None;
280 }
281 self.events.iter().enumerate().find_map(|(i, event)| {
282 if self.consumed[i] {
283 return None;
284 }
285 if let Event::Mouse(mouse) = event
286 && matches!(mouse.kind, MouseKind::Drag(MouseButton::Left))
287 {
288 return Some((mouse.x, mouse.y));
289 }
290 None
291 })
292 }
293
294 /// Return the position of a left mouse button release event this frame, if any.
295 ///
296 /// Returns `None` if no unconsumed mouse-up event occurred.
297 pub fn mouse_up(&self) -> Option<(u32, u32)> {
298 if (self.rollback.modal_active || self.prev_modal_active)
299 && self.rollback.overlay_depth == 0
300 {
301 return None;
302 }
303 self.events.iter().enumerate().find_map(|(i, event)| {
304 if self.consumed[i] {
305 return None;
306 }
307 if let Event::Mouse(mouse) = event
308 && matches!(mouse.kind, MouseKind::Up(MouseButton::Left))
309 {
310 return Some((mouse.x, mouse.y));
311 }
312 None
313 })
314 }
315
316 /// Return the position of a mouse button down event for the specified button.
317 ///
318 /// This is a generalized version of [`mouse_down`](Self::mouse_down) that
319 /// accepts any [`MouseButton`].
320 pub fn mouse_down_button(&self, button: MouseButton) -> Option<(u32, u32)> {
321 if (self.rollback.modal_active || self.prev_modal_active)
322 && self.rollback.overlay_depth == 0
323 {
324 return None;
325 }
326 self.events.iter().enumerate().find_map(|(i, event)| {
327 if self.consumed[i] {
328 return None;
329 }
330 if let Event::Mouse(mouse) = event
331 && matches!(&mouse.kind, MouseKind::Down(b) if *b == button)
332 {
333 return Some((mouse.x, mouse.y));
334 }
335 None
336 })
337 }
338
339 /// Return the position of a mouse drag event for the specified button.
340 pub fn mouse_drag_button(&self, button: MouseButton) -> Option<(u32, u32)> {
341 if (self.rollback.modal_active || self.prev_modal_active)
342 && self.rollback.overlay_depth == 0
343 {
344 return None;
345 }
346 self.events.iter().enumerate().find_map(|(i, event)| {
347 if self.consumed[i] {
348 return None;
349 }
350 if let Event::Mouse(mouse) = event
351 && matches!(&mouse.kind, MouseKind::Drag(b) if *b == button)
352 {
353 return Some((mouse.x, mouse.y));
354 }
355 None
356 })
357 }
358
359 /// Return the position of a mouse button release event for the specified button.
360 pub fn mouse_up_button(&self, button: MouseButton) -> Option<(u32, u32)> {
361 if (self.rollback.modal_active || self.prev_modal_active)
362 && self.rollback.overlay_depth == 0
363 {
364 return None;
365 }
366 self.events.iter().enumerate().find_map(|(i, event)| {
367 if self.consumed[i] {
368 return None;
369 }
370 if let Event::Mouse(mouse) = event
371 && matches!(&mouse.kind, MouseKind::Up(b) if *b == button)
372 {
373 return Some((mouse.x, mouse.y));
374 }
375 None
376 })
377 }
378
379 /// Return the current mouse cursor position, if known.
380 ///
381 /// The position is updated on every mouse move or click event. Returns
382 /// `None` until the first mouse event is received.
383 pub fn mouse_pos(&self) -> Option<(u32, u32)> {
384 self.mouse_pos
385 }
386
387 /// Return the first unconsumed paste event text, if any.
388 pub fn paste(&self) -> Option<&str> {
389 if (self.rollback.modal_active || self.prev_modal_active)
390 && self.rollback.overlay_depth == 0
391 {
392 return None;
393 }
394 self.events.iter().enumerate().find_map(|(i, event)| {
395 if self.consumed[i] {
396 return None;
397 }
398 if let Event::Paste(text) = event {
399 return Some(text.as_str());
400 }
401 None
402 })
403 }
404
405 /// Check if an unconsumed scroll-up event occurred this frame.
406 pub fn scroll_up(&self) -> bool {
407 if (self.rollback.modal_active || self.prev_modal_active)
408 && self.rollback.overlay_depth == 0
409 {
410 return false;
411 }
412 self.events.iter().enumerate().any(|(i, event)| {
413 !self.consumed[i]
414 && matches!(event, Event::Mouse(mouse) if matches!(mouse.kind, MouseKind::ScrollUp))
415 })
416 }
417
418 /// Check if an unconsumed scroll-down event occurred this frame.
419 pub fn scroll_down(&self) -> bool {
420 if (self.rollback.modal_active || self.prev_modal_active)
421 && self.rollback.overlay_depth == 0
422 {
423 return false;
424 }
425 self.events.iter().enumerate().any(|(i, event)| {
426 !self.consumed[i]
427 && matches!(event, Event::Mouse(mouse) if matches!(mouse.kind, MouseKind::ScrollDown))
428 })
429 }
430
431 /// Check if an unconsumed scroll-left event occurred this frame.
432 pub fn scroll_left(&self) -> bool {
433 if (self.rollback.modal_active || self.prev_modal_active)
434 && self.rollback.overlay_depth == 0
435 {
436 return false;
437 }
438 self.events.iter().enumerate().any(|(i, event)| {
439 !self.consumed[i]
440 && matches!(event, Event::Mouse(mouse) if matches!(mouse.kind, MouseKind::ScrollLeft))
441 })
442 }
443
444 /// Check if an unconsumed scroll-right event occurred this frame.
445 pub fn scroll_right(&self) -> bool {
446 if (self.rollback.modal_active || self.prev_modal_active)
447 && self.rollback.overlay_depth == 0
448 {
449 return false;
450 }
451 self.events.iter().enumerate().any(|(i, event)| {
452 !self.consumed[i]
453 && matches!(event, Event::Mouse(mouse) if matches!(mouse.kind, MouseKind::ScrollRight))
454 })
455 }
456
457 /// Iterate over unconsumed events this frame, respecting the modal guard.
458 ///
459 /// Returns an empty iterator when a modal is active and the caller is not
460 /// inside an overlay. Use [`raw_events`](Self::raw_events) to bypass the
461 /// modal guard (e.g., for global hotkeys).
462 ///
463 /// # Example
464 ///
465 /// ```no_run
466 /// # slt::run(|ui: &mut slt::Context| {
467 /// for event in ui.events() {
468 /// if let slt::Event::Mouse(mouse) = event {
469 /// if matches!(mouse.kind, slt::MouseKind::Down(slt::MouseButton::Right)) {
470 /// // handle right-click
471 /// }
472 /// }
473 /// }
474 /// # });
475 /// ```
476 pub fn events(&self) -> impl Iterator<Item = &Event> {
477 let blocked = (self.rollback.modal_active || self.prev_modal_active)
478 && self.rollback.overlay_depth == 0;
479 self.events.iter().enumerate().filter_map(move |(i, e)| {
480 if blocked || self.consumed[i] {
481 None
482 } else {
483 Some(e)
484 }
485 })
486 }
487
488 /// Iterate over all unconsumed events, bypassing the modal guard.
489 ///
490 /// Use this for global shortcuts that must work even when a modal or
491 /// overlay is active. Prefer [`events`](Self::events) for normal use.
492 pub fn raw_events(&self) -> impl Iterator<Item = &Event> + '_ {
493 self.events
494 .iter()
495 .enumerate()
496 .filter_map(|(i, e)| if self.consumed[i] { None } else { Some(e) })
497 }
498
499 /// Signal the run loop to exit after this frame.
500 pub fn quit(&mut self) {
501 self.should_quit = true;
502 }
503
504 /// Copy text to the system clipboard via OSC 52.
505 ///
506 /// Works transparently over SSH connections. The text is queued and
507 /// written to the terminal after the current frame renders.
508 ///
509 /// Requires a terminal that supports OSC 52 (most modern terminals:
510 /// Ghostty, kitty, WezTerm, iTerm2, Windows Terminal).
511 pub fn copy_to_clipboard(&mut self, text: impl Into<String>) {
512 self.clipboard_text = Some(text.into());
513 }
514
515 /// Get the current theme.
516 pub fn theme(&self) -> &Theme {
517 &self.theme
518 }
519
520 /// Resolve a [`ThemeColor`] token against the current theme.
521 pub fn color(&self, token: ThemeColor) -> Color {
522 self.theme.resolve(token)
523 }
524
525 /// Get the current spacing scale from the theme.
526 pub fn spacing(&self) -> Spacing {
527 self.theme.spacing
528 }
529
530 /// Change the theme for subsequent rendering.
531 ///
532 /// All widgets rendered after this call will use the new theme's colors.
533 pub fn set_theme(&mut self, theme: Theme) {
534 self.theme = theme;
535 }
536
537 /// Check if dark mode is active.
538 pub fn is_dark_mode(&self) -> bool {
539 self.rollback.dark_mode
540 }
541
542 /// Set dark mode. When true, dark_* style variants are applied.
543 pub fn set_dark_mode(&mut self, dark: bool) {
544 self.rollback.dark_mode = dark;
545 }
546
547 // ── info ─────────────────────────────────────────────────────────
548
549 /// Get the terminal width in cells.
550 pub fn width(&self) -> u32 {
551 self.area_width
552 }
553
554 /// Get the current terminal width breakpoint.
555 ///
556 /// Returns a [`Breakpoint`] based on the terminal width:
557 /// - `Xs`: < 40 columns
558 /// - `Sm`: 40-79 columns
559 /// - `Md`: 80-119 columns
560 /// - `Lg`: 120-159 columns
561 /// - `Xl`: >= 160 columns
562 ///
563 /// Use this for responsive layouts that adapt to terminal size:
564 /// ```no_run
565 /// # use slt::{Breakpoint, Context};
566 /// # slt::run(|ui: &mut Context| {
567 /// match ui.breakpoint() {
568 /// Breakpoint::Xs | Breakpoint::Sm => {
569 /// ui.col(|ui| { ui.text("Stacked layout"); });
570 /// }
571 /// _ => {
572 /// ui.row(|ui| { ui.text("Side-by-side layout"); });
573 /// }
574 /// }
575 /// # });
576 /// ```
577 pub fn breakpoint(&self) -> Breakpoint {
578 let w = self.area_width;
579 if w < 40 {
580 Breakpoint::Xs
581 } else if w < 80 {
582 Breakpoint::Sm
583 } else if w < 120 {
584 Breakpoint::Md
585 } else if w < 160 {
586 Breakpoint::Lg
587 } else {
588 Breakpoint::Xl
589 }
590 }
591
592 /// Get the terminal height in cells.
593 pub fn height(&self) -> u32 {
594 self.area_height
595 }
596
597 /// Get the current tick count (increments each frame).
598 ///
599 /// Useful for animations and time-based logic. The tick starts at 0 and
600 /// increases by 1 on every rendered frame.
601 pub fn tick(&self) -> u64 {
602 self.tick
603 }
604
605 /// Return whether the layout debugger is enabled.
606 ///
607 /// The debugger is toggled with F12 at runtime.
608 pub fn debug_enabled(&self) -> bool {
609 self.debug
610 }
611
612 /// Return which layers the F12 debug overlay outlines (issue #201).
613 ///
614 /// Default is [`crate::DebugLayer::All`], which outlines the base tree
615 /// plus any active overlays/modals. See
616 /// [`set_debug_layer`](Self::set_debug_layer) to narrow the outline to
617 /// a specific layer.
618 ///
619 /// # Example
620 ///
621 /// ```no_run
622 /// use slt::{Context, DebugLayer};
623 ///
624 /// slt::run(|ui: &mut Context| {
625 /// // Read the current layer to drive a UI badge or debug toolbar.
626 /// match ui.debug_layer() {
627 /// DebugLayer::All => ui.text("layer: all"),
628 /// DebugLayer::TopMost => ui.text("layer: topmost"),
629 /// DebugLayer::BaseOnly => ui.text("layer: base"),
630 /// };
631 /// }).unwrap();
632 /// ```
633 pub fn debug_layer(&self) -> crate::DebugLayer {
634 self.debug_layer
635 }
636
637 /// Choose which layers the F12 debug overlay outlines (issue #201).
638 ///
639 /// Persists across frames. The default ([`crate::DebugLayer::All`])
640 /// matches the reporter's expectation that F12 reflects everything the
641 /// renderer is drawing. Use [`crate::DebugLayer::TopMost`] to focus on
642 /// the active modal / overlay only, or [`crate::DebugLayer::BaseOnly`]
643 /// to keep the legacy behavior of skipping overlays.
644 ///
645 /// # Runtime keybinding
646 ///
647 /// At runtime, **Shift+F12** cycles through `All` → `TopMost` →
648 /// `BaseOnly` → `All`. Plain F12 still toggles the overlay on/off.
649 /// The two keys are independent: enabling the overlay does not change
650 /// the active layer, and cycling layers does not enable the overlay.
651 ///
652 /// # Example
653 ///
654 /// ```no_run
655 /// use slt::{Context, DebugLayer};
656 ///
657 /// slt::run(|ui: &mut Context| {
658 /// // Toggle between viewing only the base tree and viewing all
659 /// // layers, e.g. from a custom debug menu.
660 /// let next = match ui.debug_layer() {
661 /// DebugLayer::All => DebugLayer::BaseOnly,
662 /// DebugLayer::BaseOnly => DebugLayer::TopMost,
663 /// DebugLayer::TopMost => DebugLayer::All,
664 /// };
665 /// ui.set_debug_layer(next);
666 /// }).unwrap();
667 /// ```
668 pub fn set_debug_layer(&mut self, layer: crate::DebugLayer) {
669 self.debug_layer = layer;
670 }
671
672 /// Return whether the devtools inspector panel is active (issue #268).
673 ///
674 /// The inspector is toggled with **Ctrl+F12** at runtime, or
675 /// programmatically via [`set_inspector`](Self::set_inspector). It is
676 /// independent of the F12 outline overlay and the Shift+F12 layer cycle.
677 /// When on, the inspector draws a resolved-style panel for the focused
678 /// widget and a focus-chain panel listing every focusable in order.
679 ///
680 /// Since 0.21.0.
681 ///
682 /// # Example
683 ///
684 /// ```no_run
685 /// use slt::Context;
686 ///
687 /// slt::run(|ui: &mut Context| {
688 /// if ui.inspector() {
689 /// ui.text("inspector on (Ctrl+F12 to hide)");
690 /// }
691 /// })
692 /// .unwrap();
693 /// ```
694 pub fn inspector(&self) -> bool {
695 self.inspector_mode
696 }
697
698 /// Show or hide the devtools inspector panel (issue #268).
699 ///
700 /// Symmetric with [`set_debug_layer`](Self::set_debug_layer): the change
701 /// persists across frames. Enables (or disables) the focused-widget
702 /// resolved-style panel and the focus-chain inspector. Equivalent to the
703 /// runtime **Ctrl+F12** toggle; does not affect the F12 outline overlay.
704 ///
705 /// Since 0.21.0.
706 ///
707 /// # Example
708 ///
709 /// ```no_run
710 /// use slt::Context;
711 ///
712 /// slt::run(|ui: &mut Context| {
713 /// // Open the inspector from a custom debug menu.
714 /// ui.set_inspector(true);
715 /// })
716 /// .unwrap();
717 /// ```
718 pub fn set_inspector(&mut self, on: bool) {
719 self.inspector_mode = on;
720 }
721}