Skip to content

Commit

Permalink
Add a hover style selector
Browse files Browse the repository at this point in the history
  • Loading branch information
Zoxc committed Oct 30, 2023
1 parent 9e69067 commit 1254ade
Show file tree
Hide file tree
Showing 4 changed files with 114 additions and 40 deletions.
11 changes: 7 additions & 4 deletions src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ pub struct ViewState {
pub(crate) node: Node,
pub(crate) children_nodes: Vec<Node>,
pub(crate) request_layout: bool,
pub(crate) hover_sensitive: bool,
pub(crate) viewport: Option<Rect>,
pub(crate) layout_rect: Rect,
pub(crate) animation: Option<Animation>,
Expand Down Expand Up @@ -76,6 +77,7 @@ impl ViewState {
viewport: None,
layout_rect: Rect::ZERO,
request_layout: true,
hover_sensitive: false,
animation: None,
base_style: None,
style: Style::BASE,
Expand Down Expand Up @@ -669,13 +671,15 @@ pub struct InteractionState {

pub(crate) struct StyleCx {
pub(crate) current: Rc<StyleMap>,
pub(crate) direct: StyleMap,
saved: Vec<Rc<StyleMap>>,
}

impl StyleCx {
fn new() -> Self {
Self {
current: Default::default(),
direct: Default::default(),
saved: Default::default(),
}
}
Expand Down Expand Up @@ -763,10 +767,9 @@ impl<'a> LayoutCx<'a> {

pub fn get_prop<P: StyleProp>(&self, _prop: P) -> Option<P::Type> {
self.style
.current
.map
.get(&P::prop_ref())
.and_then(|v| v.downcast_ref().cloned())
.direct
.get_prop::<P>()
.or_else(|| self.style.current.get_prop::<P>())
}

pub(crate) fn clear(&mut self) {
Expand Down
117 changes: 98 additions & 19 deletions src/style.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
use floem_renderer::cosmic_text::{LineHeightValue, Style as FontStyle, Weight};
use peniko::Color;
use std::any::Any;
use std::collections::hash_map::Entry;
use std::collections::HashMap;
use std::hash::Hash;
use std::hash::Hasher;
Expand All @@ -42,6 +43,7 @@ use taffy::{
style::{FlexWrap, LengthPercentage, Style as TaffyStyle},
};

use crate::context::InteractionState;
use crate::context::LayoutCx;
use crate::unit::{Px, PxPct, PxPctAuto, UnitExt};
use crate::views::scroll_bar_color;
Expand Down Expand Up @@ -196,11 +198,83 @@ macro_rules! prop_extracter {
};
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum StyleMapValue<T> {
Val(T),
/// Use the default value for the style, typically from the underlying `ComputedStyle`
Unset,
}

impl<T> StyleMapValue<T> {
pub(crate) fn as_ref(&self) -> Option<&T> {
match self {
Self::Val(v) => Some(v),
Self::Unset => None,
}
}
}

#[derive(Default, Clone, Debug)]
pub(crate) struct StyleMap {
pub(crate) map: HashMap<StylePropRef, Rc<dyn Any>>,
pub(crate) map: HashMap<StylePropRef, StyleMapValue<Rc<dyn Any>>>,
pub(crate) selectors: HashMap<StyleSelector, StyleMap>,
}

impl StyleMap {
pub(crate) fn get_prop<P: StyleProp>(&self) -> Option<P::Type> {
self.map
.get(&P::prop_ref())
.and_then(|v| v.as_ref())
.and_then(|v| v.downcast_ref().cloned())
}

pub(crate) fn hover_sensitive(&self) -> bool {
self.selectors
.iter()
.any(|(selector, map)| *selector == StyleSelector::Hover || map.hover_sensitive())
}

pub(crate) fn apply_interact_state(&mut self, interact_state: InteractionState) {
if interact_state.is_hovered && !interact_state.is_disabled {
if let Some(mut map) = self.selectors.remove(&StyleSelector::Hover) {
map.apply_interact_state(interact_state);
self.apply(map);
}
}
}

pub(crate) fn apply_only_inherited(map: &mut Rc<StyleMap>, over: &StyleMap) {
let any_inherited = over.map.iter().any(|(p, _)| p.info.inherited);

if any_inherited {
let inherited = over
.map
.iter()
.filter(|(p, _)| p.info.inherited)
.map(|(p, v)| (*p, v.clone()));

Rc::make_mut(map).map.extend(inherited);
}
}

fn set_selector(&mut self, selector: StyleSelector, map: StyleMap) {
match self.selectors.entry(selector) {
Entry::Occupied(mut e) => e.get_mut().apply(map),
Entry::Vacant(e) => {
e.insert(map);
}
}
}

fn apply(&mut self, over: StyleMap) {
self.map.extend(over.map);
for (selector, map) in over.selectors {
self.set_selector(selector, map);
}
}
}

#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub enum StyleSelector {
Hover,
Focus,
Expand Down Expand Up @@ -350,7 +424,7 @@ macro_rules! define_styles {

#[derive(Debug, Clone)]
pub struct Style {
pub(crate) other: Option<HashMap<StylePropRef, StyleValue<Rc<dyn Any>>>>,
pub(crate) other: Option<StyleMap>,
$(
pub $name: StyleValue<$typ>,
)*
Expand Down Expand Up @@ -378,12 +452,7 @@ macro_rules! define_styles {
/// for any missing values.
pub fn compute(self, underlying: &ComputedStyle) -> ComputedStyle {
ComputedStyle {
other: StyleMap {
map: self.other.unwrap_or_default()
.into_iter()
.filter_map(|(k, mut v)| v.as_mut().map(|v| (k, v.clone())))
.collect()
},
other: self.other.unwrap_or_default(),
$(
$name: self.$name.unwrap_or_else(|| underlying.$name.clone()),
)*
Expand All @@ -399,16 +468,8 @@ macro_rules! define_styles {
/// `ComputedStyle` or using the value in the `Style`.
pub fn apply(self, over: Style) -> Style {
let mut other = self.other.unwrap_or_default();
for (k, v) in over.other.unwrap_or_default() {
match v {
StyleValue::Val(..) => {
other.insert(k, v);
},
StyleValue::Base => (),
StyleValue::Unset => {
other.remove(&k);
}
}
if let Some(over) = over.other {
other.apply(over);
}
Style {
other: Some(other),
Expand Down Expand Up @@ -519,7 +580,25 @@ impl Style {

pub fn set_style_value<P: StyleProp>(mut self, _prop: P, value: StyleValue<P::Type>) -> Self {
let mut other = self.other.unwrap_or_default();
other.insert(P::prop_ref(), value.map(|v| -> Rc<dyn Any> { Rc::new(v) }));
let insert: StyleMapValue<Rc<dyn Any>> = match value {
StyleValue::Val(value) => StyleMapValue::Val(Rc::new(value)),
StyleValue::Unset => StyleMapValue::Unset,
StyleValue::Base => {
other.map.remove(&P::prop_ref());
self.other = Some(other);
return self;
}
};
other.map.insert(P::prop_ref(), insert);
self.other = Some(other);
self
}

pub fn hover(mut self, style: impl Fn(Style) -> Style + 'static) -> Self {
// FIXME: Ignores field style properties.
let over = style(Style::BASE).other.unwrap_or_default();
let mut other = self.other.unwrap_or_default();
other.set_selector(StyleSelector::Hover, over);
self.other = Some(other);
self
}
Expand Down
25 changes: 8 additions & 17 deletions src/view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@
//!
//!

use std::{any::Any, rc::Rc};
use std::any::Any;

use bitflags::bitflags;
use floem_renderer::Renderer;
Expand Down Expand Up @@ -196,22 +196,13 @@ pub trait View {
cx.app_state_mut().compute_style(self.id(), view_style);
let style = cx.app_state_mut().get_computed_style(self.id()).clone();

cx.style.current = Rc::new(StyleMap {
map: cx
.style
.current
.map
.iter()
.map(|(p, v)| (*p, v.clone()))
.chain(
style
.other
.map
.into_iter()
.filter(|(p, _)| p.info.inherited),
)
.collect(),
});
cx.app_state_mut().view_state(self.id()).hover_sensitive = style.other.hover_sensitive();

let interact_state = cx.app_state().get_interact_state(&self.id());
cx.style.direct = style.other.clone();
cx.style.direct.apply_interact_state(interact_state);
StyleMap::apply_only_inherited(&mut cx.style.current, &cx.style.direct);

if style.color.is_some() {
cx.color = style.color;
}
Expand Down
1 change: 1 addition & 0 deletions src/window_handle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,7 @@ impl WindowHandle {
if view_state.hover_style.is_some()
|| view_state.active_style.is_some()
|| view_state.animation.is_some()
|| view_state.hover_sensitive
{
cx.app_state.request_layout(*id);
}
Expand Down

0 comments on commit 1254ade

Please sign in to comment.