toolbar.rs

  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// }