Add grid support to GPUI (#36153)
Release Notes: - N/A --------- Co-authored-by: Anthony <anthony@zed.dev>
This commit is contained in:
parent
e67b2da20c
commit
32f9de6124
6 changed files with 278 additions and 6 deletions
|
@ -305,3 +305,7 @@ path = "examples/uniform_list.rs"
|
|||
[[example]]
|
||||
name = "window_shadow"
|
||||
path = "examples/window_shadow.rs"
|
||||
|
||||
[[example]]
|
||||
name = "grid_layout"
|
||||
path = "examples/grid_layout.rs"
|
||||
|
|
80
crates/gpui/examples/grid_layout.rs
Normal file
80
crates/gpui/examples/grid_layout.rs
Normal file
|
@ -0,0 +1,80 @@
|
|||
use gpui::{
|
||||
App, Application, Bounds, Context, Hsla, Window, WindowBounds, WindowOptions, div, prelude::*,
|
||||
px, rgb, size,
|
||||
};
|
||||
|
||||
// https://en.wikipedia.org/wiki/Holy_grail_(web_design)
|
||||
struct HolyGrailExample {}
|
||||
|
||||
impl Render for HolyGrailExample {
|
||||
fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
|
||||
let block = |color: Hsla| {
|
||||
div()
|
||||
.size_full()
|
||||
.bg(color)
|
||||
.border_1()
|
||||
.border_dashed()
|
||||
.rounded_md()
|
||||
.border_color(gpui::white())
|
||||
.items_center()
|
||||
};
|
||||
|
||||
div()
|
||||
.gap_1()
|
||||
.grid()
|
||||
.bg(rgb(0x505050))
|
||||
.size(px(500.0))
|
||||
.shadow_lg()
|
||||
.border_1()
|
||||
.size_full()
|
||||
.grid_cols(5)
|
||||
.grid_rows(5)
|
||||
.child(
|
||||
block(gpui::white())
|
||||
.row_span(1)
|
||||
.col_span_full()
|
||||
.child("Header"),
|
||||
)
|
||||
.child(
|
||||
block(gpui::red())
|
||||
.col_span(1)
|
||||
.h_56()
|
||||
.child("Table of contents"),
|
||||
)
|
||||
.child(
|
||||
block(gpui::green())
|
||||
.col_span(3)
|
||||
.row_span(3)
|
||||
.child("Content"),
|
||||
)
|
||||
.child(
|
||||
block(gpui::blue())
|
||||
.col_span(1)
|
||||
.row_span(3)
|
||||
.child("AD :(")
|
||||
.text_color(gpui::white()),
|
||||
)
|
||||
.child(
|
||||
block(gpui::black())
|
||||
.row_span(1)
|
||||
.col_span_full()
|
||||
.text_color(gpui::white())
|
||||
.child("Footer"),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
Application::new().run(|cx: &mut App| {
|
||||
let bounds = Bounds::centered(None, size(px(500.), px(500.0)), cx);
|
||||
cx.open_window(
|
||||
WindowOptions {
|
||||
window_bounds: Some(WindowBounds::Windowed(bounds)),
|
||||
..Default::default()
|
||||
},
|
||||
|_, cx| cx.new(|_| HolyGrailExample {}),
|
||||
)
|
||||
.unwrap();
|
||||
cx.activate(true);
|
||||
});
|
||||
}
|
|
@ -9,12 +9,14 @@ use refineable::Refineable;
|
|||
use schemars::{JsonSchema, json_schema};
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer, de};
|
||||
use std::borrow::Cow;
|
||||
use std::ops::Range;
|
||||
use std::{
|
||||
cmp::{self, PartialOrd},
|
||||
fmt::{self, Display},
|
||||
hash::Hash,
|
||||
ops::{Add, Div, Mul, MulAssign, Neg, Sub},
|
||||
};
|
||||
use taffy::prelude::{TaffyGridLine, TaffyGridSpan};
|
||||
|
||||
use crate::{App, DisplayId};
|
||||
|
||||
|
@ -3608,6 +3610,37 @@ impl From<()> for Length {
|
|||
}
|
||||
}
|
||||
|
||||
/// A location in a grid layout.
|
||||
#[derive(Clone, PartialEq, Debug, Serialize, Deserialize, JsonSchema, Default)]
|
||||
pub struct GridLocation {
|
||||
/// The rows this item uses within the grid.
|
||||
pub row: Range<GridPlacement>,
|
||||
/// The columns this item uses within the grid.
|
||||
pub column: Range<GridPlacement>,
|
||||
}
|
||||
|
||||
/// The placement of an item within a grid layout's column or row.
|
||||
#[derive(Clone, Copy, PartialEq, Debug, Serialize, Deserialize, JsonSchema, Default)]
|
||||
pub enum GridPlacement {
|
||||
/// The grid line index to place this item.
|
||||
Line(i16),
|
||||
/// The number of grid lines to span.
|
||||
Span(u16),
|
||||
/// Automatically determine the placement, equivalent to Span(1)
|
||||
#[default]
|
||||
Auto,
|
||||
}
|
||||
|
||||
impl From<GridPlacement> for taffy::GridPlacement {
|
||||
fn from(placement: GridPlacement) -> Self {
|
||||
match placement {
|
||||
GridPlacement::Line(index) => taffy::GridPlacement::from_line_index(index),
|
||||
GridPlacement::Span(span) => taffy::GridPlacement::from_span(span),
|
||||
GridPlacement::Auto => taffy::GridPlacement::Auto,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Provides a trait for types that can calculate half of their value.
|
||||
///
|
||||
/// The `Half` trait is used for types that can be evenly divided, returning a new instance of the same type
|
||||
|
|
|
@ -7,7 +7,7 @@ use std::{
|
|||
use crate::{
|
||||
AbsoluteLength, App, Background, BackgroundTag, BorderStyle, Bounds, ContentMask, Corners,
|
||||
CornersRefinement, CursorStyle, DefiniteLength, DevicePixels, Edges, EdgesRefinement, Font,
|
||||
FontFallbacks, FontFeatures, FontStyle, FontWeight, Hsla, Length, Pixels, Point,
|
||||
FontFallbacks, FontFeatures, FontStyle, FontWeight, GridLocation, Hsla, Length, Pixels, Point,
|
||||
PointRefinement, Rgba, SharedString, Size, SizeRefinement, Styled, TextRun, Window, black, phi,
|
||||
point, quad, rems, size,
|
||||
};
|
||||
|
@ -260,6 +260,17 @@ pub struct Style {
|
|||
/// The opacity of this element
|
||||
pub opacity: Option<f32>,
|
||||
|
||||
/// The grid columns of this element
|
||||
/// Equivalent to the Tailwind `grid-cols-<number>`
|
||||
pub grid_cols: Option<u16>,
|
||||
|
||||
/// The row span of this element
|
||||
/// Equivalent to the Tailwind `grid-rows-<number>`
|
||||
pub grid_rows: Option<u16>,
|
||||
|
||||
/// 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,
|
||||
|
@ -275,6 +286,13 @@ impl Styled for StyleRefinement {
|
|||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
|
@ -757,6 +775,9 @@ impl Default for Style {
|
|||
text: TextStyleRefinement::default(),
|
||||
mouse_cursor: None,
|
||||
opacity: None,
|
||||
grid_rows: None,
|
||||
grid_cols: None,
|
||||
grid_location: None,
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
debug: false,
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
use crate::{
|
||||
self as gpui, AbsoluteLength, AlignContent, AlignItems, BorderStyle, CursorStyle,
|
||||
DefiniteLength, Display, Fill, FlexDirection, FlexWrap, Font, FontStyle, FontWeight, Hsla,
|
||||
JustifyContent, Length, SharedString, StrikethroughStyle, StyleRefinement, TextAlign,
|
||||
TextOverflow, TextStyleRefinement, UnderlineStyle, WhiteSpace, px, relative, rems,
|
||||
DefiniteLength, Display, Fill, FlexDirection, FlexWrap, Font, FontStyle, FontWeight,
|
||||
GridPlacement, Hsla, JustifyContent, Length, SharedString, StrikethroughStyle, StyleRefinement,
|
||||
TextAlign, TextOverflow, TextStyleRefinement, UnderlineStyle, WhiteSpace, px, relative, rems,
|
||||
};
|
||||
pub use gpui_macros::{
|
||||
border_style_methods, box_shadow_style_methods, cursor_style_methods, margin_style_methods,
|
||||
|
@ -46,6 +46,13 @@ pub trait Styled: Sized {
|
|||
self
|
||||
}
|
||||
|
||||
/// Sets the display type of the element to `grid`.
|
||||
/// [Docs](https://tailwindcss.com/docs/display)
|
||||
fn grid(mut self) -> Self {
|
||||
self.style().display = Some(Display::Grid);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the whitespace of the element to `normal`.
|
||||
/// [Docs](https://tailwindcss.com/docs/whitespace#normal)
|
||||
fn whitespace_normal(mut self) -> Self {
|
||||
|
@ -640,6 +647,102 @@ pub trait Styled: Sized {
|
|||
self
|
||||
}
|
||||
|
||||
/// Sets the grid columns of this element.
|
||||
fn grid_cols(mut self, cols: u16) -> Self {
|
||||
self.style().grid_cols = Some(cols);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the grid rows of this element.
|
||||
fn grid_rows(mut self, rows: u16) -> Self {
|
||||
self.style().grid_rows = Some(rows);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the column start of this element.
|
||||
fn col_start(mut self, start: i16) -> Self {
|
||||
let grid_location = self.style().grid_location_mut();
|
||||
grid_location.column.start = GridPlacement::Line(start);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the column start of this element to auto.
|
||||
fn col_start_auto(mut self) -> Self {
|
||||
let grid_location = self.style().grid_location_mut();
|
||||
grid_location.column.start = GridPlacement::Auto;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the column end of this element.
|
||||
fn col_end(mut self, end: i16) -> Self {
|
||||
let grid_location = self.style().grid_location_mut();
|
||||
grid_location.column.end = GridPlacement::Line(end);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the column end of this element to auto.
|
||||
fn col_end_auto(mut self) -> Self {
|
||||
let grid_location = self.style().grid_location_mut();
|
||||
grid_location.column.end = GridPlacement::Auto;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the column span of this element.
|
||||
fn col_span(mut self, span: u16) -> Self {
|
||||
let grid_location = self.style().grid_location_mut();
|
||||
grid_location.column = GridPlacement::Span(span)..GridPlacement::Span(span);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the row span of this element.
|
||||
fn col_span_full(mut self) -> Self {
|
||||
let grid_location = self.style().grid_location_mut();
|
||||
grid_location.column = GridPlacement::Line(1)..GridPlacement::Line(-1);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the row start of this element.
|
||||
fn row_start(mut self, start: i16) -> Self {
|
||||
let grid_location = self.style().grid_location_mut();
|
||||
grid_location.row.start = GridPlacement::Line(start);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the row start of this element to "auto"
|
||||
fn row_start_auto(mut self) -> Self {
|
||||
let grid_location = self.style().grid_location_mut();
|
||||
grid_location.row.start = GridPlacement::Auto;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the row end of this element.
|
||||
fn row_end(mut self, end: i16) -> Self {
|
||||
let grid_location = self.style().grid_location_mut();
|
||||
grid_location.row.end = GridPlacement::Line(end);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the row end of this element to "auto"
|
||||
fn row_end_auto(mut self) -> Self {
|
||||
let grid_location = self.style().grid_location_mut();
|
||||
grid_location.row.end = GridPlacement::Auto;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the row span of this element.
|
||||
fn row_span(mut self, span: u16) -> Self {
|
||||
let grid_location = self.style().grid_location_mut();
|
||||
grid_location.row = GridPlacement::Span(span)..GridPlacement::Span(span);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the row span of this element.
|
||||
fn row_span_full(mut self) -> Self {
|
||||
let grid_location = self.style().grid_location_mut();
|
||||
grid_location.row = GridPlacement::Line(1)..GridPlacement::Line(-1);
|
||||
self
|
||||
}
|
||||
|
||||
/// Draws a debug border around this element.
|
||||
#[cfg(debug_assertions)]
|
||||
fn debug(mut self) -> Self {
|
||||
|
|
|
@ -3,7 +3,7 @@ use crate::{
|
|||
};
|
||||
use collections::{FxHashMap, FxHashSet};
|
||||
use smallvec::SmallVec;
|
||||
use std::fmt::Debug;
|
||||
use std::{fmt::Debug, ops::Range};
|
||||
use taffy::{
|
||||
TaffyTree, TraversePartialTree as _,
|
||||
geometry::{Point as TaffyPoint, Rect as TaffyRect, Size as TaffySize},
|
||||
|
@ -251,6 +251,25 @@ trait ToTaffy<Output> {
|
|||
|
||||
impl ToTaffy<taffy::style::Style> for Style {
|
||||
fn to_taffy(&self, rem_size: Pixels) -> taffy::style::Style {
|
||||
use taffy::style_helpers::{fr, length, minmax, repeat};
|
||||
|
||||
fn to_grid_line(
|
||||
placement: &Range<crate::GridPlacement>,
|
||||
) -> taffy::Line<taffy::GridPlacement> {
|
||||
taffy::Line {
|
||||
start: placement.start.into(),
|
||||
end: placement.end.into(),
|
||||
}
|
||||
}
|
||||
|
||||
fn to_grid_repeat<T: taffy::style::CheapCloneStr>(
|
||||
unit: &Option<u16>,
|
||||
) -> Vec<taffy::GridTemplateComponent<T>> {
|
||||
// grid-template-columns: repeat(<number>, minmax(0, 1fr));
|
||||
unit.map(|count| vec![repeat(count, vec![minmax(length(0.0), fr(1.0))])])
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
taffy::style::Style {
|
||||
display: self.display.into(),
|
||||
overflow: self.overflow.into(),
|
||||
|
@ -274,7 +293,19 @@ impl ToTaffy<taffy::style::Style> for Style {
|
|||
flex_basis: self.flex_basis.to_taffy(rem_size),
|
||||
flex_grow: self.flex_grow,
|
||||
flex_shrink: self.flex_shrink,
|
||||
..Default::default() // Ignore grid properties for now
|
||||
grid_template_rows: to_grid_repeat(&self.grid_rows),
|
||||
grid_template_columns: to_grid_repeat(&self.grid_cols),
|
||||
grid_row: self
|
||||
.grid_location
|
||||
.as_ref()
|
||||
.map(|location| to_grid_line(&location.row))
|
||||
.unwrap_or_default(),
|
||||
grid_column: self
|
||||
.grid_location
|
||||
.as_ref()
|
||||
.map(|location| to_grid_line(&location.column))
|
||||
.unwrap_or_default(),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue