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