kas_widgets/
access_label.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//! Label with access key
7
8#[allow(unused)] use super::Label;
9use kas::prelude::*;
10use kas::theme::{Text, TextClass};
11
12// NOTE: AccessLabel requires a different text class. Once specialization is
13// stable we can simply replace the `draw` method, but for now we use a whole
14// new type.
15#[impl_self]
16mod AccessLabel {
17    /// A label supporting an access key
18    ///
19    /// An `AccessLabel` is a variant of [`Label`] supporting an access key,
20    /// for example "&Edit" binds an action to <kbd>Alt+E</kbd> since by default
21    /// <kbd>Alt</kbd> must be held to use access keys.
22    /// The access key is parsed from the input `text` (see [`AccessString`])
23    /// and underlined when <kbd>Alt</kbd> is held.
24    ///
25    /// Vertical alignment defaults to centred, horizontal
26    /// alignment depends on the script direction if not specified.
27    /// Line-wrapping is enabled by default.
28    ///
29    /// ### Action bindings
30    ///
31    /// This widget attempts to bind itself to its access key unless
32    /// [a different target is set](Self::set_target). If the binding succeeds
33    /// and the access key is used, the target will receive navigation focus
34    /// (if supported; otherwise the first supporting ancestor is focussed) and
35    /// `Event::Command(Command::Activate)` (likewise, an ancestor may handle
36    /// the event). This `AccessLabel` does not support focus and will not
37    /// handle the [`Command::Activate`] event.
38    #[derive(Clone, Debug, Default)]
39    #[widget]
40    #[layout(self.text)]
41    pub struct AccessLabel {
42        core: widget_core!(),
43        target: Id,
44        text: Text<AccessString>,
45    }
46
47    impl Self {
48        /// Construct from `text`
49        #[inline]
50        pub fn new(text: impl Into<AccessString>) -> Self {
51            AccessLabel {
52                core: Default::default(),
53                target: Default::default(),
54                text: Text::new(text.into(), TextClass::AccessLabel(true)),
55            }
56        }
57
58        /// Set the access key target
59        ///
60        /// This method should be called from [`Events::configure`] or
61        /// [`Events::configure_recurse`].
62        #[inline]
63        pub fn set_target(&mut self, target: Id) {
64            self.target = target;
65        }
66
67        /// Get text class
68        #[inline]
69        pub fn class(&self) -> TextClass {
70            self.text.class()
71        }
72
73        /// Set text class
74        ///
75        /// Default: `AccessLabel::Label(true)`
76        #[inline]
77        pub fn set_class(&mut self, class: TextClass) {
78            self.text.set_class(class);
79        }
80
81        /// Set text class (inline)
82        ///
83        /// Default: `AccessLabel::Label(true)`
84        #[inline]
85        pub fn with_class(mut self, class: TextClass) -> Self {
86            self.text.set_class(class);
87            self
88        }
89
90        /// Get whether line-wrapping is enabled
91        #[inline]
92        pub fn wrap(&self) -> bool {
93            self.class().multi_line()
94        }
95
96        /// Enable/disable line wrapping
97        ///
98        /// This is equivalent to `label.set_class(TextClass::AccessLabel(wrap))`.
99        ///
100        /// By default this is enabled.
101        #[inline]
102        pub fn set_wrap(&mut self, wrap: bool) {
103            self.text.set_class(TextClass::AccessLabel(wrap));
104        }
105
106        /// Enable/disable line wrapping (inline)
107        #[inline]
108        pub fn with_wrap(mut self, wrap: bool) -> Self {
109            self.text.set_class(TextClass::AccessLabel(wrap));
110            self
111        }
112
113        /// Get text contents
114        pub fn as_str(&self) -> &str {
115            self.text.as_str()
116        }
117
118        /// Get read access to the text object
119        #[inline]
120        pub fn text(&self) -> &Text<AccessString> {
121            &self.text
122        }
123
124        /// Set text in an existing `Label`
125        pub fn set_text(&mut self, cx: &mut EventState, text: AccessString) {
126            self.text.set_text(text);
127            let act = self.text.reprepare_action();
128            cx.action(self, act);
129        }
130    }
131
132    impl Layout for Self {
133        fn set_rect(&mut self, cx: &mut ConfigCx, rect: Rect, hints: AlignHints) {
134            self.text
135                .set_rect(cx, rect, hints.combine(AlignHints::VERT_CENTER));
136        }
137
138        fn draw(&self, mut draw: DrawCx) {
139            let rect = self.text.rect();
140            if let Some((key, effects)) = self.text.text().key()
141                && draw.access_key(&self.target, key)
142            {
143                // Stop on first successful binding and draw
144                draw.text_with_effects(rect.pos, rect, &self.text, effects);
145            } else {
146                draw.text(rect, &self.text);
147            }
148        }
149    }
150
151    impl Tile for Self {
152        fn role(&self, _: &mut dyn RoleCx) -> Role<'_> {
153            if let Some((key, _)) = self.text.text().key() {
154                Role::AccessLabel(self.text.as_str(), key.clone())
155            } else {
156                Role::Label(self.text.as_str())
157            }
158        }
159    }
160
161    impl Events for Self {
162        type Data = ();
163
164        fn configure(&mut self, cx: &mut ConfigCx) {
165            self.target = self.id();
166            cx.text_configure(&mut self.text);
167        }
168    }
169}