1use crate::{ItemHandle, Settings};
2use gpui::{
3 elements::*, AnyViewHandle, ElementBox, Entity, MutableAppContext, RenderContext, View,
4 ViewContext, ViewHandle,
5};
6
7pub trait ToolbarItemView: View {
8 fn set_active_pane_item(
9 &mut self,
10 active_pane_item: Option<&dyn crate::ItemHandle>,
11 cx: &mut ViewContext<Self>,
12 );
13}
14
15trait ToolbarItemViewHandle {
16 fn to_any(&self) -> AnyViewHandle;
17 fn set_active_pane_item(
18 &self,
19 active_pane_item: Option<&dyn ItemHandle>,
20 cx: &mut MutableAppContext,
21 );
22}
23
24pub struct Toolbar {
25 active_pane_item: Option<Box<dyn ItemHandle>>,
26 left_items: Vec<Box<dyn ToolbarItemViewHandle>>,
27 right_items: Vec<Box<dyn ToolbarItemViewHandle>>,
28}
29
30impl Entity for Toolbar {
31 type Event = ();
32}
33
34impl View for Toolbar {
35 fn ui_name() -> &'static str {
36 "Toolbar"
37 }
38
39 fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
40 let theme = &cx.global::<Settings>().theme.workspace.toolbar;
41 Flex::row()
42 .with_children(self.left_items.iter().map(|i| {
43 ChildView::new(i.as_ref())
44 .aligned()
45 .contained()
46 .with_margin_right(theme.item_spacing)
47 .boxed()
48 }))
49 .with_child(Empty::new().flexible(1., true).boxed())
50 .with_children(self.right_items.iter().map(|i| {
51 ChildView::new(i.as_ref())
52 .aligned()
53 .contained()
54 .with_margin_left(theme.item_spacing)
55 .boxed()
56 }))
57 .contained()
58 .with_style(theme.container)
59 .constrained()
60 .with_height(theme.height)
61 .boxed()
62 }
63}
64
65impl Toolbar {
66 pub fn new() -> Self {
67 Self {
68 active_pane_item: None,
69 left_items: Default::default(),
70 right_items: Default::default(),
71 }
72 }
73
74 pub fn add_left_item<T>(&mut self, item: ViewHandle<T>, cx: &mut ViewContext<Self>)
75 where
76 T: 'static + ToolbarItemView,
77 {
78 item.set_active_pane_item(self.active_pane_item.as_deref(), cx);
79 self.left_items.push(Box::new(item));
80 cx.notify();
81 }
82
83 pub fn add_right_item<T>(&mut self, item: ViewHandle<T>, cx: &mut ViewContext<Self>)
84 where
85 T: 'static + ToolbarItemView,
86 {
87 item.set_active_pane_item(self.active_pane_item.as_deref(), cx);
88 self.right_items.push(Box::new(item));
89 cx.notify();
90 }
91
92 pub fn set_active_pane_item(
93 &mut self,
94 item: Option<&dyn ItemHandle>,
95 cx: &mut ViewContext<Self>,
96 ) {
97 self.active_pane_item = item.map(|item| item.boxed_clone());
98 for tool in self.left_items.iter().chain(&self.right_items) {
99 tool.set_active_pane_item(item, cx);
100 }
101 }
102
103 pub fn item_of_type<T: ToolbarItemView>(&self) -> Option<ViewHandle<T>> {
104 self.left_items
105 .iter()
106 .chain(&self.right_items)
107 .find_map(|tool| tool.to_any().downcast())
108 }
109}
110
111impl<T: ToolbarItemView> ToolbarItemViewHandle for ViewHandle<T> {
112 fn to_any(&self) -> AnyViewHandle {
113 self.into()
114 }
115
116 fn set_active_pane_item(
117 &self,
118 active_pane_item: Option<&dyn ItemHandle>,
119 cx: &mut MutableAppContext,
120 ) {
121 self.update(cx, |this, cx| {
122 this.set_active_pane_item(active_pane_item, cx)
123 });
124 }
125}
126
127impl Into<AnyViewHandle> for &dyn ToolbarItemViewHandle {
128 fn into(self) -> AnyViewHandle {
129 self.to_any()
130 }
131}