dock.rs

  1use crate::{status_bar::StatusItemView, Axis, Workspace};
  2use gpui::{
  3    div, Action, AnyView, AppContext, Div, Entity, EntityId, EventEmitter, ParentElement, Render,
  4    Subscription, View, ViewContext, WeakView, WindowContext,
  5};
  6use schemars::JsonSchema;
  7use serde::{Deserialize, Serialize};
  8use std::sync::Arc;
  9
 10pub enum PanelEvent {
 11    ChangePosition,
 12    ZoomIn,
 13    ZoomOut,
 14    Activate,
 15    Close,
 16    Focus,
 17}
 18
 19pub trait Panel: Render + EventEmitter<PanelEvent> {
 20    fn persistent_name(&self) -> &'static str;
 21    fn position(&self, cx: &WindowContext) -> DockPosition;
 22    fn position_is_valid(&self, position: DockPosition) -> bool;
 23    fn set_position(&mut self, position: DockPosition, cx: &mut ViewContext<Self>);
 24    fn size(&self, cx: &WindowContext) -> f32;
 25    fn set_size(&mut self, size: Option<f32>, cx: &mut ViewContext<Self>);
 26    fn icon_path(&self, cx: &WindowContext) -> Option<&'static str>;
 27    fn icon_tooltip(&self) -> (String, Option<Box<dyn Action>>);
 28    fn icon_label(&self, _: &WindowContext) -> Option<String> {
 29        None
 30    }
 31    fn is_zoomed(&self, _cx: &WindowContext) -> bool {
 32        false
 33    }
 34    fn set_zoomed(&mut self, _zoomed: bool, _cx: &mut ViewContext<Self>) {}
 35    fn set_active(&mut self, _active: bool, _cx: &mut ViewContext<Self>) {}
 36    fn has_focus(&self, cx: &WindowContext) -> bool;
 37}
 38
 39pub trait PanelHandle: Send + Sync {
 40    fn id(&self) -> EntityId;
 41    fn persistent_name(&self, cx: &WindowContext) -> &'static str;
 42    fn position(&self, cx: &WindowContext) -> DockPosition;
 43    fn position_is_valid(&self, position: DockPosition, cx: &WindowContext) -> bool;
 44    fn set_position(&self, position: DockPosition, cx: &mut WindowContext);
 45    fn is_zoomed(&self, cx: &WindowContext) -> bool;
 46    fn set_zoomed(&self, zoomed: bool, cx: &mut WindowContext);
 47    fn set_active(&self, active: bool, cx: &mut WindowContext);
 48    fn size(&self, cx: &WindowContext) -> f32;
 49    fn set_size(&self, size: Option<f32>, cx: &mut WindowContext);
 50    fn icon_path(&self, cx: &WindowContext) -> Option<&'static str>;
 51    fn icon_tooltip(&self, cx: &WindowContext) -> (String, Option<Box<dyn Action>>);
 52    fn icon_label(&self, cx: &WindowContext) -> Option<String>;
 53    fn has_focus(&self, cx: &WindowContext) -> bool;
 54    fn to_any(&self) -> AnyView;
 55}
 56
 57impl<T> PanelHandle for View<T>
 58where
 59    T: Panel,
 60{
 61    fn id(&self) -> EntityId {
 62        self.entity_id()
 63    }
 64
 65    fn persistent_name(&self, cx: &WindowContext) -> &'static str {
 66        self.read(cx).persistent_name()
 67    }
 68
 69    fn position(&self, cx: &WindowContext) -> DockPosition {
 70        self.read(cx).position(cx)
 71    }
 72
 73    fn position_is_valid(&self, position: DockPosition, cx: &WindowContext) -> bool {
 74        self.read(cx).position_is_valid(position)
 75    }
 76
 77    fn set_position(&self, position: DockPosition, cx: &mut WindowContext) {
 78        self.update(cx, |this, cx| this.set_position(position, cx))
 79    }
 80
 81    fn is_zoomed(&self, cx: &WindowContext) -> bool {
 82        self.read(cx).is_zoomed(cx)
 83    }
 84
 85    fn set_zoomed(&self, zoomed: bool, cx: &mut WindowContext) {
 86        self.update(cx, |this, cx| this.set_zoomed(zoomed, cx))
 87    }
 88
 89    fn set_active(&self, active: bool, cx: &mut WindowContext) {
 90        self.update(cx, |this, cx| this.set_active(active, cx))
 91    }
 92
 93    fn size(&self, cx: &WindowContext) -> f32 {
 94        self.read(cx).size(cx)
 95    }
 96
 97    fn set_size(&self, size: Option<f32>, cx: &mut WindowContext) {
 98        self.update(cx, |this, cx| this.set_size(size, cx))
 99    }
