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