kas_core/widgets/
decorations.rs

1// Licensed under the Apache License, Version 2.0 (the "License");
2// you may not use this file except in compliance with the License.
3// You may obtain a copy of the License in the LICENSE-APACHE file or at:
4//     https://www.apache.org/licenses/LICENSE-2.0
5
6//! Window title-bar and border decorations
7//!
8//! Note: due to definition in kas-core, some widgets must be duplicated.
9
10use super::{Label, MarkButton};
11use crate::event::CursorIcon;
12use crate::prelude::*;
13use crate::theme::MarkStyle;
14use crate::window::ResizeDirection;
15use kas_macros::impl_self;
16use std::fmt::Debug;
17
18#[impl_self]
19mod Border {
20    /// A border region
21    ///
22    /// Does not draw anything; used solely for event handling.
23    #[widget]
24    pub(crate) struct Border {
25        core: widget_core!(),
26        resizable: bool,
27        direction: ResizeDirection,
28    }
29
30    impl Self {
31        pub fn new(direction: ResizeDirection) -> Self {
32            Border {
33                core: Default::default(),
34                resizable: true,
35                direction,
36            }
37        }
38
39        pub fn set_resizable(&mut self, resizable: bool) {
40            self.resizable = resizable;
41        }
42    }
43
44    impl Layout for Self {
45        fn size_rules(&mut self, _: SizeCx, _axis: AxisInfo) -> SizeRules {
46            SizeRules::EMPTY
47        }
48
49        fn draw(&self, _: DrawCx) {}
50    }
51
52    impl Tile for Self {
53        fn role(&self, _: &mut dyn RoleCx) -> Role<'_> {
54            Role::Border
55        }
56    }
57
58    impl Events for Self {
59        type Data = ();
60
61        fn mouse_over_icon(&self) -> Option<CursorIcon> {
62            if self.resizable {
63                Some(self.direction.into())
64            } else {
65                None
66            }
67        }
68
69        fn handle_event(&mut self, cx: &mut EventCx, _: &Self::Data, event: Event) -> IsUsed {
70            match event {
71                Event::PressStart(_) => {
72                    cx.drag_resize_window(self.direction);
73                    Used
74                }
75                _ => Unused,
76            }
77        }
78    }
79}
80
81#[derive(Copy, Clone, Debug)]
82enum TitleBarButton {
83    Minimize,
84    Maximize,
85    Close,
86}
87
88#[impl_self]
89mod TitleBarButtons {
90    /// A set of title-bar buttons
91    ///
92    /// Currently, this consists of minimise, maximise and close buttons.
93    #[derive(Clone, Default)]
94    #[widget]
95    #[layout(row! [
96        MarkButton::new_msg(MarkStyle::Chevron(Direction::Down), "Minimize", TitleBarButton::Minimize),
97        MarkButton::new_msg(MarkStyle::Chevron(Direction::Up), "Maximize", TitleBarButton::Maximize),
98        MarkButton::new_msg(MarkStyle::X, "Close", TitleBarButton::Close),
99    ])]
100    pub struct TitleBarButtons {
101        core: widget_core!(),
102    }
103
104    impl Self {
105        /// Construct
106        #[inline]
107        pub fn new() -> Self {
108            TitleBarButtons {
109                core: Default::default(),
110            }
111        }
112    }
113
114    impl Events for Self {
115        type Data = ();
116
117        fn handle_messages(&mut self, cx: &mut EventCx, _: &Self::Data) {
118            if let Some(msg) = cx.try_pop() {
119                match msg {
120                    TitleBarButton::Minimize => {
121                        if let Some(w) = cx.winit_window() {
122                            w.set_minimized(true);
123                        }
124                    }
125                    TitleBarButton::Maximize => {
126                        if let Some(w) = cx.winit_window() {
127                            w.set_maximized(!w.is_maximized());
128                        }
129                    }
130                    TitleBarButton::Close => cx.action(self, Action::CLOSE),
131                }
132            }
133        }
134    }
135}
136
137#[impl_self]
138mod TitleBar {
139    /// A window's title bar (part of decoration)
140    #[derive(Clone, Default)]
141    #[widget]
142    #[layout(row! [self.title.align(AlignHints::CENTER), self.buttons])]
143    pub struct TitleBar {
144        core: widget_core!(),
145        #[widget]
146        title: Label<String>,
147        #[widget]
148        buttons: TitleBarButtons,
149    }
150
151    impl Self {
152        /// Construct a title bar
153        #[inline]
154        pub fn new(title: impl ToString) -> Self {
155            TitleBar {
156                core: Default::default(),
157                title: Label::new(title.to_string()),
158                buttons: Default::default(),
159            }
160        }
161
162        /// Get the title
163        pub fn title(&self) -> &str {
164            self.title.as_str()
165        }
166
167        /// Set the title
168        pub fn set_title(&mut self, cx: &mut EventState, title: String) {
169            self.title.set_string(cx, title)
170        }
171    }
172
173    impl Tile for Self {
174        fn role(&self, cx: &mut dyn RoleCx) -> Role<'_> {
175            cx.set_label(self.title.id());
176            Role::TitleBar
177        }
178    }
179
180    impl Events for Self {
181        type Data = ();
182
183        fn handle_event(&mut self, cx: &mut EventCx, _: &Self::Data, event: Event) -> IsUsed {
184            match event {
185                Event::PressStart(_) => {
186                    cx.drag_window();
187                    Used
188                }
189                _ => Unused,
190            }
191        }
192    }
193}