ui.rs

  1use gpui::{
  2    color::Color,
  3    elements::{
  4        ConstrainedBox, Container, ContainerStyle, Empty, Flex, KeystrokeLabel, Label,
  5        MouseEventHandler, ParentElement, Svg,
  6    },
  7    Action, Element, ElementBox, EventContext, RenderContext, View,
  8};
  9use serde::Deserialize;
 10
 11use crate::ContainedText;
 12
 13#[derive(Clone, Deserialize, Default)]
 14pub struct CheckboxStyle {
 15    pub icon: IconStyle,
 16    pub label: ContainedText,
 17    pub default: ContainerStyle,
 18    pub checked: ContainerStyle,
 19    pub hovered: ContainerStyle,
 20    pub hovered_and_checked: ContainerStyle,
 21}
 22
 23pub fn checkbox<T: 'static, V: View>(
 24    label: &'static str,
 25    style: &CheckboxStyle,
 26    checked: bool,
 27    cx: &mut RenderContext<V>,
 28    change: fn(checked: bool, cx: &mut EventContext) -> (),
 29) -> MouseEventHandler<T> {
 30    let label = Label::new(label, style.label.text.clone())
 31        .contained()
 32        .with_style(style.label.container)
 33        .boxed();
 34
 35    checkbox_with_label(label, style, checked, cx, change)
 36}
 37
 38pub fn checkbox_with_label<T: 'static, V: View>(
 39    label: ElementBox,
 40    style: &CheckboxStyle,
 41    checked: bool,
 42    cx: &mut RenderContext<V>,
 43    change: fn(checked: bool, cx: &mut EventContext) -> (),
 44) -> MouseEventHandler<T> {
 45    MouseEventHandler::<T>::new(0, cx, |state, _| {
 46        let indicator = if checked {
 47            icon(&style.icon)
 48        } else {
 49            Empty::new()
 50                .constrained()
 51                .with_width(style.icon.dimensions.width)
 52                .with_height(style.icon.dimensions.height)
 53        };
 54
 55        Flex::row()
 56            .with_children([
 57                indicator
 58                    .contained()
 59                    .with_style(if checked {
 60                        if state.hovered() {
 61                            style.hovered_and_checked
 62                        } else {
 63                            style.checked
 64                        }
 65                    } else {
 66                        if state.hovered() {
 67                            style.hovered
 68                        } else {
 69                            style.default
 70                        }
 71                    })
 72                    .boxed(),
 73                label,
 74            ])
 75            .align_children_center()
 76            .boxed()
 77    })
 78    .on_click(gpui::MouseButton::Left, move |_, cx| change(!checked, cx))
 79    .with_cursor_style(gpui::CursorStyle::PointingHand)
 80}
 81
 82#[derive(Clone, Deserialize, Default)]
 83pub struct IconStyle {
 84    pub color: Color,
 85    pub icon: String,
 86    pub dimensions: Dimensions,
 87}
 88
 89#[derive(Clone, Deserialize, Default)]
 90pub struct Dimensions {
 91    pub width: f32,
 92    pub height: f32,
 93}
 94
 95pub fn icon(style: &IconStyle) -> ConstrainedBox {
 96    Svg::new(style.icon.clone())
 97        .with_color(style.color)
 98        .constrained()
 99        .with_width(style.dimensions.width)
100        .with_height(style.dimensions.height)
101}
102
103pub fn keystroke_label<V: View>(
104    label_text: &'static str,
105    label_style: &ContainedText,
106    keystroke_style: &ContainedText,
107    action: Box<dyn Action>,
108    cx: &mut RenderContext<V>,
109) -> Container {
110    // FIXME: Put the theme in it's own global so we can
111    // query the keystroke style on our own
112    keystroke_label_for(
113        cx.window_id(),
114        cx.handle().id(),
115        label_text,
116        label_style,
117        keystroke_style,
118        action,
119    )
120}
121
122pub fn keystroke_label_for(
123    window_id: usize,
124    view_id: usize,
125    label_text: &'static str,
126    label_style: &ContainedText,
127    keystroke_style: &ContainedText,
128    action: Box<dyn Action>,
129) -> Container {
130    Flex::row()
131        .with_child(
132            Label::new(label_text, label_style.text.clone())
133                .contained()
134                .boxed(),
135        )
136        .with_child({
137            KeystrokeLabel::new(
138                window_id,
139                view_id,
140                action,
141                keystroke_style.container,
142                keystroke_style.text.clone(),
143            )
144            .flex_float()
145            .boxed()
146        })
147        .contained()
148        .with_style(label_style.container)
149}