1use super::AccessLabel;
9use kas::event::Key;
10use kas::prelude::*;
11use kas::theme::{Background, FrameStyle};
12use std::fmt::Debug;
13
14#[impl_self]
15mod Button {
16 #[widget]
24 #[layout(
25 frame!(self.inner)
26 .with_style(self.frame_style)
27 .with_background(self.bg)
28 .align(AlignHints::CENTER)
29 )]
30 pub struct Button<W: Widget> {
31 core: widget_core!(),
32 key: Option<Key>,
33 bg: Background,
34 frame_style: FrameStyle,
35 #[widget]
36 pub inner: W,
37 on_press: Option<Box<dyn Fn(&mut EventCx, &W::Data)>>,
38 }
39
40 impl Self {
41 #[inline]
43 pub fn new(inner: W) -> Self {
44 Button {
45 core: Default::default(),
46 key: Default::default(),
47 frame_style: FrameStyle::Button,
48 bg: Background::Default,
49 inner,
50 on_press: None,
51 }
52 }
53
54 #[inline]
56 #[must_use]
57 pub fn with(mut self, f: impl Fn(&mut EventCx, &W::Data) + 'static) -> Self {
58 debug_assert!(self.on_press.is_none());
59 self.on_press = Some(Box::new(f));
60 self
61 }
62
63 #[inline]
65 #[must_use]
66 pub fn with_msg<M>(self, msg: M) -> Self
67 where
68 M: Clone + Debug + 'static,
69 {
70 self.with(move |cx, _| cx.push(msg.clone()))
71 }
72
73 #[inline]
79 pub fn new_msg<M: Clone + Debug + 'static>(inner: W, msg: M) -> Self {
80 Self::new(inner).with_msg(msg)
81 }
82
83 #[must_use]
85 pub fn with_access_key(mut self, key: Key) -> Self {
86 debug_assert!(self.key.is_none());
87 self.key = Some(key);
88 self
89 }
90
91 #[inline]
95 #[must_use]
96 pub fn with_background(mut self, bg: Background) -> Self {
97 self.bg = bg;
98 self
99 }
100
101 #[inline]
105 #[must_use]
106 pub fn with_frame_style(mut self, style: FrameStyle) -> Self {
107 self.frame_style = style;
108 self
109 }
110 }
111
112 impl Layout for Self {
113 fn draw(&self, mut draw: DrawCx) {
114 if let Some(key) = self.key.as_ref() {
115 let _ = draw.access_key(self.id_ref(), key);
116 }
117 kas::MacroDefinedLayout::draw(self, draw);
118 }
119 }
120
121 impl Tile for Self {
122 fn navigable(&self) -> bool {
123 true
124 }
125
126 fn role(&self, _: &mut dyn RoleCx) -> Role<'_> {
127 Role::Button
128 }
129 }
130
131 impl Events for Self {
132 const REDRAW_ON_MOUSE_OVER: bool = true;
133
134 type Data = W::Data;
135
136 fn probe(&self, _: Coord) -> Id {
137 self.id()
138 }
139
140 fn handle_event(&mut self, cx: &mut EventCx, data: &W::Data, event: Event) -> IsUsed {
141 event.on_click(cx, self.id(), |cx| {
142 if let Some(f) = self.on_press.as_ref() {
143 f(cx, data);
144 }
145 })
146 }
147
148 fn handle_messages(&mut self, cx: &mut EventCx, data: &W::Data) {
149 if let Some(kas::messages::Activate(code)) = cx.try_pop() {
150 if let Some(f) = self.on_press.as_ref() {
151 f(cx, data);
152 }
153 cx.depress_with_key(&self, code);
154 }
155 }
156 }
157
158 impl Button<AccessLabel> {
159 pub fn label(label: impl Into<AccessString>) -> Self {
165 Button::new(AccessLabel::new(label))
166 }
167
168 pub fn label_msg<M>(label: impl Into<AccessString>, msg: M) -> Self
174 where
175 M: Clone + Debug + 'static,
176 {
177 Button::new_msg(AccessLabel::new(label), msg)
178 }
179 }
180}