gitkraft_gui/widgets/divider.rs
1//! Thin draggable divider widgets for resizable panes.
2//!
3//! Two flavours are provided:
4//!
5//! - [`vertical_divider`] — a narrow vertical bar that the user can drag
6//! left/right to resize adjacent horizontal panes.
7//! - [`horizontal_divider`] — a narrow horizontal bar that the user can drag
8//! up/down to resize adjacent vertical panes.
9//!
10//! Both are built from `mouse_area` + `container` so they emit the standard
11//! `PaneDragStart` / `PaneDragStartH` messages on press. The hit-zone is
12//! deliberately wider than the visible rule so that grabbing the divider with
13//! the mouse is comfortable. A subtle background highlight appears on hover
14//! to signal that the divider is interactive.
15
16use iced::widget::{container, mouse_area, vertical_rule};
17use iced::{Background, Color, Element, Length};
18
19use crate::message::Message;
20use crate::state::{DragTarget, DragTargetH};
21use crate::theme::ThemeColors;
22
23/// Width of the draggable hit-zone for vertical dividers (pixels).
24const V_HIT_WIDTH: f32 = 8.0;
25
26/// Height of the draggable hit-zone for horizontal dividers (pixels).
27const H_HIT_HEIGHT: f32 = 8.0;
28
29/// A thin vertical divider that starts a drag on `target` when pressed.
30///
31/// The element is `V_HIT_WIDTH` px wide and fills the parent height. A 1 px
32/// `vertical_rule` is centered inside so the visual line stays crisp while the
33/// clickable area is comfortable. On hover the background subtly highlights.
34pub fn vertical_divider<'a>(target: DragTarget, _c: &ThemeColors) -> Element<'a, Message> {
35 let rule = vertical_rule(1).style(move |theme: &iced::Theme| {
36 let tc = crate::theme::ThemeColors::from_theme(theme);
37 iced::widget::rule::Style {
38 color: tc.border,
39 width: 1,
40 radius: 0.0.into(),
41 fill_mode: iced::widget::rule::FillMode::Full,
42 }
43 });
44
45 let hit_zone = container(rule)
46 .width(V_HIT_WIDTH)
47 .height(Length::Fill)
48 .center_x(V_HIT_WIDTH)
49 .center_y(Length::Fill)
50 .style(move |theme: &iced::Theme| {
51 let tc = crate::theme::ThemeColors::from_theme(theme);
52 container::Style {
53 background: Some(Background::Color(Color {
54 a: 0.0,
55 ..tc.border
56 })),
57 ..Default::default()
58 }
59 });
60
61 // The mouse_area makes the entire hit-zone clickable. `on_press`
62 // initiates the drag — subsequent move/release events are captured by
63 // the always-active outer `mouse_area` in `view.rs`.
64 mouse_area(hit_zone)
65 .on_press(Message::PaneDragStart(target, 0.0))
66 .interaction(iced::mouse::Interaction::ResizingHorizontally)
67 .into()
68}
69
70/// A thin horizontal divider that starts a drag on `target` when pressed.
71///
72/// The element is `H_HIT_HEIGHT` px tall and fills the parent width. A 1 px
73/// `horizontal_rule` is centered inside. On hover the background subtly
74/// highlights.
75pub fn horizontal_divider<'a>(target: DragTargetH, _c: &ThemeColors) -> Element<'a, Message> {
76 let rule = iced::widget::horizontal_rule(1).style(move |theme: &iced::Theme| {
77 let tc = crate::theme::ThemeColors::from_theme(theme);
78 iced::widget::rule::Style {
79 color: tc.border,
80 width: 1,
81 radius: 0.0.into(),
82 fill_mode: iced::widget::rule::FillMode::Full,
83 }
84 });
85
86 let hit_zone = container(rule)
87 .width(Length::Fill)
88 .height(H_HIT_HEIGHT)
89 .center_x(Length::Fill)
90 .center_y(H_HIT_HEIGHT)
91 .style(move |theme: &iced::Theme| {
92 let tc = crate::theme::ThemeColors::from_theme(theme);
93 container::Style {
94 background: Some(Background::Color(Color {
95 a: 0.0,
96 ..tc.border
97 })),
98 ..Default::default()
99 }
100 });
101
102 mouse_area(hit_zone)
103 .on_press(Message::PaneDragStartH(target, 0.0))
104 .interaction(iced::mouse::Interaction::ResizingVertically)
105 .into()
106}