use winapi::shared::minwindef::{UINT, LPARAM, WPARAM};
use winapi::um::winnt::WCHAR;
use crate::win32::window_helper as wh;
use crate::win32::base_helper::{check_hwnd, to_utf16, from_utf16};
use crate::{Icon, NwgError};
use super::{ControlBase, ControlHandle};
use std::{mem, ptr};
const NOT_BOUND: &'static str = "Tooltip is not yet bound to a winapi object";
const BAD_HANDLE: &'static str = "INTERNAL ERROR: Tooltip handle is not HWND!";
#[derive(Copy, Clone, Debug)]
pub enum TooltipIcon {
None,
Info,
Warning,
Error,
InfoLarge,
WarningLarge,
ErrorLarge
}
#[derive(Default, PartialEq, Eq)]
pub struct Tooltip {
pub handle: ControlHandle
}
impl Tooltip {
pub fn builder<'a>() -> TooltipBuilder<'a> {
TooltipBuilder {
title: None,
ico: None,
default_ico: None,
register: Vec::new(),
register_cb: Vec::new()
}
}
pub fn text(&self, owner: &ControlHandle, buffer_size: Option<usize>) -> String {
use winapi::um::commctrl::{TTM_GETTEXTW, TTTOOLINFOW, TTF_IDISHWND, TTF_SUBCLASS};
use winapi::shared::{basetsd::UINT_PTR, windef::RECT};
let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
let owner_handle = {
if owner.blank() { panic!(NOT_BOUND); }
owner.hwnd().expect(BAD_HANDLE)
};
let buffer_size = buffer_size.unwrap_or(200);
let mut text: Vec<WCHAR> = Vec::with_capacity(buffer_size);
unsafe { text.set_len(buffer_size); }
let mut tool = TTTOOLINFOW {
cbSize: mem::size_of::<TTTOOLINFOW>() as UINT,
uFlags: TTF_IDISHWND | TTF_SUBCLASS,
hwnd: owner_handle,
uId: owner_handle as UINT_PTR,
rect: RECT { left: 0, top: 0, right: 0, bottom: 0 },
hinst: ptr::null_mut(),
lpszText: text.as_mut_ptr(),
lParam: 0,
lpReserved: ptr::null_mut()
};
let tool_ptr = &mut tool as *mut TTTOOLINFOW;
wh::send_message(handle, TTM_GETTEXTW, 0, tool_ptr as LPARAM);
from_utf16(&text)
}
pub fn set_text<'a>(&self, owner: &ControlHandle, text: &'a str) {
use winapi::um::commctrl::{TTM_UPDATETIPTEXTW, TTTOOLINFOW, TTF_IDISHWND, TTF_SUBCLASS};
use winapi::shared::{basetsd::UINT_PTR, windef::RECT};
let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
let mut text = to_utf16(text);
let owner_handle = {
if owner.blank() { panic!(NOT_BOUND); }
owner.hwnd().expect(BAD_HANDLE)
};
let tool = TTTOOLINFOW {
cbSize: mem::size_of::<TTTOOLINFOW>() as UINT,
uFlags: TTF_IDISHWND | TTF_SUBCLASS,
hwnd: owner_handle,
uId: owner_handle as UINT_PTR,
rect: RECT { left: 0, top: 0, right: 0, bottom: 0 },
hinst: ptr::null_mut(),
lpszText: text.as_mut_ptr(),
lParam: 0,
lpReserved: ptr::null_mut()
};
let tool_ptr = &tool as *const TTTOOLINFOW;
wh::send_message(handle, TTM_UPDATETIPTEXTW, 0, tool_ptr as LPARAM);
}
pub fn set_decoration<'a>(&self, title: &'a str, ico: &Icon) {
use winapi::um::commctrl::{TTM_SETTITLEW};
let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
let title = to_utf16(title);
wh::send_message(handle, TTM_SETTITLEW, ico.handle as WPARAM, title.as_ptr() as LPARAM);
}
pub fn set_default_decoration<'a>(&self, title: &'a str, icon: TooltipIcon) {
use winapi::um::commctrl::{TTM_SETTITLEW};
use winapi::um::commctrl::{TTI_NONE, TTI_INFO, TTI_WARNING, TTI_ERROR, TTI_INFO_LARGE, TTI_WARNING_LARGE, TTI_ERROR_LARGE};
let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
let bitmap_handle = match icon {
TooltipIcon::None => TTI_NONE,
TooltipIcon::Info => TTI_INFO,
TooltipIcon::Warning => TTI_WARNING,
TooltipIcon::Error => TTI_ERROR,
TooltipIcon::InfoLarge => TTI_INFO_LARGE,
TooltipIcon::WarningLarge => TTI_WARNING_LARGE,
TooltipIcon::ErrorLarge => TTI_ERROR_LARGE
};
let title = to_utf16(title);
wh::send_message(handle, TTM_SETTITLEW, bitmap_handle as WPARAM, title.as_ptr() as LPARAM);
}
pub fn hide(&self) {
use winapi::um::commctrl::{TTM_POP};
let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
wh::send_message(handle, TTM_POP, 0, 0);
}
pub fn count(&self) -> usize {
use winapi::um::commctrl::{TTM_GETTOOLCOUNT};
let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
wh::send_message(handle, TTM_GETTOOLCOUNT, 0, 0) as usize
}
pub fn set_delay_time(&self, delay: Option<u16>) {
use winapi::um::commctrl::{TTDT_INITIAL, TTM_SETDELAYTIME};
let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
let value = match delay {
Some(d) => d & 0xFFFF,
None => u16::max_value() & 0xFFFF,
};
wh::send_message(handle, TTM_SETDELAYTIME, TTDT_INITIAL as WPARAM, value as LPARAM);
}
pub fn delay_time(&self) -> u16 {
use winapi::um::commctrl::{TTDT_INITIAL, TTM_GETDELAYTIME};
let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
wh::send_message(handle, TTM_GETDELAYTIME, TTDT_INITIAL as WPARAM, 0) as u16
}
pub fn set_enabled(&self, v: bool) {
use winapi::um::commctrl::TTM_ACTIVATE;
let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
wh::send_message(handle, TTM_ACTIVATE, v as WPARAM, 0);
}
pub fn register<'a, W: Into<ControlHandle>>(&self, owner: W, text: &'a str) {
use winapi::um::commctrl::{TTM_ADDTOOLW, TTTOOLINFOW, TTF_IDISHWND, TTF_SUBCLASS};
use winapi::shared::{basetsd::UINT_PTR, windef::RECT};
let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
let owner = owner.into();
let mut text = to_utf16(text);
let owner_handle = {
if owner.blank() { panic!(NOT_BOUND); }
owner.hwnd().expect(BAD_HANDLE)
};
let tool = TTTOOLINFOW {
cbSize: mem::size_of::<TTTOOLINFOW>() as UINT,
uFlags: TTF_IDISHWND | TTF_SUBCLASS,
hwnd: owner_handle,
uId: owner_handle as UINT_PTR,
rect: RECT { left: 0, top: 0, right: 0, bottom: 0 },
hinst: ptr::null_mut(),
lpszText: text.as_mut_ptr(),
lParam: 0,
lpReserved: ptr::null_mut()
};
let tool_ptr = &tool as *const TTTOOLINFOW;
wh::send_message(handle, TTM_ADDTOOLW, 0, tool_ptr as LPARAM);
}
pub fn register_callback<W: Into<ControlHandle>>(&self, owner: W) {
use winapi::um::commctrl::{TTM_ADDTOOLW, TTTOOLINFOW, TTF_IDISHWND, TTF_SUBCLASS, LPSTR_TEXTCALLBACKW};
use winapi::shared::{basetsd::UINT_PTR, windef::RECT};
let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
let owner = owner.into();
let owner_handle = {
if owner.blank() { panic!(NOT_BOUND); }
owner.hwnd().expect(BAD_HANDLE)
};
let tool = TTTOOLINFOW {
cbSize: mem::size_of::<TTTOOLINFOW>() as UINT,
uFlags: TTF_IDISHWND | TTF_SUBCLASS,
hwnd: owner_handle,
uId: owner_handle as UINT_PTR,
rect: RECT { left: 0, top: 0, right: 0, bottom: 0 },
hinst: ptr::null_mut(),
lpszText: LPSTR_TEXTCALLBACKW,
lParam: 0,
lpReserved: ptr::null_mut()
};
let tool_ptr = &tool as *const TTTOOLINFOW;
wh::send_message(handle, TTM_ADDTOOLW, 0, tool_ptr as LPARAM);
}
pub fn unregister<W: Into<ControlHandle>>(&self, owner: W) {
use winapi::um::commctrl::{TTM_DELTOOLW, TTTOOLINFOW, TTF_IDISHWND, TTF_SUBCLASS};
use winapi::shared::{basetsd::UINT_PTR, windef::RECT};
let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
let owner = owner.into();
let owner_handle = {
if owner.blank() { panic!(NOT_BOUND); }
owner.hwnd().expect(BAD_HANDLE)
};
let tool = TTTOOLINFOW {
cbSize: mem::size_of::<TTTOOLINFOW>() as UINT,
uFlags: TTF_IDISHWND | TTF_SUBCLASS,
hwnd: owner_handle,
uId: owner_handle as UINT_PTR,
rect: RECT { left: 0, top: 0, right: 0, bottom: 0 },
hinst: ptr::null_mut(),
lpszText: ptr::null_mut(),
lParam: 0,
lpReserved: ptr::null_mut()
};
let tool_ptr = &tool as *const TTTOOLINFOW;
wh::send_message(handle, TTM_DELTOOLW, 0, tool_ptr as LPARAM);
}
pub fn class_name(&self) -> &'static str {
winapi::um::commctrl::TOOLTIPS_CLASS
}
pub fn flags(&self) -> u32 {
0
}
pub fn forced_flags(&self) -> u32 {
use winapi::um::winuser::{WS_POPUP};
use winapi::um::commctrl::{TTS_ALWAYSTIP, TTS_NOPREFIX};
WS_POPUP | TTS_ALWAYSTIP | TTS_NOPREFIX
}
}
impl Drop for Tooltip {
fn drop(&mut self) {
self.handle.destroy();
}
}
pub struct TooltipBuilder<'a> {
title: Option<&'a str>,
ico: Option<&'a Icon>,
default_ico: Option<TooltipIcon>,
register: Vec<(ControlHandle, &'a str)>,
register_cb: Vec<ControlHandle>,
}
impl<'a> TooltipBuilder<'a> {
pub fn register<W: Into<ControlHandle>>(mut self, widget: W, text: &'a str) -> TooltipBuilder<'a> {
self.register.push((widget.into(), text));
self
}
pub fn register_callback<W: Into<ControlHandle>>(mut self, widget: W) -> TooltipBuilder<'a> {
self.register_cb.push(widget.into());
self
}
pub fn decoration(mut self, title: Option<&'a str>, ico: Option<&'a Icon>) -> TooltipBuilder<'a> {
self.title = title;
self.ico = ico;
self
}
pub fn default_decoration(mut self, title: Option<&'a str>, ico: Option<TooltipIcon>) -> TooltipBuilder<'a> {
self.title = title;
self.default_ico = ico;
self
}
pub fn build(self, tooltip: &mut Tooltip) -> Result<(), NwgError> {
tooltip.handle = ControlBase::build_hwnd()
.class_name(tooltip.class_name())
.forced_flags(tooltip.forced_flags())
.flags(tooltip.flags())
.build()?;
if self.title.is_some() || self.ico.is_some() || self.default_ico.is_some() {
let title = self.title.unwrap_or("");
match (self.ico, self.default_ico) {
(Some(ico), None) | (Some(ico), _) => tooltip.set_decoration(title, ico),
(None, Some(ico)) => tooltip.set_default_decoration(title, ico),
(None, None) => tooltip.set_default_decoration(title, TooltipIcon::None),
}
}
for (handle, text) in self.register {
tooltip.register(&handle, text);
}
for handle in self.register_cb {
tooltip.register_callback(&handle);
}
Ok(())
}
}