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