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 ) -> ToolbarItemLocation;
13
14 fn location_for_event(
15 &self,
16 _event: &Self::Event,
17 current_location: ToolbarItemLocation,
18 ) -> ToolbarItemLocation {
19 current_location
20 }
21}
22
23trait ToolbarItemViewHandle {
24 fn id(&self) -> usize;
25 fn to_any(&self) -> AnyViewHandle;
26 fn set_active_pane_item(
27 &self,
28 active_pane_item: Option<&dyn ItemHandle>,
29 cx: &mut MutableAppContext,
30 ) -> ToolbarItemLocation;
31}
32
33#[derive(Copy, Clone, Debug, PartialEq, Eq)]
34pub enum ToolbarItemLocation {
35 Hidden,
36 PrimaryLeft,
37 PrimaryRight,
38 Secondary,
39}
40
41pub struct Toolbar {
42 active_pane_item: Option<Box<dyn ItemHandle>>,
43 items: Vec<(Box<dyn ToolbarItemViewHandle>, ToolbarItemLocation)>,
44}
45
46impl Entity for Toolbar {
47 type Event = ();
48}
49
50impl View for Toolbar {
51 fn ui_name() -> &'static str {
52 "Toolbar"
53 }
54
55 fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
56 let theme = &cx.global::<Settings>().theme.workspace.toolbar;
57
58 let mut primary_left_items = Vec::new();
59 let mut primary_right_items = Vec::new();
60 let mut secondary_item = None;
61
62 for (item, position) in &self.items {
63 match position {
64 ToolbarItemLocation::Hidden => {}
65 ToolbarItemLocation::PrimaryLeft => primary_left_items.push(item),
66 ToolbarItemLocation::PrimaryRight => primary_right_items.push(item),
67 ToolbarItemLocation::Secondary => secondary_item = Some(item),
68 }
69 }
70
71 Flex::column()
72 .with_child(
73 Flex::row()
74 .with_children(primary_left_items.iter().map(|i| {
75 ChildView::new(i.as_ref())
76 .aligned()
77 .contained()
78 .with_margin_right(theme.item_spacing)
79 .boxed()
80 }))
81 .with_children(primary_right_items.iter().map(|i| {
82 ChildView::new(i.as_ref())
83 .aligned()
84 .contained()
85 .with_margin_left(theme.item_spacing)
86 .flex_float()
87 .boxed()
88 }))
89 .constrained()
90 .with_height(theme.height)
91 .boxed(),
92 )
93 .with_children(secondary_item.map(|item| {
94 ChildView::new(item.as_ref())
95 .constrained()
96 .with_height(theme.height)
97 .boxed()
98 }))
99 .contained()
100 .with_style(theme.container)
101 .boxed()
102 }
103}
104
105impl Toolbar {
106 pub fn new() -> Self {
107 Self {
108 active_pane_item: None,
109 items: Default::default(),
110 }
111 }
112
113 pub fn add_item<T>(&mut self, item: ViewHandle<T>, cx: &mut ViewContext<Self>)
114 where
115 T: 'static + ToolbarItemView,
116 {
117 let location = item.set_active_pane_item(self.active_pane_item.as_deref(), cx);
118 cx.subscribe(&item, |this, item, event, cx| {
119 if let Some((_, current_location)) =
120 this.items.iter_mut().find(|(i, _)| i.id() == item.id())
121 {
122 let new_location = item.read(cx).location_for_event(event, *current_location);
123 if new_location != *current_location {
124 *current_location = new_location;
125 cx.notify();
126 }
127 }
128 })
129 .detach();
130 self.items.push((Box::new(item), dbg!(location)));
131 cx.notify();
132 }
133
134 pub fn set_active_pane_item(
135 &mut self,
136 pane_item: Option<&dyn ItemHandle>,
137 cx: &mut ViewContext<Self>,
138 ) {
139 self.active_pane_item = pane_item.map(|item| item.boxed_clone());
140 for (toolbar_item, current_location) in self.items.iter_mut() {
141 let new_location = toolbar_item.set_active_pane_item(pane_item, cx);
142 if new_location != *current_location {
143 *current_location = new_location;
144 cx.notify();
145 }
146 }
147 }
148
149 pub fn item_of_type<T: ToolbarItemView>(&self) -> Option<ViewHandle<T>> {
150 self.items
151 .iter()
152 .find_map(|(item, _)| item.to_any().downcast())
153 }
154}
155
156impl<T: ToolbarItemView> ToolbarItemViewHandle for ViewHandle<T> {
157 fn id(&self) -> usize {
158 self.id()
159 }
160
161 fn to_any(&self) -> AnyViewHandle {
162 self.into()
163 }
164
165 fn set_active_pane_item(
166 &self,
167 active_pane_item: Option<&dyn ItemHandle>,
168 cx: &mut MutableAppContext,
169 ) -> ToolbarItemLocation {
170 self.update(cx, |this, cx| {
171 this.set_active_pane_item(active_pane_item, cx)
172 })
173 }
174}
175
176impl Into<AnyViewHandle> for &dyn ToolbarItemViewHandle {
177 fn into(self) -> AnyViewHandle {
178 self.to_any()
179 }
180}