Skip to content

Commit 20d7862

Browse files
authored
Add resizable widget (#793)
1 parent deb7e51 commit 20d7862

File tree

10 files changed

+629
-61
lines changed

10 files changed

+629
-61
lines changed

examples/layout/src/draggable_sidebar.rs

+13-57
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,10 @@
11
use floem::{
2-
event::{Event, EventListener, EventPropagation},
2+
event::EventListener,
33
prelude::*,
4-
style::CursorStyle,
5-
taffy::Position,
4+
style::{CustomStylable, CustomStyle},
65
};
76

8-
const SIDEBAR_WIDTH: f64 = 100.0;
9-
107
pub fn draggable_sidebar_view() -> impl IntoView {
11-
let sidebar_width = create_rw_signal(SIDEBAR_WIDTH);
12-
let is_sidebar_dragging = create_rw_signal(false);
13-
148
let side_bar = VirtualStack::with_view(
159
|| 0..100,
1610
move |item| {
@@ -20,18 +14,17 @@ pub fn draggable_sidebar_view() -> impl IntoView {
2014
.padding(10.0)
2115
.padding_top(3.0)
2216
.padding_bottom(3.0)
23-
.width(sidebar_width.get())
17+
.width_full()
2418
.items_start()
2519
.border_bottom(1.0)
2620
.border_color(Color::from_rgb8(205, 205, 205))
2721
})
2822
},
2923
)
30-
.style(move |s| s.flex_col().width(sidebar_width.get() - 1.0))
24+
.style(move |s| s.flex_col().width_full())
3125
.scroll()
3226
.style(move |s| {
33-
s.width(sidebar_width.get())
34-
.border_right(1.0)
27+
s.border_right(1.0)
3528
.border_top(1.0)
3629
.border_color(Color::from_rgb8(205, 205, 205))
3730
});
@@ -52,52 +45,15 @@ pub fn draggable_sidebar_view() -> impl IntoView {
5245
.border_color(Color::from_rgb8(205, 205, 205))
5346
});
5447

55-
let dragger = ""
56-
.style(move |s| {
57-
s.position(Position::Absolute)
58-
.inset_top(0)
59-
.inset_bottom(0)
60-
.inset_left(sidebar_width.get())
61-
.width(10)
62-
.border_left(1)
63-
.border_color(Color::from_rgb8(205, 205, 205))
64-
.hover(|s| {
65-
s.border_left(2)
66-
.border_color(Color::from_rgb8(41, 98, 218))
67-
.cursor(CursorStyle::ColResize)
68-
})
69-
.apply_if(is_sidebar_dragging.get(), |s| {
70-
s.border_left(2).border_color(Color::from_rgb8(41, 98, 218))
71-
})
72-
})
73-
.draggable()
74-
.dragging_style(|s| s.border_color(palette::css::TRANSPARENT))
75-
.on_event(EventListener::DragStart, move |_| {
76-
is_sidebar_dragging.set(true);
77-
EventPropagation::Continue
78-
})
79-
.on_event(EventListener::DragEnd, move |_| {
80-
is_sidebar_dragging.set(false);
81-
EventPropagation::Continue
82-
})
83-
.on_event(EventListener::DoubleClick, move |_| {
84-
sidebar_width.set(SIDEBAR_WIDTH);
85-
EventPropagation::Continue
86-
});
87-
88-
let view = h_stack((side_bar, main_window, dragger))
89-
.on_event(EventListener::PointerMove, move |event| {
90-
let pos = match event {
91-
Event::PointerMove(p) => p.pos,
92-
_ => (0.0, 0.0).into(),
93-
};
48+
let dragger_color = Color::from_rgb8(205, 205, 205);
49+
let active_dragger_color = Color::from_rgb8(41, 98, 218);
9450

95-
if is_sidebar_dragging.get() {
96-
sidebar_width.set(pos.x);
97-
}
98-
EventPropagation::Continue
99-
})
100-
.style(|s| s.width_full().height_full());
51+
let view = resizable::resizable((side_bar, main_window))
52+
.style(|s| s.width_full().height_full())
53+
.custom_style(move |s| {
54+
s.handle_color(dragger_color)
55+
.active(|s| s.handle_color(active_dragger_color))
56+
});
10157

10258
let id = view.id();
10359
view.on_event_stop(EventListener::KeyUp, move |e| {

src/context.rs

+9
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,15 @@ impl EventCx<'_> {
154154
}
155155
}
156156
}
157+
if let Event::PointerMove(_event) = &event {
158+
let view_state = view_state.borrow();
159+
let style = view_state.combined_style.builtin();
160+
if let Some(cursor) = style.cursor() {
161+
if self.app_state.cursor.is_none() {
162+
self.app_state.cursor = Some(cursor);
163+
}
164+
}
165+
}
157166
return (EventPropagation::Stop, PointerEventConsumed::Yes);
158167
}
159168

src/id.rs

+5
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,11 @@ impl ViewId {
166166
VIEW_STORAGE.with_borrow(|s| s.children.get(*self).cloned().unwrap_or_default())
167167
}
168168

