Skip to content

Commit

Permalink
Add TaffyLayout view and widget
Browse files Browse the repository at this point in the history
  • Loading branch information
nicoburns committed Nov 2, 2023
1 parent a231cf6 commit 0fd0e4a
Show file tree
Hide file tree
Showing 4 changed files with 539 additions and 0 deletions.
2 changes: 2 additions & 0 deletions src/view/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ mod button;
// mod use_state;
mod linear_layout;
mod list;
mod taffy_layout;
#[allow(clippy::module_inception)]
mod view;

Expand All @@ -29,4 +30,5 @@ pub use xilem_core::{Id, IdPath, VecSplice};
pub use button::button;
pub use linear_layout::{h_stack, v_stack, LinearLayout};
pub use list::{list, List};
pub use taffy_layout::{div, flex_column, flex_row, TaffyLayout};
pub use view::{Adapt, AdaptState, Cx, Memoize, View, ViewMarker, ViewSequence};
137 changes: 137 additions & 0 deletions src/view/taffy_layout.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
// Copyright 2022 The Druid Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::{any::Any, marker::PhantomData};

use crate::view::{Id, VecSplice, ViewMarker, ViewSequence};
use crate::widget::{self, ChangeFlags};
use crate::MessageResult;

mod taffy {
pub use taffy::compute::*;
pub use taffy::style::*;
pub use taffy::style_helpers::*;
pub use taffy::tree::*;
}

use super::{Cx, View};

/// TaffyLayout is a simple view which does layout for the specified ViewSequence.
///
/// Each Element is positioned on the specified Axis starting at the beginning with the given spacing
///
/// This View is only temporary is probably going to be replaced by something like Druid's Flex
/// widget.
pub struct TaffyLayout<T, A, VT: ViewSequence<T, A>> {
children: VT,
style: taffy::Style,
phantom: PhantomData<fn() -> (T, A)>,
}

/// creates a vertical [`TaffyLayout`].
pub fn flex_column<T, A, VT: ViewSequence<T, A>>(children: VT) -> TaffyLayout<T, A, VT> {
TaffyLayout::new_flex(children, taffy::FlexDirection::Column)
}

/// creates a horizontal [`TaffyLayout`].
pub fn flex_row<T, A, VT: ViewSequence<T, A>>(children: VT) -> TaffyLayout<T, A, VT> {
TaffyLayout::new_flex(children, taffy::FlexDirection::Row)
}

/// creates a horizontal [`TaffyLayout`].
pub fn div<T, A, VT: ViewSequence<T, A>>(children: VT) -> TaffyLayout<T, A, VT> {
TaffyLayout::new(children, taffy::Display::Block)
}

impl<T, A, VT: ViewSequence<T, A>> TaffyLayout<T, A, VT> {
pub fn new(children: VT, display: taffy::Display) -> Self {
let phantom = Default::default();
TaffyLayout {
children,
style: taffy::Style {
display,
..Default::default()
},
phantom,
}
}

pub fn new_flex(children: VT, flex_direction: taffy::FlexDirection) -> Self {
let phantom = Default::default();
let display = taffy::Display::Flex;
TaffyLayout {
children,
style: taffy::Style {
display,
flex_direction,
..Default::default()
},
phantom,
}
}

pub fn with_style(mut self, style_modifier: impl FnOnce(&mut taffy::Style)) -> Self {
style_modifier(&mut self.style);
self
}
}

impl<T, A, VT: ViewSequence<T, A>> ViewMarker for TaffyLayout<T, A, VT> {}

impl<T, A, VT: ViewSequence<T, A>> View<T, A> for TaffyLayout<T, A, VT> {
type State = VT::State;

type Element = widget::TaffyLayout;

fn build(&self, cx: &mut Cx) -> (Id, Self::State, Self::Element) {
let mut elements = vec![];
let (id, state) = cx.with_new_id(|cx| self.children.build(cx, &mut elements));
let column = widget::TaffyLayout::new(elements, self.style.clone());
(id, state, column)
}

fn rebuild(
&self,
cx: &mut Cx,
prev: &Self,
id: &mut Id,
state: &mut Self::State,
element: &mut Self::Element,
) -> ChangeFlags {
let mut scratch = vec![];
let mut splice = VecSplice::new(&mut element.children, &mut scratch);

let mut flags = cx.with_id(*id, |cx| {
self.children
.rebuild(cx, &prev.children, state, &mut splice)
});

if self.style != prev.style {
element.style = self.style.clone();
flags |= ChangeFlags::LAYOUT;
}

flags
}

fn message(
&self,
id_path: &[Id],
state: &mut Self::State,
event: Box<dyn Any>,
app_state: &mut T,
) -> MessageResult<A> {
self.children.message(id_path, state, event, app_state)
}
}
2 changes: 2 additions & 0 deletions src/widget/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ mod core;
mod linear_layout;
mod piet_scene_helpers;
mod raw_event;
mod taffy_layout;
//mod scroll_view;
//mod text;
#[allow(clippy::module_inception)]
Expand All @@ -33,4 +34,5 @@ pub use button::Button;
pub use contexts::{AccessCx, CxState, EventCx, LayoutCx, LifeCycleCx, PaintCx, UpdateCx};
pub use linear_layout::LinearLayout;
pub use raw_event::{Event, LifeCycle, MouseEvent, ViewContext};
pub use taffy_layout::TaffyLayout;
pub use widget::{AnyWidget, Widget};
Loading

0 comments on commit 0fd0e4a

Please sign in to comment.