use winapi::shared::{
windef::HBRUSH,
minwindef::{WPARAM, LPARAM}
};
use winapi::um::{
winuser::{WS_VISIBLE, WS_TABSTOP},
wingdi::DeleteObject,
};
use winapi::um::commctrl::{TBS_AUTOTICKS, TBS_VERT, TBS_HORZ, TBS_TOP, TBS_BOTTOM, TBS_LEFT, TBS_RIGHT, TBS_NOTICKS, TBS_ENABLESELRANGE};
use crate::win32::window_helper as wh;
use crate::win32::base_helper::check_hwnd;
use crate::{NwgError, RawEventHandler};
use super::{ControlBase, ControlHandle};
use std::cell::RefCell;
use std::ops::Range;
const NOT_BOUND: &'static str = "TrackBar is not yet bound to a winapi object";
const BAD_HANDLE: &'static str = "INTERNAL ERROR: TrackBar handle is not HWND!";
bitflags! {
pub struct TrackBarFlags: u32 {
const VISIBLE = WS_VISIBLE;
const AUTO_TICK = TBS_AUTOTICKS;
const VERTICAL = TBS_VERT;
const HORIZONTAL = TBS_HORZ;
const TICK_TOP = TBS_TOP;
const TICK_BOTTOM = TBS_BOTTOM;
const TICK_LEFT = TBS_LEFT;
const TICK_RIGHT = TBS_RIGHT;
const NO_TICK = TBS_NOTICKS;
const RANGE = TBS_ENABLESELRANGE;
const TAB_STOP = WS_TABSTOP;
}
}
#[derive(Default)]
pub struct TrackBar {
pub handle: ControlHandle,
background_brush: Option<HBRUSH>,
handler0: RefCell<Option<RawEventHandler>>,
}
impl TrackBar {
pub fn builder() -> TrackBarBuilder {
TrackBarBuilder {
size: (100, 20),
position: (0, 0),
focus: false,
range: None,
selected_range: None,
pos: None,
flags: None,
ex_flags: 0,
parent: None,
background_color: None
}
}
pub fn pos(&self) -> usize {
use winapi::um::commctrl::TBM_GETPOS;
let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
wh::send_message(handle, TBM_GETPOS, 0, 0) as usize
}
pub fn set_pos(&self, p: usize) {
use winapi::um::commctrl::TBM_SETPOSNOTIFY;
let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
wh::send_message(handle, TBM_SETPOSNOTIFY, 1, p as LPARAM);
}
pub fn selection_range_pos(&self) -> Range<usize> {
use winapi::um::commctrl::{TBM_GETSELEND, TBM_GETSELSTART};
let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
let end = wh::send_message(handle, TBM_GETSELEND, 0, 0) as usize;
let start = wh::send_message(handle, TBM_GETSELSTART, 0, 0) as usize;
start..end
}
pub fn set_selection_range_pos(&self, value: Range<usize>) {
use winapi::um::commctrl::{TBM_SETSELEND, TBM_SETSELSTART};
let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
wh::send_message(handle, TBM_SETSELEND, 0, value.end as LPARAM);
wh::send_message(handle, TBM_SETSELSTART, 1, value.start as LPARAM);
}
pub fn range_min(&self) -> usize {
use winapi::um::commctrl::TBM_GETRANGEMIN;
let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
wh::send_message(handle, TBM_GETRANGEMIN, 0, 0) as usize
}
pub fn set_range_min(&self, min: usize) {
use winapi::um::commctrl::TBM_SETRANGEMIN;
let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
wh::send_message(handle, TBM_SETRANGEMIN, 1, min as LPARAM);
}
pub fn range_max(&self) -> usize {
use winapi::um::commctrl::TBM_GETRANGEMAX;
let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
wh::send_message(handle, TBM_GETRANGEMAX, 0, 0) as usize
}
pub fn set_range_max(&self, max: usize) {
use winapi::um::commctrl::TBM_SETRANGEMAX;
let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
wh::send_message(handle, TBM_SETRANGEMAX, 1, max as LPARAM);
}
pub fn tics_len(&self) -> usize {
use winapi::um::commctrl::TBM_GETNUMTICS;
let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
wh::send_message(handle, TBM_GETNUMTICS, 0, 0) as usize
}
pub fn tic_value(&self, index: usize) -> usize {
use winapi::um::commctrl::TBM_GETTIC;
let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
wh::send_message(handle, TBM_GETTIC, index as WPARAM, 0) as usize
}
pub fn enabled(&self) -> bool {
let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
unsafe { wh::get_window_enabled(handle) }
}
pub fn set_enabled(&self, v: bool) {
let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
unsafe { wh::set_window_enabled(handle, v) }
}
pub fn visible(&self) -> bool {
let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
unsafe { wh::get_window_visibility(handle) }
}
pub fn set_visible(&self, v: bool) {
let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
unsafe { wh::set_window_visibility(handle, v) }
}
pub fn size(&self) -> (u32, u32) {
let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
unsafe { wh::get_window_size(handle) }
}
pub fn set_size(&self, x: u32, y: u32) {
let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
unsafe { wh::set_window_size(handle, x, y, false) }
}
pub fn position(&self) -> (i32, i32) {
let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
unsafe { wh::get_window_position(handle) }
}
pub fn set_position(&self, x: i32, y: i32) {
let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
unsafe { wh::set_window_position(handle, x, y) }
}
pub fn focus(&self) -> bool {
let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
unsafe { wh::get_focus(handle) }
}
pub fn set_focus(&self) {
let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
unsafe { wh::set_focus(handle); }
}
pub fn class_name(&self) -> &'static str {
winapi::um::commctrl::TRACKBAR_CLASS
}
pub fn flags(&self) -> u32 {
WS_VISIBLE | TBS_AUTOTICKS | WS_TABSTOP
}
pub fn forced_flags(&self) -> u32 {
use winapi::um::winuser::{WS_CHILD};
WS_CHILD
}
fn hook_background_color(&mut self, c: [u8; 3]) {
use crate::bind_raw_event_handler_inner;
use winapi::um::winuser::{WM_CTLCOLORSTATIC};
use winapi::shared::{basetsd::UINT_PTR, windef::{HWND}, minwindef::LRESULT};
use winapi::um::wingdi::{CreateSolidBrush, RGB};
if self.handle.blank() { panic!("{}", NOT_BOUND); }
let handle = self.handle.hwnd().expect(BAD_HANDLE);
let parent_handle = ControlHandle::Hwnd(wh::get_window_parent(handle));
let brush = unsafe { CreateSolidBrush(RGB(c[0], c[1], c[2])) };
self.background_brush = Some(brush);
let handler = bind_raw_event_handler_inner(&parent_handle, handle as UINT_PTR, move |_hwnd, msg, _w, l| {
match msg {
WM_CTLCOLORSTATIC => {
let child = l as HWND;
if child == handle {
return Some(brush as LRESULT);
}
},
_ => {}
}
None
});
*self.handler0.borrow_mut() = Some(handler.unwrap());
}
}
impl Drop for TrackBar {
fn drop(&mut self) {
use crate::unbind_raw_event_handler;
let handler = self.handler0.borrow();
if let Some(h) = handler.as_ref() {
drop(unbind_raw_event_handler(h));
}
if let Some(bg) = self.background_brush {
unsafe { DeleteObject(bg as _); }
}
self.handle.destroy();
}
}
pub struct TrackBarBuilder {
size: (i32, i32),
position: (i32, i32),
focus: bool,
range: Option<Range<usize>>,
selected_range: Option<Range<usize>>,
pos: Option<usize>,
flags: Option<TrackBarFlags>,
ex_flags: u32,
parent: Option<ControlHandle>,
background_color: Option<[u8; 3]>,
}
impl TrackBarBuilder {
pub fn flags(mut self, flags: TrackBarFlags) -> TrackBarBuilder {
self.flags = Some(flags);
self
}
pub fn ex_flags(mut self, flags: u32) -> TrackBarBuilder {
self.ex_flags = flags;
self
}
pub fn size(mut self, size: (i32, i32)) -> TrackBarBuilder {
self.size = size;
self
}
pub fn position(mut self, pos: (i32, i32)) -> TrackBarBuilder {
self.position = pos;
self
}
pub fn focus(mut self, focus: bool) -> TrackBarBuilder {
self.focus = focus;
self
}
pub fn range(mut self, range: Option<Range<usize>>) -> TrackBarBuilder {
self.range = range;
self
}
pub fn selected_range(mut self, range: Option<Range<usize>>) -> TrackBarBuilder {
self.selected_range = range;
self
}
pub fn pos(mut self, pos: Option<usize>) -> TrackBarBuilder {
self.pos = pos;
self
}
pub fn parent<C: Into<ControlHandle>>(mut self, p: C) -> TrackBarBuilder {
self.parent = Some(p.into());
self
}
pub fn background_color(mut self, color: Option<[u8;3]>) -> TrackBarBuilder {
self.background_color = color;
self
}
pub fn build(self, out: &mut TrackBar) -> Result<(), NwgError> {
let flags = self.flags.map(|f| f.bits()).unwrap_or(out.flags());
let parent = match self.parent {
Some(p) => Ok(p),
None => Err(NwgError::no_parent("TrackBar"))
}?;
*out = Default::default();
out.handle = ControlBase::build_hwnd()
.class_name(out.class_name())
.forced_flags(out.forced_flags())
.flags(flags)
.ex_flags(self.ex_flags)
.size(self.size)
.position(self.position)
.parent(Some(parent))
.build()?;
if self.background_color.is_some() {
out.hook_background_color(self.background_color.unwrap());
}
if self.focus {
out.set_focus();
}
if let Some(range) = self.range {
out.set_range_min(range.start);
out.set_range_max(range.end);
}
if let Some(range) = self.selected_range {
out.set_selection_range_pos(range);
}
if let Some(pos) = self.pos {
out.set_pos(pos);
}
Ok(())
}
}
impl PartialEq for TrackBar {
fn eq(&self, other: &Self) -> bool {
self.handle == other.handle
}
}