elements.rs

  1use std::ops::Range;
  2
  3use gpui::color::Color;
  4use gpui::geometry::rect::RectF;
  5use gpui::geometry::vector::IntoVector2F;
  6use gpui::json::{self, ToJson};
  7use gpui::{scene::Path, LayoutContext};
  8use gpui::{Element, SceneBuilder, View, ViewContext};
  9
 10type CreatePath = fn(RectF, Color) -> Path;
 11type AdjustBorder = fn(RectF, f32) -> RectF;
 12type BorderThickness = f32;
 13
 14pub(crate) struct ButtonSide {
 15    color: Color,
 16    factory: CreatePath,
 17    /// After the outline is drawn with border color,
 18    /// the drawing bounds have to be adjusted by different factors in different dimensions.
 19    border_adjustment: AdjustBorder,
 20    border: Option<(BorderThickness, Color)>,
 21}
 22
 23impl ButtonSide {
 24    fn new(color: Color, factory: CreatePath, border_adjustment: AdjustBorder) -> Self {
 25        Self {
 26            color,
 27            factory,
 28            border_adjustment,
 29            border: None,
 30        }
 31    }
 32    pub fn with_border(mut self, width: f32, color: Color) -> Self {
 33        self.border = Some((width, color));
 34        self
 35    }
 36    pub fn left(color: Color) -> Self {
 37        Self::new(color, left_button_side, left_button_border_adjust)
 38    }
 39    pub fn right(color: Color) -> Self {
 40        Self::new(color, right_button_side, right_button_border_adjust)
 41    }
 42}
 43
 44fn left_button_border_adjust(bounds: RectF, width: f32) -> RectF {
 45    let width = width.into_vector_2f();
 46    let mut lower_right = bounds.clone().lower_right();
 47    lower_right.set_x(lower_right.x() + width.x());
 48    RectF::from_points(bounds.origin() + width, lower_right)
 49}
 50fn right_button_border_adjust(bounds: RectF, width: f32) -> RectF {
 51    let width = width.into_vector_2f();
 52    let mut origin = bounds.clone().origin();
 53    origin.set_x(origin.x() - width.x());
 54    RectF::from_points(origin, bounds.lower_right() - width)
 55}
 56fn left_button_side(bounds: RectF, color: Color) -> Path {
 57    use gpui::geometry::PathBuilder;
 58    let mut path = PathBuilder::new();
 59    path.reset(bounds.lower_right());
 60    path.line_to(bounds.upper_right());
 61    let mut middle_point = bounds.origin();
 62    let distance_to_line = (middle_point.y() - bounds.lower_left().y()) / 4.;
 63    middle_point.set_y(middle_point.y() - distance_to_line);
 64    path.curve_to(middle_point, bounds.origin());
 65    let mut target = bounds.lower_left();
 66    target.set_y(target.y() + distance_to_line);
 67    path.line_to(target);
 68    path.curve_to(bounds.lower_right(), bounds.lower_left());
 69    path.build(color, None)
 70}
 71
 72fn right_button_side(bounds: RectF, color: Color) -> Path {
 73    use gpui::geometry::PathBuilder;
 74    let mut path = PathBuilder::new();
 75    path.reset(bounds.lower_left());
 76    path.line_to(bounds.origin());
 77    let mut middle_point = bounds.upper_right();
 78    let distance_to_line = (middle_point.y() - bounds.lower_right().y()) / 4.;
 79    middle_point.set_y(middle_point.y() - distance_to_line);
 80    path.curve_to(middle_point, bounds.upper_right());
 81    let mut target = bounds.lower_right();
 82    target.set_y(target.y() + distance_to_line);
 83    path.line_to(target);
 84    path.curve_to(bounds.lower_left(), bounds.lower_right());
 85    path.build(color, None)
 86}
 87
 88impl<V: View> Element<V> for ButtonSide {
 89    type LayoutState = ();
 90
 91    type PaintState = ();
 92
 93    fn layout(
 94        &mut self,
 95        constraint: gpui::SizeConstraint,
 96        _: &mut V,
 97        _: &mut LayoutContext<V>,
 98    ) -> (gpui::geometry::vector::Vector2F, Self::LayoutState) {
 99        (constraint.max, ())
100    }
101
102    fn paint(
103        &mut self,
104        scene: &mut SceneBuilder,
105        bounds: RectF,
106        _: RectF,
107        _: &mut Self::LayoutState,
108        _: &mut V,
109        _: &mut ViewContext<V>,
110    ) -> Self::PaintState {
111        let mut bounds = bounds;
112        if let Some((border_width, border_color)) = self.border.as_ref() {
113            scene.push_path((self.factory)(bounds, border_color.clone()));
114            bounds = (self.border_adjustment)(bounds, *border_width);
115        };
116        scene.push_path((self.factory)(bounds, self.color));
117    }
118
119    fn rect_for_text_range(
120        &self,
121        _: Range<usize>,
122        _: RectF,
123        _: RectF,
124        _: &Self::LayoutState,
125        _: &Self::PaintState,
126        _: &V,
127        _: &ViewContext<V>,
128    ) -> Option<RectF> {
129        None
130    }
131
132    fn debug(
133        &self,
134        bounds: RectF,
135        _: &Self::LayoutState,
136        _: &Self::PaintState,
137        _: &V,
138        _: &ViewContext<V>,
139    ) -> gpui::json::Value {
140        json::json!({
141            "type": "ButtonSide",
142            "bounds": bounds.to_json(),
143            "color": self.color.to_json(),
144        })
145    }
146}