mirror of
https://github.com/penpot/penpot.git
synced 2025-04-30 05:36:21 +02:00
Merge pull request #6235 from penpot/alotor-perf-grid-layout-modifiers-2
✨ Grid layout modifiers
This commit is contained in:
commit
c2522329fd
6 changed files with 662 additions and 143 deletions
|
@ -39,7 +39,7 @@
|
||||||
;;
|
;;
|
||||||
;; 5. If any track still has an infinite growth limit set its growth limit to its base size.
|
;; 5. If any track still has an infinite growth limit set its growth limit to its base size.
|
||||||
|
|
||||||
;; - Distribute extra space accross spaned tracks
|
;; - Distribute extra space accross spaned tracks
|
||||||
;; - Maximize tracks
|
;; - Maximize tracks
|
||||||
;;
|
;;
|
||||||
;; - Expand flexible tracks
|
;; - Expand flexible tracks
|
||||||
|
@ -198,7 +198,7 @@
|
||||||
|
|
||||||
track-list))
|
track-list))
|
||||||
|
|
||||||
(defn add-auto-size
|
(defn stretch-tracks
|
||||||
[track-list add-size]
|
[track-list add-size]
|
||||||
(->> track-list
|
(->> track-list
|
||||||
(mapv (fn [{:keys [type size max-size] :as track}]
|
(mapv (fn [{:keys [type size max-size] :as track}]
|
||||||
|
@ -493,11 +493,11 @@
|
||||||
|
|
||||||
column-tracks (cond-> column-tracks
|
column-tracks (cond-> column-tracks
|
||||||
(= :stretch (:layout-justify-content parent))
|
(= :stretch (:layout-justify-content parent))
|
||||||
(add-auto-size column-add-auto))
|
(stretch-tracks column-add-auto))
|
||||||
|
|
||||||
row-tracks (cond-> row-tracks
|
row-tracks (cond-> row-tracks
|
||||||
(= :stretch (:layout-align-content parent))
|
(= :stretch (:layout-align-content parent))
|
||||||
(add-auto-size row-add-auto))
|
(stretch-tracks row-add-auto))
|
||||||
|
|
||||||
column-total-size (tracks-total-size column-tracks)
|
column-total-size (tracks-total-size column-tracks)
|
||||||
row-total-size (tracks-total-size row-tracks)
|
row-total-size (tracks-total-size row-tracks)
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
(:require
|
(:require
|
||||||
[app.common.transit :as t]
|
[app.common.transit :as t]
|
||||||
[app.common.types.shape :as shape]
|
[app.common.types.shape :as shape]
|
||||||
|
[app.common.types.shape.layout :as ctl]
|
||||||
[app.render-wasm.api :as api]
|
[app.render-wasm.api :as api]
|
||||||
[beicon.v2.core :as rx]
|
[beicon.v2.core :as rx]
|
||||||
[clojure.core :as c]
|
[clojure.core :as c]
|
||||||
|
@ -110,54 +111,99 @@
|
||||||
(defn- set-wasm-attrs
|
(defn- set-wasm-attrs
|
||||||
[self k v]
|
[self k v]
|
||||||
(when ^boolean shape/*wasm-sync*
|
(when ^boolean shape/*wasm-sync*
|
||||||
(api/use-shape (:id self))
|
(binding [shape/*wasm-sync* false]
|
||||||
(let [pending (case k
|
(let [self (assoc self k v)]
|
||||||
:parent-id (api/set-parent-id v)
|
(api/use-shape (:id self))
|
||||||
:type (api/set-shape-type v)
|
(let [pending (case k
|
||||||
:bool-type (api/set-shape-bool-type v)
|
:parent-id (api/set-parent-id v)
|
||||||
:selrect (api/set-shape-selrect v)
|
:type (api/set-shape-type v)
|
||||||
:show-content (if (= (:type self) :frame)
|
:bool-type (api/set-shape-bool-type v)
|
||||||
(api/set-shape-clip-content (not v))
|
:selrect (api/set-shape-selrect v)
|
||||||
(api/set-shape-clip-content false))
|
:show-content (if (= (:type self) :frame)
|
||||||
:rotation (api/set-shape-rotation v)
|
(api/set-shape-clip-content (not v))
|
||||||
:transform (api/set-shape-transform v)
|
(api/set-shape-clip-content false))
|
||||||
:fills (into [] (api/set-shape-fills v))
|
:rotation (api/set-shape-rotation v)
|
||||||
:strokes (into [] (api/set-shape-strokes v))
|
:transform (api/set-shape-transform v)
|
||||||
:blend-mode (api/set-shape-blend-mode v)
|
:fills (into [] (api/set-shape-fills v))
|
||||||
:opacity (api/set-shape-opacity v)
|
:strokes (into [] (api/set-shape-strokes v))
|
||||||
:hidden (api/set-shape-hidden v)
|
:blend-mode (api/set-shape-blend-mode v)
|
||||||
:shapes (api/set-shape-children v)
|
:opacity (api/set-shape-opacity v)
|
||||||
:blur (api/set-shape-blur v)
|
:hidden (api/set-shape-hidden v)
|
||||||
:shadow (api/set-shape-shadows v)
|
:shapes (api/set-shape-children v)
|
||||||
:constraints-h (api/set-constraints-h v)
|
:blur (api/set-shape-blur v)
|
||||||
:constraints-v (api/set-constraints-v v)
|
:shadow (api/set-shape-shadows v)
|
||||||
:svg-attrs (when (= (:type self) :path)
|
:constraints-h (api/set-constraints-h v)
|
||||||
(api/set-shape-path-attrs v))
|
:constraints-v (api/set-constraints-v v)
|
||||||
:masked-group (when (and (= (:type self) :group) (:masked-group self))
|
|
||||||
(api/set-masked (:masked-group self)))
|
|
||||||
:content (cond
|
|
||||||
(= (:type self) :path)
|
|
||||||
(api/set-shape-path-content v)
|
|
||||||
|
|
||||||
(= (:type self) :svg-raw)
|
:svg-attrs
|
||||||
(api/set-shape-svg-raw-content (api/get-static-markup self))
|
(when (= (:type self) :path)
|
||||||
|
(api/set-shape-path-attrs v))
|
||||||
|
|
||||||
(= (:type self) :text)
|
:masked-group
|
||||||
(api/set-shape-text-content v))
|
(when (and (= (:type self) :group) (:masked-group self))
|
||||||
nil)]
|
(api/set-masked (:masked-group self)))
|
||||||
|
|
||||||
;; TODO: set-wasm-attrs is called twice with every set
|
:content
|
||||||
(if (and pending (seq pending))
|
(cond
|
||||||
(->> (rx/from pending)
|
(= (:type self) :path)
|
||||||
(rx/mapcat identity)
|
(api/set-shape-path-content v)
|
||||||
(rx/reduce conj [])
|
|
||||||
(rx/subs! (fn [_]
|
(= (:type self) :svg-raw)
|
||||||
(api/update-shape-tiles)
|
(api/set-shape-svg-raw-content (api/get-static-markup self))
|
||||||
(api/clear-drawing-cache)
|
|
||||||
(api/request-render "set-wasm-attrs-pending"))))
|
(= (:type self) :text)
|
||||||
(do
|
(into [] (api/set-shape-text-content v)))
|
||||||
(api/update-shape-tiles)
|
|
||||||
(api/request-render "set-wasm-attrs"))))))
|
(:layout-item-margin
|
||||||
|
:layout-item-margin-type
|
||||||
|
:layout-item-h-sizing
|
||||||
|
:layout-item-v-sizing
|
||||||
|
:layout-item-max-h
|
||||||
|
:layout-item-min-h
|
||||||
|
:layout-item-max-w
|
||||||
|
:layout-item-min-w
|
||||||
|
:layout-item-absolute
|
||||||
|
:layout-item-z-index)
|
||||||
|
(api/set-layout-child self)
|
||||||
|
|
||||||
|
(:layout-grid-rows
|
||||||
|
:layout-grid-columns
|
||||||
|
:layout-grid-cells)
|
||||||
|
(when (ctl/grid-layout? self)
|
||||||
|
(api/set-grid-layout self))
|
||||||
|
|
||||||
|
(:layout
|
||||||
|
:layout-flex-dir
|
||||||
|
:layout-gap-type
|
||||||
|
:layout-gap
|
||||||
|
:layout-align-items
|
||||||
|
:layout-align-content
|
||||||
|
:layout-justify-items
|
||||||
|
:layout-justify-content
|
||||||
|
:layout-wrap-type
|
||||||
|
:layout-padding-type
|
||||||
|
:layout-padding)
|
||||||
|
(cond
|
||||||
|
(ctl/grid-layout? self)
|
||||||
|
(api/set-grid-layout self)
|
||||||
|
|
||||||
|
(ctl/flex-layout? self)
|
||||||
|
(api/set-flex-layout self))
|
||||||
|
|
||||||
|
nil)]
|
||||||
|
|
||||||
|
;; TODO: set-wasm-attrs is called twice with every set
|
||||||
|
(if (and pending (seq pending))
|
||||||
|
(->> (rx/from pending)
|
||||||
|
(rx/mapcat identity)
|
||||||
|
(rx/reduce conj [])
|
||||||
|
(rx/subs! (fn [_]
|
||||||
|
(api/update-shape-tiles)
|
||||||
|
(api/clear-drawing-cache)
|
||||||
|
(api/request-render "set-wasm-attrs-pending"))))
|
||||||
|
(do
|
||||||
|
(api/update-shape-tiles)
|
||||||
|
(api/request-render "set-wasm-attrs"))))))))
|
||||||
(defn- impl-assoc
|
(defn- impl-assoc
|
||||||
[self k v]
|
[self k v]
|
||||||
(set-wasm-attrs self k v)
|
(set-wasm-attrs self k v)
|
||||||
|
|
|
@ -44,7 +44,7 @@ impl GridDirection {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq, Copy)]
|
||||||
pub enum AlignItems {
|
pub enum AlignItems {
|
||||||
Start,
|
Start,
|
||||||
End,
|
End,
|
||||||
|
@ -64,7 +64,7 @@ impl AlignItems {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq, Copy)]
|
||||||
pub enum AlignContent {
|
pub enum AlignContent {
|
||||||
Start,
|
Start,
|
||||||
End,
|
End,
|
||||||
|
@ -90,7 +90,7 @@ impl AlignContent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq, Copy)]
|
||||||
pub enum JustifyItems {
|
pub enum JustifyItems {
|
||||||
Start,
|
Start,
|
||||||
End,
|
End,
|
||||||
|
@ -110,7 +110,7 @@ impl JustifyItems {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq, Copy)]
|
||||||
pub enum JustifyContent {
|
pub enum JustifyContent {
|
||||||
Start,
|
Start,
|
||||||
End,
|
End,
|
||||||
|
@ -151,7 +151,7 @@ impl WrapType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||||
pub enum GridTrackType {
|
pub enum GridTrackType {
|
||||||
Percent,
|
Percent,
|
||||||
Flex,
|
Flex,
|
||||||
|
@ -173,8 +173,8 @@ impl GridTrackType {
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct GridTrack {
|
pub struct GridTrack {
|
||||||
track_type: GridTrackType,
|
pub track_type: GridTrackType,
|
||||||
value: f32,
|
pub value: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GridTrack {
|
impl GridTrack {
|
||||||
|
@ -188,13 +188,13 @@ impl GridTrack {
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct GridCell {
|
pub struct GridCell {
|
||||||
row: i32,
|
pub row: i32,
|
||||||
row_span: i32,
|
pub row_span: i32,
|
||||||
column: i32,
|
pub column: i32,
|
||||||
column_span: i32,
|
pub column_span: i32,
|
||||||
align_self: Option<AlignSelf>,
|
pub align_self: Option<AlignSelf>,
|
||||||
justify_self: Option<JustifySelf>,
|
pub justify_self: Option<JustifySelf>,
|
||||||
shape: Option<Uuid>,
|
pub shape: Option<Uuid>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GridCell {
|
impl GridCell {
|
||||||
|
|
|
@ -254,11 +254,11 @@ pub fn propagate_modifiers(state: &State, modifiers: Vec<TransformEntry>) -> Vec
|
||||||
entries.append(&mut children);
|
entries.append(&mut children);
|
||||||
}
|
}
|
||||||
|
|
||||||
// if let Some(Layout::GridLayout(layout_data, grid_data)) = &frame_data.layout {
|
if let Some(Layout::GridLayout(layout_data, grid_data)) = &frame_data.layout {
|
||||||
// let mut children =
|
let mut children =
|
||||||
// grid_layout::reflow_grid_layout(shape, layout_data, grid_data, shapes, &bounds);
|
grid_layout::reflow_grid_layout(shape, layout_data, grid_data, shapes, &bounds);
|
||||||
// entries.append(&mut children);
|
entries.append(&mut children);
|
||||||
// }
|
}
|
||||||
reflown.insert(*id);
|
reflown.insert(*id);
|
||||||
}
|
}
|
||||||
layout_reflows = Vec::new();
|
layout_reflows = Vec::new();
|
||||||
|
|
|
@ -366,91 +366,140 @@ fn calculate_track_positions(
|
||||||
align_content = &AlignContent::Start;
|
align_content = &AlignContent::Start;
|
||||||
}
|
}
|
||||||
|
|
||||||
match align_content {
|
let total_across_size_gap: f32 =
|
||||||
AlignContent::End => {
|
total_across_size + (tracks.len() - 1) as f32 * layout_axis.gap_across;
|
||||||
let total_across_size_gap: f32 =
|
|
||||||
total_across_size + (tracks.len() - 1) as f32 * layout_axis.gap_across;
|
|
||||||
|
|
||||||
let delta =
|
let (real_margin, real_gap) = match align_content {
|
||||||
layout_axis.across_size - total_across_size_gap - layout_axis.padding_across_end;
|
AlignContent::End => (
|
||||||
let mut next_anchor = layout_bounds.nw + layout_axis.across_v * delta;
|
layout_axis.across_size - total_across_size_gap - layout_axis.padding_across_end,
|
||||||
|
layout_axis.gap_across,
|
||||||
|
),
|
||||||
|
|
||||||
for track in tracks.iter_mut() {
|
AlignContent::Center => (
|
||||||
track.anchor = next_anchor;
|
(layout_axis.across_size - total_across_size_gap) / 2.0,
|
||||||
next_anchor = next_anchor
|
layout_axis.gap_across,
|
||||||
+ layout_axis.across_v * (track.across_size + layout_axis.gap_across);
|
),
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
AlignContent::Center => {
|
AlignContent::SpaceBetween => (
|
||||||
let total_across_size_gap: f32 =
|
layout_axis.padding_across_start,
|
||||||
total_across_size + (tracks.len() - 1) as f32 * layout_axis.gap_across;
|
f32::max(
|
||||||
let center_margin = (layout_axis.across_size - total_across_size_gap) / 2.0;
|
|
||||||
|
|
||||||
let mut next_anchor = layout_bounds.nw + layout_axis.across_v * center_margin;
|
|
||||||
|
|
||||||
for track in tracks.iter_mut() {
|
|
||||||
track.anchor = next_anchor;
|
|
||||||
next_anchor = next_anchor
|
|
||||||
+ layout_axis.across_v * (track.across_size + layout_axis.gap_across);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
AlignContent::SpaceBetween => {
|
|
||||||
let mut next_anchor =
|
|
||||||
layout_bounds.nw + layout_axis.across_v * layout_axis.padding_across_start;
|
|
||||||
|
|
||||||
let effective_gap = f32::max(
|
|
||||||
layout_axis.gap_across,
|
layout_axis.gap_across,
|
||||||
(layout_axis.across_space() - total_across_size) / (tracks.len() - 1) as f32,
|
(layout_axis.across_space() - total_across_size) / (tracks.len() - 1) as f32,
|
||||||
);
|
),
|
||||||
|
),
|
||||||
for track in tracks.iter_mut() {
|
|
||||||
track.anchor = next_anchor;
|
|
||||||
next_anchor =
|
|
||||||
next_anchor + layout_axis.across_v * (track.across_size + effective_gap);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
AlignContent::SpaceAround => {
|
AlignContent::SpaceAround => {
|
||||||
let effective_gap =
|
let effective_gap =
|
||||||
(layout_axis.across_space() - total_across_size) / tracks.len() as f32;
|
(layout_axis.across_space() - total_across_size) / tracks.len() as f32;
|
||||||
|
(effective_gap / 2.0, effective_gap)
|
||||||
let mut next_anchor = layout_bounds.nw
|
|
||||||
+ layout_axis.across_v * (layout_axis.padding_across_start + effective_gap / 2.0);
|
|
||||||
|
|
||||||
for track in tracks.iter_mut() {
|
|
||||||
track.anchor = next_anchor;
|
|
||||||
next_anchor =
|
|
||||||
next_anchor + layout_axis.across_v * (track.across_size + effective_gap);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AlignContent::SpaceEvenly => {
|
AlignContent::SpaceEvenly => {
|
||||||
let effective_gap =
|
let effective_gap =
|
||||||
(layout_axis.across_space() - total_across_size) / (tracks.len() + 1) as f32;
|
(layout_axis.across_space() - total_across_size) / (tracks.len() + 1) as f32;
|
||||||
|
(
|
||||||
let mut next_anchor = layout_bounds.nw
|
layout_axis.padding_across_start + effective_gap,
|
||||||
+ layout_axis.across_v * (layout_axis.padding_across_start + effective_gap);
|
effective_gap,
|
||||||
|
)
|
||||||
for track in tracks.iter_mut() {
|
|
||||||
track.anchor = next_anchor;
|
|
||||||
next_anchor =
|
|
||||||
next_anchor + layout_axis.across_v * (track.across_size + effective_gap);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => {
|
_ => (layout_axis.padding_across_start, layout_axis.gap_across),
|
||||||
let mut next_anchor =
|
};
|
||||||
layout_bounds.nw + layout_axis.across_v * layout_axis.padding_across_start;
|
|
||||||
|
|
||||||
for track in tracks.iter_mut() {
|
let mut next_anchor = layout_bounds.nw + layout_axis.across_v * real_margin;
|
||||||
track.anchor = next_anchor;
|
|
||||||
next_anchor = next_anchor
|
for track in tracks.iter_mut() {
|
||||||
+ layout_axis.across_v * (track.across_size + layout_axis.gap_across);
|
track.anchor = next_anchor;
|
||||||
}
|
next_anchor = next_anchor + layout_axis.across_v * real_gap;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
match align_content {
|
||||||
|
AlignContent::End => {
|
||||||
|
let total_across_size_gap: f32 =
|
||||||
|
total_across_size + (tracks.len() - 1) as f32 * layout_axis.gap_across;
|
||||||
|
|
||||||
|
let delta =
|
||||||
|
layout_axis.across_size - total_across_size_gap - layout_axis.padding_across_end;
|
||||||
|
let mut next_anchor = layout_bounds.nw + layout_axis.across_v * delta;
|
||||||
|
|
||||||
|
for track in tracks.iter_mut() {
|
||||||
|
track.anchor = next_anchor;
|
||||||
|
next_anchor = next_anchor
|
||||||
|
+ layout_axis.across_v * (track.across_size + layout_axis.gap_across);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AlignContent::Center => {
|
||||||
|
let total_across_size_gap: f32 =
|
||||||
|
total_across_size + (tracks.len() - 1) as f32 * layout_axis.gap_across;
|
||||||
|
let center_margin = (layout_axis.across_size - total_across_size_gap) / 2.0;
|
||||||
|
|
||||||
|
let mut next_anchor = layout_bounds.nw + layout_axis.across_v * center_margin;
|
||||||
|
|
||||||
|
for track in tracks.iter_mut() {
|
||||||
|
track.anchor = next_anchor;
|
||||||
|
next_anchor = next_anchor
|
||||||
|
+ layout_axis.across_v * (track.across_size + layout_axis.gap_across);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AlignContent::SpaceBetween => {
|
||||||
|
let mut next_anchor =
|
||||||
|
layout_bounds.nw + layout_axis.across_v * layout_axis.padding_across_start;
|
||||||
|
|
||||||
|
let effective_gap = f32::max(
|
||||||
|
layout_axis.gap_across,
|
||||||
|
(layout_axis.across_space() - total_across_size) / (tracks.len() - 1) as f32,
|
||||||
|
);
|
||||||
|
|
||||||
|
for track in tracks.iter_mut() {
|
||||||
|
track.anchor = next_anchor;
|
||||||
|
next_anchor =
|
||||||
|
next_anchor + layout_axis.across_v * (track.across_size + effective_gap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AlignContent::SpaceAround => {
|
||||||
|
let effective_gap =
|
||||||
|
(layout_axis.across_space() - total_across_size) / tracks.len() as f32;
|
||||||
|
|
||||||
|
let mut next_anchor = layout_bounds.nw
|
||||||
|
+ layout_axis.across_v * (layout_axis.padding_across_start + effective_gap / 2.0);
|
||||||
|
|
||||||
|
for track in tracks.iter_mut() {
|
||||||
|
track.anchor = next_anchor;
|
||||||
|
next_anchor =
|
||||||
|
next_anchor + layout_axis.across_v * (track.across_size + effective_gap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AlignContent::SpaceEvenly => {
|
||||||
|
let effective_gap =
|
||||||
|
(layout_axis.across_space() - total_across_size) / (tracks.len() + 1) as f32;
|
||||||
|
|
||||||
|
let mut next_anchor = layout_bounds.nw
|
||||||
|
+ layout_axis.across_v * (layout_axis.padding_across_start + effective_gap);
|
||||||
|
|
||||||
|
for track in tracks.iter_mut() {
|
||||||
|
track.anchor = next_anchor;
|
||||||
|
next_anchor =
|
||||||
|
next_anchor + layout_axis.across_v * (track.across_size + effective_gap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => {
|
||||||
|
let mut next_anchor =
|
||||||
|
layout_bounds.nw + layout_axis.across_v * layout_axis.padding_across_start;
|
||||||
|
|
||||||
|
for track in tracks.iter_mut() {
|
||||||
|
track.anchor = next_anchor;
|
||||||
|
next_anchor = next_anchor
|
||||||
|
+ layout_axis.across_v * (track.across_size + layout_axis.gap_across);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}*/
|
||||||
}
|
}
|
||||||
|
|
||||||
fn calculate_track_data(
|
fn calculate_track_data(
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
#![allow(dead_code, unused_variables)]
|
#![allow(dead_code, unused_variables)]
|
||||||
use crate::math::{Bounds, Matrix, Point, Vector, VectorExt};
|
use crate::math::{intersect_rays, Bounds, Matrix, Point, Ray, Vector, VectorExt};
|
||||||
use crate::shapes::{GridData, LayoutData, Modifier, Shape};
|
use crate::shapes::{
|
||||||
|
AlignContent, AlignItems, AlignSelf, GridCell, GridData, GridTrack, GridTrackType,
|
||||||
|
JustifyContent, JustifyItems, JustifySelf, LayoutData, LayoutItem, Modifier, Shape,
|
||||||
|
};
|
||||||
use crate::uuid::Uuid;
|
use crate::uuid::Uuid;
|
||||||
use std::collections::{HashMap, VecDeque};
|
use std::collections::{HashMap, VecDeque};
|
||||||
|
|
||||||
|
@ -9,10 +12,365 @@ use super::common::GetBounds;
|
||||||
const MIN_SIZE: f32 = 0.01;
|
const MIN_SIZE: f32 = 0.01;
|
||||||
const MAX_SIZE: f32 = f32::INFINITY;
|
const MAX_SIZE: f32 = f32::INFINITY;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
struct CellData<'a> {
|
struct CellData<'a> {
|
||||||
shape: &'a Shape,
|
shape: &'a Shape,
|
||||||
main_size: f32,
|
anchor: Point,
|
||||||
across_size: f32,
|
width: f32,
|
||||||
|
height: f32,
|
||||||
|
align_self: Option<AlignSelf>,
|
||||||
|
justify_self: Option<JustifySelf>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct TrackData {
|
||||||
|
track_type: GridTrackType,
|
||||||
|
value: f32,
|
||||||
|
size: f32,
|
||||||
|
max_size: f32,
|
||||||
|
anchor_start: Point,
|
||||||
|
anchor_end: Point,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn calculate_tracks(
|
||||||
|
is_column: bool,
|
||||||
|
layout_data: &LayoutData,
|
||||||
|
grid_data: &GridData,
|
||||||
|
layout_bounds: &Bounds,
|
||||||
|
cells: &Vec<GridCell>,
|
||||||
|
shapes: &HashMap<Uuid, Shape>,
|
||||||
|
bounds: &HashMap<Uuid, Bounds>,
|
||||||
|
) -> Vec<TrackData> {
|
||||||
|
let layout_size = if is_column {
|
||||||
|
layout_bounds.width() - layout_data.padding_left - layout_data.padding_right
|
||||||
|
} else {
|
||||||
|
layout_bounds.height() - layout_data.padding_top - layout_data.padding_bottom
|
||||||
|
};
|
||||||
|
|
||||||
|
let grid_tracks = if is_column {
|
||||||
|
&grid_data.columns
|
||||||
|
} else {
|
||||||
|
&grid_data.rows
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut tracks = init_tracks(grid_tracks, layout_size);
|
||||||
|
set_auto_base_size(is_column, &mut tracks, cells, shapes, bounds);
|
||||||
|
|
||||||
|
set_auto_multi_span(is_column, layout_data, &layout_bounds, &mut tracks);
|
||||||
|
set_flex_multi_span(is_column, layout_data, &layout_bounds, &mut tracks);
|
||||||
|
|
||||||
|
set_fr_value(is_column, layout_data, &mut tracks, layout_size, 0.0);
|
||||||
|
stretch_tracks(is_column, layout_data, &mut tracks, layout_size);
|
||||||
|
assign_anchors(is_column, layout_data, &layout_bounds, &mut tracks);
|
||||||
|
|
||||||
|
return tracks;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init_tracks(track: &Vec<GridTrack>, size: f32) -> Vec<TrackData> {
|
||||||
|
track
|
||||||
|
.iter()
|
||||||
|
.map(|t| {
|
||||||
|
let (size, max_size) = match t.track_type {
|
||||||
|
GridTrackType::Fixed => (t.value, t.value),
|
||||||
|
GridTrackType::Percent => (size * t.value / 100.0, size * t.value / 100.0),
|
||||||
|
_ => (MIN_SIZE, MAX_SIZE),
|
||||||
|
};
|
||||||
|
TrackData {
|
||||||
|
track_type: t.track_type,
|
||||||
|
value: t.value,
|
||||||
|
size,
|
||||||
|
max_size,
|
||||||
|
anchor_start: Point::default(),
|
||||||
|
anchor_end: Point::default(),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Go through cells to adjust auto sizes for span=1. Base is the max of its children
|
||||||
|
fn set_auto_base_size(
|
||||||
|
column: bool,
|
||||||
|
tracks: &mut Vec<TrackData>,
|
||||||
|
cells: &Vec<GridCell>,
|
||||||
|
shapes: &HashMap<Uuid, Shape>,
|
||||||
|
bounds: &HashMap<Uuid, Bounds>,
|
||||||
|
) {
|
||||||
|
for cell in cells {
|
||||||
|
let (prop, prop_span) = if column {
|
||||||
|
(cell.column, cell.column_span)
|
||||||
|
} else {
|
||||||
|
(cell.row, cell.row_span)
|
||||||
|
};
|
||||||
|
|
||||||
|
if prop_span != 1 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let track = &mut tracks[(prop - 1) as usize];
|
||||||
|
|
||||||
|
if track.track_type != GridTrackType::Auto && track.track_type != GridTrackType::Flex {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let Some(shape) = cell.shape.and_then(|id| shapes.get(&id)) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
let bounds = bounds.find(shape);
|
||||||
|
|
||||||
|
let shape_size = if column {
|
||||||
|
bounds.width()
|
||||||
|
} else {
|
||||||
|
bounds.height()
|
||||||
|
};
|
||||||
|
|
||||||
|
let min_size = if column && shape.is_layout_horizontal_fill() {
|
||||||
|
shape.layout_item.and_then(|i| i.min_w).unwrap_or(MIN_SIZE)
|
||||||
|
} else if !column && shape.is_layout_vertical_fill() {
|
||||||
|
shape.layout_item.and_then(|i| i.min_h).unwrap_or(MIN_SIZE)
|
||||||
|
} else {
|
||||||
|
shape_size
|
||||||
|
};
|
||||||
|
|
||||||
|
track.size = f32::max(track.size, min_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adjust multi-spaned cells with no flex columns
|
||||||
|
fn set_auto_multi_span(
|
||||||
|
_column: bool,
|
||||||
|
_layout_data: &LayoutData,
|
||||||
|
_layout_bounds: &Bounds,
|
||||||
|
_tracks: &mut Vec<TrackData>,
|
||||||
|
) {
|
||||||
|
// Sort descendant order of prop-span
|
||||||
|
// Remove groups with flex (will be set in flex_multi_span)
|
||||||
|
// Retrieve teh value we need to distribute (fixed cell size minus gaps)
|
||||||
|
// Distribute the size between the tracks that already have a set value
|
||||||
|
// Distribute the space between auto tracks
|
||||||
|
// If we still have more space we distribute equally between all tracks
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adjust multi-spaned cells with flex columns
|
||||||
|
fn set_flex_multi_span(
|
||||||
|
_column: bool,
|
||||||
|
_layout_data: &LayoutData,
|
||||||
|
_layout_bounds: &Bounds,
|
||||||
|
_tracks: &mut Vec<TrackData>,
|
||||||
|
) {
|
||||||
|
// Sort descendant order of prop-span
|
||||||
|
// Remove groups without flex (will be set in flex_auto_span)
|
||||||
|
// Retrieve the value that we need to distribute (fixed size of cell minus gaps)
|
||||||
|
// Distribute the size first between the tracks that have the fixed size
|
||||||
|
// When finished we distribute equally between the the rest of the tracks
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate the `fr` unit and adjust the size
|
||||||
|
fn set_fr_value(
|
||||||
|
column: bool,
|
||||||
|
layout_data: &LayoutData,
|
||||||
|
tracks: &mut Vec<TrackData>,
|
||||||
|
layout_size: f32,
|
||||||
|
min_fr_size: f32,
|
||||||
|
) {
|
||||||
|
let tot_gap: f32 = if column {
|
||||||
|
layout_data.column_gap * (tracks.len() - 1) as f32
|
||||||
|
} else {
|
||||||
|
layout_data.row_gap * (tracks.len() - 1) as f32
|
||||||
|
};
|
||||||
|
|
||||||
|
// Total size already used
|
||||||
|
let tot_size: f32 = tracks
|
||||||
|
.iter()
|
||||||
|
.filter(|t| t.track_type != GridTrackType::Flex)
|
||||||
|
.map(|t| t.size)
|
||||||
|
.sum::<f32>()
|
||||||
|
+ tot_gap;
|
||||||
|
|
||||||
|
// Get the total of frs to divide the space into
|
||||||
|
let tot_frs: f32 = tracks
|
||||||
|
.iter()
|
||||||
|
.filter(|t| t.track_type == GridTrackType::Flex)
|
||||||
|
.map(|t| t.value)
|
||||||
|
.sum();
|
||||||
|
|
||||||
|
// Divide the space between FRS
|
||||||
|
let fr = f32::max(min_fr_size, (layout_size - tot_size) / tot_frs);
|
||||||
|
|
||||||
|
// Assign the space to the FRS
|
||||||
|
tracks
|
||||||
|
.iter_mut()
|
||||||
|
.filter(|t| t.track_type == GridTrackType::Flex)
|
||||||
|
.for_each(|t| t.size = f32::max(t.size, fr * t.value));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn stretch_tracks(
|
||||||
|
column: bool,
|
||||||
|
layout_data: &LayoutData,
|
||||||
|
tracks: &mut Vec<TrackData>,
|
||||||
|
layout_size: f32,
|
||||||
|
) {
|
||||||
|
if (column && layout_data.justify_content != JustifyContent::Stretch)
|
||||||
|
|| (!column && layout_data.align_content != AlignContent::Stretch)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let tot_gap: f32 = if column {
|
||||||
|
layout_data.column_gap * (tracks.len() - 1) as f32
|
||||||
|
} else {
|
||||||
|
layout_data.row_gap * (tracks.len() - 1) as f32
|
||||||
|
};
|
||||||
|
|
||||||
|
// Total size already used
|
||||||
|
let tot_size: f32 = tracks.iter().map(|t| t.size).sum::<f32>() + tot_gap;
|
||||||
|
|
||||||
|
let auto_tracks = tracks
|
||||||
|
.iter_mut()
|
||||||
|
.filter(|t| t.track_type == GridTrackType::Auto)
|
||||||
|
.count() as f32;
|
||||||
|
|
||||||
|
let free_space = layout_size - tot_size;
|
||||||
|
let add_size = free_space / auto_tracks;
|
||||||
|
|
||||||
|
// Assign the space to the FRS
|
||||||
|
tracks
|
||||||
|
.iter_mut()
|
||||||
|
.filter(|t| t.track_type == GridTrackType::Auto)
|
||||||
|
.for_each(|t| t.size = f32::min(t.max_size, t.size + add_size));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn justify_to_align(justify: JustifyContent) -> AlignContent {
|
||||||
|
match justify {
|
||||||
|
JustifyContent::Start => AlignContent::Start,
|
||||||
|
JustifyContent::End => AlignContent::End,
|
||||||
|
JustifyContent::Center => AlignContent::Center,
|
||||||
|
JustifyContent::SpaceBetween => AlignContent::SpaceBetween,
|
||||||
|
JustifyContent::SpaceAround => AlignContent::SpaceAround,
|
||||||
|
JustifyContent::SpaceEvenly => AlignContent::SpaceEvenly,
|
||||||
|
JustifyContent::Stretch => AlignContent::Stretch,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn assign_anchors(
|
||||||
|
column: bool,
|
||||||
|
layout_data: &LayoutData,
|
||||||
|
layout_bounds: &Bounds,
|
||||||
|
tracks: &mut Vec<TrackData>,
|
||||||
|
) {
|
||||||
|
let tot_track_length = tracks.iter().map(|t| t.size).sum::<f32>();
|
||||||
|
|
||||||
|
let mut cursor = layout_bounds.nw;
|
||||||
|
|
||||||
|
let (v, gap, size, padding_start, padding_end, align) = if column {
|
||||||
|
(
|
||||||
|
layout_bounds.hv(1.0),
|
||||||
|
layout_data.column_gap,
|
||||||
|
layout_bounds.width(),
|
||||||
|
layout_data.padding_left,
|
||||||
|
layout_data.padding_right,
|
||||||
|
justify_to_align(layout_data.justify_content),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
(
|
||||||
|
layout_bounds.vv(1.0),
|
||||||
|
layout_data.row_gap,
|
||||||
|
layout_bounds.height(),
|
||||||
|
layout_data.padding_top,
|
||||||
|
layout_data.padding_bottom,
|
||||||
|
layout_data.align_content,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
let tot_gap = gap * (tracks.len() - 1) as f32;
|
||||||
|
let tot_size = tot_track_length + tot_gap;
|
||||||
|
let padding = padding_start + padding_end;
|
||||||
|
let pad_size = size - padding;
|
||||||
|
|
||||||
|
let (real_margin, real_gap) = match align {
|
||||||
|
AlignContent::End => (size - padding_end - tot_size, gap),
|
||||||
|
AlignContent::Center => ((size - tot_size) / 2.0, gap),
|
||||||
|
AlignContent::SpaceAround => {
|
||||||
|
let effective_gap = (pad_size - tot_track_length) / tracks.len() as f32;
|
||||||
|
(padding_start + effective_gap / 2.0, effective_gap)
|
||||||
|
}
|
||||||
|
AlignContent::SpaceBetween => (
|
||||||
|
padding_start,
|
||||||
|
f32::max(
|
||||||
|
gap,
|
||||||
|
(pad_size - tot_track_length) / (tracks.len() - 1) as f32,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
_ => (padding_start + 0.0, gap),
|
||||||
|
};
|
||||||
|
|
||||||
|
cursor = cursor + (v * real_margin);
|
||||||
|
|
||||||
|
for track in tracks {
|
||||||
|
track.anchor_start = cursor;
|
||||||
|
track.anchor_end = cursor + (v * track.size);
|
||||||
|
cursor = track.anchor_end + (v * real_gap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cell_bounds(
|
||||||
|
layout_bounds: &Bounds,
|
||||||
|
column_start: Point,
|
||||||
|
column_end: Point,
|
||||||
|
row_start: Point,
|
||||||
|
row_end: Point,
|
||||||
|
) -> Option<Bounds> {
|
||||||
|
let hv = layout_bounds.hv(1.0);
|
||||||
|
let vv = layout_bounds.vv(1.0);
|
||||||
|
let nw = intersect_rays(&Ray::new(column_start, vv), &Ray::new(row_start, hv))?;
|
||||||
|
let ne = intersect_rays(&Ray::new(column_end, vv), &Ray::new(row_start, hv))?;
|
||||||
|
let sw = intersect_rays(&Ray::new(column_start, vv), &Ray::new(row_end, hv))?;
|
||||||
|
let se = intersect_rays(&Ray::new(column_end, vv), &Ray::new(row_end, hv))?;
|
||||||
|
Some(Bounds::new(nw, ne, se, sw))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_cell_data<'a>(
|
||||||
|
layout_bounds: &Bounds,
|
||||||
|
shapes: &'a HashMap<Uuid, Shape>,
|
||||||
|
cells: &Vec<GridCell>,
|
||||||
|
column_tracks: &Vec<TrackData>,
|
||||||
|
row_tracks: &Vec<TrackData>,
|
||||||
|
) -> Vec<CellData<'a>> {
|
||||||
|
let mut result = Vec::<CellData<'a>>::new();
|
||||||
|
|
||||||
|
for cell in cells {
|
||||||
|
let Some(shape_id) = cell.shape else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
let Some(shape) = shapes.get(&shape_id) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
let column_start = (cell.column - 1) as usize;
|
||||||
|
let column_end = (cell.column + cell.column_span - 2) as usize;
|
||||||
|
let row_start = (cell.row - 1) as usize;
|
||||||
|
let row_end = (cell.row + cell.row_span - 2) as usize;
|
||||||
|
let Some(cell_bounds) = cell_bounds(
|
||||||
|
layout_bounds,
|
||||||
|
column_tracks[column_start].anchor_start,
|
||||||
|
column_tracks[column_end].anchor_end,
|
||||||
|
row_tracks[row_start].anchor_start,
|
||||||
|
row_tracks[row_end].anchor_end,
|
||||||
|
) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
result.push(CellData {
|
||||||
|
shape,
|
||||||
|
anchor: cell_bounds.nw,
|
||||||
|
width: cell_bounds.width(),
|
||||||
|
height: cell_bounds.height(),
|
||||||
|
align_self: cell.align_self,
|
||||||
|
justify_self: cell.justify_self,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
fn calculate_cell_data<'a>(
|
fn calculate_cell_data<'a>(
|
||||||
|
@ -22,11 +380,70 @@ fn calculate_cell_data<'a>(
|
||||||
shapes: &'a HashMap<Uuid, Shape>,
|
shapes: &'a HashMap<Uuid, Shape>,
|
||||||
bounds: &HashMap<Uuid, Bounds>,
|
bounds: &HashMap<Uuid, Bounds>,
|
||||||
) -> Vec<CellData<'a>> {
|
) -> Vec<CellData<'a>> {
|
||||||
todo!()
|
let result: Vec<CellData<'a>> = vec![];
|
||||||
|
|
||||||
|
let layout_bounds = bounds.find(shape);
|
||||||
|
|
||||||
|
let column_tracks = calculate_tracks(
|
||||||
|
true,
|
||||||
|
layout_data,
|
||||||
|
grid_data,
|
||||||
|
&layout_bounds,
|
||||||
|
&grid_data.cells,
|
||||||
|
shapes,
|
||||||
|
bounds,
|
||||||
|
);
|
||||||
|
let row_tracks = calculate_tracks(
|
||||||
|
false,
|
||||||
|
layout_data,
|
||||||
|
grid_data,
|
||||||
|
&layout_bounds,
|
||||||
|
&grid_data.cells,
|
||||||
|
shapes,
|
||||||
|
bounds,
|
||||||
|
);
|
||||||
|
|
||||||
|
create_cell_data(
|
||||||
|
&layout_bounds,
|
||||||
|
shapes,
|
||||||
|
&grid_data.cells,
|
||||||
|
&column_tracks,
|
||||||
|
&row_tracks,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn child_position(child_bounds: &Bounds, cell: &CellData) -> Point {
|
fn child_position(
|
||||||
todo!()
|
layout_bounds: &Bounds,
|
||||||
|
layout_data: &LayoutData,
|
||||||
|
child_bounds: &Bounds,
|
||||||
|
layout_item: Option<LayoutItem>,
|
||||||
|
cell: &CellData,
|
||||||
|
) -> Point {
|
||||||
|
let hv = layout_bounds.hv(1.0);
|
||||||
|
let vv = layout_bounds.vv(1.0);
|
||||||
|
|
||||||
|
let margin_left = layout_item.map(|i| i.margin_left).unwrap_or(0.0);
|
||||||
|
let margin_top = layout_item.map(|i| i.margin_top).unwrap_or(0.0);
|
||||||
|
let margin_right = layout_item.map(|i| i.margin_right).unwrap_or(0.0);
|
||||||
|
let margin_bottom = layout_item.map(|i| i.margin_bottom).unwrap_or(0.0);
|
||||||
|
|
||||||
|
cell.anchor
|
||||||
|
+ vv * match (cell.align_self, layout_data.align_items) {
|
||||||
|
(Some(AlignSelf::Start), _) => margin_left,
|
||||||
|
(Some(AlignSelf::Center), _) => (cell.height - child_bounds.height()) / 2.0,
|
||||||
|
(Some(AlignSelf::End), _) => margin_right + cell.height - child_bounds.height(),
|
||||||
|
(_, AlignItems::Center) => (cell.height - child_bounds.height()) / 2.0,
|
||||||
|
(_, AlignItems::End) => margin_right + cell.height - child_bounds.height(),
|
||||||
|
_ => margin_left,
|
||||||
|
}
|
||||||
|
+ hv * match (cell.justify_self, layout_data.justify_items) {
|
||||||
|
(Some(JustifySelf::Start), _) => margin_top,
|
||||||
|
(Some(JustifySelf::Center), _) => (cell.width - child_bounds.width()) / 2.0,
|
||||||
|
(Some(JustifySelf::End), _) => margin_bottom + cell.width - child_bounds.width(),
|
||||||
|
(_, JustifyItems::Center) => (cell.width - child_bounds.width()) / 2.0,
|
||||||
|
(_, JustifyItems::End) => margin_bottom + cell.width - child_bounds.width(),
|
||||||
|
_ => margin_top,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reflow_grid_layout<'a>(
|
pub fn reflow_grid_layout<'a>(
|
||||||
|
@ -39,11 +456,18 @@ pub fn reflow_grid_layout<'a>(
|
||||||
let mut result = VecDeque::new();
|
let mut result = VecDeque::new();
|
||||||
|
|
||||||
let cells = calculate_cell_data(shape, layout_data, grid_data, shapes, bounds);
|
let cells = calculate_cell_data(shape, layout_data, grid_data, shapes, bounds);
|
||||||
|
let layout_bounds = bounds.find(shape);
|
||||||
|
|
||||||
for cell in cells.iter() {
|
for cell in cells.iter() {
|
||||||
let child = cell.shape;
|
let child = cell.shape;
|
||||||
let child_bounds = bounds.find(child);
|
let child_bounds = bounds.find(child);
|
||||||
let position = child_position(&child_bounds, cell);
|
let position = child_position(
|
||||||
|
&layout_bounds,
|
||||||
|
&layout_data,
|
||||||
|
&child_bounds,
|
||||||
|
child.layout_item,
|
||||||
|
cell,
|
||||||
|
);
|
||||||
|
|
||||||
let mut transform = Matrix::default();
|
let mut transform = Matrix::default();
|
||||||
let delta_v = Vector::new_points(&child_bounds.nw, &position);
|
let delta_v = Vector::new_points(&child_bounds.nw, &position);
|
||||||
|
|
Loading…
Add table
Reference in a new issue