Skip to content

Commit

Permalink
Improve Virtual Stack and List
Browse files Browse the repository at this point in the history
  • Loading branch information
jrmoulton committed Feb 7, 2025
1 parent a49bc20 commit 99f93e7
Show file tree
Hide file tree
Showing 18 changed files with 882 additions and 621 deletions.
56 changes: 22 additions & 34 deletions examples/layout/src/draggable_sidebar.rs
Original file line number Diff line number Diff line change
@@ -1,46 +1,34 @@
use floem::{
event::{Event, EventListener, EventPropagation},
peniko::color::palette,
peniko::Color,
reactive::{create_rw_signal, create_signal, SignalGet, SignalUpdate},
style::{CursorStyle, Position},
views::{
container, h_stack, label, scroll, virtual_stack, Decorators, VirtualDirection,
VirtualItemSize,
},
IntoView, View,
prelude::*,
style::CursorStyle,
taffy::Position,
};

const SIDEBAR_WIDTH: f64 = 100.0;

pub fn draggable_sidebar_view() -> impl IntoView {
let long_list: im::Vector<i32> = (0..100).collect();
let (long_list, _set_long_list) = create_signal(long_list);
let sidebar_width = create_rw_signal(SIDEBAR_WIDTH);
let is_sidebar_dragging = create_rw_signal(false);

let side_bar = scroll({
virtual_stack(
VirtualDirection::Vertical,
VirtualItemSize::Fixed(Box::new(|| 22.0)),
move || long_list.get(),
move |item| *item,
move |item| {
label(move || format!("Item {} with long lines", item)).style(move |s| {
s.text_ellipsis()
.height(22)
.padding(10.0)
.padding_top(3.0)
.padding_bottom(3.0)
.width(sidebar_width.get())
.items_start()
.border_bottom(1.0)
.border_color(Color::from_rgb8(205, 205, 205))
})
},
)
.style(move |s| s.flex_col().width(sidebar_width.get() - 1.0))
})
let side_bar = VirtualStack::with_view(
|| 0..100,
move |item| {
label(move || format!("Item {} with long lines", item)).style(move |s| {
s.text_ellipsis()
.height(22)
.padding(10.0)
.padding_top(3.0)
.padding_bottom(3.0)
.width(sidebar_width.get())
.items_start()
.border_bottom(1.0)
.border_color(Color::from_rgb8(205, 205, 205))
})
},
)
.style(move |s| s.flex_col().width(sidebar_width.get() - 1.0))
.scroll()
.style(move |s| {
s.width(sidebar_width.get())
.border_right(1.0)
Expand All @@ -64,7 +52,7 @@ pub fn draggable_sidebar_view() -> impl IntoView {
.border_color(Color::from_rgb8(205, 205, 205))
});

let dragger = label(|| "")
let dragger = ""
.style(move |s| {
s.position(Position::Absolute)
.inset_top(0)
Expand Down
147 changes: 65 additions & 82 deletions examples/layout/src/holy_grail.rs
Original file line number Diff line number Diff line change
@@ -1,109 +1,92 @@
use floem::{
event::EventListener,
peniko::Color,
reactive::{create_rw_signal, SignalGet},
style::Position,
views::{
container, h_stack, label, scroll, v_stack, virtual_stack, Decorators, VirtualDirection,
VirtualItemSize,
},
IntoView, View,
};
use floem::{event::EventListener, prelude::*, taffy::Position};

const SIDEBAR_WIDTH: f64 = 140.0;
const TOPBAR_HEIGHT: f64 = 30.0;
const SIDEBAR_ITEM_HEIGHT: f64 = 21.0;

pub fn holy_grail_view() -> impl IntoView {
let long_list: im::Vector<i32> = (0..100).collect();
let long_list = create_rw_signal(long_list);

let top_bar = label(|| String::from("Top bar"))
.style(|s| s.padding(10.0).width_full().height(TOPBAR_HEIGHT));

let side_bar_right = scroll({
virtual_stack(
VirtualDirection::Vertical,
VirtualItemSize::Fixed(Box::new(|| SIDEBAR_ITEM_HEIGHT)),
move || long_list.get(),
move |item| *item,
move |item| {
label(move || item.to_string()).style(move |s| {
s.padding(10.0)
.padding_top(3.0)
.padding_bottom(3.0)
.width(SIDEBAR_WIDTH)
.height(SIDEBAR_ITEM_HEIGHT)
.items_start()
.border_bottom(1.0)
.border_color(Color::from_rgb8(205, 205, 205))
})
},
)
.style(|s| s.flex_col().width(SIDEBAR_WIDTH - 1.0))
})
let side_bar_right = VirtualStack::with_view(
|| 0..100,
|item| {
label(move || item.to_string()).style(move |s| {
s.padding(10.0)
.padding_top(3.0)
.padding_bottom(3.0)
.width(SIDEBAR_WIDTH)
.height(SIDEBAR_ITEM_HEIGHT)
.items_start()
.border_bottom(1.0)
.border_color(Color::from_rgb8(205, 205, 205))
})
},
)
.style(|s| s.flex_col().width(SIDEBAR_WIDTH - 1.0))
.scroll()
.style(|s| {
s.width(SIDEBAR_WIDTH)
.border_left(1.0)
.border_top(1.0)
.border_color(Color::from_rgb8(205, 205, 205))
});

let side_bar_left = scroll({
virtual_stack(
VirtualDirection::Vertical,
VirtualItemSize::Fixed(Box::new(|| SIDEBAR_ITEM_HEIGHT)),
move || long_list.get(),
move |item| *item,
move |item| {
label(move || item.to_string()).style(move |s| {
s.padding(10.0)
.padding_top(3.0)
.padding_bottom(3.0)
.width(SIDEBAR_WIDTH)
.height(SIDEBAR_ITEM_HEIGHT)
.items_start()
.border_bottom(1.0)
.border_color(Color::from_rgb8(205, 205, 205))
})
},
)
.style(|s| s.flex_col().width(SIDEBAR_WIDTH - 1.0))
})
let side_bar_left = VirtualStack::with_view(
|| 0..100,
move |item| {
label(move || item.to_string()).style(move |s| {
s.padding(10.0)
.padding_top(3.0)
.padding_bottom(3.0)
.width(SIDEBAR_WIDTH)
.height(SIDEBAR_ITEM_HEIGHT)
.items_start()
.border_bottom(1.0)
.border_color(Color::from_rgb8(205, 205, 205))
})
},
)
.style(|s| s.flex_col().width(SIDEBAR_WIDTH - 1.0))
.scroll()
.style(|s| {
s.width(SIDEBAR_WIDTH)
.border_right(1.0)
.border_top(1.0)
.border_color(Color::from_rgb8(205, 205, 205))
});

let main_window = scroll(
container(label(move || String::from("Hello world")).style(|s| s.padding(10.0)))
.style(|s| s.flex_col().items_start().padding_bottom(10.0)),
)
.style(|s| s.flex_col().flex_basis(0).min_width(0).flex_grow(1.0))
.style(|s| {
s.border_top(1.0)
.border_color(Color::from_rgb8(205, 205, 205))
.width_full()
.min_width(150.0)
});

let content = h_stack((side_bar_left, main_window, side_bar_right)).style(|s| {
s.position(Position::Absolute)
.inset_top(TOPBAR_HEIGHT)
.inset_bottom(0.0)
.width_full()
});
let main_window = "Hello world"
.style(|s| s.padding(10.0))
.container()
.style(|s| s.flex_col().items_start().padding_bottom(10.0))
.scroll()
.style(|s| s.flex_col().flex_basis(0).min_width(0).flex_grow(1.0))
.style(|s| {
s.border_top(1.0)
.border_color(Color::from_rgb8(205, 205, 205))
.width_full()
.min_width(150.0)
});

let view = v_stack((top_bar, content)).style(|s| s.width_full().height_full());
let content = (side_bar_left, main_window, side_bar_right)
.h_stack()
.style(|s| {
s.position(Position::Absolute)
.inset_top(TOPBAR_HEIGHT)
.inset_bottom(0.0)
.width_full()
});

let id = view.id();
view.on_event_stop(EventListener::KeyUp, move |e| {
if let floem::event::Event::KeyUp(e) = e {
if e.key.logical_key == floem::keyboard::Key::Named(floem::keyboard::NamedKey::F11) {
id.inspect();
(top_bar, content)
.v_stack()
.style(|s| s.width_full().height_full())
.on_event_stop(EventListener::KeyUp, move |e| {
if let floem::event::Event::KeyUp(e) = e {
if e.key.logical_key == floem::keyboard::Key::Named(floem::keyboard::NamedKey::F11)
{
floem::action::inspect();
}
}
}
})
})
}
7 changes: 1 addition & 6 deletions examples/layout/src/left_sidebar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,7 @@ use floem::{
peniko::Color,
reactive::{create_rw_signal, SignalGet},
style::Position,
views::{
container, h_stack, label, scroll, v_stack, virtual_stack, Decorators, VirtualDirection,
VirtualItemSize,
},
views::{container, h_stack, label, scroll, v_stack, virtual_stack, Decorators},
IntoView, View,
};

Expand All @@ -23,8 +20,6 @@ pub fn left_sidebar_view() -> impl IntoView {

let side_bar = scroll({
virtual_stack(
VirtualDirection::Vertical,
VirtualItemSize::Fixed(Box::new(|| SIDEBAR_ITEM_HEIGHT)),
move || long_list.get(),
move |item| *item,
move |item| {
Expand Down
7 changes: 1 addition & 6 deletions examples/layout/src/right_sidebar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,7 @@ use floem::{
peniko::Color,
reactive::{create_rw_signal, SignalGet},
style::Position,
views::{
container, h_stack, label, scroll, v_stack, virtual_stack, Decorators, VirtualDirection,
VirtualItemSize,
},
views::{container, h_stack, label, scroll, v_stack, virtual_stack, Decorators},
IntoView, View,
};

Expand All @@ -23,8 +20,6 @@ pub fn right_sidebar_view() -> impl IntoView {

let side_bar = scroll({
virtual_stack(
VirtualDirection::Vertical,
VirtualItemSize::Fixed(Box::new(|| SIDEBAR_ITEM_HEIGHT)),
move || long_list.get(),
move |item| *item,
move |item| {
Expand Down
34 changes: 15 additions & 19 deletions examples/stacks/src/dyn_stack.rs
Original file line number Diff line number Diff line change
@@ -1,29 +1,25 @@
use floem::{
reactive::{create_rw_signal, SignalGet, SignalUpdate},
views::{dyn_stack, scroll, ButtonClass, Decorators},
IntoView,
};
use floem::prelude::*;

pub fn dyn_stack_view() -> impl IntoView {
// With the dyn_stack you can change the stack at runtime by controlling
// your stack with a signal.

let long_list: im::Vector<i32> = (0..10).collect();
let long_list = create_rw_signal(long_list);
let long_list = RwSignal::new(long_list);

(
"Add an item".class(ButtonClass).on_click_stop(move |_| {
long_list.update(|list| list.push_front(list.len() as i32 + 1))
}),
scroll(
dyn_stack(
move || long_list.get(),
move |item| *item,
move |item| item.style(|s| s.height(20).justify_center()),
)
.style(|s| s.flex_col().width_full()),
)
.style(|s| s.width(100).height(200).border(1)),
let button = button("Add an item")
.action(move || long_list.update(|list| list.push_back(list.len() as i32 + 1)));

let stack = dyn_stack(
move || long_list.get(),
move |item| *item,
move |item| item.style(|s| s.height(20).justify_center()),
)
.style(|s| s.flex_col().width_full())
.scroll()
.style(|s| s.width(100).height(200).border(1));

(button, stack)
.h_stack()
.style(|s| s.flex_col().column_gap(5).margin_top(10))
}
34 changes: 12 additions & 22 deletions examples/stacks/src/virtual_stack.rs
Original file line number Diff line number Diff line change
@@ -1,31 +1,21 @@
use floem::{
reactive::{create_rw_signal, SignalGet, SignalUpdate},
views::{scroll, virtual_stack, ButtonClass, Decorators, VirtualDirection, VirtualItemSize},
IntoView,
};
use floem::prelude::*;

pub fn virtual_stack_view() -> impl IntoView {
// A virtual list is optimized to only render the views that are visible
// making it ideal for large lists with a lot of views.

let long_list: im::Vector<i32> = (0..1000000).collect();
let long_list = create_rw_signal(long_list);
let long_list = RwSignal::new(long_list);

(
"Add an item".class(ButtonClass).on_click_stop(move |_| {
long_list.update(|list| list.push_front(list.len() as i32 + 1))
}),
scroll(
virtual_stack(
VirtualDirection::Vertical,
VirtualItemSize::Fixed(Box::new(|| 20.0)),
move || long_list.get(),
move |item| *item,
move |item| item.style(|s| s.height(20).justify_center()),
)
.style(|s| s.flex_col().width_full()),
)
.style(|s| s.width(100).height(200).border(1)),
)
let button = button("Add an item")
.action(move || long_list.update(|list| list.push_back(list.len() as i32 + 1)));

let virtual_stack = VirtualStack::new(move || long_list.get())
.style(|s| s.flex_col().width_full())
.scroll()
.style(|s| s.width(100).height(200).border(1));

(button, virtual_stack)
.h_stack()
.style(|s| s.flex_col().column_gap(5).margin_top(10))
}
Loading

0 comments on commit 99f93e7

Please sign in to comment.