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