Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow stacking style calls #244

Merged
merged 2 commits into from
Jan 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions examples/widget-gallery/src/context_menu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,21 @@ pub fn menu_view() -> impl View {
stack({
(
label(|| "Click me (Popout menu)")
.base_style(|s| s.padding(10.0).margin_bottom(10.0).border(1.0))
.style(|s| s.padding(10.0).margin_bottom(10.0).border(1.0))
.popout_menu(|| {
Menu::new("")
.entry(MenuItem::new("I am a menu item!"))
.separator()
.entry(MenuItem::new("I am another menu item"))
}),
label(|| "Right click me (Context menu)")
.base_style(|s| s.padding(10.0).border(1.0))
.style(|s| s.padding(10.0).border(1.0))
.context_menu(|| {
Menu::new("")
.entry(MenuItem::new("Menu item"))
.entry(MenuItem::new("Menu item with something on the\tright"))
}),
)
})
.base_style(|s| s.flex_col())
.style(|s| s.flex_col())
}
14 changes: 7 additions & 7 deletions src/id.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use crate::{
event::EventListener,
style::{Style, StyleClassRef, StyleSelector},
update::{UpdateMessage, CENTRAL_DEFERRED_UPDATE_MESSAGES, CENTRAL_UPDATE_MESSAGES},
view_data::ChangeFlags,
view_data::{ChangeFlags, StackOffset},
};

thread_local! {
Expand Down Expand Up @@ -150,12 +150,12 @@ impl Id {
}
}

pub fn update_base_style(&self, style: Style) {
self.add_update_message(UpdateMessage::BaseStyle { id: *self, style });
}

