Skip to main content

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::{FrameStyle, 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, _: &mut 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(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.close_own_window(),
131                }
132            }
133        }
134    }
135}
136
137#[impl_self]
138mod TitleBar {
139    /// A window's title bar (part of decoration)
140    ///
141    /// This widget has no external margin.
142    #[widget]
143    #[layout(frame!(row! [
144        self.title.align(AlignHints::CENTER).with_stretch(Stretch::Maximize, Stretch::None),
145        self.buttons,
146    ]).with_style(FrameStyle::None))]
147    pub struct TitleBar {
148        core: widget_core!(),
149        #[widget]
150        title: Label<String>,
151        #[widget]
152        buttons: TitleBarButtons,
153    }
154
155    impl Self {
156        /// Construct a title bar
157        #[inline]
158        pub fn new(title: impl ToString) -> Self {
159            TitleBar {
160                core: Default::default(),
161                title: Label::new(title.to_string()),
162                buttons: Default::default(),
163            }
164        }
165
166        /// Get the title
167        pub fn title(&self) -> &str {
168            self.title.as_str()
169        }
170
171        /// Set the title
172        pub fn set_title(&mut self, cx: &mut ConfigCx, title: String) {
173            self.title.set_string(cx, title)
174        }
175    }
176
177    impl Tile for Self {
178        fn role(&self, cx: &mut dyn RoleCx) -> Role<'_> {
179            cx.set_label(self.title.id());
180            Role::TitleBar
181        }
182    }
183
184    impl Events for Self {
185        type Data = ();
186
187        fn handle_event(&mut self, cx: &mut EventCx, _: &Self::Data, event: Event) -> IsUsed {
188            match event {
189                Event::PressStart(_) => {
190                    cx.drag_window();
191                    Used
192                }
193                _ => Unused,
194            }
195        }
196    }
197}