1use crate::ItemHandle;
2use gpui::{
3 AnyView, AppContext, Entity, EntityId, EventEmitter, Render, View, ViewContext, WindowContext,
4};
5
6pub trait ToolbarItemView: Render + EventEmitter {
7 fn set_active_pane_item(
8 &mut self,
9 active_pane_item: Option<&dyn crate::ItemHandle>,
10 cx: &mut ViewContext<Self>,
11 ) -> ToolbarItemLocation;
12
13 fn location_for_event(
14 &self,
15 _event: &Self::Event,
16 current_location: ToolbarItemLocation,
17 _cx: &AppContext,
18 ) -> ToolbarItemLocation {
19 current_location
20 }
21
22 fn pane_focus_update(&mut self, _pane_focused: bool, _cx: &mut ViewContext<Self>) {}
23
24 /// Number of times toolbar's height will be repeated to get the effective height.
25 /// Useful when multiple rows one under each other are needed.
26 /// The rows have the same width and act as a whole when reacting to resizes and similar events.
27 fn row_count(&self, _cx: &WindowContext) -> usize {
28 1
29 }
30}
31
32trait ToolbarItemViewHandle: Send {
33 fn id(&self) -> EntityId;
34 fn to_any(&self) -> AnyView;
35 fn set_active_pane_item(
36 &self,
37 active_pane_item: Option<&dyn ItemHandle>,
38 cx: &mut WindowContext,
39 ) -> ToolbarItemLocation;
40 fn focus_changed(&mut self, pane_focused: bool, cx: &mut WindowContext);
41 fn row_count(&self, cx: &WindowContext) -> usize;
42}
43
44#[derive(Copy, Clone, Debug, PartialEq)]
45pub enum ToolbarItemLocation {
46 Hidden,
47 PrimaryLeft { flex: Option<(f32, bool)> },
48 PrimaryRight { flex: Option<(f32, bool)> },
49 Secondary,
50}
51
52pub struct Toolbar {
53 active_item: Option<Box<dyn ItemHandle>>,
54 hidden: bool,
55 can_navigate: bool,
56 items: Vec<(Box<dyn ToolbarItemViewHandle>, ToolbarItemLocation)>,
57}
58
59// todo!()
60// impl View for Toolbar {
61// fn ui_name() -> &'static str {
62// "Toolbar"
63// }
64
65// fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
66// let theme = &theme::current(cx).workspace.toolbar;
67
68// let mut primary_left_items = Vec::new();
69// let mut primary_right_items = Vec::new();
70// let mut secondary_item = None;
71// let spacing = theme.item_spacing;
72// let mut primary_items_row_count = 1;
73
74// for (item, position) in &self.items {
75// match *position {
76// ToolbarItemLocation::Hidden => {}
77
78// ToolbarItemLocation::PrimaryLeft { flex } => {
79// primary_items_row_count = primary_items_row_count.max(item.row_count(cx));
80// let left_item = ChildView::new(item.as_any(), cx).aligned();
81// if let Some((flex, expanded)) = flex {
82// primary_left_items.push(left_item.flex(flex, expanded).into_any());
83// } else {
84// primary_left_items.push(left_item.into_any());
85// }
86// }
87
88// ToolbarItemLocation::PrimaryRight { flex } => {
89// primary_items_row_count = primary_items_row_count.max(item.row_count(cx));
90// let right_item = ChildView::new(item.as_any(), cx).aligned().flex_float();
91// if let Some((flex, expanded)) = flex {
92// primary_right_items.push(right_item.flex(flex, expanded).into_any());
93// } else {
94// primary_right_items.push(right_item.into_any());
95// }
96// }
97
98// ToolbarItemLocation::Secondary => {
99// secondary_item = Some(
100// ChildView::new(item.as_any(), cx)
101// .constrained()
102// .with_height(theme.height * item.row_count(cx) as f32)
103// .into_any(),
104// );
105// }
106// }
107// }
108
109// let container_style = theme.container;
110// let height = theme.height * primary_items_row_count as f32;
111
112// let mut primary_items = Flex::row().with_spacing(spacing);
113// primary_items.extend(primary_left_items);
114// primary_items.extend(primary_right_items);
115
116// let mut toolbar = Flex::column();
117// if !primary_items.is_empty() {
118// toolbar.add_child(primary_items.constrained().with_height(height));
119// }
120// if let Some(secondary_item) = secondary_item {
121// toolbar.add_child(secondary_item);
122// }
123
124// if toolbar.is_empty() {
125// toolbar.into_any_named("toolbar")
126// } else {
127// toolbar
128// .contained()
129// .with_style(container_style)
130// .into_any_named("toolbar")
131// }
132// }
133// }
134
135// <<<<<<< HEAD
136// =======
137// #[allow(clippy::too_many_arguments)]
138// fn nav_button<A: Action, F: 'static + Fn(&mut Toolbar, &mut ViewContext<Toolbar>)>(
139// svg_path: &'static str,
140// style: theme::Interactive<theme::IconButton>,
141// nav_button_height: f32,
142// tooltip_style: TooltipStyle,
143// enabled: bool,
144// spacing: f32,
145// on_click: F,
146// tooltip_action: A,
147// action_name: &'static str,
148// cx: &mut ViewContext<Toolbar>,
149// ) -> AnyElement<Toolbar> {
150// MouseEventHandler::new::<A, _>(0, cx, |state, _| {
151// let style = if enabled {
152// style.style_for(state)
153// } else {
154// style.disabled_style()
155// };
156// Svg::new(svg_path)
157// .with_color(style.color)
158// .constrained()
159// .with_width(style.icon_width)
160// .aligned()
161// .contained()
162// .with_style(style.container)
163// .constrained()
164// .with_width(style.button_width)
165// .with_height(nav_button_height)
166// .aligned()
167// .top()
168// })
169// .with_cursor_style(if enabled {
170// CursorStyle::PointingHand
171// } else {
172// CursorStyle::default()
173// })
174// .on_click(MouseButton::Left, move |_, toolbar, cx| {
175// on_click(toolbar, cx)
176// })
177// .with_tooltip::<A>(
178// 0,
179// action_name,
180// Some(Box::new(tooltip_action)),
181// tooltip_style,
182// cx,
183// )
184// .contained()
185// .with_margin_right(spacing)
186// .into_any_named("nav button")
187// }
188
189// >>>>>>> 139cbbfd3aebd0863a7d51b0c12d748764cf0b2e
190impl Toolbar {
191 pub fn new() -> Self {
192 Self {
193 active_item: None,
194 items: Default::default(),
195 hidden: false,
196 can_navigate: true,
197 }
198 }
199
200 pub fn set_can_navigate(&mut self, can_navigate: bool, cx: &mut ViewContext<Self>) {
201 self.can_navigate = can_navigate;
202 cx.notify();
203 }
204
205 pub fn add_item<T>(&mut self, item: View<T>, cx: &mut ViewContext<Self>)
206 where
207 T: 'static + ToolbarItemView,
208 {
209 let location = item.set_active_pane_item(self.active_item.as_deref(), cx);
210 cx.subscribe(&item, |this, item, event, cx| {
211 if let Some((_, current_location)) =
212 this.items.iter_mut().find(|(i, _)| i.id() == item.id())
213 {
214 let new_location = item
215 .read(cx)
216 .location_for_event(event, *current_location, cx);
217 if new_location != *current_location {
218 *current_location = new_location;
219 cx.notify();
220 }
221 }
222 })
223 .detach();
224 self.items.push((Box::new(item), location));
225 cx.notify();
226 }
227
228 pub fn set_active_item(&mut self, item: Option<&dyn ItemHandle>, cx: &mut ViewContext<Self>) {
229 self.active_item = item.map(|item| item.boxed_clone());
230 self.hidden = self
231 .active_item
232 .as_ref()
233 .map(|item| !item.show_toolbar(cx))
234 .unwrap_or(false);
235
236 for (toolbar_item, current_location) in self.items.iter_mut() {
237 let new_location = toolbar_item.set_active_pane_item(item, cx);
238 if new_location != *current_location {
239 *current_location = new_location;
240 cx.notify();
241 }
242 }
243 }
244
245 pub fn focus_changed(&mut self, focused: bool, cx: &mut ViewContext<Self>) {
246 for (toolbar_item, _) in self.items.iter_mut() {
247 toolbar_item.focus_changed(focused, cx);
248 }
249 }
250
251 pub fn item_of_type<T: ToolbarItemView>(&self) -> Option<View<T>> {
252 self.items
253 .iter()
254 .find_map(|(item, _)| item.to_any().downcast().ok())
255 }
256
257 pub fn hidden(&self) -> bool {
258 self.hidden
259 }
260}
261
262impl<T: ToolbarItemView> ToolbarItemViewHandle for View<T> {
263 fn id(&self) -> EntityId {
264 self.entity_id()
265 }
266
267 fn to_any(&self) -> AnyView {
268 self.clone().into()
269 }
270
271 fn set_active_pane_item(
272 &self,
273 active_pane_item: Option<&dyn ItemHandle>,
274 cx: &mut WindowContext,
275 ) -> ToolbarItemLocation {
276 self.update(cx, |this, cx| {
277 this.set_active_pane_item(active_pane_item, cx)
278 })
279 }
280
281 fn focus_changed(&mut self, pane_focused: bool, cx: &mut WindowContext) {
282 self.update(cx, |this, cx| {
283 this.pane_focus_update(pane_focused, cx);
284 cx.notify();
285 });
286 }
287
288 fn row_count(&self, cx: &WindowContext) -> usize {
289 self.read(cx).row_count(cx)
290 }
291}
292
293// todo!()
294// impl From<&dyn ToolbarItemViewHandle> for AnyViewHandle {
295// fn from(val: &dyn ToolbarItemViewHandle) -> Self {
296// val.as_any().clone()
297// }
298// }