169+
/// Get access to the list of `ViewId`s that are associated with the children views of this `ViewId`
170+
pub fn with_children<R>(&self, children: impl Fn(&[ViewId]) -> R) -> R {
171+
VIEW_STORAGE.with_borrow(|s| children(s.children.get(*self).map_or(&[], |v| v)))
172+
}
173+
169174
/// Get the `ViewId` that has been set as this `ViewId`'s parent
170175
pub fn parent(&self) -> Option<ViewId> {
171176
VIEW_STORAGE.with_borrow(|s| s.parent.get(*self).cloned().flatten())

src/style.rs

+55-4
Original file line numberDiff line numberDiff line change
@@ -1472,8 +1472,9 @@ pub enum TextOverflow {
14721472
Ellipsis,
14731473
}
14741474

1475-
#[derive(Debug, Clone, Copy, PartialEq)]
1475+
#[derive(Debug, Clone, Copy, PartialEq, Default)]
14761476
pub enum CursorStyle {
1477+
#[default]
14771478
Default,
14781479
Pointer,
14791480
Text,
@@ -2459,9 +2460,59 @@ impl Style {
24592460
}
24602461
}
24612462

2462-
pub trait CustomStylable<S: Default + Into<Style> + 'static>:
2463-
IntoView<V = Self::DV> + Sized
2464-
{
2463+
pub trait CustomStyle: Default + Clone + Into<Style> + From<Style> {
2464+
fn hover(self, style: impl FnOnce(Self) -> Self) -> Self {
2465+
let self_style: Style = self.into();
2466+
let new = self_style.selector(StyleSelector::Hover, |_| style(Self::default()).into());
2467+
new.into()
2468+
}
2469+
2470+
fn focus(self, style: impl FnOnce(Self) -> Self) -> Self {
2471+
let self_style: Style = self.into();
2472+
let new = self_style.selector(StyleSelector::Focus, |_| style(Self::default()).into());
2473+
new.into()
2474+
}
2475+
2476+
/// Similar to the `:focus-visible` css selector, this style only activates when tab navigation is used.
2477+
fn focus_visible(self, style: impl FnOnce(Self) -> Self) -> Self {
2478+
let self_style: Style = self.into();
2479+
let new = self_style.selector(StyleSelector::FocusVisible, |_| {
2480+
style(Self::default()).into()
2481+
});
2482+
new.into()
2483+
}
2484+
2485+
fn selected(self, style: impl FnOnce(Self) -> Self) -> Self {
2486+
let self_style: Style = self.into();
2487+
let new = self_style.selector(StyleSelector::Selected, |_| style(Self::default()).into());
2488+
new.into()
2489+
}
2490+
2491+
fn disabled(self, style: impl FnOnce(Self) -> Self) -> Self {
2492+
let self_style: Style = self.into();
2493+
let new = self_style.selector(StyleSelector::Disabled, |_| style(Self::default()).into());
2494+
new.into()
2495+
}
2496+
2497+
fn active(self, style: impl FnOnce(Self) -> Self) -> Self {
2498+
let self_style: Style = self.into();
2499+
let new = self_style.selector(StyleSelector::Active, |_| style(Self::default()).into());
2500+
new.into()
2501+
}
2502+
2503+
fn responsive(self, size: ScreenSize, style: impl FnOnce(Self) -> Self) -> Self {
2504+
let over = style(Self::default());
2505+
let over_style: Style = over.into();
2506+
let mut self_style: Style = self.into();
2507+
for breakpoint in size.breakpoints() {
2508+
self_style.set_breakpoint(breakpoint, over_style.clone());
2509+
}
2510+
self_style.into()
2511+
}
2512+
}
2513+
impl<T> CustomStyle for T where T: Default + Clone + Into<Style> + From<Style> {}
2514+
2515+
pub trait CustomStylable<S: CustomStyle + 'static>: IntoView<V = Self::DV> + Sized {
24652516
type DV: View;
24662517

24672518
/// # Add a custom style to the view with access to this view's specialized custom style.

src/views/dropdown.rs

+5
Original file line numberDiff line numberDiff line change
@@ -598,6 +598,11 @@ impl From<DropdownCustomStyle> for Style {
598598
val.0
599599
}
600600
}
601+
impl From<Style> for DropdownCustomStyle {
602+
fn from(val: Style) -> Self {
603+
Self(val)
604+
}
605+
}
601606
impl<T: Clone> CustomStylable<DropdownCustomStyle> for Dropdown<T> {
602607
type DV = Self;
603608
}

src/views/label.rs

+5
Original file line numberDiff line numberDiff line change
@@ -524,6 +524,11 @@ impl From<LabelCustomStyle> for Style {
524524
value.0
525525
}
526526
}
527+
impl From<Style> for LabelCustomStyle {
528+
fn from(value: Style) -> Self {
529+
Self(value)
530+
}
531+
}
527532

528533
impl CustomStylable<LabelCustomStyle> for Label {
529534
type DV = Self;

src/views/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,8 @@ pub mod dropdown;
151151

152152
pub mod slider;
153153

154+
pub mod resizable;
155+
154156
mod radio_button;
155157
pub use radio_button::*;
156158

0 commit comments

Comments
 (0)