toolbar.rs

  1use crate::{ItemHandle, Settings};
  2use gpui::{
  3    elements::*, AnyViewHandle, ElementBox, Entity, MutableAppContext, RenderContext, View,
  4    ViewContext, ViewHandle,
  5};
  6
  7pub trait ToolbarItemView: View {
  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    ) -> ToolbarItemLocation {
 19        current_location
 20    }
 21}
 22
 23trait ToolbarItemViewHandle {
 24    fn id(&self) -> usize;
 25    fn to_any(&self) -> AnyViewHandle;
 26    fn set_active_pane_item(
 27        &self,
 28        active_pane_item: Option<&dyn ItemHandle>,
 29        cx: &mut MutableAppContext,
 30    ) -> ToolbarItemLocation;
 31}
 32
 33#[derive(Copy, Clone, Debug, PartialEq, Eq)]
 34pub enum ToolbarItemLocation {
 35    Hidden,
 36    PrimaryLeft,
 37    PrimaryRight,
 38    Secondary,
 39}
 40
 41pub struct Toolbar {
 42    active_pane_item: Option<Box<dyn ItemHandle>>,
 43    items: Vec<(Box<dyn ToolbarItemViewHandle>, ToolbarItemLocation)>,
 44}
 45
 46impl Entity for Toolbar {
 47    type Event = ();
 48}
 49
 50impl View for Toolbar {
 51    fn ui_name() -> &'static str {
 52        "Toolbar"
 53    }
 54
 55    fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
 56        let theme = &cx.global::<Settings>().theme.workspace.toolbar;
 57
 58        let mut primary_left_items = Vec::new();
 59        let mut primary_right_items = Vec::new();
 60        let mut secondary_item = None;
 61
 62        for (item, position) in &self.items {
 63            match position {
 64                ToolbarItemLocation::Hidden => {}
 65                ToolbarItemLocation::PrimaryLeft => primary_left_items.push(item),
 66                ToolbarItemLocation::PrimaryRight => primary_right_items.push(item),
 67                ToolbarItemLocation::Secondary => secondary_item = Some(item),
 68            }
 69        }
 70
 71        Flex::column()
 72            .with_child(
 73                Flex::row()
 74                    .with_children(primary_left_items.iter().map(|i| {
 75                        ChildView::new(i.as_ref())
 76                            .aligned()
 77                            .contained()
 78                            .with_margin_right(theme.item_spacing)
 79                            .boxed()
 80                    }))
 81                    .with_children(primary_right_items.iter().map(|i| {
 82                        ChildView::new(i.as_ref())
 83                            .aligned()
 84                            .contained()
 85                            .with_margin_left(theme.item_spacing)
 86                            .flex_float()
 87                            .boxed()
 88                    }))
 89                    .constrained()
 90                    .with_height(theme.height)
 91                    .boxed(),
 92            )
 93            .with_children(secondary_item.map(|item| {
 94                ChildView::new(item.as_ref())
 95                    .constrained()
 96                    .with_height(theme.height)
 97                    .boxed()
 98            }))
 99            .contained()
100            .with_style(theme.container)
101            .boxed()
102    }
103}
104
105impl Toolbar {
106    pub fn new() -> Self {
107        Self {
108            active_pane_item: None,
109            items: Default::default(),
110        }
111    }
112
113    pub fn add_item<T>(&mut self, item: ViewHandle<T>, cx: &mut ViewContext<Self>)
114    where
115        T: 'static + ToolbarItemView,
116    {
117        let location = item.set_active_pane_item(self.active_pane_item.as_deref(), cx);
118        cx.subscribe(&item, |this, item, event, cx| {
119            if let Some((_, current_location)) =
120                this.items.iter_mut().find(|(i, _)| i.id() == item.id())
121            {
122                let new_location = item.read(cx).location_for_event(event, *current_location);
123                if new_location != *current_location {
124                    *current_location = new_location;
125                    cx.notify();
126                }
127            }
128        })
129        .detach();
130        self.items.push((Box::new(item), dbg!(location)));
131        cx.notify();
132    }
133
134    pub fn set_active_pane_item(
135        &mut self,
136        pane_item: Option<&dyn ItemHandle>,
137        cx: &mut ViewContext<Self>,
138    ) {
139        self.active_pane_item = pane_item.map(|item| item.boxed_clone());
140        for (toolbar_item, current_location) in self.items.iter_mut() {
141            let new_location = toolbar_item.set_active_pane_item(pane_item, cx);
142            if new_location != *current_location {
143                *current_location = new_location;
144                cx.notify();
145            }
146        }
147    }
148
149    pub fn item_of_type<T: ToolbarItemView>(&self) -> Option<ViewHandle<T>> {
150        self.items
151            .iter()
152            .find_map(|(item, _)| item.to_any().downcast())
153    }
154}
155
156impl<T: ToolbarItemView> ToolbarItemViewHandle for ViewHandle<T> {
157    fn id(&self) -> usize {
158        self.id()
159    }
160
161    fn to_any(&self) -> AnyViewHandle {
162        self.into()
163    }
164
165    fn set_active_pane_item(
166        &self,
167        active_pane_item: Option<&dyn ItemHandle>,
168        cx: &mut MutableAppContext,
169    ) -> ToolbarItemLocation {
170        self.update(cx, |this, cx| {
171            this.set_active_pane_item(active_pane_item, cx)
172        })
173    }
174}
175
176impl Into<AnyViewHandle> for &dyn ToolbarItemViewHandle {
177    fn into(self) -> AnyViewHandle {
178        self.to_any()
179    }
180}