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