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