1use std::cell::RefCell;
2use std::rc::Rc;
3use winapi::um::winuser::{WS_DISABLED, WS_EX_CONTROLPARENT, WS_TABSTOP, WS_VISIBLE};
4
5use super::{Button, ButtonFlags, ControlBase, ControlHandle, TextInput, TextInputFlags};
6use crate::win32::base_helper::check_hwnd;
7use crate::win32::window_helper as wh;
8use crate::{
9 Font, NwgError, RawEventHandler, bind_raw_event_handler_inner, unbind_raw_event_handler,
10};
11
12const NOT_BOUND: &'static str = "UpDown is not yet bound to a winapi object";
13const BAD_HANDLE: &'static str = "INTERNAL ERROR: UpDown handle is not HWND!";
14
15bitflags! {
16 pub struct NumberSelectFlags: u32 {
25 const NONE = 0;
26 const VISIBLE = WS_VISIBLE;
27 const DISABLED = WS_DISABLED;
28 const TAB_STOP = WS_TABSTOP;
29 }
30}
31
32#[derive(Copy, Clone, Debug)]
34pub enum NumberSelectData {
35 Int {
36 value: i64,
37 step: i64,
38 max: i64,
39 min: i64,
40 },
41 Float {
42 value: f64,
43 step: f64,
44 max: f64,
45 min: f64,
46 decimals: u8,
47 },
48}
49
50impl NumberSelectData {
51 pub fn formatted_value(&self) -> String {
52 match self {
53 NumberSelectData::Int { value, .. } => format!("{}", value),
54 NumberSelectData::Float {
55 value, decimals, ..
56 } => format!("{:.*}", *decimals as usize, value),
57 }
58 }
59
60 pub fn decrease(&mut self) {
61 match self {
62 NumberSelectData::Int {
63 value, step, min, ..
64 } => {
65 *value -= *step;
66 *value = i64::max(*value, *min);
67 }
68 NumberSelectData::Float {
69 value, step, min, ..
70 } => {
71 *value -= *step;
72 *value = f64::max(*value, *min);
73 }
74 }
75 }
76
77 pub fn increase(&mut self) {
78 match self {
79 NumberSelectData::Int {
80 value, step, max, ..
81 } => {
82 *value += *step;
83 *value = i64::min(*value, *max);
84 }
85 NumberSelectData::Float {
86 value, step, max, ..
87 } => {
88 *value += *step;
89 *value = f64::min(*value, *max);
90 }
91 }
92 }
93}
94
95impl Default for NumberSelectData {
96 fn default() -> NumberSelectData {
97 NumberSelectData::Int {
98 value: 0,
99 step: 1,
100 max: i64::max_value(),
101 min: i64::min_value(),
102 }
103 }
104}
105
106#[derive(Default)]
137pub struct NumberSelect {
138 pub handle: ControlHandle,
139 data: Rc<RefCell<NumberSelectData>>,
140 edit: TextInput,
141 btn_up: Button,
142 btn_down: Button,
143 handler: Option<RawEventHandler>,
144}
145
146impl NumberSelect {
147 pub fn builder<'a>() -> NumberSelectBuilder<'a> {
148 NumberSelectBuilder {
149 size: (100, 25),
150 position: (0, 0),
151 data: NumberSelectData::default(),
152 enabled: true,
153 flags: None,
154 font: None,
155 parent: None,
156 }
157 }
158
159 pub fn data(&self) -> NumberSelectData {
162 self.data.borrow().clone()
163 }
164
165 pub fn set_data(&self, v: NumberSelectData) {
168 *self.data.borrow_mut() = v;
169 self.edit.set_text(&v.formatted_value());
170 }
171
172 pub fn font(&self) -> Option<Font> {
174 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
175 let font_handle = wh::get_window_font(handle);
176 if font_handle.is_null() {
177 None
178 } else {
179 Some(Font {
180 handle: font_handle,
181 })
182 }
183 }
184
185 pub fn set_font(&self, font: Option<&Font>) {
187 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
188
189 wh::set_window_font(handle, font.map(|f| f.handle), true);
190 }
191
192 pub fn focus(&self) -> bool {
194 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
195 wh::get_focus(handle)
196 }
197
198 pub fn set_focus(&self) {
200 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
201
202 wh::set_focus(handle);
203 }
204
205 pub fn enabled(&self) -> bool {
207 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
208 wh::get_window_enabled(handle)
209 }
210
211 pub fn set_enabled(&self, v: bool) {
213 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
214 wh::set_window_enabled(handle, v)
215 }
216
217 pub fn visible(&self) -> bool {
220 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
221 wh::get_window_visibility(handle)
222 }
223
224 pub fn set_visible(&self, v: bool) {
226 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
227 wh::set_window_visibility(handle, v)
228 }
229
230 pub fn size(&self) -> (u32, u32) {
232 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
233 wh::get_window_size(handle)
234 }
235
236 pub fn set_size(&self, x: u32, y: u32) {
238 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
239 wh::set_window_size(handle, x, y, false)
240 }
241
242 pub fn position(&self) -> (i32, i32) {
244 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
245 wh::get_window_position(handle)
246 }
247
248 pub fn set_position(&self, x: i32, y: i32) {
250 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
251 wh::set_window_position(handle, x, y)
252 }
253
254 pub fn class_name(&self) -> &'static str {
256 "NativeWindowsGuiWindow"
257 }
258
259 pub fn flags(&self) -> u32 {
261 ::winapi::um::winuser::WS_VISIBLE
262 }
263
264 pub fn forced_flags(&self) -> u32 {
266 use winapi::um::winuser::{WS_BORDER, WS_CHILD, WS_CLIPCHILDREN};
267 WS_CHILD | WS_BORDER | WS_CLIPCHILDREN
268 }
269}
270
271impl Drop for NumberSelect {
272 fn drop(&mut self) {
273 if let Some(h) = self.handler.as_ref() {
274 drop(unbind_raw_event_handler(h));
275 }
276
277 self.handle.destroy();
278 }
279}
280
281pub struct NumberSelectBuilder<'a> {
282 size: (i32, i32),
283 position: (i32, i32),
284 data: NumberSelectData,
285 enabled: bool,
286 flags: Option<NumberSelectFlags>,
287 font: Option<&'a Font>,
288 parent: Option<ControlHandle>,
289}
290
291impl<'a> NumberSelectBuilder<'a> {
292 pub fn flags(mut self, flags: NumberSelectFlags) -> NumberSelectBuilder<'a> {
293 self.flags = Some(flags);
294 self
295 }
296
297 pub fn size(mut self, size: (i32, i32)) -> NumberSelectBuilder<'a> {
298 self.size = size;
299 self
300 }
301
302 pub fn position(mut self, pos: (i32, i32)) -> NumberSelectBuilder<'a> {
303 self.position = pos;
304 self
305 }
306
307 pub fn enabled(mut self, e: bool) -> NumberSelectBuilder<'a> {
308 self.enabled = e;
309 self
310 }
311
312 pub fn font(mut self, font: Option<&'a Font>) -> NumberSelectBuilder<'a> {
313 self.font = font;
314 self
315 }
316
317 pub fn value_int(mut self, v: i64) -> NumberSelectBuilder<'a> {
319 match &mut self.data {
320 NumberSelectData::Int { value, .. } => {
321 *value = v;
322 }
323 data => {
324 *data = NumberSelectData::Int {
325 value: v,
326 step: 1,
327 max: i64::max_value(),
328 min: i64::min_value(),
329 }
330 }
331 }
332 self
333 }
334
335 pub fn step_int(mut self, v: i64) -> NumberSelectBuilder<'a> {
336 match &mut self.data {
337 NumberSelectData::Int { step, .. } => {
338 *step = v;
339 }
340 data => {
341 *data = NumberSelectData::Int {
342 value: 0,
343 step: v,
344 max: i64::max_value(),
345 min: i64::min_value(),
346 }
347 }
348 }
349 self
350 }
351
352 pub fn max_int(mut self, v: i64) -> NumberSelectBuilder<'a> {
353 match &mut self.data {
354 NumberSelectData::Int { max, .. } => {
355 *max = v;
356 }
357 data => {
358 *data = NumberSelectData::Int {
359 value: 0,
360 step: 1,
361 max: v,
362 min: i64::min_value(),
363 }
364 }
365 }
366 self
367 }
368
369 pub fn min_int(mut self, v: i64) -> NumberSelectBuilder<'a> {
370 match &mut self.data {
371 NumberSelectData::Int { min, .. } => {
372 *min = v;
373 }
374 data => {
375 *data = NumberSelectData::Int {
376 value: 0,
377 step: 1,
378 max: i64::max_value(),
379 min: v,
380 }
381 }
382 }
383 self
384 }
385
386 pub fn value_float(mut self, v: f64) -> NumberSelectBuilder<'a> {
388 match &mut self.data {
389 NumberSelectData::Float { value, .. } => {
390 *value = v;
391 }
392 data => {
393 *data = NumberSelectData::Float {
394 value: v,
395 step: 1.0,
396 max: 1000000.0,
397 min: -1000000.0,
398 decimals: 2,
399 }
400 }
401 }
402 self
403 }
404
405 pub fn step_float(mut self, v: f64) -> NumberSelectBuilder<'a> {
406 match &mut self.data {
407 NumberSelectData::Float { step, .. } => {
408 *step = v;
409 }
410 data => {
411 *data = NumberSelectData::Float {
412 value: 0.0,
413 step: v,
414 max: 1000000.0,
415 min: -1000000.0,
416 decimals: 2,
417 }
418 }
419 }
420 self
421 }
422
423 pub fn max_float(mut self, v: f64) -> NumberSelectBuilder<'a> {
424 match &mut self.data {
425 NumberSelectData::Float { max, .. } => {
426 *max = v;
427 }
428 data => {
429 *data = NumberSelectData::Float {
430 value: 0.0,
431 step: 1.0,
432 max: v,
433 min: -1000000.0,
434 decimals: 2,
435 }
436 }
437 }
438 self
439 }
440
441 pub fn min_float(mut self, v: f64) -> NumberSelectBuilder<'a> {
442 match &mut self.data {
443 NumberSelectData::Float { min, .. } => {
444 *min = v;
445 }
446 data => {
447 *data = NumberSelectData::Float {
448 value: 0.0,
449 step: 1.0,
450 max: 1000000.0,
451 min: v,
452 decimals: 2,
453 }
454 }
455 }
456 self
457 }
458
459 pub fn decimals(mut self, v: u8) -> NumberSelectBuilder<'a> {
460 match &mut self.data {
461 NumberSelectData::Float { decimals, .. } => {
462 *decimals = v;
463 }
464 data => {
465 *data = NumberSelectData::Float {
466 value: 0.0,
467 step: 1.0,
468 max: 1000000.0,
469 min: -1000000.0,
470 decimals: v,
471 }
472 }
473 }
474 self
475 }
476
477 pub fn parent<C: Into<ControlHandle>>(mut self, p: C) -> NumberSelectBuilder<'a> {
478 self.parent = Some(p.into());
479 self
480 }
481
482 pub fn build(self, out: &mut NumberSelect) -> Result<(), NwgError> {
483 let flags = self.flags.map(|f| f.bits()).unwrap_or(out.flags());
484 let (btn_flags, text_flags) = if flags & WS_TABSTOP == WS_TABSTOP {
485 (
486 ButtonFlags::VISIBLE | ButtonFlags::TAB_STOP,
487 TextInputFlags::VISIBLE | TextInputFlags::TAB_STOP,
488 )
489 } else {
490 (ButtonFlags::VISIBLE, TextInputFlags::VISIBLE)
491 };
492
493 let parent = match self.parent {
494 Some(p) => Ok(p),
495 None => Err(NwgError::no_parent("NumberSelect")),
496 }?;
497
498 *out = Default::default();
499
500 let (w, h) = self.size;
501
502 if out.handler.is_some() {
503 unbind_raw_event_handler(out.handler.as_ref().unwrap())?;
504 }
505
506 *out = NumberSelect::default();
507 *out.data.borrow_mut() = self.data;
508
509 out.handle = ControlBase::build_hwnd()
510 .class_name(out.class_name())
511 .forced_flags(out.forced_flags())
512 .ex_flags(WS_EX_CONTROLPARENT)
513 .flags(flags)
514 .size(self.size)
515 .position(self.position)
516 .parent(Some(parent))
517 .build()?;
518
519 TextInput::builder()
520 .text(&self.data.formatted_value())
521 .size((w - 19, h))
522 .parent(&out.handle)
523 .flags(text_flags)
524 .build(&mut out.edit)?;
525
526 Button::builder()
527 .text("+")
528 .size((20, h / 2 + 1))
529 .position((w - 20, -1))
530 .parent(&out.handle)
531 .flags(btn_flags)
532 .build(&mut out.btn_up)?;
533
534 Button::builder()
535 .text("-")
536 .size((20, h / 2 + 1))
537 .position((w - 20, (h / 2) - 1))
538 .parent(&out.handle)
539 .flags(btn_flags)
540 .build(&mut out.btn_down)?;
541
542 if self.font.is_some() {
543 out.btn_up.set_font(self.font);
544 out.btn_down.set_font(self.font);
545 out.edit.set_font(self.font);
546 } else {
547 let font = Font::global_default();
548 let font_ref = font.as_ref();
549 out.btn_up.set_font(font_ref);
550 out.btn_down.set_font(font_ref);
551 out.edit.set_font(font_ref);
552 }
553
554 let handler_data = out.data.clone();
555 let plus_button = out.btn_up.handle.clone();
556 let minus_button = out.btn_down.handle.clone();
557 let text_handle = out.edit.handle.clone();
558
559 let handler = bind_raw_event_handler_inner(&out.handle, 0x4545, move |_hwnd, msg, w, l| {
560 use winapi::shared::minwindef::HIWORD;
561 use winapi::shared::windef::HWND;
562 use winapi::um::winuser::{BN_CLICKED, WM_COMMAND};
563
564 match msg {
565 WM_COMMAND => {
566 let handle = ControlHandle::Hwnd(l as HWND);
567 let message = HIWORD(w as u32) as u16;
568 if message == BN_CLICKED && handle == plus_button {
569 let mut data = handler_data.borrow_mut();
570 data.increase();
571
572 let handle = text_handle.hwnd().unwrap();
573 let text = data.formatted_value();
574
575 wh::set_window_text(handle, &text);
576 } else if message == BN_CLICKED && handle == minus_button {
577 let mut data = handler_data.borrow_mut();
578 data.decrease();
579
580 let handle = text_handle.hwnd().unwrap();
581 let text = data.formatted_value();
582
583 wh::set_window_text(handle, &text);
584 }
585 }
586
587 _ => {}
588 }
589 None
590 });
591
592 out.handler = Some(handler.unwrap());
593
594 if !self.enabled {
595 out.set_enabled(self.enabled);
596 }
597
598 Ok(())
599 }
600}