100
101    fn icon_path(&self, cx: &WindowContext) -> Option<&'static str> {
102        self.read(cx).icon_path(cx)
103    }
104
105    fn icon_tooltip(&self, cx: &WindowContext) -> (String, Option<Box<dyn Action>>) {
106        self.read(cx).icon_tooltip()
107    }
108
109    fn icon_label(&self, cx: &WindowContext) -> Option<String> {
110        self.read(cx).icon_label(cx)
111    }
112
113    fn has_focus(&self, cx: &WindowContext) -> bool {
114        self.read(cx).has_focus(cx)
115    }
116
117    fn to_any(&self) -> AnyView {
118        self.clone().into()
119    }
120}
121
122impl From<&dyn PanelHandle> for AnyView {
123    fn from(val: &dyn PanelHandle) -> Self {
124        val.to_any()
125    }
126}
127
128pub struct Dock {
129    position: DockPosition,
130    panel_entries: Vec<PanelEntry>,
131    is_open: bool,
132    active_panel_index: usize,
133}
134
135#[derive(Clone, Copy, Debug, Serialize, Deserialize, JsonSchema, PartialEq)]
136#[serde(rename_all = "lowercase")]
137pub enum DockPosition {
138    Left,
139    Bottom,
140    Right,
141}
142
143impl DockPosition {
144    fn to_label(&self) -> &'static str {
145        match self {
146            Self::Left => "left",
147            Self::Bottom => "bottom",
148            Self::Right => "right",
149        }
150    }
151
152    // todo!()
153    // fn to_resize_handle_side(self) -> HandleSide {
154    //     match self {
155    //         Self::Left => HandleSide::Right,
156    //         Self::Bottom => HandleSide::Top,
157    //         Self::Right => HandleSide::Left,
158    //     }
159    // }
160
161    pub fn axis(&self) -> Axis {
162        match self {
163            Self::Left | Self::Right => Axis::Horizontal,
164            Self::Bottom => Axis::Vertical,
165        }
166    }
167}
168
169struct PanelEntry {
170    panel: Arc<dyn PanelHandle>,
171    // todo!()
172    // context_menu: View<ContextMenu>,
173    _subscriptions: [Subscription; 2],
174}
175
176pub struct PanelButtons {
177    dock: View<Dock>,
178    workspace: WeakView<Workspace>,
179}
180
181impl Dock {
182    pub fn new(position: DockPosition) -> Self {
183        Self {
184            position,
185            panel_entries: Default::default(),
186            active_panel_index: 0,
187            is_open: false,
188        }
189    }
190
191    pub fn position(&self) -> DockPosition {
192        self.position
193    }
194
195    pub fn is_open(&self) -> bool {
196        self.is_open
197    }
198
199    //     pub fn has_focus(&self, cx: &WindowContext) -> bool {
200    //         self.visible_panel()
201    //             .map_or(false, |panel| panel.has_focus(cx))
202    //     }
203
204    //     pub fn panel<T: Panel>(&self) -> Option<View<T>> {
205    //         self.panel_entries
206    //             .iter()
207    //             .find_map(|entry| entry.panel.as_any().clone().downcast())
208    //     }
209
210    //     pub fn panel_index_for_type<T: Panel>(&self) -> Option<usize> {
211    //         self.panel_entries
212    //             .iter()
213    //             .position(|entry| entry.panel.as_any().is::<T>())
214    //     }
215
216    pub fn panel_index_for_ui_name(&self, _ui_name: &str, _cx: &AppContext) -> Option<usize> {
217        todo!()
218        // self.panel_entries.iter().position(|entry| {
219        //     let panel = entry.panel.as_any();
220        //     cx.view_ui_name(panel.window(), panel.id()) == Some(ui_name)
221        // })
222    }
223
224    pub fn active_panel_index(&self) -> usize {
225        self.active_panel_index
226    }
227
228    pub(crate) fn set_open(&mut self, open: bool, cx: &mut ViewContext<Self>) {
229        if open != self.is_open {
230            self.is_open = open;
231            if let Some(active_panel) = self.panel_entries.get(self.active_panel_index) {
232                active_panel.panel.set_active(open, cx);
233            }
234
235            cx.notify();
236        }
237    }
238
239    // todo!()
240    // pub fn set_panel_zoomed(&mut self, panel: &AnyView, zoomed: bool, cx: &mut ViewContext<Self>) {
241    //     for entry in &mut self.panel_entries {
242    //         if entry.panel.as_any() == panel {
243    //             if zoomed != entry.panel.is_zoomed(cx) {
244    //                 entry.panel.set_zoomed(zoomed, cx);
245    //             }
246    //         } else if entry.panel.is_zoomed(cx) {
247    //             entry.panel.set_zoomed(false, cx);
248    //         }
249    //     }
250
251    //     cx.notify();
252    // }
253
254    pub fn zoom_out(&mut self, cx: &mut ViewContext<Self>) {
255        for entry in &mut self.panel_entries {
256            if entry.panel.is_zoomed(cx) {
257                entry.panel.set_zoomed(false, cx);
258            }
259        }
260    }
261
262    pub(crate) fn add_panel<T: Panel>(&mut self, panel: View<T>, cx: &mut ViewContext<Self>) {
263        let subscriptions = [
264            cx.observe(&panel, |_, _, cx| cx.notify()),
265            cx.subscribe(&panel, |this, panel, event, cx| {
266                match event {
267                    PanelEvent::ChangePosition => {
268                        //todo!()
269                        // see: Workspace::add_panel_with_extra_event_handler
270                    }
271                    PanelEvent::ZoomIn => {
272                        //todo!()
273                        // see: Workspace::add_panel_with_extra_event_handler
274                    }
275                    PanelEvent::ZoomOut => {
276                        // todo!()
277                        // // see: Workspace::add_panel_with_extra_event_handler
278                    }
279                    PanelEvent::Activate => {
280                        if let Some(ix) = this
281                            .panel_entries
282                            .iter()
283                            .position(|entry| entry.panel.id() == panel.id())
284                        {
285                            this.set_open(true, cx);
286                            this.activate_panel(ix, cx);
287                            //` todo!()
288                            // cx.focus(&panel);
289                        }
290                    }
291                    PanelEvent::Close => {
292                        if this.visible_panel().map_or(false, |p| p.id() == panel.id()) {
293                            this.set_open(false, cx);
294                        }
295                    }
296                    PanelEvent::Focus => todo!(),
297                }
298            }),
299        ];
300
301        // todo!()
302        // let dock_view_id = cx.view_id();
303        self.panel_entries.push(PanelEntry {
304            panel: Arc::new(panel),
305            // todo!()
306            // context_menu: cx.add_view(|cx| {
307            //     let mut menu = ContextMenu::new(dock_view_id, cx);
308            //     menu.set_position_mode(OverlayPositionMode::Local);
309            //     menu
310            // }),
311            _subscriptions: subscriptions,
312        });
313        cx.notify()
314    }
315
316    pub fn remove_panel<T: Panel>(&mut self, panel: &View<T>, cx: &mut ViewContext<Self>) {
317        if let Some(panel_ix) = self
318            .panel_entries
319            .iter()
320            .position(|entry| entry.panel.id() == panel.id())
321        {
322            if panel_ix == self.active_panel_index {
323                self.active_panel_index = 0;
324                self.set_open(false, cx);
325            } else if panel_ix < self.active_panel_index {
326                self.active_panel_index -= 1;
327            }
328            self.panel_entries.remove(panel_ix);
329            cx.notify();
330        }
331    }
332
333    pub fn panels_len(&self) -> usize {
334        self.panel_entries.len()
335    }
336
337    pub fn activate_panel(&mut self, panel_ix: usize, cx: &mut ViewContext<Self>) {
338        if panel_ix != self.active_panel_index {
339            if let Some(active_panel) = self.panel_entries.get(self.active_panel_index) {
340                active_panel.panel.set_active(false, cx);
341            }
342
343            self.active_panel_index = panel_ix;
344            if let Some(active_panel) = self.panel_entries.get(self.active_panel_index) {
345                active_panel.panel.set_active(true, cx);
346            }
347
348            cx.notify();
349        }
350    }
351
352    pub fn visible_panel(&self) -> Option<&Arc<dyn PanelHandle>> {
353        let entry = self.visible_entry()?;
354        Some(&entry.panel)
355    }
356
357    pub fn active_panel(&self) -> Option<&Arc<dyn PanelHandle>> {
358        Some(&self.panel_entries.get(self.active_panel_index)?.panel)
359    }
360
361    fn visible_entry(&self) -> Option<&PanelEntry> {
362        if self.is_open {
363            self.panel_entries.get(self.active_panel_index)
364        } else {
365            None
366        }
367    }
368
369    pub fn zoomed_panel(&self, cx: &WindowContext) -> Option<Arc<dyn PanelHandle>> {
370        let entry = self.visible_entry()?;
371        if entry.panel.is_zoomed(cx) {
372            Some(entry.panel.clone())
373        } else {
374            None
375        }
376    }
377
378    pub fn panel_size(&self, panel: &dyn PanelHandle, cx: &WindowContext) -> Option<f32> {
379        self.panel_entries
380            .iter()
381            .find(|entry| entry.panel.id() == panel.id())
382            .map(|entry| entry.panel.size(cx))
383    }
384
385    pub fn active_panel_size(&self, cx: &WindowContext) -> Option<f32> {
386        if self.is_open {
387            self.panel_entries
388                .get(self.active_panel_index)
389                .map(|entry| entry.panel.size(cx))
390        } else {
391            None
392        }
393    }
394
395    pub fn resize_active_panel(&mut self, size: Option<f32>, cx: &mut ViewContext<Self>) {
396        if let Some(entry) = self.panel_entries.get_mut(self.active_panel_index) {
397            entry.panel.set_size(size, cx);
398            cx.notify();
399        }
400    }
401
402    //     pub fn render_placeholder(&self, cx: &WindowContext) -> AnyElement<Workspace> {
403    //         todo!()
404    // if let Some(active_entry) = self.visible_entry() {
405    //     Empty::new()
406    //         .into_any()
407    //         .contained()
408    //         .with_style(self.style(cx))
409    //         .resizable::<WorkspaceBounds>(
410    //             self.position.to_resize_handle_side(),
411    //             active_entry.panel.size(cx),
412    //             |_, _, _| {},
413    //         )
414    //         .into_any()
415    // } else {
416    //     Empty::new().into_any()
417    // }
418    //     }
419}
420
421impl Render for Dock {
422    type Element = Div<Self>;
423
424    fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
425        todo!()
426    }
427}
428
429// todo!()
430// impl View for Dock {
431//     fn ui_name() -> &'static str {
432//         "Dock"
433//     }
434
435//     fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
436//         if let Some(active_entry) = self.visible_entry() {
437//             let style = self.style(cx);
438//             ChildView::new(active_entry.panel.as_any(), cx)
439//                 .contained()
440//                 .with_style(style)
441//                 .resizable::<WorkspaceBounds>(
442//                     self.position.to_resize_handle_side(),
443//                     active_entry.panel.size(cx),
444//                     |dock: &mut Self, size, cx| dock.resize_active_panel(size, cx),
445//                 )
446//                 .into_any()
447//         } else {
448//             Empty::new().into_any()
449//         }
450//     }
451
452//     fn focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
453//         if cx.is_self_focused() {
454//             if let Some(active_entry) = self.visible_entry() {
455//                 cx.focus(active_entry.panel.as_any());
456//             } else {
457//                 cx.focus_parent();
458//             }
459//         }
460//     }
461// }
462
463impl PanelButtons {
464    pub fn new(
465        dock: View<Dock>,
466        workspace: WeakView<Workspace>,
467        cx: &mut ViewContext<Self>,
468    ) -> Self {
469        cx.observe(&dock, |_, _, cx| cx.notify()).detach();
470        Self { dock, workspace }
471    }
472}
473
474// impl Render for PanelButtons {
475//     type Element = ();
476
477//     fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
478//         todo!("")
479//     }
480
481//     fn ui_name() -> &'static str {
482//         "PanelButtons"
483//     }
484
485//     fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
486//         let theme = &settings::get::<ThemeSettings>(cx).theme;
487//         let tooltip_style = theme.tooltip.clone();
488//         let theme = &theme.workspace.status_bar.panel_buttons;
489//         let button_style = theme.button.clone();
490//         let dock = self.dock.read(cx);
491//         let active_ix = dock.active_panel_index;
492//         let is_open = dock.is_open;
493//         let dock_position = dock.position;
494//         let group_style = match dock_position {
495//             DockPosition::Left => theme.group_left,
496//             DockPosition::Bottom => theme.group_bottom,
497//             DockPosition::Right => theme.group_right,
498//         };
499//         let menu_corner = match dock_position {
500//             DockPosition::Left => AnchorCorner::BottomLeft,
501//             DockPosition::Bottom | DockPosition::Right => AnchorCorner::BottomRight,
502//         };
503
504//         let panels = dock
505//             .panel_entries
506//             .iter()
507//             .map(|item| (item.panel.clone(), item.context_menu.clone()))
508//             .collect::<Vec<_>>();
509//         Flex::row()
510//             .with_children(panels.into_iter().enumerate().filter_map(
511//                 |(panel_ix, (view, context_menu))| {
512//                     let icon_path = view.icon_path(cx)?;
513//                     let is_active = is_open && panel_ix == active_ix;
514//                     let (tooltip, tooltip_action) = if is_active {
515//                         (
516//                             format!("Close {} dock", dock_position.to_label()),
517//                             Some(match dock_position {
518//                                 DockPosition::Left => crate::ToggleLeftDock.boxed_clone(),
519//                                 DockPosition::Bottom => crate::ToggleBottomDock.boxed_clone(),
520//                                 DockPosition::Right => crate::ToggleRightDock.boxed_clone(),
521//                             }),
522//                         )
523//                     } else {
524//                         view.icon_tooltip(cx)
525//                     };
526//                     Some(
527//                         Stack::new()
528//                             .with_child(
529//                                 MouseEventHandler::new::<Self, _>(panel_ix, cx, |state, cx| {
530//                                     let style = button_style.in_state(is_active);
531
532//                                     let style = style.style_for(state);
533//                                     Flex::row()
534//                                         .with_child(
535//                                             Svg::new(icon_path)
536//                                                 .with_color(style.icon_color)
537//                                                 .constrained()
538//                                                 .with_width(style.icon_size)
539//                                                 .aligned(),
540//                                         )
541//                                         .with_children(if let Some(label) = view.icon_label(cx) {
542//                                             Some(
543//                                                 Label::new(label, style.label.text.clone())
544//                                                     .contained()
545//                                                     .with_style(style.label.container)
546//                                                     .aligned(),
547//                                             )
548//                                         } else {
549//                                             None
550//                                         })
551//                                         .constrained()
552//                                         .with_height(style.icon_size)
553//                                         .contained()
554//                                         .with_style(style.container)
555//                                 })
556//                                 .with_cursor_style(CursorStyle::PointingHand)
557//                                 .on_click(MouseButton::Left, {
558//                                     let tooltip_action =
559//                                         tooltip_action.as_ref().map(|action| action.boxed_clone());
560//                                     move |_, this, cx| {
561//                                         if let Some(tooltip_action) = &tooltip_action {
562//                                             let window = cx.window();
563//                                             let view_id = this.workspace.id();
564//                                             let tooltip_action = tooltip_action.boxed_clone();
565//                                             cx.spawn(|_, mut cx| async move {
566//                                                 window.dispatch_action(
567//                                                     view_id,
568//                                                     &*tooltip_action,
569//                                                     &mut cx,
570//                                                 );
571//                                             })
572//                                             .detach();
573//                                         }
574//                                     }
575//                                 })
576//                                 .on_click(MouseButton::Right, {
577//                                     let view = view.clone();
578//                                     let menu = context_menu.clone();
579//                                     move |_, _, cx| {
580//                                         const POSITIONS: [DockPosition; 3] = [
581//                                             DockPosition::Left,
582//                                             DockPosition::Right,
583//                                             DockPosition::Bottom,
584//                                         ];
585
586//                                         menu.update(cx, |menu, cx| {
587//                                             let items = POSITIONS
588//                                                 .into_iter()
589//                                                 .filter(|position| {
590//                                                     *position != dock_position
591//                                                         && view.position_is_valid(*position, cx)
592//                                                 })
593//                                                 .map(|position| {
594//                                                     let view = view.clone();
595//                                                     ContextMenuItem::handler(
596//                                                         format!("Dock {}", position.to_label()),
597//                                                         move |cx| view.set_position(position, cx),
598//                                                     )
599//                                                 })
600//                                                 .collect();
601//                                             menu.show(Default::default(), menu_corner, items, cx);
602//                                         })
603//                                     }
604//                                 })
605//                                 .with_tooltip::<Self>(
606//                                     panel_ix,
607//                                     tooltip,
608//                                     tooltip_action,
609//                                     tooltip_style.clone(),
610//                                     cx,
611//                                 ),
612//                             )
613//                             .with_child(ChildView::new(&context_menu, cx)),
614//                     )
615//                 },
616//             ))
617//             .contained()
618//             .with_style(group_style)
619//             .into_any()
620//     }
621// }
622
623impl Render for PanelButtons {
624    type Element = Div<Self>;
625
626    fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
627        // todo!()
628        let dock = self.dock.read(cx);
629        div().children(
630            dock.panel_entries
631                .iter()
632                .map(|panel| panel.panel.persistent_name(cx)),
633        )
634    }
635}
636
637impl StatusItemView for PanelButtons {
638    fn set_active_pane_item(
639        &mut self,
640        _active_pane_item: Option<&dyn crate::ItemHandle>,
641        _cx: &mut ViewContext<Self>,
642    ) {
643        // Nothing to do, panel buttons don't depend on the active center item
644    }
645}
646
647#[cfg(any(test, feature = "test-support"))]
648pub mod test {
649    use super::*;
650    use gpui::{div, Div, ViewContext, WindowContext};
651
652    pub struct TestPanel {
653        pub position: DockPosition,
654        pub zoomed: bool,
655        pub active: bool,
656        pub has_focus: bool,
657        pub size: f32,
658    }
659
660    impl EventEmitter<PanelEvent> for TestPanel {}
661
662    impl TestPanel {
663        pub fn new(position: DockPosition) -> Self {
664            Self {
665                position,
666                zoomed: false,
667                active: false,
668                has_focus: false,
669                size: 300.,
670            }
671        }
672    }
673
674    impl Render for TestPanel {
675        type Element = Div<Self>;
676
677        fn render(&mut self, _cx: &mut ViewContext<Self>) -> Self::Element {
678            div()
679        }
680    }
681
682    impl Panel for TestPanel {
683        fn persistent_name(&self) -> &'static str {
684            "TestPanel"
685        }
686
687        fn position(&self, _: &gpui::WindowContext) -> super::DockPosition {
688            self.position
689        }
690
691        fn position_is_valid(&self, _: super::DockPosition) -> bool {
692            true
693        }
694
695        fn set_position(&mut self, position: DockPosition, cx: &mut ViewContext<Self>) {
696            self.position = position;
697            cx.emit(PanelEvent::ChangePosition);
698        }
699
700        fn size(&self, _: &WindowContext) -> f32 {
701            self.size
702        }
703
704        fn set_size(&mut self, size: Option<f32>, _: &mut ViewContext<Self>) {
705            self.size = size.unwrap_or(300.);
706        }
707
708        fn icon_path(&self, _: &WindowContext) -> Option<&'static str> {
709            Some("icons/test_panel.svg")
710        }
711
712        fn icon_tooltip(&self) -> (String, Option<Box<dyn Action>>) {
713            ("Test Panel".into(), None)
714        }
715
716        fn is_zoomed(&self, _: &WindowContext) -> bool {
717            self.zoomed
718        }
719
720        fn set_zoomed(&mut self, zoomed: bool, _cx: &mut ViewContext<Self>) {
721            self.zoomed = zoomed;
722        }
723
724        fn set_active(&mut self, active: bool, _cx: &mut ViewContext<Self>) {
725            self.active = active;
726        }
727
728        fn has_focus(&self, _cx: &WindowContext) -> bool {
729            self.has_focus
730        }
731    }
732}