1use winapi::um::winuser::{WS_VISIBLE, WS_DISABLED, WS_TABSTOP, WS_EX_CONTROLPARENT};
2use std::cell::RefCell;
3use std::rc::Rc;
4
5use crate::win32::window_helper as wh;
6use crate::win32::base_helper::check_hwnd;
7use crate::{NwgError, Font, RawEventHandler, bind_raw_event_handler_inner, unbind_raw_event_handler};
8use super::{ControlBase, ControlHandle, TextInput, Button, ButtonFlags, TextInputFlags};
9
10const NOT_BOUND: &'static str = "UpDown is not yet bound to a winapi object";
11const BAD_HANDLE: &'static str = "INTERNAL ERROR: UpDown handle is not HWND!";
12
13
14bitflags! {
15 pub struct NumberSelectFlags: u32 {
24 const NONE = 0;
25 const VISIBLE = WS_VISIBLE;
26 const DISABLED = WS_DISABLED;
27 const TAB_STOP = WS_TABSTOP;
28 }
29}
30
31#[derive(Copy, Clone, Debug)]
33pub enum NumberSelectData {
34 Int { value: i64, step: i64, max: i64, min: i64 },
35 Float { value: f64, step: f64, max: f64, min: f64, decimals: u8 },
36}
37
38impl NumberSelectData {
39
40 pub fn formatted_value(&self) -> String {
41 match self {
42 NumberSelectData::Int{ value, ..} => format!("{}", value),
43 NumberSelectData::Float{ value, decimals, ..} => format!("{:.*}", *decimals as usize, value),
44 }
45 }
46
47 pub fn decrease(&mut self) {
48 match self {
49 NumberSelectData::Int{ value, step, min, ..} => {
50 *value -= *step;
51 *value = i64::max(*value, *min);
52 },
53 NumberSelectData::Float{ value, step, min, ..} => {
54 *value -= *step;
55 *value = f64::max(*value, *min);
56 }
57 }
58 }
59
60 pub fn increase(&mut self) {
61 match self {
62 NumberSelectData::Int{ value, step, max, ..} => {
63 *value += *step;
64 *value = i64::min(*value, *max);
65 },
66 NumberSelectData::Float{ value, step, max, ..} => {
67 *value += *step;
68 *value = f64::min(*value, *max);
69 }
70 }
71 }
72
73}
74
75impl Default for NumberSelectData {
76 fn default() -> NumberSelectData {
77 NumberSelectData::Int {
78 value: 0,
79 step: 1,
80 max: i64::max_value(),
81 min: i64::min_value(),
82 }
83 }
84}
85
86#[derive(Default)]
117pub struct NumberSelect {
118 pub handle: ControlHandle,
119 data: Rc<RefCell<NumberSelectData>>,
120 edit: TextInput,
121 btn_up: Button,
122 btn_down: Button,
123 handler: Option<RawEventHandler>
124}
125
126impl NumberSelect {
127
128 pub fn builder<'a>() -> NumberSelectBuilder<'a> {
129 NumberSelectBuilder {
130 size: (100, 25),
131 position: (0, 0),
132 data: NumberSelectData::default(),
133 enabled: true,
134 flags: None,
135 font: None,
136 parent: None
137 }
138 }
139
140 pub fn data(&self) -> NumberSelectData {
143 self.data.borrow().clone()
144 }
145
146 pub fn set_data(&self, v: NumberSelectData) {
149 *self.data.borrow_mut() = v;
150 self.edit.set_text(&v.formatted_value());
151 }
152
153 pub fn font(&self) -> Option<Font> {
155 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
156 let font_handle = wh::get_window_font(handle);
157 if font_handle.is_null() {
158 None
159 } else {
160 Some(Font { handle: font_handle })
161 }
162 }
163
164 pub fn set_font(&self, font: Option<&Font>) {
166 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
167 unsafe { wh::set_window_font(handle, font.map(|f| f.handle), true); }
168 }
169
170 pub fn focus(&self) -> bool {
172 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
173 unsafe { wh::get_focus(handle) }
174 }
175
176 pub fn set_focus(&self) {
178 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
179 unsafe { wh::set_focus(handle); }
180 }
181
182 pub fn enabled(&self) -> bool {
184 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
185 unsafe { wh::get_window_enabled(handle) }
186 }
187
188 pub fn set_enabled(&self, v: bool) {
190 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
191 unsafe { wh::set_window_enabled(handle, v) }
192 }
193
194 pub fn visible(&self) -> bool {
197 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
198 unsafe { wh::get_window_visibility(handle) }
199 }
200
201 pub fn set_visible(&self, v: bool) {
203 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
204 unsafe { wh::set_window_visibility(handle, v) }
205 }
206
207 pub fn size(&self) -> (u32, u32) {
209 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
210 unsafe { wh::get_window_size(handle) }
211 }
212
213 pub fn set_size(&self, x: u32, y: u32) {
215 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
216 unsafe { wh::set_window_size(handle, x, y, false) }
217 }
218
219 pub fn position(&self) -> (i32, i32) {
221 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
222 unsafe { wh::get_window_position(handle) }
223 }
224
225 pub fn set_position(&self, x: i32, y: i32) {
227 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
228 unsafe { wh::set_window_position(handle, x, y) }
229 }
230
231 pub fn class_name(&self) -> &'static str {
233 "NativeWindowsGuiWindow"
234 }
235
236 pub fn flags(&self) -> u32 {
238 ::winapi::um::winuser::WS_VISIBLE
239 }
240
241 pub fn forced_flags(&self) -> u32 {
243 use winapi::um::winuser::{WS_BORDER, WS_CHILD, WS_CLIPCHILDREN};
244 WS_CHILD | WS_BORDER | WS_CLIPCHILDREN
245 }
246
247}
248
249impl Drop for NumberSelect {
250
251 fn drop(&mut self) {
252 if let Some(h) = self.handler.as_ref() {
253 drop(unbind_raw_event_handler(h));
254 }
255
256 self.handle.destroy();
257 }
258
259}
260
261pub struct NumberSelectBuilder<'a> {
262 size: (i32, i32),
263 position: (i32, i32),
264 data: NumberSelectData,
265 enabled: bool,
266 flags: Option<NumberSelectFlags>,
267 font: Option<&'a Font>,
268 parent: Option<ControlHandle>
269}
270
271impl<'a> NumberSelectBuilder<'a> {
272
273 pub fn flags(mut self, flags: NumberSelectFlags) -> NumberSelectBuilder<'a> {
274 self.flags = Some(flags);
275 self
276 }
277
278 pub fn size(mut self, size: (i32, i32)) -> NumberSelectBuilder<'a> {
279 self.size = size;
280 self
281 }
282
283 pub fn position(mut self, pos: (i32, i32)) -> NumberSelectBuilder<'a> {
284 self.position = pos;
285 self
286 }
287
288 pub fn enabled(mut self, e: bool) -> NumberSelectBuilder<'a> {
289 self.enabled = e;
290 self
291 }
292
293 pub fn font(mut self, font: Option<&'a Font>) -> NumberSelectBuilder<'a> {
294 self.font = font;
295 self
296 }
297
298 pub fn value_int(mut self, v: i64) -> NumberSelectBuilder<'a> {
300 match &mut self.data {
301 NumberSelectData::Int { value, .. } => { *value = v; }
302 data => *data = NumberSelectData::Int { value: v, step: 1, max: i64::max_value(), min: i64::min_value() }
303 }
304 self
305 }
306
307 pub fn step_int(mut self, v: i64) -> NumberSelectBuilder<'a> {
308 match &mut self.data {
309 NumberSelectData::Int { step, .. } => { *step = v; }
310 data => *data = NumberSelectData::Int { value: 0, step: v, max: i64::max_value(), min: i64::min_value() }
311 }
312 self
313 }
314
315 pub fn max_int(mut self, v: i64) -> NumberSelectBuilder<'a> {
316 match &mut self.data {
317 NumberSelectData::Int { max, .. } => { *max = v; }
318 data => *data = NumberSelectData::Int { value: 0, step: 1, max: v, min: i64::min_value() }
319 }
320 self
321 }
322
323 pub fn min_int(mut self, v: i64) -> NumberSelectBuilder<'a> {
324 match &mut self.data {
325 NumberSelectData::Int { min, .. } => { *min = v; }
326 data => *data = NumberSelectData::Int { value: 0, step: 1, max: i64::max_value(), min: v }
327 }
328 self
329 }
330
331 pub fn value_float(mut self, v: f64) -> NumberSelectBuilder<'a> {
333 match &mut self.data {
334 NumberSelectData::Float { value, .. } => { *value = v; }
335 data => *data = NumberSelectData::Float { value: v, step: 1.0, max: 1000000.0, min: -1000000.0, decimals: 2 }
336 }
337 self
338 }
339
340 pub fn step_float(mut self, v: f64) -> NumberSelectBuilder<'a> {
341 match &mut self.data {
342 NumberSelectData::Float { step, .. } => { *step = v; }
343 data => *data = NumberSelectData::Float { value: 0.0, step: v, max: 1000000.0, min: -1000000.0, decimals: 2 }
344 }
345 self
346 }
347
348 pub fn max_float(mut self, v: f64) -> NumberSelectBuilder<'a> {
349 match &mut self.data {
350 NumberSelectData::Float { max, .. } => { *max = v; }
351 data => *data = NumberSelectData::Float { value: 0.0, step: 1.0, max: v, min: -1000000.0, decimals: 2 }
352 }
353 self
354 }
355
356 pub fn min_float(mut self, v: f64) -> NumberSelectBuilder<'a> {
357 match &mut self.data {
358 NumberSelectData::Float { min, .. } => { *min = v; }
359 data => *data = NumberSelectData::Float { value: 0.0, step: 1.0, max: 1000000.0, min: v, decimals: 2 }
360 }
361 self
362 }
363
364 pub fn decimals(mut self, v: u8) -> NumberSelectBuilder<'a> {
365 match &mut self.data {
366 NumberSelectData::Float { decimals, .. } => { *decimals = v; }
367 data => *data = NumberSelectData::Float { value: 0.0, step: 1.0, max: 1000000.0, min: -1000000.0, decimals: v }
368 }
369 self
370 }
371
372 pub fn parent<C: Into<ControlHandle>>(mut self, p: C) -> NumberSelectBuilder<'a> {
373 self.parent = Some(p.into());
374 self
375 }
376
377 pub fn build(self, out: &mut NumberSelect) -> Result<(), NwgError> {
378 let flags = self.flags.map(|f| f.bits()).unwrap_or(out.flags());
379 let (btn_flags, text_flags) = if flags & WS_TABSTOP == WS_TABSTOP {
380 (ButtonFlags::VISIBLE | ButtonFlags::TAB_STOP, TextInputFlags::VISIBLE | TextInputFlags::TAB_STOP)
381 } else {
382 (ButtonFlags::VISIBLE, TextInputFlags::VISIBLE)
383 };
384
385 let parent = match self.parent {
386 Some(p) => Ok(p),
387 None => Err(NwgError::no_parent("NumberSelect"))
388 }?;
389
390 *out = Default::default();
391
392 let (w, h) = self.size;
393
394 if out.handler.is_some() {
395 unbind_raw_event_handler(out.handler.as_ref().unwrap())?;
396 }
397
398 *out = NumberSelect::default();
399 *out.data.borrow_mut() = self.data;
400
401 out.handle = ControlBase::build_hwnd()
402 .class_name(out.class_name())
403 .forced_flags(out.forced_flags())
404 .ex_flags(WS_EX_CONTROLPARENT)
405 .flags(flags)
406 .size(self.size)
407 .position(self.position)
408 .parent(Some(parent))
409 .build()?;
410
411 TextInput::builder()
412 .text(&self.data.formatted_value())
413 .size((w-19, h))
414 .parent(&out.handle)
415 .flags(text_flags)
416 .build(&mut out.edit)?;
417
418 Button::builder()
419 .text("+")
420 .size((20, h/2+1))
421 .position((w-20, -1))
422 .parent(&out.handle)
423 .flags(btn_flags)
424 .build(&mut out.btn_up)?;
425
426 Button::builder()
427 .text("-")
428 .size((20, h/2+1))
429 .position((w-20, (h/2)-1))
430 .parent(&out.handle)
431 .flags(btn_flags)
432 .build(&mut out.btn_down)?;
433
434 if self.font.is_some() {
435 out.btn_up.set_font(self.font);
436 out.btn_down.set_font(self.font);
437 out.edit.set_font(self.font);
438 } else {
439 let font = Font::global_default();
440 let font_ref = font.as_ref();
441 out.btn_up.set_font(font_ref);
442 out.btn_down.set_font(font_ref);
443 out.edit.set_font(font_ref);
444 }
445
446 let handler_data = out.data.clone();
447 let plus_button = out.btn_up.handle.clone();
448 let minus_button = out.btn_down.handle.clone();
449 let text_handle = out.edit.handle.clone();
450
451 let handler = bind_raw_event_handler_inner(&out.handle, 0x4545, move |_hwnd, msg, w, l| {
452 use winapi::shared::windef::HWND;
453 use winapi::um::winuser::{WM_COMMAND, BN_CLICKED};
454 use winapi::shared::minwindef::HIWORD;
455
456 match msg {
457 WM_COMMAND => {
458 let handle = ControlHandle::Hwnd(l as HWND);
459 let message = HIWORD(w as u32) as u16;
460 if message == BN_CLICKED && handle == plus_button {
461 let mut data = handler_data.borrow_mut();
462 data.increase();
463
464 let handle = text_handle.hwnd().unwrap();
465 let text = data.formatted_value();
466 unsafe { wh::set_window_text(handle, &text); }
467 } else if message == BN_CLICKED && handle == minus_button {
468 let mut data = handler_data.borrow_mut();
469 data.decrease();
470
471 let handle = text_handle.hwnd().unwrap();
472 let text = data.formatted_value();
473 unsafe { wh::set_window_text(handle, &text); }
474 }
475 },
476
477 _ => {}
478 }
479 None
480 });
481
482 out.handler = Some(handler.unwrap());
483
484 if !self.enabled {
485 out.set_enabled(self.enabled);
486 }
487
488 Ok(())
489 }
490
491}