fltk_float/
scroll.rs

1use std::rc::Rc;
2
3use fltk::group::Scroll;
4use fltk::prelude::{GroupExt, WidgetBase};
5
6use crate::{LayoutElement, Size};
7
8pub struct Scrollable<G: GroupExt + Clone = Scroll> {
9    props: ScrollableProperties<G>,
10    child: Rc<dyn LayoutElement>,
11}
12
13pub struct ScrollableBuilder<G: GroupExt + Clone = Scroll> {
14    props: ScrollableProperties<G>,
15}
16
17struct ScrollableProperties<G: GroupExt + Clone> {
18    group: G,
19    mode: ScrollMode,
20    horz_gap: i32,
21    vert_gap: i32,
22}
23
24#[derive(Debug, Clone, Copy, PartialEq, Eq)]
25pub enum ScrollMode {
26    Vertical,
27    Horizontal,
28    Both,
29}
30
31impl<G: GroupExt + Clone> LayoutElement for Scrollable<G> {
32    fn min_size(&self) -> Size {
33        let scrollbar_size = fltk::app::scrollbar_size();
34        let mut min_size = self.child.min_size();
35        min_size.width += scrollbar_size + self.props.horz_gap;
36        min_size.height += scrollbar_size + self.props.vert_gap;
37        if self.props.mode != ScrollMode::Vertical {
38            min_size.width = scrollbar_size + self.props.horz_gap;
39        }
40        if self.props.mode != ScrollMode::Horizontal {
41            min_size.height = scrollbar_size + self.props.vert_gap;
42        }
43        min_size
44    }
45
46    fn layout(&self, x: i32, y: i32, width: i32, height: i32) {
47        self.props.group.clone().resize(x, y, width, height);
48        self.layout_children();
49    }
50}
51
52impl Scrollable {
53    pub fn builder() -> ScrollableBuilder {
54        ScrollableBuilder::new(Scroll::default_fill())
55    }
56}
57
58impl<G: GroupExt + Clone> Scrollable<G> {
59    pub fn group(&self) -> G {
60        self.props.group.clone()
61    }
62
63    pub fn layout_children(&self) {
64        let x = self.props.group.x();
65        let y = self.props.group.y();
66        let mut width = self.props.group.width();
67        let mut height = self.props.group.height();
68
69        let scrollbar_size = fltk::app::scrollbar_size();
70        let child_min_size = self.child.min_size();
71
72        let horz_scroll = width < child_min_size.width;
73        let vert_scroll = height < child_min_size.height;
74
75        if horz_scroll {
76            height -= scrollbar_size + self.props.vert_gap;
77        }
78        if vert_scroll {
79            width -= scrollbar_size + self.props.horz_gap;
80        }
81
82        width = std::cmp::max(width, child_min_size.width);
83        height = std::cmp::max(height, child_min_size.height);
84
85        self.child.layout(x, y, width, height);
86    }
87
88    fn new(props: ScrollableProperties<G>, child: Rc<dyn LayoutElement>) -> Self {
89        Self { props, child }
90    }
91}
92impl<G: GroupExt + Clone> ScrollableBuilder<G> {
93    pub fn new(group: G) -> Self {
94        Self {
95            props: ScrollableProperties {
96                group,
97                mode: ScrollMode::Vertical,
98                horz_gap: 0,
99                vert_gap: 0,
100            },
101        }
102    }
103
104    pub fn with_mode(mut self, mode: ScrollMode) -> Self {
105        self.props.mode = mode;
106        self
107    }
108
109    pub fn with_horz_gap(mut self, gap: i32) -> Self {
110        self.props.horz_gap = gap;
111        self
112    }
113
114    pub fn with_vert_gap(mut self, gap: i32) -> Self {
115        self.props.vert_gap = gap;
116        self
117    }
118
119    pub fn with_gap(mut self, horz: i32, vert: i32) -> Self {
120        self.props.horz_gap = horz;
121        self.props.vert_gap = vert;
122        self
123    }
124
125    pub fn add<E: LayoutElement + 'static>(self, element: E) -> Scrollable<G> {
126        self.add_shared(Rc::new(element))
127    }
128
129    pub fn add_shared(self, element: Rc<dyn LayoutElement>) -> Scrollable<G> {
130        self.props.group.end();
131        Scrollable::new(self.props, element)
132    }
133}