toolbar.rs

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