Skip to content

Commit 00f86ec

Browse files
authored
Add custom method (#794)
1 parent 20d7862 commit 00f86ec

23 files changed

+181
-84
lines changed

examples/widget-gallery/src/rich_text.rs

+2-4
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,8 @@ use floem::{
88
};
99

1010
pub fn rich_text_view() -> impl IntoView {
11-
let builder = "This".red().italic()
12-
+ " is easy to build".blue()
13-
+ "\nTest value: "
14-
+ 5.to_string().green();
11+
let builder =
12+
"This".red().italic() + " is rich text".blue() + "\nTest value: " + 5.to_string().green();
1513

1614
let text = "
1715
// floem is a ui lib, homepage https://github.com/lapce/floem

src/id.rs

+19-1
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,25 @@ impl ViewId {
119119
}
120120

121121
/// Set the children views of this Id
122-
pub fn set_children(&self, children: Vec<impl IntoView>) {
122+
/// See also [`Self::set_children_vec`]
123+
pub fn set_children<const N: usize, V: IntoView>(&self, children: [V; N]) {
124+
VIEW_STORAGE.with_borrow_mut(|s| {
125+
let mut children_ids = Vec::new();
126+
for child in children {
127+
let child_view = child.into_view();
128+
let child_view_id = child_view.id();
129+
children_ids.push(child_view_id);
130+
s.parent.insert(child_view_id, Some(*self));
131+
s.views
132+
.insert(child_view_id, Rc::new(RefCell::new(child_view.into_any())));
133+
}
134+
s.children.insert(*self, children_ids);
135+
});
136+
}
137+
138+
/// Set the children views of this Id using a Vector
139+
/// See also [`Self::set_children`]
140+
pub fn set_children_vec(&self, children: Vec<impl IntoView>) {
123141
VIEW_STORAGE.with_borrow_mut(|s| {
124142
let mut children_ids = Vec::new();
125143
for child in children {

src/style.rs

+63-2
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ impl StylePropValue for MinTrackSizingFunction {}
8989
impl StylePropValue for MaxTrackSizingFunction {}
9090
impl<T: StylePropValue, M: StylePropValue> StylePropValue for MinMax<T, M> {}
9191
impl<T: StylePropValue> StylePropValue for Line<T> {}
92+
impl StylePropValue for taffy::GridAutoFlow {}
9293
impl StylePropValue for GridPlacement {}
9394
impl StylePropValue for CursorStyle {}
9495
impl StylePropValue for BoxShadow {
@@ -1195,7 +1196,7 @@ impl Style {
11951196
self
11961197
}
11971198

1198-
fn get_nested_map(&self, key: StyleKey) -> Option<Style> {
1199+
pub(crate) fn get_nested_map(&self, key: StyleKey) -> Option<Style> {
11991200
self.map
12001201
.get(&key)
12011202
.map(|map| map.downcast_ref::<Style>().unwrap().clone())
@@ -1355,6 +1356,10 @@ impl Style {
13551356
pub fn apply_overriding_styles(self, overrides: impl Iterator<Item = Style>) -> Style {
13561357
overrides.fold(self, |acc, x| acc.apply(x))
13571358
}
1359+
1360+
pub(crate) fn clear(&mut self) {
1361+
self.map.clear();
1362+
}
13581363
}
13591364

13601365
impl Debug for Style {
@@ -1636,6 +1641,7 @@ define_builtin_props!(
16361641
GridTemplateColumns grid_template_columns: Vec<TrackSizingFunction> {} = Vec::new(),
16371642
GridAutoRows grid_auto_rows: Vec<MinMax<MinTrackSizingFunction, MaxTrackSizingFunction>> {} = Vec::new(),
16381643
GridAutoColumns grid_auto_columns: Vec<MinMax<MinTrackSizingFunction, MaxTrackSizingFunction>> {} = Vec::new(),
1644+
GridAutoFlow grid_auto_flow: taffy::GridAutoFlow {} = taffy::GridAutoFlow::Row,
16391645
GridRow grid_row: Line<GridPlacement> {} = Line::default(),
16401646
GridColumn grid_column: Line<GridPlacement> {} = Line::default(),
16411647
AlignSelf align_self: Option<AlignItems> {} = None,
@@ -1878,6 +1884,30 @@ impl Style {
18781884
self
18791885
}
18801886

1887+
/// Applies a `CustomStyle` type to the `CustomStyle`'s associated style class.
1888+
///
1889+
/// For example: if the `CustomStyle` you use is `DropdownCustomStyle` then it
1890+
/// will apply the custom style to that custom style type's associated style class
1891+
/// which, in this example, is `DropdownClass`.
1892+
///
1893+
/// This is especially useful when building a stylesheet or targeting a child view.
1894+
///
1895+
/// # Examples
1896+
/// ```
1897+
/// // In a style sheet or on a parent view
1898+
/// use floem::prelude::*;
1899+
/// use floem::style::Style;
1900+
/// Style::new().custom_style_class(|s: dropdown::DropdownCustomStyle| s.close_on_accept(false));
1901+
/// // This property is now set on the `DropdownClass` class and will be applied to any dropdowns that are children of this view.
1902+
/// ```
1903+
///
1904+
/// See also: [`Style::custom`](Self::custom) and [`Style::apply_custom`](Self::apply_custom).
1905+
pub fn custom_style_class<CS: CustomStyle>(mut self, style: impl FnOnce(CS) -> CS) -> Self {
1906+
let over = style(CS::default());
1907+
self.set_class(CS::StyleClass::class_ref(), over.into());
1908+
self
1909+
}
1910+
18811911
pub fn width_full(self) -> Self {
18821912
self.width_pct(100.0)
18831913
}
@@ -2362,6 +2392,28 @@ impl Style {
23622392
}
23632393
}
23642394

2395+
/// Applies a `CustomStyle` type into this style.
2396+
///
2397+
/// # Examples
2398+
/// ```
2399+
/// use floem::prelude::*;
2400+
/// text("test").style(|s| s.custom(|s: LabelCustomStyle| s.selectable(false)));
2401+
/// ```
2402+
///
2403+
/// See also: [`apply_custom`](Self::apply_custom), [`custom_style_class`](Self::custom_style_class)
2404+
pub fn custom<CS: CustomStyle>(self, custom: impl FnOnce(CS) -> CS) -> Self {
2405+
self.apply(custom(CS::default()).into())
2406+
}
2407+
2408+
/// Applies a `CustomStyle` type into this style.
2409+
///
2410+
/// # Examples
2411+
/// ```
2412+
/// use floem::prelude::*;
2413+
/// text("test").style(|s| s.apply_custom(LabelCustomStyle::new().selectable(false)));
2414+
/// ```
2415+
///
2416+
/// See also: [`custom`](Self::custom), [`custom_style_class`](Self::custom_style_class)
23652417
pub fn apply_custom<CS: Into<Style>>(self, custom_style: CS) -> Self {
23662418
self.apply(custom_style.into())
23672419
}
@@ -2455,12 +2507,22 @@ impl Style {
24552507
grid_column: style.grid_column(),
24562508
grid_auto_rows: style.grid_auto_rows(),
24572509
grid_auto_columns: style.grid_auto_columns(),
2510+
grid_auto_flow: style.grid_auto_flow(),
24582511
..Default::default()
24592512
}
24602513
}
24612514
}
24622515

24632516
pub trait CustomStyle: Default + Clone + Into<Style> + From<Style> {
2517+
type StyleClass: StyleClass;
2518+
2519+
/// Get access to a normal style
2520+
fn style(self, style: impl FnOnce(Style) -> Style) -> Self {
2521+
let self_style = self.into();
2522+
let new = style(self_style);
2523+
new.into()
2524+
}
2525+
24642526
fn hover(self, style: impl FnOnce(Self) -> Self) -> Self {
24652527
let self_style: Style = self.into();
24662528
let new = self_style.selector(StyleSelector::Hover, |_| style(Self::default()).into());
@@ -2510,7 +2572,6 @@ pub trait CustomStyle: Default + Clone + Into<Style> + From<Style> {
25102572
self_style.into()
25112573
}
25122574
}
2513-
impl<T> CustomStyle for T where T: Default + Clone + Into<Style> + From<Style> {}
25142575

25152576
pub trait CustomStylable<S: CustomStyle + 'static>: IntoView<V = Self::DV> + Sized {
25162577
type DV: View;

src/theme.rs

+19-29
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
11
use crate::{
2-
style::{Background, CursorStyle, Foreground, Style, Transition},
2+
style::{Background, CursorStyle, CustomStyle, Foreground, Style, Transition},
33
unit::{DurationUnitExt, UnitExt},
44
views::{
5-
dropdown, scroll,
6-
slider::{self, SliderClass},
7-
ButtonClass, CheckboxClass, LabelClass, LabelCustomStyle, LabeledCheckboxClass,
8-
LabeledRadioButtonClass, ListClass, ListItemClass, PlaceholderTextClass, RadioButtonClass,
9-
RadioButtonDotClass, TextInputClass, ToggleButtonCircleRad, ToggleButtonClass,
10-
ToggleButtonInset, TooltipClass,
5+
dropdown, scroll, slider, ButtonClass, CheckboxClass, LabelCustomStyle,
6+
LabeledCheckboxClass, LabeledRadioButtonClass, ListClass, ListItemClass,
7+
PlaceholderTextClass, RadioButtonClass, RadioButtonDotClass, TextInputClass,
8+
ToggleButtonCircleRad, ToggleButtonClass, ToggleButtonInset, TooltipClass,
119
},
1210
};
1311
use peniko::color::palette;
@@ -57,10 +55,7 @@ pub(crate) fn default_theme() -> Theme {
5755
.apply(focus_style.clone());
5856

5957
let button_style = Style::new()
60-
.apply(LabelCustomStyle::new().selectable(false).style())
61-
.class(LabelClass, |s| {
62-
s.apply(LabelCustomStyle::new().selectable(false).style())
63-
})
58+
.custom_style_class(|s: LabelCustomStyle| s.selectable(false))
6459
.background(Color::from_rgb8(240, 240, 240))
6560
.disabled(|s| {
6661
s.background(Color::from_rgb8(180, 188, 175).with_alpha(0.3))
@@ -228,13 +223,12 @@ pub(crate) fn default_theme() -> Theme {
228223
.class(LabeledRadioButtonClass, |_| labeled_radio_button_style)
229224
.class(TextInputClass, |_| input_style)
230225
.class(ButtonClass, |_| button_style)
231-
.apply_custom(
232-
scroll::ScrollCustomStyle::new()
233-
.handle_border_radius(4.0)
226+
.custom_style_class(|s: scroll::ScrollCustomStyle| {
227+
s.handle_border_radius(4.0)
234228
.handle_background(Color::from_rgba8(166, 166, 166, 140))
235229
.handle_thickness(16.0)
236-
.handle_rounded(false),
237-
)
230+
.handle_rounded(false)
231+
})
238232
.class(scroll::Handle, |s| {
239233
s.active(|s| s.background(Color::from_rgb8(166, 166, 166)))
240234
.hover(|s| s.background(Color::from_rgb8(184, 184, 184)))
@@ -243,19 +237,15 @@ pub(crate) fn default_theme() -> Theme {
243237
s.hover(|s| s.background(Color::from_rgba8(166, 166, 166, 30)))
244238
})
245239
.class(ToggleButtonClass, |_| toggle_button_style)
246-
.class(SliderClass, |s| {
247-
s.apply_custom(
248-
slider::SliderCustomStyle::new()
249-
.bar_color(palette::css::BLACK)
250-
.bar_radius(100.pct())
251-
.accent_bar_color(palette::css::GREEN)
252-
.accent_bar_radius(100.pct())
253-
.handle_color(Brush::Solid(palette::css::DARK_GRAY))
254-
.handle_radius(100.pct())
255-
.edge_align(true),
256-
)
257-
.height(15)
258-
.width(100)
240+
.custom_style_class(|s: slider::SliderCustomStyle| {
241+
s.bar_color(palette::css::BLACK)
242+
.bar_radius(100.pct())
243+
.accent_bar_color(palette::css::GREEN)
244+
.accent_bar_radius(100.pct())
245+
.handle_color(Brush::Solid(palette::css::DARK_GRAY))
246+
.handle_radius(100.pct())
247+
.edge_align(true)
248+
.style(|s| s.size(100, 15))
259249
})
260250
.class(PlaceholderTextClass, |s| {
261251
s.color(Color::from_rgba8(158, 158, 158, 30))

src/view_state.rs

+13-5
Original file line numberDiff line numberDiff line change
@@ -249,16 +249,24 @@ impl ViewState {
249249
context: &Style,
250250
) -> bool {
251251
let mut new_frame = false;
252-
let mut computed_style = Style::new();
252+
// we are just using the combined style and then clearing here to avoid creating an entirely new style map
253+
// because the clone is cheap, this is fine
254+
let mut computed_style = self.combined_style.clone();
255+
computed_style.clear();
256+
// we will apply the views style to the context so that if a style class is used on a view, that class will be directly applied instead of only applying to children
257+
let mut context = context.clone();
253258
if let Some(view_style) = view_style {
259+
context.apply_mut(view_style.clone());
254260
computed_style.apply_mut(view_style);
255261
}
262+
// self.style has precedence over the supplied view style so it comes after
263+
let self_style = self.style();
264+
context.apply_mut(self_style.clone());
265+
computed_style.apply_mut(self_style);
256266
if let Some(view_class) = view_class {
257-
computed_style = computed_style.apply_classes_from_context(&[view_class], context);
267+
computed_style = computed_style.apply_classes_from_context(&[view_class], &context);
258268
}
259-
computed_style = computed_style
260-
.apply_classes_from_context(&self.classes, context)
261-
.apply(self.style());
269+
computed_style = computed_style.apply_classes_from_context(&self.classes, &context);
262270

263271
self.has_style_selectors = computed_style.selectors();
264272

src/views/canvas.rs

+1-9
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,8 @@
1-
#![deny(missing_docs)]
2-
#![cfg_attr(not(feature = "vello"), allow(unused_imports))]
31
use floem_reactive::{create_tracker, SignalTracker};
4-
use floem_renderer::Renderer;
5-
use peniko::{
6-
kurbo::{Affine, Rect, Size},
7-
Mix,
8-
};
2+
use peniko::kurbo::Size;
93

104
use crate::{context::PaintCx, id::ViewId, view::View};
115

12-
use super::{text, Decorators};
13-
146
/// A canvas view
157
#[allow(clippy::type_complexity)]
168
pub struct Canvas {

src/views/clip.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ pub struct Clip {
1818
pub fn clip<V: IntoView + 'static>(child: V) -> Clip {
1919
let child = child.into_view();
2020
let id = ViewId::new();
21-
id.set_children(vec![child]);
21+
id.set_children([child]);
2222
Clip { id }
2323
}
2424

src/views/container.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ pub struct Container {
1616
/// set of styles completely separate from the child View that is being wrapped.
1717
pub fn container<V: IntoView + 'static>(child: V) -> Container {
1818
let id = ViewId::new();
19-
id.set_children(vec![child.into_view()]);
19+
id.set_children([child.into_view()]);
2020

2121
Container { id }
2222
}

src/views/drag_resize_window_area.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ pub fn drag_resize_window_area<V: IntoView + 'static>(
3131
child: V,
3232
) -> DragResizeWindowArea {
3333
let id = ViewId::new();
34-
id.set_children(vec![child.into_view()]);
34+
id.set_children([child.into_view()]);
3535
DragResizeWindowArea { id }
3636
.on_event_stop(EventListener::PointerDown, move |_| {
3737
drag_resize_window(direction)

src/views/drag_window_area.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ pub struct DragWindowArea {
1717
/// This can be useful when the window has the title bar turned off and you want to be able to still drag the window.
1818
pub fn drag_window_area<V: IntoView + 'static>(child: V) -> DragWindowArea {
1919
let id = ViewId::new();
20-
id.set_children(vec![child]);
20+
id.set_children([child]);
2121
DragWindowArea { id }
2222
.on_event_stop(EventListener::PointerDown, |e| {
2323
if let Event::PointerDown(input_event) = e {

src/views/dropdown.rs

+13-8
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use crate::{
2020
event::{Event, EventListener, EventPropagation},
2121
id::ViewId,
2222
prop, prop_extractor,
23-
style::{CustomStylable, Style, StyleClass, Width},
23+
style::{CustomStylable, CustomStyle, Style, StyleClass, Width},
2424
style_class,
2525
unit::PxPctAuto,
2626
view::{default_compute_layout, IntoView, View},
@@ -202,9 +202,9 @@ impl<T: 'static + Clone> View for Dropdown<T> {
202202
cx.app_state_mut().request_paint(self.id);
203203
}
204204
self.list_style = cx
205-
.indirect_style()
206-
.clone()
207-
.apply_classes_from_context(&[scroll::ScrollClass::class_ref()], cx.indirect_style());
205+
.style()
206+
.get_nested_map(scroll::ScrollClass::key())
207+
.unwrap_or_default();
208208

209209
for child in self.id.children() {
210210
cx.style_view(child);
@@ -239,7 +239,7 @@ impl<T: 'static + Clone> View for Dropdown<T> {
239239
let old_main_view = self.main_view;
240240
let (main_view, main_view_scope) = (self.main_fn)(*val);
241241
let main_view_id = main_view.id();
242-
self.id.set_children(vec![main_view]);
242+
self.id.set_children([main_view]);
243243
self.main_view = main_view_id;
244244
self.main_view_scope = main_view_scope;
245245

@@ -374,7 +374,8 @@ impl<T: Clone> Dropdown<T> {
374374
.keyboard_navigable()
375375
.on_event_stop(EventListener::FocusLost, move |_| {
376376
dropdown_id.update_state(Message::ListFocusLost);
377-
});
377+
})
378+
.on_event_stop(EventListener::PointerMove, |_| {});
378379
let inner_list_id = inner_list.id();
379380
scroll(inner_list)
380381
.on_event_stop(EventListener::FocusGained, move |_| {
@@ -392,7 +393,7 @@ impl<T: Clone> Dropdown<T> {
392393
let (child, main_view_scope) = main_fn(initial.clone());
393394
let main_view = child.id();
394395

395-
dropdown_id.set_children(vec![child]);
396+
dropdown_id.set_children([child]);
396397

397398
Self {
398399
id: dropdown_id,
@@ -486,7 +487,7 @@ impl<T: Clone> Dropdown<T> {
486487
let main_view = child.id();
487488
self.main_view_scope = main_view_scope;
488489
self.main_view = main_view;
489-
self.id.set_children(vec![child]);
490+
self.id.set_children([child]);
490491
self
491492
}
492493

@@ -603,6 +604,10 @@ impl From<Style> for DropdownCustomStyle {
603604
Self(val)
604605
}
605606
}
607+
impl CustomStyle for DropdownCustomStyle {
608+
type StyleClass = DropdownClass;
609+
}
610+
606611
impl<T: Clone> CustomStylable<DropdownCustomStyle> for Dropdown<T> {
607612
type DV = Self;
608613
}

0 commit comments

Comments
 (0)