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}