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}