native_windows_gui2/controls/
scroll_bar.rs1use 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, mem, ops::Range};
6use winapi::um::winuser::{SBS_HORZ, SBS_VERT, WS_CHILD, WS_DISABLED, WS_TABSTOP, WS_VISIBLE};
7
8const NOT_BOUND: &'static str = "Scroll bar is not yet bound to a winapi object";
9const BAD_HANDLE: &'static str = "INTERNAL ERROR: Scroll bar handle is not HWND!";
10
11bitflags! {
12 pub struct ScrollBarFlags: u32 {
23 const NONE = 0;
24 const VISIBLE = WS_VISIBLE;
25 const DISABLED = WS_DISABLED;
26 const TAB_STOP = WS_TABSTOP;
27 const HORIZONTAL = SBS_HORZ;
28 const VERTICAL = SBS_VERT;
29 }
30}
31
32#[derive(Default)]
68pub struct ScrollBar {
69 pub handle: ControlHandle,
70 handler0: RefCell<Option<RawEventHandler>>,
71 handler1: RefCell<Option<RawEventHandler>>,
72}
73
74impl ScrollBar {
75 pub fn builder<'a>() -> ScrollBarBuilder {
76 ScrollBarBuilder {
77 size: (25, 100),
78 position: (0, 0),
79 enabled: true,
80 flags: None,
81 ex_flags: 0,
82 parent: None,
83 focus: false,
84 range: None,
85 pos: None,
86 }
87 }
88
89 pub fn pos(&self) -> usize {
92 use winapi::um::winuser::SBM_GETPOS;
93
94 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
95 wh::send_message(handle, SBM_GETPOS, 0, 0) as usize
96 }
97
98 pub fn set_pos(&self, p: usize) {
100 use winapi::um::winuser::SBM_SETPOS;
101
102 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
103 wh::send_message(handle, SBM_SETPOS, p, 1);
104 }
105
106 pub fn range(&self) -> Range<usize> {
108 use winapi::um::winuser::SBM_GETRANGE;
109
110 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
111
112 let mut min: u32 = 0;
113 let mut max: u32 = 0;
114
115 wh::send_message(
116 handle,
117 SBM_GETRANGE,
118 &mut min as *mut u32 as _,
119 &mut max as *mut u32 as _,
120 );
121
122 (min as usize)..(max as usize)
123 }
124
125 pub fn set_range(&self, range: Range<usize>) {
127 use winapi::um::winuser::SBM_SETRANGE;
128
129 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
130 wh::send_message(handle, SBM_SETRANGE, range.start as _, range.end as _);
131 }
132
133 pub fn focus(&self) -> bool {
135 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
136 wh::get_focus(handle)
137 }
138
139 pub fn set_focus(&self) {
141 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
142
143 wh::set_focus(handle);
144 }
145
146 pub fn enabled(&self) -> bool {
148 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
149 wh::get_window_enabled(handle)
150 }
151
152 pub fn set_enabled(&self, v: bool) {
154 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
155 wh::set_window_enabled(handle, v)
156 }
157
158 pub fn visible(&self) -> bool {
161 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
162 wh::get_window_visibility(handle)
163 }
164
165 pub fn set_visible(&self, v: bool) {
167 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
168 wh::set_window_visibility(handle, v)
169 }
170
171 pub fn size(&self) -> (u32, u32) {
173 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
174 wh::get_window_size(handle)
175 }
176
177 pub fn set_size(&self, x: u32, y: u32) {
179 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
180 wh::set_window_size(handle, x, y, false)
181 }
182
183 pub fn position(&self) -> (i32, i32) {
185 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
186 wh::get_window_position(handle)
187 }
188
189 pub fn set_position(&self, x: i32, y: i32) {
191 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
192 wh::set_window_position(handle, x, y)
193 }
194
195 pub fn class_name(&self) -> &'static str {
197 "SCROLLBAR"
198 }
199
200 pub fn flags(&self) -> u32 {
202 WS_VISIBLE | WS_TABSTOP | SBS_VERT
203 }
204
205 pub fn forced_flags(&self) -> u32 {
207 WS_CHILD
208 }
209
210 fn hook_scrollbar_controls(&self) {
212 use crate::bind_raw_event_handler_inner;
213 use winapi::shared::{
214 minwindef::{LOWORD, TRUE},
215 windef::HWND,
216 };
217 use winapi::um::winuser::{
218 GET_WHEEL_DELTA_WPARAM, GetScrollInfo, SB_BOTTOM, SB_CTL, SB_LINEDOWN, SB_LINELEFT,
219 SB_LINERIGHT, SB_LINEUP, SB_PAGEDOWN, SB_PAGELEFT, SB_PAGERIGHT, SB_PAGEUP,
220 SB_THUMBTRACK, SB_TOP, SCROLLINFO, SIF_ALL, SIF_POS, SetScrollInfo, WM_HSCROLL,
221 WM_MOUSEWHEEL, WM_VSCROLL,
222 };
223
224 if self.handle.blank() {
225 panic!("{}", NOT_BOUND);
226 }
227 let handle = self.handle.hwnd().expect(BAD_HANDLE);
228 let parent_handle = ControlHandle::Hwnd(wh::get_window_parent(handle));
229
230 let handler1 =
231 bind_raw_event_handler_inner(&parent_handle, handle as _, move |_hwnd, msg, w, l| {
232 let mut si: SCROLLINFO = unsafe { mem::zeroed() };
233 match msg {
234 WM_HSCROLL => {
235 if (l as HWND) != handle {
236 return None;
237 }
238
239 si.cbSize = mem::size_of::<SCROLLINFO>() as u32;
240 si.fMask = SIF_ALL;
241 unsafe {
242 GetScrollInfo(handle, SB_CTL as i32, &mut si);
243 }
244
245 let event = LOWORD(w as u32) as isize;
246 match event {
247 SB_LINELEFT => {
248 si.nPos -= 1;
249 }
250 SB_LINERIGHT => {
251 si.nPos += 1;
252 }
253 SB_PAGELEFT => {
254 si.nPos -= si.nPage as i32;
255 }
256 SB_PAGERIGHT => {
257 si.nPos += si.nPage as i32;
258 }
259 SB_THUMBTRACK => {
260 si.nPos = si.nTrackPos;
261 }
262 _ => {}
263 }
264
265 si.fMask = SIF_POS;
266 unsafe {
267 SetScrollInfo(handle, SB_CTL as _, &si, TRUE);
268 }
269 }
271 WM_VSCROLL => {
272 if (l as HWND) != handle {
273 return None;
274 }
275
276 si.cbSize = mem::size_of::<SCROLLINFO>() as u32;
277 si.fMask = SIF_ALL;
278 unsafe {
279 GetScrollInfo(handle, SB_CTL as i32, &mut si);
280 }
281
282 let event = LOWORD(w as u32) as isize;
283 match event {
284 SB_TOP => {
285 si.nPos = si.nMin;
286 }
287 SB_BOTTOM => {
288 si.nPos = si.nMax;
289 }
290 SB_LINEUP => {
291 si.nPos -= 1;
292 }
293 SB_LINEDOWN => {
294 si.nPos += 1;
295 }
296 SB_PAGEUP => {
297 si.nPos -= si.nPage as i32;
298 }
299 SB_PAGEDOWN => {
300 si.nPos += si.nPage as i32;
301 }
302 SB_THUMBTRACK => {
303 si.nPos = si.nTrackPos;
304 }
305 _ => {}
306 }
307
308 si.fMask = SIF_POS;
309 unsafe {
310 SetScrollInfo(handle, SB_CTL as _, &si, TRUE);
311 }
312 }
314 _ => {}
315 }
316
317 None
318 });
319
320 let handler2 = bind_raw_event_handler_inner(&self.handle, 0, move |_hwnd, msg, w, _l| {
321 unsafe {
322 match msg {
323 WM_MOUSEWHEEL => {
324 let mut si: SCROLLINFO = mem::zeroed();
325
326 si.cbSize = mem::size_of::<SCROLLINFO>() as u32;
327 si.fMask = SIF_ALL;
328 GetScrollInfo(handle, SB_CTL as i32, &mut si);
329
330 let delta = GET_WHEEL_DELTA_WPARAM(w);
331 match delta > 0 {
332 true => {
333 si.nPos -= 1;
334 }
335 false => {
336 si.nPos += 1;
337 }
338 }
339
340 si.fMask = SIF_POS;
341 SetScrollInfo(handle, SB_CTL as _, &si, TRUE);
342 return Some(0);
343 }
344 _ => {}
345 }
346 }
347
348 None
349 });
350
351 *self.handler0.borrow_mut() = Some(handler1.unwrap());
352 *self.handler1.borrow_mut() = Some(handler2.unwrap());
353 }
354}
355
356impl Drop for ScrollBar {
357 fn drop(&mut self) {
358 use crate::unbind_raw_event_handler;
359
360 let handler = self.handler0.borrow();
361 if let Some(h) = handler.as_ref() {
362 drop(unbind_raw_event_handler(h));
363 }
364
365 let handler = self.handler1.borrow();
366 if let Some(h) = handler.as_ref() {
367 drop(unbind_raw_event_handler(h));
368 }
369
370 self.handle.destroy();
371 }
372}
373
374pub struct ScrollBarBuilder {
375 size: (i32, i32),
376 position: (i32, i32),
377 enabled: bool,
378 flags: Option<ScrollBarFlags>,
379 ex_flags: u32,
380 parent: Option<ControlHandle>,
381 focus: bool,
382 range: Option<Range<usize>>,
383 pos: Option<usize>,
384}
385
386impl ScrollBarBuilder {
387 pub fn flags(mut self, flags: ScrollBarFlags) -> ScrollBarBuilder {
388 self.flags = Some(flags);
389 self
390 }
391
392 pub fn ex_flags(mut self, flags: u32) -> ScrollBarBuilder {
393 self.ex_flags = flags;
394 self
395 }
396
397 pub fn size(mut self, size: (i32, i32)) -> ScrollBarBuilder {
398 self.size = size;
399 self
400 }
401
402 pub fn position(mut self, pos: (i32, i32)) -> ScrollBarBuilder {
403 self.position = pos;
404 self
405 }
406
407 pub fn enabled(mut self, e: bool) -> ScrollBarBuilder {
408 self.enabled = e;
409 self
410 }
411
412 pub fn focus(mut self, focus: bool) -> ScrollBarBuilder {
413 self.focus = focus;
414 self
415 }
416
417 pub fn range(mut self, range: Option<Range<usize>>) -> ScrollBarBuilder {
418 self.range = range;
419 self
420 }
421
422 pub fn pos(mut self, pos: Option<usize>) -> ScrollBarBuilder {
423 self.pos = pos;
424 self
425 }
426
427 pub fn parent<C: Into<ControlHandle>>(mut self, p: C) -> ScrollBarBuilder {
428 self.parent = Some(p.into());
429 self
430 }
431
432 pub fn build(self, out: &mut ScrollBar) -> Result<(), NwgError> {
433 let flags = self.flags.map(|f| f.bits()).unwrap_or(out.flags());
434
435 let parent = match self.parent {
436 Some(p) => Ok(p),
437 None => Err(NwgError::no_parent("ScrollBar")),
438 }?;
439
440 *out = Default::default();
441
442 out.handle = ControlBase::build_hwnd()
443 .class_name(out.class_name())
444 .forced_flags(out.forced_flags())
445 .flags(flags)
446 .ex_flags(self.ex_flags)
447 .size(self.size)
448 .position(self.position)
449 .parent(Some(parent))
450 .build()?;
451
452 out.set_enabled(self.enabled);
453
454 if let Some(range) = self.range {
455 out.set_range(range);
456 }
457
458 if let Some(pos) = self.pos {
459 out.set_pos(pos);
460 }
461
462 if self.focus {
463 out.set_focus();
464 }
465
466 out.hook_scrollbar_controls();
467
468 Ok(())
469 }
470}
471
472impl PartialEq for ScrollBar {
473 fn eq(&self, other: &Self) -> bool {
474 self.handle == other.handle
475 }
476}