1mod events;
34mod helpers;
35mod inspector;
36mod profiler;
37mod state;
38mod style;
39mod time_travel;
40
41pub use events::{EventFilter, EventLogger, EventType, LoggedEvent};
42pub use inspector::{ComponentPicker, Inspector, InspectorConfig, PickerMode, WidgetNode};
43pub use profiler::{ComponentStats, Frame, Profiler, ProfilerView, RenderEvent, RenderReason};
44pub use state::{StateDebugger, StateEntry, StateValue};
45pub use style::{ComputedProperty, PropertySource, StyleCategory, StyleInspector};
46pub use time_travel::{
47 Action, SnapshotValue, StateDiff, StateSnapshot, TimeTravelConfig, TimeTravelDebugger,
48 TimeTravelView,
49};
50
51use crate::layout::Rect;
52use crate::render::Buffer;
53use crate::style::Color;
54use std::sync::atomic::{AtomicBool, Ordering};
55
56#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
62pub enum DevToolsPosition {
63 #[default]
65 Right,
66 Bottom,
68 Left,
70 Overlay,
72}
73
74#[derive(Debug, Clone)]
76pub struct DevToolsConfig {
77 pub position: DevToolsPosition,
79 pub size: u16,
81 pub visible: bool,
83 pub active_tab: DevToolsTab,
85 pub bg_color: Color,
87 pub fg_color: Color,
89 pub accent_color: Color,
91}
92
93impl Default for DevToolsConfig {
94 fn default() -> Self {
95 Self {
96 position: DevToolsPosition::Right,
97 size: 50,
98 visible: false,
99 active_tab: DevToolsTab::Inspector,
100 bg_color: Color::rgb(25, 25, 35),
101 fg_color: Color::rgb(200, 200, 210),
102 accent_color: Color::rgb(130, 180, 255),
103 }
104 }
105}
106
107#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
109pub enum DevToolsTab {
110 #[default]
112 Inspector,
113 State,
115 Styles,
117 Events,
119 Profiler,
121 TimeTravel,
123}
124
125impl DevToolsTab {
126 pub fn label(&self) -> &'static str {
128 match self {
129 Self::Inspector => "Inspector",
130 Self::State => "State",
131 Self::Styles => "Styles",
132 Self::Events => "Events",
133 Self::Profiler => "Profiler",
134 Self::TimeTravel => "Travel",
135 }
136 }
137
138 pub fn all() -> &'static [DevToolsTab] {
140 &[
141 DevToolsTab::Inspector,
142 DevToolsTab::State,
143 DevToolsTab::Styles,
144 DevToolsTab::Events,
145 DevToolsTab::Profiler,
146 DevToolsTab::TimeTravel,
147 ]
148 }
149
150 pub fn next(&self) -> Self {
152 match self {
153 Self::Inspector => Self::State,
154 Self::State => Self::Styles,
155 Self::Styles => Self::Events,
156 Self::Events => Self::Profiler,
157 Self::Profiler => Self::TimeTravel,
158 Self::TimeTravel => Self::Inspector,
159 }
160 }
161
162 pub fn prev(&self) -> Self {
164 match self {
165 Self::Inspector => Self::TimeTravel,
166 Self::State => Self::Inspector,
167 Self::Styles => Self::State,
168 Self::Events => Self::Styles,
169 Self::Profiler => Self::Events,
170 Self::TimeTravel => Self::Profiler,
171 }
172 }
173}
174
175pub struct DevTools {
181 config: DevToolsConfig,
183 inspector: Inspector,
185 state: StateDebugger,
187 styles: StyleInspector,
189 events: EventLogger,
191 profiler: Profiler,
193 time_travel: TimeTravelDebugger,
195}
196
197impl DevTools {
198 pub fn new() -> Self {
200 Self {
201 config: DevToolsConfig::default(),
202 inspector: Inspector::new(),
203 state: StateDebugger::new(),
204 styles: StyleInspector::new(),
205 events: EventLogger::new(),
206 profiler: Profiler::new(),
207 time_travel: TimeTravelDebugger::new(),
208 }
209 }
210
211 pub fn config(mut self, config: DevToolsConfig) -> Self {
213 self.config = config;
214 self
215 }
216
217 pub fn position(mut self, position: DevToolsPosition) -> Self {
219 self.config.position = position;
220 self
221 }
222
223 pub fn size(mut self, size: u16) -> Self {
225 self.config.size = size;
226 self
227 }
228
229 pub fn toggle(&mut self) {
231 self.config.visible = !self.config.visible;
232 }
233
234 pub fn set_visible(&mut self, visible: bool) {
236 self.config.visible = visible;
237 }
238
239 pub fn is_visible(&self) -> bool {
241 self.config.visible
242 }
243
244 pub fn set_tab(&mut self, tab: DevToolsTab) {
246 self.config.active_tab = tab;
247 }
248
249 pub fn next_tab(&mut self) {
251 self.config.active_tab = self.config.active_tab.next();
252 }
253
254 pub fn prev_tab(&mut self) {
256 self.config.active_tab = self.config.active_tab.prev();
257 }
258
259 pub fn inspector(&self) -> &Inspector {
261 &self.inspector
262 }
263
264 pub fn inspector_mut(&mut self) -> &mut Inspector {
266 &mut self.inspector
267 }
268
269 pub fn state(&self) -> &StateDebugger {
271 &self.state
272 }
273
274 pub fn state_mut(&mut self) -> &mut StateDebugger {
276 &mut self.state
277 }
278
279 pub fn styles(&self) -> &StyleInspector {
281 &self.styles
282 }
283
284 pub fn styles_mut(&mut self) -> &mut StyleInspector {
286 &mut self.styles
287 }
288
289 pub fn events(&self) -> &EventLogger {
291 &self.events
292 }
293
294 pub fn events_mut(&mut self) -> &mut EventLogger {
296 &mut self.events
297 }
298
299 pub fn profiler(&self) -> &Profiler {
301 &self.profiler
302 }
303
304 pub fn profiler_mut(&mut self) -> &mut Profiler {
306 &mut self.profiler
307 }
308
309 pub fn time_travel(&self) -> &TimeTravelDebugger {
311 &self.time_travel
312 }
313
314 pub fn time_travel_mut(&mut self) -> &mut TimeTravelDebugger {
316 &mut self.time_travel
317 }
318
319 pub fn panel_rect(&self, area: Rect) -> Option<Rect> {
321 if !self.config.visible {
322 return None;
323 }
324
325 let size = self.config.size;
326
327 Some(match self.config.position {
328 DevToolsPosition::Right => Rect::new(
329 area.x + area.width.saturating_sub(size),
330 area.y,
331 size.min(area.width),
332 area.height,
333 ),
334 DevToolsPosition::Left => Rect::new(area.x, area.y, size.min(area.width), area.height),
335 DevToolsPosition::Bottom => Rect::new(
336 area.x,
337 area.y + area.height.saturating_sub(size),
338 area.width,
339 size.min(area.height),
340 ),
341 DevToolsPosition::Overlay => {
342 let width = (area.width * 2 / 3).min(80);
343 let height = (area.height * 2 / 3).min(30);
344 Rect::new(
345 area.x + (area.width - width) / 2,
346 area.y + (area.height - height) / 2,
347 width,
348 height,
349 )
350 }
351 })
352 }
353
354 pub fn content_rect(&self, area: Rect) -> Rect {
356 if !self.config.visible {
357 return area;
358 }
359
360 let size = self.config.size;
361
362 match self.config.position {
363 DevToolsPosition::Right => {
364 Rect::new(area.x, area.y, area.width.saturating_sub(size), area.height)
365 }
366 DevToolsPosition::Left => Rect::new(
367 area.x + size.min(area.width),
368 area.y,
369 area.width.saturating_sub(size),
370 area.height,
371 ),
372 DevToolsPosition::Bottom => {
373 Rect::new(area.x, area.y, area.width, area.height.saturating_sub(size))
374 }
375 DevToolsPosition::Overlay => area,
376 }
377 }
378
379 pub fn render(&self, buffer: &mut Buffer, area: Rect) {
381 if let Some(panel) = self.panel_rect(area) {
382 self.render_panel(buffer, panel);
383 }
384 }
385
386 fn render_panel(&self, buffer: &mut Buffer, area: Rect) {
387 for y in area.y..area.y + area.height {
389 for x in area.x..area.x + area.width {
390 if let Some(cell) = buffer.get_mut(x, y) {
391 cell.symbol = ' ';
392 cell.bg = Some(self.config.bg_color);
393 cell.fg = Some(self.config.fg_color);
394 }
395 }
396 }
397
398 self.draw_border(buffer, area);
400
401 let tab_area = Rect::new(area.x + 1, area.y + 1, area.width - 2, 1);
403 self.render_tabs(buffer, tab_area);
404
405 let content_area = Rect::new(
407 area.x + 1,
408 area.y + 3,
409 area.width - 2,
410 area.height.saturating_sub(4),
411 );
412
413 match self.config.active_tab {
414 DevToolsTab::Inspector => {
415 self.inspector
416 .render_content(buffer, content_area, &self.config)
417 }
418 DevToolsTab::State => self
419 .state
420 .render_content(buffer, content_area, &self.config),
421 DevToolsTab::Styles => self
422 .styles
423 .render_content(buffer, content_area, &self.config),
424 DevToolsTab::Events => self
425 .events
426 .render_content(buffer, content_area, &self.config),
427 DevToolsTab::Profiler => {
428 self.profiler
429 .render_content(buffer, content_area, &self.config)
430 }
431 DevToolsTab::TimeTravel => {
432 self.time_travel
433 .render_content(buffer, content_area, &self.config)
434 }
435 }
436 }
437
438 fn render_tabs(&self, buffer: &mut Buffer, area: Rect) {
439 let mut x = area.x;
440
441 for tab in DevToolsTab::all() {
442 let label = format!(" {} ", tab.label());
443 let is_active = *tab == self.config.active_tab;
444
445 let (fg, bg) = if is_active {
446 (self.config.bg_color, self.config.accent_color)
447 } else {
448 (self.config.fg_color, self.config.bg_color)
449 };
450
451 for ch in label.chars() {
452 if x < area.x + area.width {
453 if let Some(cell) = buffer.get_mut(x, area.y) {
454 cell.symbol = ch;
455 cell.fg = Some(fg);
456 cell.bg = Some(bg);
457 }
458 x += 1;
459 }
460 }
461
462 x += 1; }
464 }
465
466 fn draw_border(&self, buffer: &mut Buffer, area: Rect) {
467 let color = self.config.accent_color;
468
469 for x in area.x..area.x + area.width {
471 if let Some(cell) = buffer.get_mut(x, area.y) {
472 cell.symbol = if x == area.x {
473 '┌'
474 } else if x == area.x + area.width - 1 {
475 '┐'
476 } else {
477 '─'
478 };
479 cell.fg = Some(color);
480 }
481 if let Some(cell) = buffer.get_mut(x, area.y + area.height - 1) {
482 cell.symbol = if x == area.x {
483 '└'
484 } else if x == area.x + area.width - 1 {
485 '┘'
486 } else {
487 '─'
488 };
489 cell.fg = Some(color);
490 }
491 }
492
493 for y in area.y + 1..area.y + area.height - 1 {
494 if let Some(cell) = buffer.get_mut(area.x, y) {
495 cell.symbol = '│';
496 cell.fg = Some(color);
497 }
498 if let Some(cell) = buffer.get_mut(area.x + area.width - 1, y) {
499 cell.symbol = '│';
500 cell.fg = Some(color);
501 }
502 }
503
504 for x in area.x..area.x + area.width {
506 if let Some(cell) = buffer.get_mut(x, area.y + 2) {
507 cell.symbol = if x == area.x {
508 '├'
509 } else if x == area.x + area.width - 1 {
510 '┤'
511 } else {
512 '─'
513 };
514 cell.fg = Some(color);
515 }
516 }
517 }
518}
519
520impl Default for DevTools {
521 fn default() -> Self {
522 Self::new()
523 }
524}
525
526static DEVTOOLS_ENABLED: AtomicBool = AtomicBool::new(false);
535
536#[deprecated(since = "2.1.0", note = "Use App::enable_devtools() instead")]
541pub fn enable_devtools() {
542 DEVTOOLS_ENABLED.store(true, Ordering::Relaxed);
543}
544
545#[deprecated(since = "2.1.0", note = "Use App::disable_devtools() instead")]
550pub fn disable_devtools() {
551 DEVTOOLS_ENABLED.store(false, Ordering::Relaxed);
552}
553
554#[deprecated(since = "2.1.0", note = "Use App::is_devtools_enabled() instead")]
559pub fn is_devtools_enabled() -> bool {
560 DEVTOOLS_ENABLED.load(Ordering::Relaxed)
561}
562
563#[deprecated(since = "2.1.0", note = "Use App::toggle_devtools() instead")]
568pub fn toggle_devtools() -> bool {
569 let was = DEVTOOLS_ENABLED.fetch_xor(true, Ordering::Relaxed);
570 !was
571}
572
573#[cfg(test)]
578mod tests {
579 use super::*;
580 use crate::app::App;
581
582 #[test]
583 fn test_devtools_config_default() {
584 let config = DevToolsConfig::default();
585 assert!(!config.visible);
586 assert_eq!(config.position, DevToolsPosition::Right);
587 assert_eq!(config.active_tab, DevToolsTab::Inspector);
588 assert_eq!(config.size, 50);
589 }
590
591 #[test]
592 fn test_devtools_tab_cycle() {
593 let tab = DevToolsTab::Inspector;
594 assert_eq!(tab.next(), DevToolsTab::State);
595 assert_eq!(tab.prev(), DevToolsTab::TimeTravel);
596 }
597
598 #[test]
599 fn test_devtools_toggle() {
600 let mut devtools = DevTools::new();
601 assert!(!devtools.is_visible());
602
603 devtools.toggle();
604 assert!(devtools.is_visible());
605
606 devtools.toggle();
607 assert!(!devtools.is_visible());
608 }
609
610 #[test]
611 fn test_panel_rect_right() {
612 let devtools = DevTools::new().size(30);
613 let mut dt = devtools;
614 dt.set_visible(true);
615
616 let area = Rect::new(0, 0, 100, 50);
617 let panel = dt.panel_rect(area).unwrap();
618
619 assert_eq!(panel.x, 70);
620 assert_eq!(panel.width, 30);
621 assert_eq!(panel.height, 50);
622 }
623
624 #[test]
625 fn test_panel_rect_left() {
626 let mut devtools = DevTools::new().size(25);
627 devtools.set_visible(true);
628 devtools.config.position = DevToolsPosition::Left;
629
630 let area = Rect::new(10, 5, 100, 50);
631 let panel = devtools.panel_rect(area).unwrap();
632
633 assert_eq!(panel.x, 10);
634 assert_eq!(panel.y, 5);
635 assert_eq!(panel.width, 25);
636 assert_eq!(panel.height, 50);
637 }
638
639 #[test]
640 fn test_panel_rect_bottom() {
641 let mut devtools = DevTools::new().size(15);
642 devtools.set_visible(true);
643 devtools.config.position = DevToolsPosition::Bottom;
644
645 let area = Rect::new(0, 0, 100, 50);
646 let panel = devtools.panel_rect(area).unwrap();
647
648 assert_eq!(panel.x, 0);
649 assert_eq!(panel.y, 35);
650 assert_eq!(panel.width, 100);
651 assert_eq!(panel.height, 15);
652 }
653
654 #[test]
655 fn test_panel_rect_overlay() {
656 let mut devtools = DevTools::new();
657 devtools.set_visible(true);
658 devtools.config.position = DevToolsPosition::Overlay;
659
660 let area = Rect::new(0, 0, 100, 50);
661 let panel = devtools.panel_rect(area).unwrap();
662
663 assert_eq!(panel.width, 66); assert_eq!(panel.height, 30); }
667
668 #[test]
669 fn test_panel_rect_invisible() {
670 let mut devtools = DevTools::new();
671 devtools.set_visible(false);
672
673 let area = Rect::new(0, 0, 100, 50);
674 assert!(devtools.panel_rect(area).is_none());
675 }
676
677 #[test]
678 fn test_content_rect_right() {
679 let mut devtools = DevTools::new().size(30);
680 devtools.set_visible(true);
681
682 let area = Rect::new(0, 0, 100, 50);
683 let content = devtools.content_rect(area);
684
685 assert_eq!(content.x, 0);
686 assert_eq!(content.y, 0);
687 assert_eq!(content.width, 70);
688 assert_eq!(content.height, 50);
689 }
690
691 #[test]
692 fn test_content_rect_left() {
693 let mut devtools = DevTools::new().size(30);
694 devtools.set_visible(true);
695 devtools.config.position = DevToolsPosition::Left;
696
697 let area = Rect::new(0, 0, 100, 50);
698 let content = devtools.content_rect(area);
699
700 assert_eq!(content.x, 30);
701 assert_eq!(content.width, 70);
702 }
703
704 #[test]
705 fn test_content_rect_bottom() {
706 let mut devtools = DevTools::new().size(20);
707 devtools.set_visible(true);
708 devtools.config.position = DevToolsPosition::Bottom;
709
710 let area = Rect::new(0, 0, 100, 50);
711 let content = devtools.content_rect(area);
712
713 assert_eq!(content.y, 0);
714 assert_eq!(content.height, 30);
715 }
716
717 #[test]
718 fn test_content_rect_overlay() {
719 let mut devtools = DevTools::new();
720 devtools.set_visible(true);
721 devtools.config.position = DevToolsPosition::Overlay;
722
723 let area = Rect::new(0, 0, 100, 50);
724 let content = devtools.content_rect(area);
725
726 assert_eq!(content, area);
728 }
729
730 #[test]
731 fn test_content_rect_invisible() {
732 let mut devtools = DevTools::new();
733 devtools.set_visible(false);
734
735 let area = Rect::new(0, 0, 100, 50);
736 let content = devtools.content_rect(area);
737
738 assert_eq!(content, area);
739 }
740
741 #[test]
742 fn test_devtools_new() {
743 let devtools = DevTools::new();
744 assert!(!devtools.is_visible());
745 assert_eq!(devtools.config.active_tab, DevToolsTab::Inspector);
746 }
747
748 #[test]
749 fn test_devtools_default() {
750 let devtools = DevTools::default();
751 assert!(!devtools.is_visible());
752 assert_eq!(devtools.config.active_tab, DevToolsTab::Inspector);
753 }
754
755 #[test]
756 fn test_devtools_config_builder() {
757 let config = DevToolsConfig {
758 position: DevToolsPosition::Left,
759 size: 40,
760 visible: true,
761 active_tab: DevToolsTab::Profiler,
762 bg_color: Color::rgb(10, 10, 10),
763 fg_color: Color::rgb(255, 255, 255),
764 accent_color: Color::rgb(100, 100, 255),
765 };
766
767 let devtools = DevTools::new().config(config);
768 assert!(devtools.is_visible());
769 assert_eq!(devtools.config.position, DevToolsPosition::Left);
770 assert_eq!(devtools.config.size, 40);
771 assert_eq!(devtools.config.active_tab, DevToolsTab::Profiler);
772 }
773
774 #[test]
775 fn test_devtools_position() {
776 let devtools = DevTools::new().position(DevToolsPosition::Bottom);
777 assert_eq!(devtools.config.position, DevToolsPosition::Bottom);
778 }
779
780 #[test]
781 fn test_devtools_size() {
782 let devtools = DevTools::new().size(60);
783 assert_eq!(devtools.config.size, 60);
784 }
785
786 #[test]
787 fn test_devtools_set_visible() {
788 let mut devtools = DevTools::new();
789 assert!(!devtools.is_visible());
790
791 devtools.set_visible(true);
792 assert!(devtools.is_visible());
793
794 devtools.set_visible(false);
795 assert!(!devtools.is_visible());
796 }
797
798 #[test]
799 fn test_devtools_set_tab() {
800 let mut devtools = DevTools::new();
801 assert_eq!(devtools.config.active_tab, DevToolsTab::Inspector);
802
803 devtools.set_tab(DevToolsTab::Profiler);
804 assert_eq!(devtools.config.active_tab, DevToolsTab::Profiler);
805 }
806
807 #[test]
808 fn test_devtools_next_tab() {
809 let mut devtools = DevTools::new();
810 assert_eq!(devtools.config.active_tab, DevToolsTab::Inspector);
811
812 devtools.next_tab();
813 assert_eq!(devtools.config.active_tab, DevToolsTab::State);
814
815 devtools.next_tab();
816 assert_eq!(devtools.config.active_tab, DevToolsTab::Styles);
817 }
818
819 #[test]
820 fn test_devtools_prev_tab() {
821 let mut devtools = DevTools::new();
822 assert_eq!(devtools.config.active_tab, DevToolsTab::Inspector);
823
824 devtools.prev_tab();
825 assert_eq!(devtools.config.active_tab, DevToolsTab::TimeTravel);
826
827 devtools.prev_tab();
828 assert_eq!(devtools.config.active_tab, DevToolsTab::Profiler);
829 }
830
831 #[test]
832 fn test_devtools_getters() {
833 let devtools = DevTools::new();
834
835 let _inspector = devtools.inspector();
837 let _state = devtools.state();
838 let _styles = devtools.styles();
839 let _events = devtools.events();
840 let _profiler = devtools.profiler();
841 let _time_travel = devtools.time_travel();
842 }
843
844 #[test]
845 fn test_devtools_getters_mut() {
846 let mut devtools = DevTools::new();
847
848 devtools.inspector_mut();
850 devtools.state_mut();
851 devtools.styles_mut();
852 devtools.events_mut();
853 devtools.profiler_mut();
854 devtools.time_travel_mut();
855 }
856
857 #[test]
858 fn test_devtools_tab_label() {
859 assert_eq!(DevToolsTab::Inspector.label(), "Inspector");
860 assert_eq!(DevToolsTab::State.label(), "State");
861 assert_eq!(DevToolsTab::Styles.label(), "Styles");
862 assert_eq!(DevToolsTab::Events.label(), "Events");
863 assert_eq!(DevToolsTab::Profiler.label(), "Profiler");
864 assert_eq!(DevToolsTab::TimeTravel.label(), "Travel");
865 }
866
867 #[test]
868 fn test_devtools_tab_all() {
869 let all = DevToolsTab::all();
870 assert_eq!(all.len(), 6);
871 assert_eq!(all[0], DevToolsTab::Inspector);
872 assert_eq!(all[5], DevToolsTab::TimeTravel);
873 }
874
875 #[test]
876 fn test_devtools_tab_next_cycle() {
877 assert_eq!(DevToolsTab::Inspector.next(), DevToolsTab::State);
878 assert_eq!(DevToolsTab::State.next(), DevToolsTab::Styles);
879 assert_eq!(DevToolsTab::Styles.next(), DevToolsTab::Events);
880 assert_eq!(DevToolsTab::Events.next(), DevToolsTab::Profiler);
881 assert_eq!(DevToolsTab::Profiler.next(), DevToolsTab::TimeTravel);
882 assert_eq!(DevToolsTab::TimeTravel.next(), DevToolsTab::Inspector);
883 }
884
885 #[test]
886 fn test_devtools_tab_prev_cycle() {
887 assert_eq!(DevToolsTab::Inspector.prev(), DevToolsTab::TimeTravel);
888 assert_eq!(DevToolsTab::TimeTravel.prev(), DevToolsTab::Profiler);
889 assert_eq!(DevToolsTab::Profiler.prev(), DevToolsTab::Events);
890 assert_eq!(DevToolsTab::Events.prev(), DevToolsTab::Styles);
891 assert_eq!(DevToolsTab::Styles.prev(), DevToolsTab::State);
892 assert_eq!(DevToolsTab::State.prev(), DevToolsTab::Inspector);
893 }
894
895 #[test]
896 fn test_devtools_position_default() {
897 assert_eq!(DevToolsPosition::default(), DevToolsPosition::Right);
898 }
899
900 #[test]
901 fn test_devtools_tab_default() {
902 assert_eq!(DevToolsTab::default(), DevToolsTab::Inspector);
903 }
904
905 #[test]
906 fn test_panel_rect_saturation() {
907 let mut devtools = DevTools::new().size(200);
908 devtools.set_visible(true);
909
910 let area = Rect::new(0, 0, 100, 50);
911 let panel = devtools.panel_rect(area).unwrap();
912
913 assert_eq!(panel.width, 100);
915 assert_eq!(panel.height, 50);
916 }
917
918 #[test]
919 fn test_content_rect_saturation() {
920 let mut devtools = DevTools::new().size(200);
921 devtools.set_visible(true);
922
923 let area = Rect::new(0, 0, 100, 50);
924 let content = devtools.content_rect(area);
925
926 assert_eq!(content.width, 0); assert_eq!(content.height, 50);
929 }
930
931 #[test]
932 fn test_global_enable_disable_devtools() {
933 let mut app = App::builder().devtools(false).build();
936 assert!(!app.is_devtools_enabled());
937
938 app.enable_devtools();
939 assert!(app.is_devtools_enabled());
940
941 app.disable_devtools();
942 assert!(!app.is_devtools_enabled());
943 }
944
945 #[test]
946 fn test_global_toggle_devtools() {
947 let mut app = App::builder().devtools(false).build();
950 assert!(!app.is_devtools_enabled());
951
952 let result = app.toggle_devtools();
954 assert!(result); assert!(app.is_devtools_enabled());
956
957 let result = app.toggle_devtools();
959 assert!(!result); assert!(!app.is_devtools_enabled());
961 }
962}