pub fn update_style(&self, style: Style) {
self.add_update_message(UpdateMessage::Style { id: *self, style });
pub(crate) fn update_style(&self, style: Style, offset: StackOffset<Style>) {
self.add_update_message(UpdateMessage::Style {
id: *self,
style,
offset,
});
}

pub fn update_class(&self, class: StyleClassRef) {
Expand Down
7 changes: 2 additions & 5 deletions src/update.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use crate::{
menu::Menu,
style::{Style, StyleClassRef, StyleSelector},
view::View,
view_data::ChangeFlags,
view_data::{ChangeFlags, StackOffset},
};

thread_local! {
Expand Down Expand Up @@ -48,13 +48,10 @@ pub(crate) enum UpdateMessage {
id: Id,
state: Box<dyn Any>,
},
BaseStyle {
id: Id,
style: Style,
},
Style {
id: Id,
style: Style,
offset: StackOffset<Style>,
},
Class {
id: Id,
Expand Down
56 changes: 47 additions & 9 deletions src/view_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,26 +14,69 @@ use crate::{
};
use bitflags::bitflags;
use kurbo::Rect;
use std::{collections::HashMap, time::Duration};
use smallvec::SmallVec;
use std::{collections::HashMap, marker::PhantomData, time::Duration};
use taffy::node::Node;

/// A stack of view attributes. Each entry is associated with a view decorator call.
#[derive(Default)]
pub(crate) struct Stack<T> {
stack: SmallVec<[T; 1]>,
}

pub(crate) struct StackOffset<T> {
offset: usize,
phantom: PhantomData<T>,
}

impl<T> Clone for StackOffset<T> {
fn clone(&self) -> Self {
*self
}
}

impl<T> Copy for StackOffset<T> {}

impl<T> Stack<T> {
pub fn next_offset(&mut self) -> StackOffset<T> {
StackOffset {
offset: self.stack.len(),
phantom: PhantomData,
}
}
pub fn push(&mut self, value: T) {
self.stack.push(value);
}
pub fn set(&mut self, offset: StackOffset<T>, value: T) {
self.stack[offset.offset] = value;
}
}

/// View data stores internal state associated with a view.
/// Each view is expected to own and give access to this data.
pub struct ViewData {
pub(crate) id: Id,
pub(crate) style: Style,
pub(crate) style: Stack<Style>,
}

impl ViewData {
pub fn new(id: Id) -> Self {
Self {
id,
style: Style::new(),
style: Default::default(),
}
}
pub fn id(&self) -> Id {
self.id
}

pub(crate) fn style(&self) -> Style {
let mut result = Style::new();
for entry in self.style.stack.iter() {
result.apply_mut(entry.clone());
}
result
}
}

pub(crate) fn update_data(id: Id, root: &mut dyn View, f: impl FnOnce(&mut ViewData)) {
Expand Down Expand Up @@ -92,7 +135,6 @@ pub struct ViewState {
pub(crate) layout_props: LayoutProps,
pub(crate) view_style_props: ViewStyleProps,
pub(crate) animation: Option<Animation>,
pub(crate) base_style: Option<Style>,
pub(crate) class: Option<StyleClassRef>,
pub(crate) dragging_style: Option<Style>,
pub(crate) combined_style: Style,
Expand All @@ -118,7 +160,6 @@ impl ViewState {
request_style_recursive: false,
has_style_selectors: StyleSelectors::default(),
animation: None,
base_style: None,
class: None,
combined_style: Style::new(),
taffy_style: taffy::style::Style::DEFAULT,
Expand Down Expand Up @@ -154,12 +195,9 @@ impl ViewState {
if let Some(view_class) = view_class {
computed_style = computed_style.apply_classes_from_context(&[view_class], context);
}
if let Some(base_style) = self.base_style.clone() {
computed_style.apply_mut(base_style);
}
computed_style = computed_style
.apply_classes_from_context(classes, context)
.apply(view_data.style.clone());
.apply(view_data.style());

'anim: {
if let Some(animation) = self.animation.as_mut() {
Expand Down
37 changes: 4 additions & 33 deletions src/views/decorator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,7 @@ use crate::{
pub trait Decorators: View + Sized {
/// Alter the style of the view.
///
/// -----
///
/// Note: repeated applications of `style` overwrite previous styles.
/// Earlier applications of `style` have lower priority than later calls.
/// ```rust
/// # use floem::{peniko::Color, view::View, views::{Decorators, label, stack}};
/// fn view() -> impl View {
Expand All @@ -32,41 +30,14 @@ pub trait Decorators: View + Sized {
/// ))
/// }
/// ```
/// If you are returning from a function that produces a view, you may want
/// to use `base_style` for the returned [`View`] instead.
fn style(mut self, style: impl Fn(Style) -> Style + 'static) -> Self {
let id = self.id();
let offset = self.view_data_mut().style.next_offset();
let style = create_updater(
move || style(Style::new()),
move |style| id.update_style(style),
move |style| id.update_style(style, offset),
);
self.view_data_mut().style = style;
self
}

/// Alter the base style of the view.
/// This is applied before `style`, and so serves as a good place to set defaults.
/// ```rust
/// # use floem::{peniko::Color, view::View, views::{Decorators, label, stack}};
/// fn view() -> impl View {
/// label(|| "Hello".to_string())
/// .base_style(|s| s.font_size(20.0).color(Color::RED))
/// }
///
/// fn other() -> impl View {
/// stack((
/// view(), // will be red and size 20
/// // will be green and size 20
/// view().style(|s| s.color(Color::GREEN)),
/// ))
/// }
/// ```
fn base_style(self, style: impl Fn(Style) -> Style + 'static) -> Self {
let id = self.id();
create_effect(move |_| {
let style = style(Style::new());
id.update_base_style(style);
});
self.view_data_mut().style.push(style);
self
}

Expand Down
2 changes: 1 addition & 1 deletion src/views/drag_resize_window_area.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ pub fn drag_resize_window_area<V: View + 'static>(
.on_event_stop(EventListener::PointerDown, move |_| {
drag_resize_window(direction)
})
.base_style(move |s| {
.style(move |s| {
let cursor = match direction {
ResizeDirection::East => CursorStyle::ColResize,
ResizeDirection::West => CursorStyle::ColResize,
Expand Down
2 changes: 1 addition & 1 deletion src/widgets/checkbox.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,6 @@ pub fn labeled_checkbox<S: Display + 'static>(
) -> impl View {
h_stack((checkbox_svg(checked), views::label(label)))
.class(LabeledCheckboxClass)
.base_style(|s| s.items_center().justify_center())
.style(|s| s.items_center().justify_center())
.keyboard_navigatable()
}
33 changes: 10 additions & 23 deletions src/window_handle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -785,30 +785,17 @@ impl WindowHandle {
cx.update_view(&mut self.view, id_path.dispatch(), state);
}
}
UpdateMessage::BaseStyle { id, style } => {
let state = cx.app_state.view_state(id);
let old_any_inherited = state
.base_style
.as_ref()
.map(|style| style.any_inherited())
.unwrap_or(false);
let new_any_inherited = style.any_inherited();
state.base_style = Some(style);
if new_any_inherited || old_any_inherited {
cx.app_state.request_style_recursive(id);
} else {
cx.request_style(id);
}
UpdateMessage::Style { id, style, offset } => {
update_data(id, &mut self.view, |data| {
let old_any_inherited = data.style().any_inherited();
data.style.set(offset, style);
if data.style().any_inherited() || old_any_inherited {
cx.app_state.request_style_recursive(id);
} else {
cx.request_style(id);
}
})
}
UpdateMessage::Style { id, style } => update_data(id, &mut self.view, |data| {
let old_any_inherited = data.style.any_inherited();
data.style = style;
if data.style.any_inherited() || old_any_inherited {
cx.app_state.request_style_recursive(id);
} else {
cx.request_style(id);
}
}),
UpdateMessage::Class { id, class } => {
let state = cx.app_state.view_state(id);
state.class = Some(class);
Expand Down