From 5672c6065fbff58e73c1334eac3f061d41e2f064 Mon Sep 17 00:00:00 2001 From: Jason Lee Date: Tue, 25 Nov 2025 10:07:31 +0800 Subject: [PATCH] chore: Split some feature into geometry.rs (#1676) --- crates/ui/src/geometry.rs | 214 ++++++++++++++++++++++++++++++++++++++ crates/ui/src/lib.rs | 6 +- crates/ui/src/styled.rs | 123 +--------------------- 3 files changed, 221 insertions(+), 122 deletions(-) create mode 100644 crates/ui/src/geometry.rs diff --git a/crates/ui/src/geometry.rs b/crates/ui/src/geometry.rs new file mode 100644 index 00000000..a50a9b8b --- /dev/null +++ b/crates/ui/src/geometry.rs @@ -0,0 +1,214 @@ +use std::fmt::{self, Display, Formatter}; + +use gpui::{AbsoluteLength, Axis, Length, Pixels}; +use serde::{Deserialize, Serialize}; + +/// A enum for defining the placement of the element. +/// +/// See also: [`Side`] if you need to define the left, right side. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +pub enum Placement { + #[serde(rename = "top")] + Top, + #[serde(rename = "bottom")] + Bottom, + #[serde(rename = "left")] + Left, + #[serde(rename = "right")] + Right, +} + +impl Display for Placement { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + Placement::Top => write!(f, "Top"), + Placement::Bottom => write!(f, "Bottom"), + Placement::Left => write!(f, "Left"), + Placement::Right => write!(f, "Right"), + } + } +} + +impl Placement { + #[inline] + pub fn is_horizontal(&self) -> bool { + match self { + Placement::Left | Placement::Right => true, + _ => false, + } + } + + #[inline] + pub fn is_vertical(&self) -> bool { + match self { + Placement::Top | Placement::Bottom => true, + _ => false, + } + } + + #[inline] + pub fn axis(&self) -> Axis { + match self { + Placement::Top | Placement::Bottom => Axis::Vertical, + Placement::Left | Placement::Right => Axis::Horizontal, + } + } +} + +/// A enum for defining the side of the element. +/// +/// See also: [`Placement`] if you need to define the 4 edges. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +pub enum Side { + #[serde(rename = "left")] + Left, + #[serde(rename = "right")] + Right, +} + +impl Side { + /// Returns true if the side is left. + #[inline] + pub fn is_left(&self) -> bool { + matches!(self, Self::Left) + } + + /// Returns true if the side is right. + #[inline] + pub fn is_right(&self) -> bool { + matches!(self, Self::Right) + } +} + +/// A trait to extend the [`Axis`] enum with utility methods. +pub trait AxisExt { + fn is_horizontal(self) -> bool; + fn is_vertical(self) -> bool; +} + +impl AxisExt for Axis { + #[inline] + fn is_horizontal(self) -> bool { + self == Axis::Horizontal + } + + #[inline] + fn is_vertical(self) -> bool { + self == Axis::Vertical + } +} + +/// A trait for converting [`Pixels`] to `f32` and `f64`. +pub trait PixelsExt { + fn as_f32(&self) -> f32; + fn as_f64(self) -> f64; +} +impl PixelsExt for Pixels { + fn as_f32(&self) -> f32 { + f32::from(self) + } + + fn as_f64(self) -> f64 { + f64::from(self) + } +} + +/// A trait to extend the [`Length`] enum with utility methods. +pub trait LengthExt { + /// Converts the [`Length`] to [`Pixels`] based on a given `base_size` and `rem_size`. + /// + /// If the [`Length`] is [`Length::Auto`], it returns `None`. + fn to_pixels(&self, base_size: AbsoluteLength, rem_size: Pixels) -> Option; +} + +impl LengthExt for Length { + fn to_pixels(&self, base_size: AbsoluteLength, rem_size: Pixels) -> Option { + match self { + Length::Auto => None, + Length::Definite(len) => Some(len.to_pixels(base_size, rem_size)), + } + } +} + +#[cfg(test)] +mod tests { + use super::Placement; + #[test] + fn test_placement() { + assert!(Placement::Left.is_horizontal()); + assert!(Placement::Right.is_horizontal()); + assert!(!Placement::Top.is_horizontal()); + assert!(!Placement::Bottom.is_horizontal()); + + assert!(Placement::Top.is_vertical()); + assert!(Placement::Bottom.is_vertical()); + assert!(!Placement::Left.is_vertical()); + assert!(!Placement::Right.is_vertical()); + + assert_eq!(Placement::Top.axis(), gpui::Axis::Vertical); + assert_eq!(Placement::Bottom.axis(), gpui::Axis::Vertical); + assert_eq!(Placement::Left.axis(), gpui::Axis::Horizontal); + assert_eq!(Placement::Right.axis(), gpui::Axis::Horizontal); + + assert_eq!(Placement::Top.to_string(), "Top"); + assert_eq!(Placement::Bottom.to_string(), "Bottom"); + assert_eq!(Placement::Left.to_string(), "Left"); + assert_eq!(Placement::Right.to_string(), "Right"); + + assert_eq!(serde_json::to_string(&Placement::Top).unwrap(), r#""top""#); + assert_eq!( + serde_json::to_string(&Placement::Bottom).unwrap(), + r#""bottom""# + ); + assert_eq!( + serde_json::to_string(&Placement::Left).unwrap(), + r#""left""# + ); + assert_eq!( + serde_json::to_string(&Placement::Right).unwrap(), + r#""right""# + ); + + assert_eq!( + serde_json::from_str::(r#""top""#).unwrap(), + Placement::Top + ); + assert_eq!( + serde_json::from_str::(r#""bottom""#).unwrap(), + Placement::Bottom + ); + assert_eq!( + serde_json::from_str::(r#""left""#).unwrap(), + Placement::Left + ); + assert_eq!( + serde_json::from_str::(r#""right""#).unwrap(), + Placement::Right + ); + } + + #[test] + fn test_side() { + use super::Side; + let left = Side::Left; + let right = Side::Right; + + assert!(left.is_left()); + assert!(!left.is_right()); + + assert!(right.is_right()); + assert!(!right.is_left()); + + // Test serialization + assert_eq!(serde_json::to_string(&left).unwrap(), r#""left""#); + assert_eq!(serde_json::to_string(&right).unwrap(), r#""right""#); + assert_eq!( + serde_json::from_str::(r#""left""#).unwrap(), + Side::Left + ); + assert_eq!( + serde_json::from_str::(r#""right""#).unwrap(), + Side::Right + ); + } +} diff --git a/crates/ui/src/lib.rs b/crates/ui/src/lib.rs index cc47ece9..72670e4f 100644 --- a/crates/ui/src/lib.rs +++ b/crates/ui/src/lib.rs @@ -2,6 +2,7 @@ use gpui::{App, SharedString}; use std::ops::Deref; mod event; +mod geometry; mod global_state; mod icon; mod index_path; @@ -75,6 +76,7 @@ pub use wry; pub use crate::Disableable; pub use event::InteractiveElementExt; +pub use geometry::*; pub use icon::*; pub use index_path::IndexPath; pub use input::{Rope, RopeExt, RopeLines}; @@ -84,8 +86,8 @@ pub use root::{Root, WindowExt}; pub use styled::*; pub use theme::*; pub use title_bar::*; -pub use virtual_list::{h_virtual_list, v_virtual_list, VirtualList, VirtualListScrollHandle}; -pub use window_border::{window_border, window_paddings, WindowBorder}; +pub use virtual_list::{VirtualList, VirtualListScrollHandle, h_virtual_list, v_virtual_list}; +pub use window_border::{WindowBorder, window_border, window_paddings}; rust_i18n::i18n!("locales", fallback = "en"); diff --git a/crates/ui/src/styled.rs b/crates/ui/src/styled.rs index bcd480dd..0df5112f 100644 --- a/crates/ui/src/styled.rs +++ b/crates/ui/src/styled.rs @@ -1,13 +1,10 @@ -use std::fmt::{self, Display, Formatter}; - use crate::{ + ActiveTheme, PixelsExt as _, scroll::{Scrollable, ScrollbarAxis}, - ActiveTheme, }; use gpui::{ - div, point, px, AbsoluteLength, App, Axis, BoxShadow, Corners, DefiniteLength, Div, Edges, - Element, FocusHandle, Hsla, Length, ParentElement, Pixels, Refineable, StyleRefinement, Styled, - Window, + App, BoxShadow, Corners, DefiniteLength, Div, Edges, Element, FocusHandle, Hsla, ParentElement, + Pixels, Refineable, StyleRefinement, Styled, Window, div, point, px, }; use serde::{Deserialize, Serialize}; @@ -640,126 +637,12 @@ impl FocusableExt for T { } } -pub trait AxisExt { - fn is_horizontal(self) -> bool; - fn is_vertical(self) -> bool; -} - -impl AxisExt for Axis { - #[inline] - fn is_horizontal(self) -> bool { - self == Axis::Horizontal - } - - #[inline] - fn is_vertical(self) -> bool { - self == Axis::Vertical - } -} - -#[derive(Clone, Copy, PartialEq, Eq, Debug)] -pub enum Placement { - Top, - Bottom, - Left, - Right, -} - -impl Display for Placement { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - match self { - Placement::Top => write!(f, "Top"), - Placement::Bottom => write!(f, "Bottom"), - Placement::Left => write!(f, "Left"), - Placement::Right => write!(f, "Right"), - } - } -} - -impl Placement { - #[inline] - pub fn is_horizontal(&self) -> bool { - match self { - Placement::Left | Placement::Right => true, - _ => false, - } - } - - #[inline] - pub fn is_vertical(&self) -> bool { - match self { - Placement::Top | Placement::Bottom => true, - _ => false, - } - } - - #[inline] - pub fn axis(&self) -> Axis { - match self { - Placement::Top | Placement::Bottom => Axis::Vertical, - Placement::Left | Placement::Right => Axis::Horizontal, - } - } -} - -/// A enum for defining the side of the element. -#[derive(Clone, Copy, PartialEq, Eq, Debug)] -pub enum Side { - Left, - Right, -} - -impl Side { - /// Returns true if the side is left. - #[inline] - pub fn is_left(&self) -> bool { - matches!(self, Self::Left) - } - - /// Returns true if the side is right. - #[inline] - pub fn is_right(&self) -> bool { - matches!(self, Self::Right) - } -} - /// A trait for defining element that can be collapsed. pub trait Collapsible { fn collapsed(self, collapsed: bool) -> Self; fn is_collapsed(&self) -> bool; } -/// A trait for converting `Pixels` to `f32` and `f64`. -pub trait PixelsExt { - fn as_f32(&self) -> f32; - fn as_f64(self) -> f64; -} -impl PixelsExt for Pixels { - fn as_f32(&self) -> f32 { - f32::from(self) - } - - fn as_f64(self) -> f64 { - f64::from(self) - } -} - -pub trait LengthExt { - /// Converts the `Length` to `Pixels` based on a given `base_size` and `rem_size`. - /// - /// If the `Length` is `Auto`, it returns `None`. - fn to_pixels(&self, base_size: AbsoluteLength, rem_size: Pixels) -> Option; -} - -impl LengthExt for Length { - fn to_pixels(&self, base_size: AbsoluteLength, rem_size: Pixels) -> Option { - match self { - Length::Auto => None, - Length::Definite(len) => Some(len.to_pixels(base_size, rem_size)), - } - } -} - #[cfg(test)] mod tests { use gpui::px;