Skip to content

Commit

Permalink
Added splitter widget
Browse files Browse the repository at this point in the history
  • Loading branch information
dparnell committed Dec 17, 2023
1 parent 6c3341d commit efe3b6f
Show file tree
Hide file tree
Showing 5 changed files with 217 additions and 6 deletions.
9 changes: 5 additions & 4 deletions examples/widget-gallery/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,11 @@ use floem::{
unit::UnitExt,
view::View,
views::{
container, container_box, h_stack, label, scroll, stack, tab, v_stack, virtual_list,
container, container_box, label, scroll, stack, tab, v_stack, virtual_list,
Decorators, VirtualListDirection, VirtualListItemSize,
},
widgets::button,
widgets::h_splitter,
EventPropagation,
};

Expand Down Expand Up @@ -116,7 +117,7 @@ fn app_view() -> impl View {
})
},
)
.style(|s| s.flex_col().width(140.0))
.style(|s| s.flex_col().width_full())
})
.style(|s| {
s.flex_col()
Expand All @@ -140,7 +141,7 @@ fn app_view() -> impl View {
})
.style(|s| s);

let left = v_stack((list, inspector)).style(|s| s.height_full().gap(0.0, 5.0));
let left = v_stack((list, inspector)).style(|s| s.height_full().width_full().gap(0.0, 5.0));

let tab = tab(
move || active_tab.get(),
Expand All @@ -163,7 +164,7 @@ fn app_view() -> impl View {

let tab = scroll(tab).style(|s| s.flex_basis(0).min_width(0).flex_grow(1.0));

let view = h_stack((left, tab))
let view = h_splitter(150.px(), left, tab)
.style(|s| s.padding(5.0).width_full().height_full().gap(5.0, 0.0))
.window_title(|| "Widget Gallery".to_owned());

Expand Down
2 changes: 1 addition & 1 deletion src/style.rs
Original file line number Diff line number Diff line change
Expand Up @@ -221,8 +221,8 @@ pub trait StyleProp: Default + Copy + 'static {
fn prop_ref() -> StylePropRef;
fn default_value() -> Self::Type;
}

#[derive(Debug)]

pub struct StylePropInfo {
pub(crate) name: fn() -> &'static str,
pub(crate) inherited: bool,
Expand Down
2 changes: 1 addition & 1 deletion src/views/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,4 @@ mod drag_resize_window_area;
pub use drag_resize_window_area::*;

mod img;
pub use img::*;
pub use img::*;
14 changes: 14 additions & 0 deletions src/widgets/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ pub use button::*;
mod text_input;
pub use text_input::*;

mod splitter;
pub use splitter::*;

pub(crate) struct Theme {
pub(crate) background: Color,
pub(crate) style: Rc<Style>,
Expand Down Expand Up @@ -191,6 +194,17 @@ pub(crate) fn default_theme() -> Theme {
.box_shadow_v_offset(2.0)
.box_shadow_color(Color::BLACK.with_alpha_factor(0.2))
})
.class(SplitterClass, |s| {
s.background(Color::RED)
})
.class(SplitterVerticalHandleClass, |s| {
s.cursor(CursorStyle::RowResize)
.height(4.0.px())
})
.class(SplitterHorizontalHandleClass, |s| {
s.cursor(CursorStyle::ColResize)
.width(4.0.px())
})
.font_size(FONT_SIZE)
.color(Color::BLACK);

Expand Down
196 changes: 196 additions & 0 deletions src/widgets/splitter.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
use kurbo::{Point, Rect, Size, Vec2};
use taffy::style::FlexDirection;
use floem_reactive::{create_rw_signal, RwSignal};
use crate::{EventPropagation, style_class, view::View};
use crate::context::{EventCx, UpdateCx};
use crate::event::Event;
use crate::id::Id;
use crate::pointer::PointerButton;
use crate::style::{Style};
use crate::unit::{PxPctAuto};
use crate::view::ViewData;
use crate::views::{clip, Decorators, empty};

style_class!(pub SplitterClass);
style_class!(pub SplitterSectionClass);
style_class!(pub SplitterHorizontalHandleClass);
style_class!(pub SplitterVerticalHandleClass);

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum SplitterDirection {
Vertical,
Horizontal
}

pub struct Splitter {
data: ViewData,
down_position: Option<Point>,
down_size: Option<Size>,
direction: SplitterDirection,
position: RwSignal<PxPctAuto>,
child_1: Box<dyn View>,
handle: Box<dyn View>,
child_2: Box<dyn View>,
}

pub fn splitter(direction: SplitterDirection, position: impl Into<PxPctAuto>, child_1: Box<dyn View>, child_2 : Box<dyn View>) -> impl View {
let position = create_rw_signal(position.into());

let handle = match direction {
SplitterDirection::Vertical => empty().class(SplitterVerticalHandleClass),
SplitterDirection::Horizontal => empty().class(SplitterHorizontalHandleClass)
};

Splitter {
data: ViewData::new(Id::next()),
down_position: None,
down_size: None,
direction,
position,
child_1: Box::new(clip(child_1).class(SplitterSectionClass).style(move |s| {
match direction {
SplitterDirection::Vertical => s.height(position.get()),
SplitterDirection::Horizontal => s.width(position.get())
}
})),
handle: Box::new(handle),
child_2: Box::new(clip(child_2).class(SplitterSectionClass)),
}
}

pub fn h_splitter(position: impl Into<PxPctAuto>, child_1: impl View + 'static, child_2: impl View + 'static) -> impl View {
splitter(SplitterDirection::Horizontal, position, Box::new(child_1), Box::new(child_2))
}

pub fn v_splitter(position: impl Into<PxPctAuto>, child_1: impl View + 'static, child_2: impl View + 'static) -> impl View {
splitter(SplitterDirection::Vertical, position, Box::new(child_1), Box::new(child_2))
}

impl View for Splitter {
fn view_data(&self) -> &ViewData {
&self.data
}

fn view_data_mut(&mut self) -> &mut ViewData {
&mut self.data
}

fn for_each_child<'a>(&'a self, for_each: &mut dyn FnMut(&'a dyn View) -> bool) {
if for_each(&self.child_1) {
} else {
if for_each(&self.handle) {

} else {
for_each(&self.child_2);
}
}
}

fn for_each_child_mut<'a>(&'a mut self, for_each: &mut dyn FnMut(&'a mut dyn View) -> bool) {
if for_each(&mut self.child_1) {
} else {
if for_each(&mut self.handle) {

} else {
for_each(&mut self.child_2);
}
}
}

fn for_each_child_rev_mut<'a>(
&'a mut self,
for_each: &mut dyn FnMut(&'a mut dyn View) -> bool,
) {
if for_each(&mut self.child_2) {
} else {
if for_each(&mut self.handle) {

} else {
for_each(&mut self.child_1);
}
}
}

fn view_style(&self) -> Option<crate::style::Style> {
Some(Style::new().flex_direction(match self.direction {
SplitterDirection::Vertical => FlexDirection::Column,
SplitterDirection::Horizontal => FlexDirection::Row
}))
}

fn debug_name(&self) -> std::borrow::Cow<'static, str> {
match self.direction {
SplitterDirection::Vertical => "Vertical Splitter".into(),
SplitterDirection::Horizontal => "Horizontal Splitter".into(),
}
}

fn update(&mut self, cx: &mut UpdateCx, state: Box<dyn std::any::Any>) {
if let Ok(state) = state.downcast() {
self.position.set(*state);
cx.request_all(self.id());
}
}

fn event(&mut self, cx: &mut EventCx, id_path: Option<&[Id]>, event: Event) -> EventPropagation {
match &event {
Event::PointerDown(evt) => {
if evt.button == PointerButton::Primary {
if let Some(handle_layout) = cx.get_layout(self.handle.id()) {
let rect = Rect::new(handle_layout.location.x as f64, handle_layout.location.y as f64, (handle_layout.location.x + handle_layout.size.width) as f64, (handle_layout.location.y + handle_layout.size.height) as f64);
if rect.contains(evt.pos) {
if let Some(child_1_layout) = cx.get_layout(self.child_1.id()) {
self.down_position = Some(evt.pos.clone());
self.down_size = Some(Size::new(child_1_layout.size.width as f64, child_1_layout.size.height as f64));

return EventPropagation::Stop;
}
}
}
}
},
Event::PointerUp(evt) => {
if evt.button == PointerButton::Primary && self.down_position.is_some() {
self.down_position = None;
return EventPropagation::Stop;
}
},
Event::PointerMove(evt) => {
if let (Some(down_pos), Some(down_size)) = (self.down_position, self.down_size) {
if let Some(size) = cx.get_size(self.id()) {
let delta = Vec2::new(evt.pos.x - down_pos.x, evt.pos.y - down_pos.y);

let new_split = if let PxPctAuto::Pct(_) = self.position.get() {
match self.direction {
SplitterDirection::Horizontal => PxPctAuto::Pct(100.0 * (down_size.width + delta.x) / size.width),
SplitterDirection::Vertical => PxPctAuto::Pct(100.0 * (down_size.height + delta.y) / size.height)
}
} else {
match self.direction {
SplitterDirection::Horizontal => PxPctAuto::Px(down_size.width + delta.x),
SplitterDirection::Vertical => PxPctAuto::Px(down_size.height + delta.y)
}
};

self.position.set(new_split);
cx.request_layout(self.id());

return EventPropagation::Stop;
}
}
},
_ => ()
}

if cx.view_event(&mut self.child_1, id_path, event.clone()).is_processed() {
return EventPropagation::Stop;
}
if cx.view_event(&mut self.handle, id_path, event.clone()).is_processed() {
return EventPropagation::Stop;
}
if cx.view_event(&mut self.child_2, id_path, event.clone()).is_processed() {
return EventPropagation::Stop;
}
EventPropagation::Continue
}
}

0 comments on commit efe3b6f

Please sign in to comment.