1use gpui::prelude::*;
4use gpui::{div, px, Context, EventEmitter, IntoElement, SharedString, Window};
5
6use super::control_metrics;
7use crate::theme::{theme, ColorName, Size};
8
9#[derive(Debug, Clone)]
11pub struct SegmentedControlEvent(pub usize);
12
13pub struct SegmentedControl {
16 options: Vec<SharedString>,
17 selected: usize,
18 size: Size,
19}
20
21impl EventEmitter<SegmentedControlEvent> for SegmentedControl {}
22
23impl SegmentedControl {
24 pub fn new(_cx: &mut Context<Self>) -> Self {
25 SegmentedControl {
26 options: Vec::new(),
27 selected: 0,
28 size: Size::Sm,
29 }
30 }
31
32 pub fn data<I, S>(mut self, options: I) -> Self
33 where
34 I: IntoIterator<Item = S>,
35 S: Into<SharedString>,
36 {
37 self.options = options.into_iter().map(Into::into).collect();
38 self
39 }
40
41 pub fn selected(mut self, index: usize) -> Self {
42 self.selected = index;
43 self
44 }
45
46 pub fn size(mut self, size: Size) -> Self {
47 self.size = size;
48 self
49 }
50
51 pub fn selected_index(&self) -> usize {
52 self.selected
53 }
54}
55
56impl Render for SegmentedControl {
57 fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
58 let t = theme(cx);
59 let (height, pad_x, font) = control_metrics(self.size);
60 let radius = t.radius(Size::Sm);
61 let track = t.color(ColorName::Gray, if t.scheme.is_dark() { 8 } else { 1 }).hsla();
62 let active_bg = t.surface().hsla();
63 let active_fg = t.text().hsla();
64 let inactive_fg = t.dimmed().hsla();
65
66 let count = self.options.len();
67 let selected = if count == 0 { 0 } else { self.selected.min(count - 1) };
68
69 let mut row = div()
70 .flex()
71 .items_center()
72 .gap(px(2.0))
73 .p(px(3.0))
74 .rounded(px(radius + 2.0))
75 .bg(track);
76
77 for (i, option) in self.options.iter().enumerate() {
78 let is_active = i == selected;
79 let mut seg = div()
80 .id(("guise-segment", i))
81 .flex()
82 .items_center()
83 .justify_center()
84 .h(px(height - 6.0))
85 .px(px(pad_x))
86 .rounded(px(radius))
87 .text_size(px(font))
88 .text_color(if is_active { active_fg } else { inactive_fg })
89 .child(option.clone())
90 .on_click(cx.listener(move |this, _ev, _window, cx| {
91 this.selected = i;
92 cx.emit(SegmentedControlEvent(i));
93 cx.notify();
94 }));
95 if is_active {
96 seg = seg.bg(active_bg).shadow_sm();
97 }
98 row = row.child(seg);
99 }
100 row
101 }
102}