1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License in the LICENSE-APACHE file or at:
//     https://www.apache.org/licenses/LICENSE-2.0

//! Scroll bar traits

use crate::event::EventCx;
use crate::geom::{Offset, Size};
use crate::{Action, Widget};
#[allow(unused)] use crate::{Events, Layout};

/// Additional functionality on scrollable widgets
///
/// This trait should be implemented by widgets supporting scrolling, enabling
/// a parent to control scrolling.
///
/// If the widget scrolls itself it should set a scroll action via [`EventCx::set_scroll`].
pub trait Scrollable: Widget {
    /// Given size `size`, returns whether `(horiz, vert)` scrolling is required
    ///
    /// Note: this is called *before* [`Layout::set_rect`], thus must may need
    /// to perform independent calculation of the content size.
    fn scroll_axes(&self, size: Size) -> (bool, bool);

    /// Get the maximum scroll offset
    ///
    /// Note: the minimum scroll offset is always zero.
    ///
    /// Note: this is called immediately after [`Layout::set_rect`], thus should
    /// be updated there (as well as by [`Events::update`] if appropriate).
    fn max_scroll_offset(&self) -> Offset;

    /// Get the current scroll offset
    ///
    /// Contents of the scroll region are translated by this offset (to convert
    /// coordinates from the outer region to the scroll region, add this offset).
    ///
    /// The offset is restricted between [`Offset::ZERO`] and
    /// [`Self::max_scroll_offset`].
    fn scroll_offset(&self) -> Offset;

    /// Set the scroll offset
    ///
    /// This may be used for programmatic scrolling, e.g. by a wrapping widget
    /// with scroll controls. Note that calling this method directly on the
    /// scrolling widget will not update any controls in a wrapping widget.
    ///
    /// The offset is clamped to the available scroll range and applied. The
    /// resulting offset is returned.
    fn set_scroll_offset(&mut self, cx: &mut EventCx, offset: Offset) -> Offset;
}

/// Scroll bar mode
///
/// Note that in addition to this mode, bars may be disabled on each axis.
#[kas_macros::impl_default(ScrollBarMode::Auto)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum ScrollBarMode {
    /// Scroll bars are always shown if enabled.
    Fixed,
    /// Automatically enable/disable scroll bars as required when resized.
    ///
    /// This has the side-effect of reserving enough space for scroll bars even
    /// when not required.
    Auto,
    /// Scroll bars float over content and are only drawn when hovered over by
    /// the mouse.
    Invisible,
}

/// Scroll bar control
pub trait HasScrollBars {
    /// Get mode
    fn get_mode(&self) -> ScrollBarMode;

    /// Set mode
    fn set_mode(&mut self, mode: ScrollBarMode) -> Action;

    /// Get currently visible bars
    ///
    /// Returns `(horiz, vert)` tuple.
    fn get_visible_bars(&self) -> (bool, bool);

    /// Set enabled bars without adjusting mode
    ///
    /// Note: if mode is `Auto` this has no effect.
    ///
    /// This requires a [`Action::RESIZE`].
    fn set_visible_bars(&mut self, bars: (bool, bool)) -> Action;

    /// Set auto mode (inline)
    #[inline]
    fn with_auto_bars(mut self) -> Self
    where
        Self: Sized,
    {
        let _ = self.set_mode(ScrollBarMode::Auto);
        self
    }

    /// Set fixed bars (inline)
    #[inline]
    fn with_fixed_bars(mut self, horiz: bool, vert: bool) -> Self
    where
        Self: Sized,
    {
        let _ = self.set_mode(ScrollBarMode::Fixed);
        let _ = self.set_visible_bars((horiz, vert));
        self
    }

    /// Set invisible bars (inline)
    #[inline]
    fn with_invisible_bars(mut self, horiz: bool, vert: bool) -> Self
    where
        Self: Sized,
    {
        let _ = self.set_mode(ScrollBarMode::Invisible);
        let _ = self.set_visible_bars((horiz, vert));
        self
    }
}