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