search_bar.rs

  1use std::borrow::Cow;
  2
  3use gpui::{
  4    elements::{Label, MouseEventHandler, Svg},
  5    platform::{CursorStyle, MouseButton},
  6    scene::{CornerRadii, MouseClick},
  7    Action, AnyElement, Element, EventContext, View, ViewContext,
  8};
  9use workspace::searchable::Direction;
 10
 11use crate::{
 12    mode::{SearchMode, Side},
 13    SelectNextMatch, SelectPrevMatch,
 14};
 15
 16pub(super) fn render_nav_button<V: View>(
 17    icon: &'static str,
 18    direction: Direction,
 19    active: bool,
 20    on_click: impl Fn(MouseClick, &mut V, &mut EventContext<V>) + 'static,
 21    cx: &mut ViewContext<V>,
 22) -> AnyElement<V> {
 23    let action: Box<dyn Action>;
 24    let tooltip;
 25
 26    match direction {
 27        Direction::Prev => {
 28            action = Box::new(SelectPrevMatch);
 29            tooltip = "Select Previous Match";
 30        }
 31        Direction::Next => {
 32            action = Box::new(SelectNextMatch);
 33            tooltip = "Select Next Match";
 34        }
 35    };
 36    let tooltip_style = theme::current(cx).tooltip.clone();
 37    let cursor_style = if active {
 38        CursorStyle::PointingHand
 39    } else {
 40        CursorStyle::default()
 41    };
 42    enum NavButton {}
 43    MouseEventHandler::new::<NavButton, _>(direction as usize, cx, |state, cx| {
 44        let theme = theme::current(cx);
 45        let style = theme
 46            .search
 47            .nav_button
 48            .in_state(active)
 49            .style_for(state)
 50            .clone();
 51        let mut container_style = style.container.clone();
 52        let label = Label::new(icon, style.label.clone()).aligned().contained();
 53        container_style.corner_radii = match direction {
 54            Direction::Prev => CornerRadii {
 55                bottom_right: 0.,
 56                top_right: 0.,
 57                ..container_style.corner_radii
 58            },
 59            Direction::Next => CornerRadii {
 60                bottom_left: 0.,
 61                top_left: 0.,
 62                ..container_style.corner_radii
 63            },
 64        };
 65        if direction == Direction::Prev {
 66            // Remove right border so that when both Next and Prev buttons are
 67            // next to one another, there's no double border between them.
 68            container_style.border.right = false;
 69        }
 70        label.with_style(container_style)
 71    })
 72    .on_click(MouseButton::Left, on_click)
 73    .with_cursor_style(cursor_style)
 74    .with_tooltip::<NavButton>(
 75        direction as usize,
 76        tooltip.to_string(),
 77        Some(action),
 78        tooltip_style,
 79        cx,
 80    )
 81    .into_any()
 82}
 83
 84pub(crate) fn render_search_mode_button<V: View>(
 85    mode: SearchMode,
 86    side: Option<Side>,
 87    is_active: bool,
 88    on_click: impl Fn(MouseClick, &mut V, &mut EventContext<V>) + 'static,
 89    cx: &mut ViewContext<V>,
 90) -> AnyElement<V> {
 91    let tooltip_style = theme::current(cx).tooltip.clone();
 92    enum SearchModeButton {}
 93    MouseEventHandler::new::<SearchModeButton, _>(mode.region_id(), cx, |state, cx| {
 94        let theme = theme::current(cx);
 95        let style = theme
 96            .search
 97            .mode_button
 98            .in_state(is_active)
 99            .style_for(state)
100            .clone();
101
102        let mut container_style = style.container;
103        if let Some(button_side) = side {
104            if button_side == Side::Left {
105                container_style.border.left = true;
106                container_style.corner_radii = CornerRadii {
107                    bottom_right: 0.,
108                    top_right: 0.,
109                    ..container_style.corner_radii
110                };
111            } else {
112                container_style.border.left = false;
113                container_style.corner_radii = CornerRadii {
114                    bottom_left: 0.,
115                    top_left: 0.,
116                    ..container_style.corner_radii
117                };
118            }
119        } else {
120            container_style.border.left = false;
121            container_style.corner_radii = CornerRadii::default();
122        }
123
124        Label::new(mode.label(), style.text)
125            .aligned()
126            .contained()
127            .with_style(container_style)
128            .constrained()
129            .with_height(theme.search.search_bar_row_height)
130    })
131    .on_click(MouseButton::Left, on_click)
132    .with_cursor_style(CursorStyle::PointingHand)
133    .with_tooltip::<SearchModeButton>(
134        mode.region_id(),
135        mode.tooltip_text().to_owned(),
136        Some(mode.activate_action()),
137        tooltip_style,
138        cx,
139    )
140    .into_any()
141}
142
143pub(crate) fn render_option_button_icon<V: View>(
144    is_active: bool,
145    icon: &'static str,
146    id: usize,
147    label: impl Into<Cow<'static, str>>,
148    action: Box<dyn Action>,
149    on_click: impl Fn(MouseClick, &mut V, &mut EventContext<V>) + 'static,
150    cx: &mut ViewContext<V>,
151) -> AnyElement<V> {
152    let tooltip_style = theme::current(cx).tooltip.clone();
153    MouseEventHandler::new::<V, _>(id, cx, |state, cx| {
154        let theme = theme::current(cx);
155        let style = theme
156            .search
157            .option_button
158            .in_state(is_active)
159            .style_for(state);
160        Svg::new(icon)
161            .with_color(style.color.clone())
162            .constrained()
163            .with_width(style.icon_width)
164            .contained()
165            .with_style(style.container)
166            .constrained()
167            .with_height(theme.search.option_button_height)
168            .with_width(style.button_width)
169    })
170    .on_click(MouseButton::Left, on_click)
171    .with_cursor_style(CursorStyle::PointingHand)
172    .with_tooltip::<V>(id, label, Some(action), tooltip_style, cx)
173    .into_any()
174}