1use super::{ControlBase, ControlHandle};
2use crate::win32::base_helper::check_hwnd;
3use crate::win32::window_helper as wh;
4use crate::{NwgError, RawEventHandler};
5use std::cell::RefCell;
6use std::ops::Range;
7use winapi::shared::{
8 minwindef::{LPARAM, WPARAM},
9 windef::HBRUSH,
10};
11use winapi::um::commctrl::{
12 TBS_AUTOTICKS, TBS_BOTTOM, TBS_ENABLESELRANGE, TBS_HORZ, TBS_LEFT, TBS_NOTICKS, TBS_RIGHT,
13 TBS_TOP, TBS_VERT,
14};
15use winapi::um::{
16 wingdi::DeleteObject,
17 winuser::{WS_TABSTOP, WS_VISIBLE},
18};
19
20const NOT_BOUND: &'static str = "TrackBar is not yet bound to a winapi object";
21const BAD_HANDLE: &'static str = "INTERNAL ERROR: TrackBar handle is not HWND!";
22
23bitflags! {
24 pub struct TrackBarFlags: u32 {
28 const VISIBLE = WS_VISIBLE;
29 const AUTO_TICK = TBS_AUTOTICKS;
30 const VERTICAL = TBS_VERT;
31 const HORIZONTAL = TBS_HORZ;
32 const TICK_TOP = TBS_TOP;
33 const TICK_BOTTOM = TBS_BOTTOM;
34 const TICK_LEFT = TBS_LEFT;
35 const TICK_RIGHT = TBS_RIGHT;
36 const NO_TICK = TBS_NOTICKS;
37 const RANGE = TBS_ENABLESELRANGE;
38 const TAB_STOP = WS_TABSTOP;
39 }
40}
41
42#[derive(Default)]
81pub struct TrackBar {
82 pub handle: ControlHandle,
83 background_brush: Option<HBRUSH>,
84 handler0: RefCell<Option<RawEventHandler>>,
85}
86
87impl TrackBar {
88 pub fn builder() -> TrackBarBuilder {
89 TrackBarBuilder {
90 size: (100, 20),
91 position: (0, 0),
92 focus: false,
93 range: None,
94 selected_range: None,
95 pos: None,
96 flags: None,
97 ex_flags: 0,
98 parent: None,
99 background_color: None,
100 }
101 }
102
103 pub fn pos(&self) -> usize {
106 use winapi::um::commctrl::TBM_GETPOS;
107
108 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
109 wh::send_message(handle, TBM_GETPOS, 0, 0) as usize
110 }
111
112 pub fn set_pos(&self, p: usize) {
114 use winapi::um::commctrl::TBM_SETPOSNOTIFY;
115
116 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
117 wh::send_message(handle, TBM_SETPOSNOTIFY, 1, p as LPARAM);
118 }
119
120 pub fn selection_range_pos(&self) -> Range<usize> {
123 use winapi::um::commctrl::{TBM_GETSELEND, TBM_GETSELSTART};
124
125 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
126
127 let end = wh::send_message(handle, TBM_GETSELEND, 0, 0) as usize;
128 let start = wh::send_message(handle, TBM_GETSELSTART, 0, 0) as usize;
129
130 start..end
131 }
132
133 pub fn set_selection_range_pos(&self, value: Range<usize>) {
136 use winapi::um::commctrl::{TBM_SETSELEND, TBM_SETSELSTART};
137
138 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
139
140 wh::send_message(handle, TBM_SETSELEND, 0, value.end as LPARAM);
141 wh::send_message(handle, TBM_SETSELSTART, 1, value.start as LPARAM);
142 }
143
144 pub fn range_min(&self) -> usize {
146 use winapi::um::commctrl::TBM_GETRANGEMIN;
147
148 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
149 wh::send_message(handle, TBM_GETRANGEMIN, 0, 0) as usize
150 }
151
152 pub fn set_range_min(&self, min: usize) {
154 use winapi::um::commctrl::TBM_SETRANGEMIN;
155
156 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
157 wh::send_message(handle, TBM_SETRANGEMIN, 1, min as LPARAM);
158 }
159
160 pub fn range_max(&self) -> usize {
162 use winapi::um::commctrl::TBM_GETRANGEMAX;
163
164 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
165 wh::send_message(handle, TBM_GETRANGEMAX, 0, 0) as usize
166 }
167
168 pub fn set_range_max(&self, max: usize) {
170 use winapi::um::commctrl::TBM_SETRANGEMAX;
171
172 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
173 wh::send_message(handle, TBM_SETRANGEMAX, 1, max as LPARAM);
174 }
175
176 pub fn tics_len(&self) -> usize {
178 use winapi::um::commctrl::TBM_GETNUMTICS;
179
180 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
181 wh::send_message(handle, TBM_GETNUMTICS, 0, 0) as usize
182 }
183
184 pub fn tic_value(&self, index: usize) -> usize {
187 use winapi::um::commctrl::TBM_GETTIC;
188
189 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
190 wh::send_message(handle, TBM_GETTIC, index as WPARAM, 0) as usize
191 }
192
193 pub fn enabled(&self) -> bool {
199 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
200 wh::get_window_enabled(handle)
201 }
202
203 pub fn set_enabled(&self, v: bool) {
205 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
206 wh::set_window_enabled(handle, v)
207 }
208
209 pub fn visible(&self) -> bool {
212 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
213 wh::get_window_visibility(handle)
214 }
215
216 pub fn set_visible(&self, v: bool) {
218 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
219 wh::set_window_visibility(handle, v)
220 }
221
222 pub fn size(&self) -> (u32, u32) {
224 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
225 wh::get_window_size(handle)
226 }
227
228 pub fn set_size(&self, x: u32, y: u32) {
230 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
231 wh::set_window_size(handle, x, y, false)
232 }
233
234 pub fn position(&self) -> (i32, i32) {
236 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
237 wh::get_window_position(handle)
238 }
239
240 pub fn set_position(&self, x: i32, y: i32) {
242 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
243 wh::set_window_position(handle, x, y)
244 }
245
246 pub fn focus(&self) -> bool {
248 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
249 wh::get_focus(handle)
250 }
251
252 pub fn set_focus(&self) {
254 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
255
256 wh::set_focus(handle);
257 }
258
259 pub fn class_name(&self) -> &'static str {
261 winapi::um::commctrl::TRACKBAR_CLASS
262 }
263
264 pub fn flags(&self) -> u32 {
266 WS_VISIBLE | TBS_AUTOTICKS | WS_TABSTOP
267 }
268
269 pub fn forced_flags(&self) -> u32 {
271 use winapi::um::winuser::WS_CHILD;
272
273 WS_CHILD
274 }
275
276 fn hook_background_color(&mut self, c: [u8; 3]) {
278 use crate::bind_raw_event_handler_inner;
279 use winapi::shared::{basetsd::UINT_PTR, minwindef::LRESULT, windef::HWND};
280 use winapi::um::wingdi::{CreateSolidBrush, RGB};
281 use winapi::um::winuser::WM_CTLCOLORSTATIC;
282
283 if self.handle.blank() {
284 panic!("{}", NOT_BOUND);
285 }
286 let handle = self.handle.hwnd().expect(BAD_HANDLE);
287
288 let parent_handle = ControlHandle::Hwnd(wh::get_window_parent(handle));
289 let brush = unsafe { CreateSolidBrush(RGB(c[0], c[1], c[2])) };
290 self.background_brush = Some(brush);
291
292 let handler = bind_raw_event_handler_inner(
293 &parent_handle,
294 handle as UINT_PTR,
295 move |_hwnd, msg, _w, l| {
296 match msg {
297 WM_CTLCOLORSTATIC => {
298 let child = l as HWND;
299 if child == handle {
300 return Some(brush as LRESULT);
301 }
302 }
303 _ => {}
304 }
305
306 None
307 },
308 );
309
310 *self.handler0.borrow_mut() = Some(handler.unwrap());
311 }
312}
313
314impl Drop for TrackBar {
315 fn drop(&mut self) {
316 use crate::unbind_raw_event_handler;
317
318 let handler = self.handler0.borrow();
319 if let Some(h) = handler.as_ref() {
320 drop(unbind_raw_event_handler(h));
321 }
322
323 if let Some(bg) = self.background_brush {
324 unsafe {
325 DeleteObject(bg as _);
326 }
327 }
328
329 self.handle.destroy();
330 }
331}
332
333pub struct TrackBarBuilder {
334 size: (i32, i32),
335 position: (i32, i32),
336 focus: bool,
337 range: Option<Range<usize>>,
338 selected_range: Option<Range<usize>>,
339 pos: Option<usize>,
340 flags: Option<TrackBarFlags>,
341 ex_flags: u32,
342 parent: Option<ControlHandle>,
343 background_color: Option<[u8; 3]>,
344}
345
346impl TrackBarBuilder {
347 pub fn flags(mut self, flags: TrackBarFlags) -> TrackBarBuilder {
348 self.flags = Some(flags);
349 self
350 }
351
352 pub fn ex_flags(mut self, flags: u32) -> TrackBarBuilder {
353 self.ex_flags = flags;
354 self
355 }
356
357 pub fn size(mut self, size: (i32, i32)) -> TrackBarBuilder {
358 self.size = size;
359 self
360 }
361
362 pub fn position(mut self, pos: (i32, i32)) -> TrackBarBuilder {
363 self.position = pos;
364 self
365 }
366
367 pub fn focus(mut self, focus: bool) -> TrackBarBuilder {
368 self.focus = focus;
369 self
370 }
371
372 pub fn range(mut self, range: Option<Range<usize>>) -> TrackBarBuilder {
373 self.range = range;
374 self
375 }
376
377 pub fn selected_range(mut self, range: Option<Range<usize>>) -> TrackBarBuilder {
378 self.selected_range = range;
379 self
380 }
381
382 pub fn pos(mut self, pos: Option<usize>) -> TrackBarBuilder {
383 self.pos = pos;
384 self
385 }
386
387 pub fn parent<C: Into<ControlHandle>>(mut self, p: C) -> TrackBarBuilder {
388 self.parent = Some(p.into());
389 self
390 }
391
392 pub fn background_color(mut self, color: Option<[u8; 3]>) -> TrackBarBuilder {
393 self.background_color = color;
394 self
395 }
396
397 pub fn build(self, out: &mut TrackBar) -> Result<(), NwgError> {
398 let flags = self.flags.map(|f| f.bits()).unwrap_or(out.flags());
399
400 let parent = match self.parent {
401 Some(p) => Ok(p),
402 None => Err(NwgError::no_parent("TrackBar")),
403 }?;
404
405 *out = Default::default();
406
407 out.handle = ControlBase::build_hwnd()
408 .class_name(out.class_name())
409 .forced_flags(out.forced_flags())
410 .flags(flags)
411 .ex_flags(self.ex_flags)
412 .size(self.size)
413 .position(self.position)
414 .parent(Some(parent))
415 .build()?;
416
417 if self.background_color.is_some() {
418 out.hook_background_color(self.background_color.unwrap());
419 }
420
421 if self.focus {
422 out.set_focus();
423 }
424
425 if let Some(range) = self.range {
426 out.set_range_min(range.start);
427 out.set_range_max(range.end);
428 }
429
430 if let Some(range) = self.selected_range {
431 out.set_selection_range_pos(range);
432 }
433
434 if let Some(pos) = self.pos {
435 out.set_pos(pos);
436 }
437
438 Ok(())
439 }
440}
441
442impl PartialEq for TrackBar {
443 fn eq(&self, other: &Self) -> bool {
444 self.handle == other.handle
445 }
446}