Skip to main content

native_windows_gui/controls/
progress_bar.rs

1/*!
2A push button is a rectangle containing an application-defined text label, an icon, or a bitmap
3that indicates what the button does when the user selects it.
4*/
5
6use winapi::um::winuser::{WS_VISIBLE, WS_DISABLED};
7use winapi::um::commctrl::{PBS_MARQUEE, PBS_VERTICAL};
8use crate::win32::window_helper as wh;
9use crate::win32::base_helper::check_hwnd;
10use crate::NwgError;
11use super::{ControlHandle, ControlBase};
12use std::ops::Range;
13
14const NOT_BOUND: &'static str = "Progress bar is not yet bound to a winapi object";
15const BAD_HANDLE: &'static str = "INTERNAL ERROR: Progress bar handle is not HWND!";
16
17
18bitflags! {
19    pub struct ProgressBarFlags: u32 {
20        const VISIBLE = WS_VISIBLE;
21        const DISABLED = WS_DISABLED;
22        const VERTICAL = PBS_VERTICAL;
23        const MARQUEE = PBS_MARQUEE;
24    }
25}
26
27#[derive(Copy, Clone, Debug, PartialEq, Eq)]
28#[repr(u8)]
29pub enum ProgressBarState {
30    Normal,
31    Error,
32    Paused
33}
34
35/**
36A progress bar is a window that an application can use to indicate the progress of a lengthy operation.
37
38Requires the `progress-bar` feature. 
39
40**Builder parameters:**
41  * `parent`:         **Required.** The progress bar parent container.
42  * `size`:           The progress bar size.
43  * `position`:       The progress bar position.
44  * `state`:          The initial state of the progress bar.
45  * `step`:           The value in which the progress bar value increase when `advance` is used.
46  * `pos`:            The initial value of the progress bar.
47  * `range`:          The minimum and maximum value in the progress bar.
48  * `enabled`:        If the progress bar is enabled. 
49  * `flags`:          A combination of the ProgressBarFlags values.
50  * `ex_flags`:       A combination of win32 window extended flags. Unlike `flags`, ex_flags must be used straight from winapi
51  * `marquee`:        Enable of disable the marquee animation (only used with the MARQUEE flags)
52  * `marquee_update`: The update interval of the marquee mode
53
54**Control events:**
55  * `MousePress(_)`: Generic mouse press events on the progress bar
56  * `OnMouseMove`: Generic mouse mouse event
57  * `OnMouseWheel`: Generic mouse wheel event
58
59```rust
60use native_windows_gui as nwg;
61fn build_progress_bar(bar: &mut nwg::ProgressBar, window: &nwg::Window) {
62    nwg::ProgressBar::builder()
63        .state(nwg::ProgressBarState::Paused)
64        .step(10)
65        .range(0..100)
66        .parent(window)
67        .build(bar);
68}
69```
70
71*/
72#[derive(Default, PartialEq, Eq)]
73pub struct ProgressBar {
74    pub handle: ControlHandle
75}
76
77impl ProgressBar {
78
79    pub fn builder() -> ProgressBarBuilder {
80        ProgressBarBuilder {
81            size: (100, 40),
82            position: (0, 0),
83            flags: None,
84            ex_flags: 0,
85            state: ProgressBarState::Normal,
86            step: 1,
87            pos: 0,
88            range: 0..100,
89            marquee_enable: false,
90            marquee_update: 0,
91            parent: None
92        }
93    }
94
95    /// Return the current state of the progress bar
96    pub fn state(&self) -> ProgressBarState {
97        use winapi::um::commctrl::{PBM_GETSTATE, PBST_NORMAL, PBST_ERROR, PBST_PAUSED};
98        
99        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
100
101        match wh::send_message(handle, PBM_GETSTATE, 0, 0) as i32 {
102            PBST_NORMAL => ProgressBarState::Normal,
103            PBST_ERROR => ProgressBarState::Error,
104            PBST_PAUSED => ProgressBarState::Paused,
105            _ => panic!("Unkown progress bar state")
106        }
107    }
108
109    /// Set the state of the progress bar
110    pub fn set_state(&self, state: ProgressBarState) {
111        use winapi::um::commctrl::{PBM_SETSTATE, PBST_NORMAL, PBST_ERROR, PBST_PAUSED};
112        use winapi::shared::minwindef::WPARAM;
113
114        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
115
116        let state = match state {
117            ProgressBarState::Normal => PBST_NORMAL,
118            ProgressBarState::Error => PBST_ERROR,
119            ProgressBarState::Paused => PBST_PAUSED
120        };
121
122        wh::send_message(handle, PBM_SETSTATE, state as WPARAM, 0);
123    }
124
125    /// Increase the bar value by the step value
126    pub fn advance(&self) {
127        use winapi::um::commctrl::PBM_STEPIT;
128
129        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
130        wh::send_message(handle, PBM_STEPIT, 0, 0);
131    }
132
133    /// Increase the bar value by a value
134    pub fn advance_delta(&self, v: u32) {
135        use winapi::um::commctrl::PBM_DELTAPOS;
136        use winapi::shared::minwindef::WPARAM;
137
138        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
139        wh::send_message(handle, PBM_DELTAPOS, v as WPARAM, 0);
140    }
141
142    /// Return the step of the progress bar.
143    pub fn step(&self) -> u32 {
144        use winapi::um::commctrl::PBM_GETSTEP;
145        
146        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
147        wh::send_message(handle, PBM_GETSTEP, 0, 0) as u32
148    }
149
150    /// Set the step of the progress bar.
151    pub fn set_step(&self, s: u32) {
152        use winapi::um::commctrl::PBM_SETSTEP;
153        use winapi::shared::minwindef::WPARAM;
154        
155        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
156        wh::send_message(handle, PBM_SETSTEP, s as WPARAM, 0);
157    }
158
159    /// Return the position of the progress bar.
160    pub fn pos(&self) -> u32 {
161        use winapi::um::commctrl::PBM_GETPOS;
162        
163        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
164        wh::send_message(handle, PBM_GETPOS, 0, 0) as u32
165    }
166
167    /// Set the position of the progress bar. If the value is outside of range
168    /// sets the value to the nearest bound.
169    pub fn set_pos(&self, p: u32) {
170        use winapi::um::commctrl::PBM_SETPOS;
171        use winapi::shared::minwindef::WPARAM;
172        
173        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
174        wh::send_message(handle, PBM_SETPOS, p as WPARAM, 0);
175    }
176
177    /// Get the range of the progress bar
178    pub fn range(&self) -> Range<u32> {
179        use winapi::um::commctrl::PBM_GETRANGE;
180        
181        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
182        
183        let low = wh::send_message(handle, PBM_GETRANGE, 1, 0) as u32;
184        let high = wh::send_message(handle, PBM_GETRANGE, 0, 0) as u32;
185
186        low..high
187    }
188
189    /// Set the range of the progress bar
190    pub fn set_range(&self, range: Range<u32>) {
191        use winapi::um::commctrl::PBM_SETRANGE32;
192        use winapi::shared::minwindef::{WPARAM, LPARAM};
193
194        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
195        wh::send_message(handle, PBM_SETRANGE32, range.start as WPARAM, range.end as LPARAM);
196    }
197
198    /// Set the progress bar marquee mode
199    pub fn set_marquee(&self, enable: bool, update_interval: u32) {
200        use winapi::shared::minwindef::{LPARAM, WPARAM};
201        use winapi::um::commctrl::PBM_SETMARQUEE;
202
203        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
204        wh::send_message(handle, PBM_SETMARQUEE, enable as WPARAM, update_interval as LPARAM);
205    }
206
207    /// Updates the flags of the progress bar.
208    pub fn add_flags(&self, styles: ProgressBarFlags) {
209        let styles = styles.bits() as u32;
210
211        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
212        let active_styles = wh::get_style(handle);
213        
214        wh::set_style(handle, active_styles | styles);
215    }
216
217    /// Removes flags from the progress bar.
218    pub fn remove_flags(&self, styles: ProgressBarFlags) {
219        let styles = styles.bits() as u32;
220
221        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
222        let active_styles = wh::get_style(handle);
223
224        wh::set_style(handle, active_styles & !styles);
225    }
226
227    /// Return true if the control currently has the keyboard focus
228    pub fn focus(&self) -> bool {
229        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
230        unsafe { wh::get_focus(handle) }
231    }
232
233    /// Set the keyboard focus on the button.
234    pub fn set_focus(&self) {
235        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
236        unsafe { wh::set_focus(handle); }
237    }
238
239    /// Return true if the control user can interact with the control, return false otherwise
240    pub fn enabled(&self) -> bool {
241        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
242        unsafe { wh::get_window_enabled(handle) }
243    }
244
245    /// Enable or disable the control
246    pub fn set_enabled(&self, v: bool) {
247        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
248        unsafe { wh::set_window_enabled(handle, v) }
249    }
250
251    /// Return true if the control is visible to the user. Will return true even if the 
252    /// control is outside of the parent client view (ex: at the position (10000, 10000))
253    pub fn visible(&self) -> bool {
254        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
255        unsafe { wh::get_window_visibility(handle) }
256    }
257
258    /// Show or hide the control to the user
259    pub fn set_visible(&self, v: bool) {
260        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
261        unsafe { wh::set_window_visibility(handle, v) }
262    }
263
264    /// Return the size of the button in the parent window
265    pub fn size(&self) -> (u32, u32) {
266        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
267        unsafe { wh::get_window_size(handle) }
268    }
269
270    /// Set the size of the button in the parent window
271    pub fn set_size(&self, x: u32, y: u32) {
272        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
273        unsafe { wh::set_window_size(handle, x, y, false) }
274    }
275
276    /// Return the position of the button in the parent window
277    pub fn position(&self) -> (i32, i32) {
278        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
279        unsafe { wh::get_window_position(handle) }
280    }
281
282    /// Set the position of the button in the parent window
283    pub fn set_position(&self, x: i32, y: i32) {
284        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
285        unsafe { wh::set_window_position(handle, x, y) }
286    }
287
288    /// Winapi class name used during control creation
289    pub fn class_name(&self) -> &'static str {
290        "msctls_progress32"
291    }
292
293    /// Winapi base flags used during window creation
294    pub fn flags(&self) -> u32 {
295        ::winapi::um::winuser::WS_VISIBLE
296    }
297
298    /// Winapi flags required by the control
299    pub fn forced_flags(&self) -> u32 {
300        use winapi::um::winuser::{WS_CHILD};
301
302        WS_CHILD
303    }
304
305}
306
307impl Drop for ProgressBar {
308    fn drop(&mut self) {
309        self.handle.destroy();
310    }
311}
312
313pub struct ProgressBarBuilder {
314    size: (i32, i32),
315    position: (i32, i32),
316    flags: Option<ProgressBarFlags>,
317    ex_flags: u32,
318    state: ProgressBarState,
319    step: u32,
320    pos: u32,
321    range: Range<u32>,
322    marquee_enable: bool,
323    marquee_update: u32,
324    parent: Option<ControlHandle>
325}
326
327impl ProgressBarBuilder {
328
329    pub fn flags(mut self, flags: ProgressBarFlags) -> ProgressBarBuilder {
330        self.flags = Some(flags);
331        self
332    }
333
334    pub fn ex_flags(mut self, flags: u32) -> ProgressBarBuilder {
335        self.ex_flags = flags;
336        self
337    }
338
339    pub fn size(mut self, size: (i32, i32)) -> ProgressBarBuilder {
340        self.size = size;
341        self
342    }
343
344    pub fn position(mut self, pos: (i32, i32)) -> ProgressBarBuilder {
345        self.position = pos;
346        self
347    }
348
349    pub fn state(mut self, state: ProgressBarState) -> ProgressBarBuilder {
350        self.state = state;
351        self
352    }
353
354    pub fn step(mut self, step: u32) -> ProgressBarBuilder {
355        self.step = step;
356        self
357    }
358
359    pub fn pos(mut self, pos: u32) -> ProgressBarBuilder {
360        self.pos = pos;
361        self
362    }
363
364    pub fn range(mut self, range: Range<u32>) -> ProgressBarBuilder {
365        self.range = range;
366        self
367    }
368
369    pub fn marquee(mut self, enable: bool) -> ProgressBarBuilder {
370        self.marquee_enable = enable;
371        self
372    }
373
374    pub fn marquee_update(mut self, update_interval: u32) -> ProgressBarBuilder {
375        self.marquee_update = update_interval;
376        self
377    }
378
379    pub fn parent<C: Into<ControlHandle>>(mut self, p: C) -> ProgressBarBuilder {
380        self.parent = Some(p.into());
381        self
382    }
383
384    pub fn build(self, out: &mut ProgressBar) -> Result<(), NwgError> {
385        let flags = self.flags.map(|f| f.bits()).unwrap_or(out.flags());
386
387        let parent = match self.parent {
388            Some(p) => Ok(p),
389            None => Err(NwgError::no_parent("Progress Bar"))
390        }?;
391
392        *out = Default::default();
393
394        out.handle = ControlBase::build_hwnd()
395            .class_name(out.class_name())
396            .forced_flags(out.forced_flags())
397            .flags(flags)
398            .ex_flags(self.ex_flags)
399            .size(self.size)
400            .position(self.position)
401            .parent(Some(parent))
402            .build()?;
403
404        out.set_state(self.state);
405        out.set_step(self.step);
406        out.set_pos(self.pos);
407        out.set_range(self.range);
408        out.set_marquee(self.marquee_enable, self.marquee_update);
409
410        Ok(())
411    }
412
413}