native_windows_gui2/controls/
radio_button.rs1use super::{ControlBase, ControlHandle};
2use crate::win32::base_helper::check_hwnd;
3use crate::win32::window_helper as wh;
4use crate::{Font, NwgError, RawEventHandler, unbind_raw_event_handler};
5use std::cell::RefCell;
6use winapi::shared::windef::HBRUSH;
7use winapi::um::{
8 wingdi::DeleteObject,
9 winuser::{WS_DISABLED, WS_GROUP, WS_TABSTOP, WS_VISIBLE},
10};
11
12const NOT_BOUND: &'static str = "RadioButton is not yet bound to a winapi object";
13const BAD_HANDLE: &'static str = "INTERNAL ERROR: RadioButton handle is not HWND!";
14
15bitflags! {
16 pub struct RadioButtonFlags: u32 {
25 const VISIBLE = WS_VISIBLE;
26 const DISABLED = WS_DISABLED;
27 const TAB_STOP = WS_TABSTOP;
28 const GROUP = WS_GROUP;
29 }
30}
31
32#[derive(Debug, Copy, Clone, PartialEq, Eq)]
34pub enum RadioButtonState {
35 Checked,
36 Unchecked,
37}
38
39#[derive(Default)]
112pub struct RadioButton {
113 pub handle: ControlHandle,
114 background_brush: Option<HBRUSH>,
115 handler0: RefCell<Option<RawEventHandler>>,
116}
117
118impl RadioButton {
119 pub fn builder<'a>() -> RadioButtonBuilder<'a> {
120 RadioButtonBuilder {
121 text: "A radio button",
122 size: (100, 25),
123 position: (0, 0),
124 focus: false,
125 background_color: None,
126 check_state: RadioButtonState::Unchecked,
127 flags: None,
128 ex_flags: 0,
129 font: None,
130 parent: None,
131 }
132 }
133
134 pub fn check_state(&self) -> RadioButtonState {
136 use winapi::um::winuser::{BM_GETCHECK, BST_CHECKED, BST_UNCHECKED};
137
138 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
139
140 match wh::send_message(handle, BM_GETCHECK, 0, 0) as usize {
141 BST_UNCHECKED => RadioButtonState::Unchecked,
142 BST_CHECKED => RadioButtonState::Checked,
143 _ => unreachable!(),
144 }
145 }
146
147 pub fn set_check_state(&self, state: RadioButtonState) {
149 use winapi::shared::minwindef::WPARAM;
150 use winapi::um::winuser::{BM_SETCHECK, BST_CHECKED, BST_UNCHECKED};
151
152 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
153
154 let x = match state {
155 RadioButtonState::Unchecked => BST_UNCHECKED,
156 RadioButtonState::Checked => BST_CHECKED,
157 };
158
159 wh::send_message(handle, BM_SETCHECK, x as WPARAM, 0);
160 }
161
162 pub fn font(&self) -> Option<Font> {
164 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
165
166 let font_handle = wh::get_window_font(handle);
167 if font_handle.is_null() {
168 None
169 } else {
170 Some(Font {
171 handle: font_handle,
172 })
173 }
174 }
175
176 pub fn set_font(&self, font: Option<&Font>) {
178 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
179
180 wh::set_window_font(handle, font.map(|f| f.handle), true);
181 }
182
183 pub fn focus(&self) -> bool {
185 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
186 wh::get_focus(handle)
187 }
188
189 pub fn set_focus(&self) {
191 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
192
193 wh::set_focus(handle);
194 }
195
196 pub fn enabled(&self) -> bool {
198 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
199 wh::get_window_enabled(handle)
200 }
201
202 pub fn set_enabled(&self, v: bool) {
204 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
205 wh::set_window_enabled(handle, v)
206 }
207
208 pub fn visible(&self) -> bool {
211 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
212 wh::get_window_visibility(handle)
213 }
214
215 pub fn set_visible(&self, v: bool) {
217 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
218 wh::set_window_visibility(handle, v)
219 }
220
221 pub fn size(&self) -> (u32, u32) {
223 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
224 wh::get_window_size(handle)
225 }
226
227 pub fn set_size(&self, x: u32, y: u32) {
229 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
230 wh::set_window_size(handle, x, y, false)
231 }
232
233 pub fn position(&self) -> (i32, i32) {
235 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
236 wh::get_window_position(handle)
237 }
238
239 pub fn set_position(&self, x: i32, y: i32) {
241 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
242 wh::set_window_position(handle, x, y)
243 }
244
245 pub fn text(&self) -> String {
247 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
248 wh::get_window_text(handle)
249 }
250
251 pub fn set_text<'a>(&self, v: &'a str) {
253 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
254 wh::set_window_text(handle, v)
255 }
256
257 pub fn class_name(&self) -> &'static str {
259 "BUTTON"
260 }
261
262 pub fn flags(&self) -> u32 {
264 WS_VISIBLE
265 }
266
267 pub fn forced_flags(&self) -> u32 {
269 use winapi::um::winuser::{BS_AUTORADIOBUTTON, BS_NOTIFY, WS_CHILD};
270
271 BS_NOTIFY | WS_CHILD | BS_AUTORADIOBUTTON
272 }
273
274 fn hook_background_color(&mut self, c: [u8; 3]) {
276 use crate::bind_raw_event_handler_inner;
277 use winapi::shared::{basetsd::UINT_PTR, minwindef::LRESULT, windef::HWND};
278 use winapi::um::wingdi::{CreateSolidBrush, RGB};
279 use winapi::um::winuser::WM_CTLCOLORSTATIC;
280
281 if self.handle.blank() {
282 panic!("{}", NOT_BOUND);
283 }
284 let handle = self.handle.hwnd().expect(BAD_HANDLE);
285
286 let parent_handle = ControlHandle::Hwnd(wh::get_window_parent(handle));
287 let brush = unsafe { CreateSolidBrush(RGB(c[0], c[1], c[2])) };
288 self.background_brush = Some(brush);
289
290 let handler = bind_raw_event_handler_inner(
291 &parent_handle,
292 handle as UINT_PTR,
293 move |_hwnd, msg, _w, l| {
294 match msg {
295 WM_CTLCOLORSTATIC => {
296 let child = l as HWND;
297 if child == handle {
298 return Some(brush as LRESULT);
299 }
300 }
301 _ => {}
302 }
303
304 None
305 },
306 );
307
308 *self.handler0.borrow_mut() = Some(handler.unwrap());
309 }
310}
311
312impl Drop for RadioButton {
313 fn drop(&mut self) {
314 let handler = self.handler0.borrow();
315 if let Some(h) = handler.as_ref() {
316 drop(unbind_raw_event_handler(h));
317 }
318
319 if let Some(bg) = self.background_brush {
320 unsafe {
321 DeleteObject(bg as _);
322 }
323 }
324
325 self.handle.destroy();
326 }
327}
328
329pub struct RadioButtonBuilder<'a> {
330 text: &'a str,
331 size: (i32, i32),
332 position: (i32, i32),
333 focus: bool,
334 background_color: Option<[u8; 3]>,
335 check_state: RadioButtonState,
336 flags: Option<RadioButtonFlags>,
337 ex_flags: u32,
338 font: Option<&'a Font>,
339 parent: Option<ControlHandle>,
340}
341
342impl<'a> RadioButtonBuilder<'a> {
343 pub fn flags(mut self, flags: RadioButtonFlags) -> RadioButtonBuilder<'a> {
344 self.flags = Some(flags);
345 self
346 }
347
348 pub fn ex_flags(mut self, flags: u32) -> RadioButtonBuilder<'a> {
349 self.ex_flags = flags;
350 self
351 }
352
353 pub fn text(mut self, text: &'a str) -> RadioButtonBuilder<'a> {
354 self.text = text;
355 self
356 }
357
358 pub fn size(mut self, size: (i32, i32)) -> RadioButtonBuilder<'a> {
359 self.size = size;
360 self
361 }
362
363 pub fn position(mut self, pos: (i32, i32)) -> RadioButtonBuilder<'a> {
364 self.position = pos;
365 self
366 }
367
368 pub fn focus(mut self, focus: bool) -> RadioButtonBuilder<'a> {
369 self.focus = focus;
370 self
371 }
372
373 pub fn check_state(mut self, check: RadioButtonState) -> RadioButtonBuilder<'a> {
374 self.check_state = check;
375 self
376 }
377
378 pub fn background_color(mut self, color: Option<[u8; 3]>) -> RadioButtonBuilder<'a> {
379 self.background_color = color;
380 self
381 }
382
383 pub fn font(mut self, font: Option<&'a Font>) -> RadioButtonBuilder<'a> {
384 self.font = font;
385 self
386 }
387
388 pub fn parent<C: Into<ControlHandle>>(mut self, p: C) -> RadioButtonBuilder<'a> {
389 self.parent = Some(p.into());
390 self
391 }
392
393 pub fn build(self, out: &mut RadioButton) -> Result<(), NwgError> {
394 let flags = self.flags.map(|f| f.bits()).unwrap_or(out.flags());
395
396 let parent = match self.parent {
397 Some(p) => Ok(p),
398 None => Err(NwgError::no_parent("RadioButton")),
399 }?;
400
401 *out = Default::default();
402
403 out.handle = ControlBase::build_hwnd()
404 .class_name(out.class_name())
405 .forced_flags(out.forced_flags())
406 .flags(flags)
407 .ex_flags(self.ex_flags)
408 .size(self.size)
409 .position(self.position)
410 .text(self.text)
411 .parent(Some(parent))
412 .build()?;
413
414 if self.font.is_some() {
415 out.set_font(self.font);
416 } else {
417 out.set_font(Font::global_default().as_ref());
418 }
419
420 if self.background_color.is_some() {
421 out.hook_background_color(self.background_color.unwrap());
422 }
423
424 if self.focus {
425 out.set_focus();
426 }
427
428 out.set_check_state(self.check_state);
429
430 Ok(())
431 }
432}
433
434impl PartialEq for RadioButton {
435 fn eq(&self, other: &Self) -> bool {
436 self.handle == other.handle
437 }
438}