Skip to main content

native_windows_gui2/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 super::{ControlBase, ControlHandle};
7use crate::NwgError;
8use crate::win32::base_helper::check_hwnd;
9use crate::win32::window_helper as wh;
10use std::ops::Range;
11use winapi::um::commctrl::{PBS_MARQUEE, PBS_SMOOTH, PBS_SMOOTHREVERSE, PBS_VERTICAL};
12use winapi::um::winuser::{WS_DISABLED, WS_VISIBLE};
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
17bitflags! {
18    pub struct ProgressBarFlags: u32 {
19        const VISIBLE = WS_VISIBLE;
20        const DISABLED = WS_DISABLED;
21        const VERTICAL = PBS_VERTICAL;
22        const MARQUEE = PBS_MARQUEE;
23        const SMOOTH = PBS_SMOOTH;
24        const SMOOTHREVERSE = PBS_SMOOTHREVERSE;
25    }
26}
27
28#[derive(Copy, Clone, Debug, PartialEq, Eq)]
29#[repr(u8)]
30pub enum ProgressBarState {
31    Normal,
32    Error,
33    Paused,
34}
35
36/**
37A progress bar is a window that an application can use to indicate the progress of a lengthy operation.
38
39Requires the `progress-bar` feature.
40
41**Builder parameters:**
42  * `parent`:         **Required.** The progress bar parent container.
43  * `size`:           The progress bar size.
44  * `position`:       The progress bar position.
45  * `state`:          The initial state of the progress bar.
46  * `step`:           The value in which the progress bar value increase when `advance` is used.
47  * `pos`:            The initial value of the progress bar.
48  * `range`:          The minimum and maximum value in the progress bar.
49  * `enabled`:        If the progress bar is enabled.
50  * `flags`:          A combination of the ProgressBarFlags values.
51  * `ex_flags`:       A combination of win32 window extended flags. Unlike `flags`, ex_flags must be used straight from winapi
52  * `marquee`:        Enable of disable the marquee animation (only used with the MARQUEE flags)
53  * `marquee_update`: The update interval of the marquee mode
54
55**Control events:**
56  * `MousePress(_)`: Generic mouse press events on the progress bar
57  * `OnMouseMove`: Generic mouse mouse event
58  * `OnMouseWheel`: Generic mouse wheel event
59
60```rust
61use native_windows_gui2 as nwg;
62fn build_progress_bar(bar: &mut nwg::ProgressBar, window: &nwg::Window) {
63    nwg::ProgressBar::builder()
64        .state(nwg::ProgressBarState::Paused)
65        .step(10)
66        .range(0..100)
67        .parent(window)
68        .build(bar);
69}
70```
71
72*/
73#[derive(Default, PartialEq, Eq)]
74pub struct ProgressBar {
75    pub handle: ControlHandle,
76}
77
78impl ProgressBar {
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_ERROR, PBST_NORMAL, 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::shared::minwindef::WPARAM;
112        use winapi::um::commctrl::{PBM_SETSTATE, PBST_ERROR, PBST_NORMAL, PBST_PAUSED};
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::shared::minwindef::WPARAM;
136        use winapi::um::commctrl::PBM_DELTAPOS;
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::shared::minwindef::WPARAM;
153        use winapi::um::commctrl::PBM_SETSTEP;
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::shared::minwindef::WPARAM;
171        use winapi::um::commctrl::PBM_SETPOS;
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::shared::minwindef::{LPARAM, WPARAM};
192        use winapi::um::commctrl::PBM_SETRANGE32;
193
194        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
195        wh::send_message(
196            handle,
197            PBM_SETRANGE32,
198            range.start as WPARAM,
199            range.end as LPARAM,
200        );
201    }
202
203    /// Set the progress bar marquee mode
204    pub fn set_marquee(&self, enable: bool, update_interval: u32) {
205        use winapi::shared::minwindef::{LPARAM, WPARAM};
206        use winapi::um::commctrl::PBM_SETMARQUEE;
207
208        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
209        wh::send_message(
210            handle,
211            PBM_SETMARQUEE,
212            enable as WPARAM,
213            update_interval as LPARAM,
214        );
215    }
216
217    /// Updates the flags of the progress bar.
218    pub fn add_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    /// Removes flags from the progress bar.
228    pub fn remove_flags(&self, styles: ProgressBarFlags) {
229        let styles = styles.bits() as u32;
230
231        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
232        let active_styles = wh::get_style(handle);
233
234        wh::set_style(handle, active_styles & !styles);
235    }
236
237    /// Return true if the control currently has the keyboard focus
238    pub fn focus(&self) -> bool {
239        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
240        wh::get_focus(handle)
241    }
242
243    /// Set the keyboard focus on the button.
244    pub fn set_focus(&self) {
245        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
246
247        wh::set_focus(handle);
248    }
249
250    /// Return true if the control user can interact with the control, return false otherwise
251    pub fn enabled(&self) -> bool {
252        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
253        wh::get_window_enabled(handle)
254    }
255
256    /// Enable or disable the control
257    pub fn set_enabled(&self, v: bool) {
258        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
259        wh::set_window_enabled(handle, v)
260    }
261
262    /// Return true if the control is visible to the user. Will return true even if the
263    /// control is outside of the parent client view (ex: at the position (10000, 10000))
264    pub fn visible(&self) -> bool {
265        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
266        wh::get_window_visibility(handle)
267    }
268
269    /// Show or hide the control to the user
270    pub fn set_visible(&self, v: bool) {
271        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
272        wh::set_window_visibility(handle, v)
273    }
274
275    /// Return the size of the button in the parent window
276    pub fn size(&self) -> (u32, u32) {
277        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
278        wh::get_window_size(handle)
279    }
280
281    /// Set the size of the button in the parent window
282    pub fn set_size(&self, x: u32, y: u32) {
283        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
284        wh::set_window_size(handle, x, y, false)
285    }
286
287    /// Return the position of the button in the parent window
288    pub fn position(&self) -> (i32, i32) {
289        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
290        wh::get_window_position(handle)
291    }
292
293    /// Set the position of the button in the parent window
294    pub fn set_position(&self, x: i32, y: i32) {
295        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
296        wh::set_window_position(handle, x, y)
297    }
298
299    /// Winapi class name used during control creation
300    pub fn class_name(&self) -> &'static str {
301        "msctls_progress32"
302    }
303
304    /// Winapi base flags used during window creation
305    pub fn flags(&self) -> u32 {
306        ::winapi::um::winuser::WS_VISIBLE
307    }
308
309    /// Winapi flags required by the control
310    pub fn forced_flags(&self) -> u32 {
311        use winapi::um::winuser::WS_CHILD;
312
313        WS_CHILD
314    }
315}
316
317impl Drop for ProgressBar {
318    fn drop(&mut self) {
319        self.handle.destroy();
320    }
321}
322
323pub struct ProgressBarBuilder {
324    size: (i32, i32),
325    position: (i32, i32),
326    flags: Option<ProgressBarFlags>,
327    ex_flags: u32,
328    state: ProgressBarState,
329    step: u32,
330    pos: u32,
331    range: Range<u32>,
332    marquee_enable: bool,
333    marquee_update: u32,
334    parent: Option<ControlHandle>,
335}
336
337impl ProgressBarBuilder {
338    pub fn flags(mut self, flags: ProgressBarFlags) -> ProgressBarBuilder {
339        self.flags = Some(flags);
340        self
341    }
342
343    pub fn ex_flags(mut self, flags: u32) -> ProgressBarBuilder {
344        self.ex_flags = flags;
345        self
346    }
347
348    pub fn size(mut self, size: (i32, i32)) -> ProgressBarBuilder {
349        self.size = size;
350        self
351    }
352
353    pub fn position(mut self, pos: (i32, i32)) -> ProgressBarBuilder {
354        self.position = pos;
355        self
356    }
357
358    pub fn state(mut self, state: ProgressBarState) -> ProgressBarBuilder {
359        self.state = state;
360        self
361    }
362
363    pub fn step(mut self, step: u32) -> ProgressBarBuilder {
364        self.step = step;
365        self
366    }
367
368    pub fn pos(mut self, pos: u32) -> ProgressBarBuilder {
369        self.pos = pos;
370        self
371    }
372
373    pub fn range(mut self, range: Range<u32>) -> ProgressBarBuilder {
374        self.range = range;
375        self
376    }
377
378    pub fn marquee(mut self, enable: bool) -> ProgressBarBuilder {
379        self.marquee_enable = enable;
380        self
381    }
382
383    pub fn marquee_update(mut self, update_interval: u32) -> ProgressBarBuilder {
384        self.marquee_update = update_interval;
385        self
386    }
387
388    pub fn parent<C: Into<ControlHandle>>(mut self, p: C) -> ProgressBarBuilder {
389        self.parent = Some(p.into());
390        self
391    }
392
393    pub fn build(self, out: &mut ProgressBar) -> Result<(), NwgError> {
394        let flags = self.flags.map(|f| f.bits()).unwrap_or(out.flags());
395
396        let parent = match self.parent {
397            Some(p) => Ok(p),
398            None => Err(NwgError::no_parent("Progress Bar")),
399        }?;
400
401        *out = Default::default();
402
403        out.handle = ControlBase::build_hwnd()
404            .class_name(out.class_name())
405            .forced_flags(out.forced_flags())
406            .flags(flags)
407            .ex_flags(self.ex_flags)
408            .size(self.size)
409            .position(self.position)
410            .parent(Some(parent))
411            .build()?;
412
413        out.set_state(self.state);
414        out.set_step(self.step);
415        out.set_pos(self.pos);
416        out.set_range(self.range);
417        out.set_marquee(self.marquee_enable, self.marquee_update);
418
419        Ok(())
420    }
421}