1525 lines · 52.5 KB · Rust
RawDownload
use std::{    hash::{Hash, Hasher},    iter, mem,    ops::Range,}; use crate::{    AbsoluteLength, App, Background, BackgroundTag, BorderStyle, Bounds, ContentMask, Corners,    CornersRefinement, CursorStyle, DefiniteLength, DevicePixels, Edges, EdgesRefinement, Font,    FontFallbacks, FontFeatures, FontStyle, FontWeight, GridLocation, Hsla, Length, Pixels, Point,    PointRefinement, Rgba, SharedString, Size, SizeRefinement, Styled, TextRun, Window, black, phi,    point, px, quad, rems, size,};use collections::HashSet;use refineable::Refineable;use schemars::JsonSchema;use serde::{Deserialize, Serialize}; /// Use this struct for interfacing with the 'debug_below' styling from your own elements./// If a parent element has this style set on it, then this struct will be set as a global in/// GPUI.#[cfg(debug_assertions)]pub struct DebugBelow; #[cfg(debug_assertions)]impl crate::Global for DebugBelow {} /// How to fit the image into the bounds of the element.pub enum ObjectFit {    /// The image will be stretched to fill the bounds of the element.    Fill,    /// The image will be scaled to fit within the bounds of the element.    Contain,    /// The image will be scaled to cover the bounds of the element.    Cover,    /// The image will be scaled down to fit within the bounds of the element.    ScaleDown,    /// The image will maintain its original size.    None,} impl ObjectFit {    /// Get the bounds of the image within the given bounds.    pub fn get_bounds(        &self,        bounds: Bounds<Pixels>,        image_size: Size<DevicePixels>,    ) -> Bounds<Pixels> {        let image_size = image_size.map(|dimension| Pixels::from(u32::from(dimension)));        let image_ratio = image_size.width / image_size.height;        let bounds_ratio = bounds.size.width / bounds.size.height;         match self {            ObjectFit::Fill => bounds,            ObjectFit::Contain => {                let new_size = if bounds_ratio > image_ratio {                    size(                        image_size.width * (bounds.size.height / image_size.height),                        bounds.size.height,                    )                } else {                    size(                        bounds.size.width,                        image_size.height * (bounds.size.width / image_size.width),                    )                };                 Bounds {                    origin: point(                        bounds.origin.x + (bounds.size.width - new_size.width) / 2.0,                        bounds.origin.y + (bounds.size.height - new_size.height) / 2.0,                    ),                    size: new_size,                }            }            ObjectFit::ScaleDown => {                // Check if the image is larger than the bounds in either dimension.                if image_size.width > bounds.size.width || image_size.height > bounds.size.height {                    // If the image is larger, use the same logic as Contain to scale it down.                    let new_size = if bounds_ratio > image_ratio {                        size(                            image_size.width * (bounds.size.height / image_size.height),                            bounds.size.height,                        )                    } else {                        size(                            bounds.size.width,                            image_size.height * (bounds.size.width / image_size.width),                        )                    };                     Bounds {                        origin: point(                            bounds.origin.x + (bounds.size.width - new_size.width) / 2.0,                            bounds.origin.y + (bounds.size.height - new_size.height) / 2.0,                        ),                        size: new_size,                    }                } else {                    // If the image is smaller than or equal to the container, display it at its original size,                    // centered within the container.                    let original_size = size(image_size.width, image_size.height);                    Bounds {                        origin: point(                            bounds.origin.x + (bounds.size.width - original_size.width) / 2.0,                            bounds.origin.y + (bounds.size.height - original_size.height) / 2.0,                        ),                        size: original_size,                    }                }            }            ObjectFit::Cover => {                let new_size = if bounds_ratio > image_ratio {                    size(                        bounds.size.width,                        image_size.height * (bounds.size.width / image_size.width),                    )                } else {                    size(                        image_size.width * (bounds.size.height / image_size.height),                        bounds.size.height,                    )                };                 Bounds {                    origin: point(                        bounds.origin.x + (bounds.size.width - new_size.width) / 2.0,                        bounds.origin.y + (bounds.size.height - new_size.height) / 2.0,                    ),                    size: new_size,                }            }            ObjectFit::None => Bounds {                origin: bounds.origin,                size: image_size,            },        }    }} /// The minimum size of a column or row in a grid layout#[derive(    Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Default, JsonSchema, Serialize, Deserialize,)]pub enum GridTemplateMinSize {    /// The column or row size may be 0    #[default]    Zero,    /// The column or row size can be determined by the min content    MinContent,    /// The column or row size can be determined by the max content    MaxContent,} /// A simplified representation of the grid-template-* value#[derive(    Copy,    Clone,    Refineable,    PartialEq,    Eq,    PartialOrd,    Ord,    Debug,    Default,    JsonSchema,    Serialize,    Deserialize,)]pub struct GridTemplate {    /// How this template directive should be repeated    pub repeat: u16,    /// The minimum size in the repeat(<>, minmax(_, 1fr)) equation    pub min_size: GridTemplateMinSize,} /// The CSS styling that can be applied to an element via the `Styled` trait#[derive(Clone, Refineable, Debug)]#[refineable(Debug, PartialEq, Serialize, Deserialize, JsonSchema)]pub struct Style {    /// What layout strategy should be used?    pub display: Display,     /// Should the element be painted on screen?    pub visibility: Visibility,     // Overflow properties    /// How children overflowing their container should affect layout    #[refineable]    pub overflow: Point<Overflow>,    /// How much space (in points) should be reserved for the scrollbars of `Overflow::Scroll` and `Overflow::Auto` nodes.    pub scrollbar_width: AbsoluteLength,    /// Whether both x and y axis should be scrollable at the same time.    pub allow_concurrent_scroll: bool,    /// Whether scrolling should be restricted to the axis indicated by the mouse wheel.    ///    /// This means that:    /// - The mouse wheel alone will only ever scroll the Y axis.    /// - Holding `Shift` and using the mouse wheel will scroll the X axis.    ///    /// ## Motivation    ///    /// On the web when scrolling with the mouse wheel, scrolling up and down will always scroll the Y axis, even when    /// the mouse is over a horizontally-scrollable element.    ///    /// The only way to scroll horizontally is to hold down `Shift` while scrolling, which then changes the scroll axis    /// to the X axis.    ///    /// Currently, GPUI operates differently from the web in that it will scroll an element in either the X or Y axis    /// when scrolling with just the mouse wheel. This causes problems when scrolling in a vertical list that contains    /// horizontally-scrollable elements, as when you get to the horizontally-scrollable elements the scroll will be    /// hijacked.    ///    /// Ideally we would match the web's behavior and not have a need for this, but right now we're adding this opt-in    /// style property to limit the potential blast radius.    pub restrict_scroll_to_axis: bool,     // Position properties    /// What should the `position` value of this struct use as a base offset?    pub position: Position,    /// How should the position of this element be tweaked relative to the layout defined?    #[refineable]    pub inset: Edges<Length>,     // Size properties    /// Sets the initial size of the item    #[refineable]    pub size: Size<Length>,    /// Controls the minimum size of the item    #[refineable]    pub min_size: Size<Length>,    /// Controls the maximum size of the item    #[refineable]    pub max_size: Size<Length>,    /// Sets the preferred aspect ratio for the item. The ratio is calculated as width divided by height.    pub aspect_ratio: Option<f32>,     // Spacing Properties    /// How large should the margin be on each side?    #[refineable]    pub margin: Edges<Length>,    /// How large should the padding be on each side?    #[refineable]    pub padding: Edges<DefiniteLength>,    /// How large should the border be on each side?    #[refineable]    pub border_widths: Edges<AbsoluteLength>,     // Alignment properties    /// How this node's children aligned in the cross/block axis?    pub align_items: Option<AlignItems>,    /// How this node should be aligned in the cross/block axis. Falls back to the parents [`AlignItems`] if not set    pub align_self: Option<AlignSelf>,    /// How should content contained within this item be aligned in the cross/block axis    pub align_content: Option<AlignContent>,    /// How should contained within this item be aligned in the main/inline axis    pub justify_content: Option<JustifyContent>,    /// How large should the gaps between items in a flex container be?    #[refineable]    pub gap: Size<DefiniteLength>,     // Flexbox properties    /// Which direction does the main axis flow in?    pub flex_direction: FlexDirection,    /// Should elements wrap, or stay in a single line?    pub flex_wrap: FlexWrap,    /// Sets the initial main axis size of the item    pub flex_basis: Length,    /// The relative rate at which this item grows when it is expanding to fill space, 0.0 is the default value, and this value must be positive.    pub flex_grow: f32,    /// The relative rate at which this item shrinks when it is contracting to fit into space, 1.0 is the default value, and this value must be positive.    pub flex_shrink: f32,     /// The fill color of this element    pub background: Option<Fill>,     /// The border color of this element    pub border_color: Option<Hsla>,     /// The border style of this element    pub border_style: BorderStyle,     /// The radius of the corners of this element    #[refineable]    pub corner_radii: Corners<AbsoluteLength>,     /// Box shadow of the element    pub box_shadow: Vec<BoxShadow>,     /// The text style of this element    #[refineable]    pub text: TextStyleRefinement,     /// The mouse cursor style shown when the mouse pointer is over an element.    pub mouse_cursor: Option<CursorStyle>,     /// The opacity of this element    pub opacity: Option<f32>,     /// The grid columns of this element    /// Roughly equivalent to the Tailwind `grid-cols-<number>`    pub grid_cols: Option<GridTemplate>,     /// The row span of this element    /// Equivalent to the Tailwind `grid-rows-<number>`    pub grid_rows: Option<GridTemplate>,     /// The grid location of this element    pub grid_location: Option<GridLocation>,     /// Whether to draw a red debugging outline around this element    #[cfg(debug_assertions)]    pub debug: bool,     /// Whether to draw a red debugging outline around this element and all of its conforming children    #[cfg(debug_assertions)]    pub debug_below: bool,} impl Styled for StyleRefinement {    fn style(&mut self) -> &mut StyleRefinement {        self    }} impl StyleRefinement {    /// The grid location of this element    pub fn grid_location_mut(&mut self) -> &mut GridLocation {        self.grid_location.get_or_insert_default()    }} /// The value of the visibility property, similar to the CSS property `visibility`#[derive(Default, Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize, JsonSchema)]pub enum Visibility {    /// The element should be drawn as normal.    #[default]    Visible,    /// The element should not be drawn, but should still take up space in the layout.    Hidden,} /// The possible values of the box-shadow property#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)]pub struct BoxShadow {    /// What color should the shadow have?    pub color: Hsla,    /// How should it be offset from its element?    pub offset: Point<Pixels>,    /// How much should the shadow be blurred?    pub blur_radius: Pixels,    /// How much should the shadow spread?    pub spread_radius: Pixels,    /// Whether this is an inset shadow (drawn inside the element's bounds).    pub inset: bool,} impl BoxShadow {    /// Creates a new [`BoxShadow`] with the given offset and color, matching the order    /// of the CSS `box-shadow` property. Use the builder methods to set blur radius,    /// spread radius, and inset.    pub fn new(offset_x: Pixels, offset_y: Pixels, color: Hsla) -> Self {        Self {            color,            offset: point(offset_x, offset_y),            blur_radius: px(0.),            spread_radius: px(0.),            inset: false,        }    }     /// Sets the shadow blur radius.    pub fn blur_radius(mut self, blur_radius: Pixels) -> Self {        self.blur_radius = blur_radius;        self    }     /// Sets the shadow spread radius.    pub fn spread_radius(mut self, spread_radius: Pixels) -> Self {        self.spread_radius = spread_radius;        self    }     /// Marks the shadow as inset (drawn inside the element's bounds).    pub fn inset(mut self) -> Self {        self.inset = true;        self    }} /// How to handle whitespace in text#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]pub enum WhiteSpace {    /// Normal line wrapping when text overflows the width of the element    #[default]    Normal,    /// No line wrapping, text will overflow the width of the element    Nowrap,} /// How to truncate text that overflows the width of the element#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]pub enum TextOverflow {    /// Truncate the text at the end when it doesn't fit, and represent this truncation by    /// displaying the provided string (e.g., "very long te…").    Truncate(SharedString),    /// Truncate the text at the start when it doesn't fit, and represent this truncation by    /// displaying the provided string at the beginning (e.g., "…ong text here").    /// Typically more adequate for file paths where the end is more important than the beginning.    TruncateStart(SharedString),    /// Truncate the text in the middle when it doesn't fit, preserving both the start and end    /// of the string (e.g., "long fi…name.rs"). Useful for filenames where both the prefix    /// and the extension are important context.    TruncateMiddle(SharedString),} /// How to align text within the element#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]pub enum TextAlign {    /// Align the text to the left of the element    #[default]    Left,     /// Center the text within the element    Center,     /// Align the text to the right of the element    Right,} /// The properties that can be used to style text in GPUI#[derive(Refineable, Clone, Debug, PartialEq)]#[refineable(Debug, PartialEq, Serialize, Deserialize, JsonSchema)]pub struct TextStyle {    /// The color of the text    pub color: Hsla,     /// The font family to use    pub font_family: SharedString,     /// The font features to use    pub font_features: FontFeatures,     /// The fallback fonts to use    pub font_fallbacks: Option<FontFallbacks>,     /// The font size to use, in pixels or rems.    pub font_size: AbsoluteLength,     /// The line height to use, in pixels or fractions    pub line_height: DefiniteLength,     /// The font weight, e.g. bold    pub font_weight: FontWeight,     /// The font style, e.g. italic    pub font_style: FontStyle,     /// The background color of the text    pub background_color: Option<Hsla>,     /// The underline style of the text    pub underline: Option<UnderlineStyle>,     /// The strikethrough style of the text    pub strikethrough: Option<StrikethroughStyle>,     /// How to handle whitespace in the text    pub white_space: WhiteSpace,     /// The text should be truncated if it overflows the width of the element    pub text_overflow: Option<TextOverflow>,     /// How the text should be aligned within the element    pub text_align: TextAlign,     /// The number of lines to display before truncating the text    pub line_clamp: Option<usize>,} impl Default for TextStyle {    fn default() -> Self {        TextStyle {            color: black(),            // todo(linux) make this configurable or choose better default            font_family: ".SystemUIFont".into(),            font_features: FontFeatures::default(),            font_fallbacks: None,            font_size: rems(1.).into(),            line_height: phi(),            font_weight: FontWeight::default(),            font_style: FontStyle::default(),            background_color: None,            underline: None,            strikethrough: None,            white_space: WhiteSpace::Normal,            text_overflow: None,            text_align: TextAlign::default(),            line_clamp: None,        }

Lines 1–500 of 1525.