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