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