use crate::ItemHandle; use gpui::{ elements::*, AnyElement, AnyViewHandle, AppContext, Entity, View, ViewContext, ViewHandle, WindowContext, }; pub trait ToolbarItemView: View { fn set_active_pane_item( &mut self, active_pane_item: Option<&dyn crate::ItemHandle>, cx: &mut ViewContext, ) -> ToolbarItemLocation; fn location_for_event( &self, _event: &Self::Event, current_location: ToolbarItemLocation, _cx: &AppContext, ) -> ToolbarItemLocation { current_location } fn pane_focus_update(&mut self, _pane_focused: bool, _cx: &mut ViewContext) {} /// Number of times toolbar's height will be repeated to get the effective height. /// Useful when multiple rows one under each other are needed. /// The rows have the same width and act as a whole when reacting to resizes and similar events. fn row_count(&self, _cx: &ViewContext) -> usize { 1 } } trait ToolbarItemViewHandle { fn id(&self) -> usize; fn as_any(&self) -> &AnyViewHandle; fn set_active_pane_item( &self, active_pane_item: Option<&dyn ItemHandle>, cx: &mut WindowContext, ) -> ToolbarItemLocation; fn focus_changed(&mut self, pane_focused: bool, cx: &mut WindowContext); fn row_count(&self, cx: &WindowContext) -> usize; } #[derive(Copy, Clone, Debug, PartialEq)] pub enum ToolbarItemLocation { Hidden, PrimaryLeft { flex: Option<(f32, bool)> }, PrimaryRight { flex: Option<(f32, bool)> }, Secondary, } pub struct Toolbar { active_item: Option>, hidden: bool, can_navigate: bool, items: Vec<(Box, ToolbarItemLocation)>, } impl Entity for Toolbar { type Event = (); } impl View for Toolbar { fn ui_name() -> &'static str { "Toolbar" } fn render(&mut self, cx: &mut ViewContext) -> AnyElement { let theme = &theme::current(cx).workspace.toolbar; let mut primary_left_items = Vec::new(); let mut primary_right_items = Vec::new(); let mut secondary_item = None; let spacing = theme.item_spacing; let mut primary_items_row_count = 1; for (item, position) in &self.items { match *position { ToolbarItemLocation::Hidden => {} ToolbarItemLocation::PrimaryLeft { flex } => { primary_items_row_count = primary_items_row_count.max(item.row_count(cx)); let left_item = ChildView::new(item.as_any(), cx).aligned(); if let Some((flex, expanded)) = flex { primary_left_items.push(left_item.flex(flex, expanded).into_any()); } else { primary_left_items.push(left_item.into_any()); } } ToolbarItemLocation::PrimaryRight { flex } => { primary_items_row_count = primary_items_row_count.max(item.row_count(cx)); let right_item = ChildView::new(item.as_any(), cx).aligned().flex_float(); if let Some((flex, expanded)) = flex { primary_right_items.push(right_item.flex(flex, expanded).into_any()); } else { primary_right_items.push(right_item.into_any()); } } ToolbarItemLocation::Secondary => { secondary_item = Some( ChildView::new(item.as_any(), cx) .constrained() .with_height(theme.height * item.row_count(cx) as f32) .into_any(), ); } } } let container_style = theme.container; let height = theme.height * primary_items_row_count as f32; let mut primary_items = Flex::row().with_spacing(spacing); primary_items.extend(primary_left_items); primary_items.extend(primary_right_items); let mut toolbar = Flex::column(); if !primary_items.is_empty() { toolbar.add_child(primary_items.constrained().with_height(height)); } if let Some(secondary_item) = secondary_item { toolbar.add_child(secondary_item); } if toolbar.is_empty() { toolbar.into_any_named("toolbar") } else { toolbar .contained() .with_style(container_style) .into_any_named("toolbar") } } } // <<<<<<< HEAD // ======= // #[allow(clippy::too_many_arguments)] // fn nav_button)>( // svg_path: &'static str, // style: theme::Interactive, // nav_button_height: f32, // tooltip_style: TooltipStyle, // enabled: bool, // spacing: f32, // on_click: F, // tooltip_action: A, // action_name: &'static str, // cx: &mut ViewContext, // ) -> AnyElement { // MouseEventHandler::new::(0, cx, |state, _| { // let style = if enabled { // style.style_for(state) // } else { // style.disabled_style() // }; // Svg::new(svg_path) // .with_color(style.color) // .constrained() // .with_width(style.icon_width) // .aligned() // .contained() // .with_style(style.container) // .constrained() // .with_width(style.button_width) // .with_height(nav_button_height) // .aligned() // .top() // }) // .with_cursor_style(if enabled { // CursorStyle::PointingHand // } else { // CursorStyle::default() // }) // .on_click(MouseButton::Left, move |_, toolbar, cx| { // on_click(toolbar, cx) // }) // .with_tooltip::( // 0, // action_name, // Some(Box::new(tooltip_action)), // tooltip_style, // cx, // ) // .contained() // .with_margin_right(spacing) // .into_any_named("nav button") // } // >>>>>>> 139cbbfd3aebd0863a7d51b0c12d748764cf0b2e impl Toolbar { pub fn new() -> Self { Self { active_item: None, items: Default::default(), hidden: false, can_navigate: true, } } pub fn set_can_navigate(&mut self, can_navigate: bool, cx: &mut ViewContext) { self.can_navigate = can_navigate; cx.notify(); } pub fn add_item(&mut self, item: ViewHandle, cx: &mut ViewContext) where T: 'static + ToolbarItemView, { let location = item.set_active_pane_item(self.active_item.as_deref(), cx); cx.subscribe(&item, |this, item, event, cx| { if let Some((_, current_location)) = this.items.iter_mut().find(|(i, _)| i.id() == item.id()) { let new_location = item .read(cx) .location_for_event(event, *current_location, cx); if new_location != *current_location { *current_location = new_location; cx.notify(); } } }) .detach(); self.items.push((Box::new(item), location)); cx.notify(); } pub fn set_active_item(&mut self, item: Option<&dyn ItemHandle>, cx: &mut ViewContext) { self.active_item = item.map(|item| item.boxed_clone()); self.hidden = self .active_item .as_ref() .map(|item| !item.show_toolbar(cx)) .unwrap_or(false); for (toolbar_item, current_location) in self.items.iter_mut() { let new_location = toolbar_item.set_active_pane_item(item, cx); if new_location != *current_location { *current_location = new_location; cx.notify(); } } } pub fn focus_changed(&mut self, focused: bool, cx: &mut ViewContext) { for (toolbar_item, _) in self.items.iter_mut() { toolbar_item.focus_changed(focused, cx); } } pub fn item_of_type(&self) -> Option> { self.items .iter() .find_map(|(item, _)| item.as_any().clone().downcast()) } pub fn hidden(&self) -> bool { self.hidden } } impl ToolbarItemViewHandle for ViewHandle { fn id(&self) -> usize { self.id() } fn as_any(&self) -> &AnyViewHandle { self } fn set_active_pane_item( &self, active_pane_item: Option<&dyn ItemHandle>, cx: &mut WindowContext, ) -> ToolbarItemLocation { self.update(cx, |this, cx| { this.set_active_pane_item(active_pane_item, cx) }) } fn focus_changed(&mut self, pane_focused: bool, cx: &mut WindowContext) { self.update(cx, |this, cx| { this.pane_focus_update(pane_focused, cx); cx.notify(); }); } fn row_count(&self, cx: &WindowContext) -> usize { self.read_with(cx, |this, cx| this.row_count(cx)) } } impl From<&dyn ToolbarItemViewHandle> for AnyViewHandle { fn from(val: &dyn ToolbarItemViewHandle) -> Self { val.as_any().clone() } }