1use winapi::shared::minwindef::{UINT, LPARAM, WPARAM};
2use winapi::um::winnt::WCHAR;
3use crate::win32::window_helper as wh;
4use crate::win32::base_helper::{check_hwnd, to_utf16, from_utf16};
5use crate::{Icon, NwgError};
6use super::{ControlBase, ControlHandle};
7use std::{mem, ptr};
8
9const NOT_BOUND: &'static str = "Tooltip is not yet bound to a winapi object";
10const BAD_HANDLE: &'static str = "INTERNAL ERROR: Tooltip handle is not HWND!";
11
12
13#[derive(Copy, Clone, Debug)]
15pub enum TooltipIcon {
16 None,
17 Info,
18 Warning,
19 Error,
20 InfoLarge,
21 WarningLarge,
22 ErrorLarge
23}
24
25#[derive(Default, PartialEq, Eq)]
90pub struct Tooltip {
91 pub handle: ControlHandle
92}
93
94impl Tooltip {
95
96 pub fn builder<'a>() -> TooltipBuilder<'a> {
97 TooltipBuilder {
98 title: None,
99 ico: None,
100 default_ico: None,
101 register: Vec::new(),
102 register_cb: Vec::new()
103 }
104 }
105
106 pub fn text(&self, owner: &ControlHandle, buffer_size: Option<usize>) -> String {
144 use winapi::um::commctrl::{TTM_GETTEXTW, TTTOOLINFOW, TTF_IDISHWND, TTF_SUBCLASS};
145 use winapi::shared::{basetsd::UINT_PTR, windef::RECT};
146
147 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
148
149 let owner_handle = {
150 if owner.blank() { panic!("{}", NOT_BOUND); }
151 owner.hwnd().expect(BAD_HANDLE)
152 };
153
154 let buffer_size = buffer_size.unwrap_or(200);
155 let mut text: Vec<WCHAR> = Vec::with_capacity(buffer_size);
156 unsafe { text.set_len(buffer_size); }
157
158 let mut tool = TTTOOLINFOW {
159 cbSize: mem::size_of::<TTTOOLINFOW>() as UINT,
160 uFlags: TTF_IDISHWND | TTF_SUBCLASS,
161 hwnd: owner_handle,
162 uId: owner_handle as UINT_PTR,
163 rect: RECT { left: 0, top: 0, right: 0, bottom: 0 },
164 hinst: ptr::null_mut(),
165 lpszText: text.as_mut_ptr(),
166 lParam: 0,
167 lpReserved: ptr::null_mut()
168 };
169
170 let tool_ptr = &mut tool as *mut TTTOOLINFOW;
171 wh::send_message(handle, TTM_GETTEXTW, 0, tool_ptr as LPARAM);
172
173 from_utf16(&text)
174 }
175
176 pub fn set_text<'a>(&self, owner: &ControlHandle, text: &'a str) {
179 use winapi::um::commctrl::{TTM_UPDATETIPTEXTW, TTTOOLINFOW, TTF_IDISHWND, TTF_SUBCLASS};
180 use winapi::shared::{basetsd::UINT_PTR, windef::RECT};
181
182 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
183
184 let mut text = to_utf16(text);
185 let owner_handle = {
186 if owner.blank() { panic!("{}", NOT_BOUND); }
187 owner.hwnd().expect(BAD_HANDLE)
188 };
189
190 let tool = TTTOOLINFOW {
191 cbSize: mem::size_of::<TTTOOLINFOW>() as UINT,
192 uFlags: TTF_IDISHWND | TTF_SUBCLASS,
193 hwnd: owner_handle,
194 uId: owner_handle as UINT_PTR,
195 rect: RECT { left: 0, top: 0, right: 0, bottom: 0 },
196 hinst: ptr::null_mut(),
197 lpszText: text.as_mut_ptr(),
198 lParam: 0,
199 lpReserved: ptr::null_mut()
200 };
201
202 let tool_ptr = &tool as *const TTTOOLINFOW;
203 wh::send_message(handle, TTM_UPDATETIPTEXTW, 0, tool_ptr as LPARAM);
204 }
205
206 pub fn set_decoration<'a>(&self, title: &'a str, ico: &Icon) {
208 use winapi::um::commctrl::{TTM_SETTITLEW};
209
210 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
211 let title = to_utf16(title);
212 wh::send_message(handle, TTM_SETTITLEW, ico.handle as WPARAM, title.as_ptr() as LPARAM);
213 }
214
215 pub fn set_default_decoration<'a>(&self, title: &'a str, icon: TooltipIcon) {
217 use winapi::um::commctrl::{TTM_SETTITLEW};
218 use winapi::um::commctrl::{TTI_NONE, TTI_INFO, TTI_WARNING, TTI_ERROR, TTI_INFO_LARGE, TTI_WARNING_LARGE, TTI_ERROR_LARGE};
219
220 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
221
222 let bitmap_handle = match icon {
223 TooltipIcon::None => TTI_NONE,
224 TooltipIcon::Info => TTI_INFO,
225 TooltipIcon::Warning => TTI_WARNING,
226 TooltipIcon::Error => TTI_ERROR,
227 TooltipIcon::InfoLarge => TTI_INFO_LARGE,
228 TooltipIcon::WarningLarge => TTI_WARNING_LARGE,
229 TooltipIcon::ErrorLarge => TTI_ERROR_LARGE
230 };
231
232 let title = to_utf16(title);
233
234 wh::send_message(handle, TTM_SETTITLEW, bitmap_handle as WPARAM, title.as_ptr() as LPARAM);
235 }
236
237 pub fn hide(&self) {
239 use winapi::um::commctrl::{TTM_POP};
240
241 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
242 wh::send_message(handle, TTM_POP, 0, 0);
243 }
244
245 pub fn count(&self) -> usize {
247 use winapi::um::commctrl::{TTM_GETTOOLCOUNT};
248
249 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
250 wh::send_message(handle, TTM_GETTOOLCOUNT, 0, 0) as usize
251 }
252
253 pub fn set_delay_time(&self, delay: Option<u16>) {
256 use winapi::um::commctrl::{TTDT_INITIAL, TTM_SETDELAYTIME};
257
258 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
259 let value = match delay {
260 Some(d) => d & 0xFFFF,
261 None => u16::max_value() & 0xFFFF,
262 };
263
264 wh::send_message(handle, TTM_SETDELAYTIME, TTDT_INITIAL as WPARAM, value as LPARAM);
265 }
266
267 pub fn delay_time(&self) -> u16 {
269 use winapi::um::commctrl::{TTDT_INITIAL, TTM_GETDELAYTIME};
270
271 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
272 wh::send_message(handle, TTM_GETDELAYTIME, TTDT_INITIAL as WPARAM, 0) as u16
273 }
274
275 pub fn set_enabled(&self, v: bool) {
278 use winapi::um::commctrl::TTM_ACTIVATE;
279
280 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
281 wh::send_message(handle, TTM_ACTIVATE, v as WPARAM, 0);
282 }
283
284 pub fn register<'a, W: Into<ControlHandle>>(&self, owner: W, text: &'a str) {
287 use winapi::um::commctrl::{TTM_ADDTOOLW, TTTOOLINFOW, TTF_IDISHWND, TTF_SUBCLASS};
288 use winapi::shared::{basetsd::UINT_PTR, windef::RECT};
289
290 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
291
292 let owner = owner.into();
293
294 let mut text = to_utf16(text);
295 let owner_handle = {
296 if owner.blank() { panic!("{}", NOT_BOUND); }
297 owner.hwnd().expect(BAD_HANDLE)
298 };
299
300 let tool = TTTOOLINFOW {
301 cbSize: mem::size_of::<TTTOOLINFOW>() as UINT,
302 uFlags: TTF_IDISHWND | TTF_SUBCLASS,
303 hwnd: owner_handle,
304 uId: owner_handle as UINT_PTR,
305 rect: RECT { left: 0, top: 0, right: 0, bottom: 0 },
306 hinst: ptr::null_mut(),
307 lpszText: text.as_mut_ptr(),
308 lParam: 0,
309 lpReserved: ptr::null_mut()
310 };
311
312 let tool_ptr = &tool as *const TTTOOLINFOW;
313 wh::send_message(handle, TTM_ADDTOOLW, 0, tool_ptr as LPARAM);
314 }
315
316 pub fn register_callback<W: Into<ControlHandle>>(&self, owner: W) {
320 use winapi::um::commctrl::{TTM_ADDTOOLW, TTTOOLINFOW, TTF_IDISHWND, TTF_SUBCLASS, LPSTR_TEXTCALLBACKW};
321 use winapi::shared::{basetsd::UINT_PTR, windef::RECT};
322
323 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
324
325 let owner = owner.into();
326 let owner_handle = {
327 if owner.blank() { panic!("{}", NOT_BOUND); }
328 owner.hwnd().expect(BAD_HANDLE)
329 };
330
331 let tool = TTTOOLINFOW {
332 cbSize: mem::size_of::<TTTOOLINFOW>() as UINT,
333 uFlags: TTF_IDISHWND | TTF_SUBCLASS,
334 hwnd: owner_handle,
335 uId: owner_handle as UINT_PTR,
336 rect: RECT { left: 0, top: 0, right: 0, bottom: 0 },
337 hinst: ptr::null_mut(),
338 lpszText: LPSTR_TEXTCALLBACKW,
339 lParam: 0,
340 lpReserved: ptr::null_mut()
341 };
342
343 let tool_ptr = &tool as *const TTTOOLINFOW;
344 wh::send_message(handle, TTM_ADDTOOLW, 0, tool_ptr as LPARAM);
345 }
346
347 pub fn unregister<W: Into<ControlHandle>>(&self, owner: W) {
349 use winapi::um::commctrl::{TTM_DELTOOLW, TTTOOLINFOW, TTF_IDISHWND, TTF_SUBCLASS};
350 use winapi::shared::{basetsd::UINT_PTR, windef::RECT};
351
352 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
353 let owner = owner.into();
354
355 let owner_handle = {
356 if owner.blank() { panic!("{}", NOT_BOUND); }
357 owner.hwnd().expect(BAD_HANDLE)
358 };
359
360 let tool = TTTOOLINFOW {
361 cbSize: mem::size_of::<TTTOOLINFOW>() as UINT,
362 uFlags: TTF_IDISHWND | TTF_SUBCLASS,
363 hwnd: owner_handle,
364 uId: owner_handle as UINT_PTR,
365 rect: RECT { left: 0, top: 0, right: 0, bottom: 0 },
366 hinst: ptr::null_mut(),
367 lpszText: ptr::null_mut(),
368 lParam: 0,
369 lpReserved: ptr::null_mut()
370 };
371
372 let tool_ptr = &tool as *const TTTOOLINFOW;
373 wh::send_message(handle, TTM_DELTOOLW, 0, tool_ptr as LPARAM);
374 }
375
376 pub fn class_name(&self) -> &'static str {
378 winapi::um::commctrl::TOOLTIPS_CLASS
379 }
380
381 pub fn flags(&self) -> u32 {
383 0
384 }
385
386 pub fn forced_flags(&self) -> u32 {
388 use winapi::um::winuser::{WS_POPUP};
389 use winapi::um::commctrl::{TTS_ALWAYSTIP, TTS_NOPREFIX};
390
391 WS_POPUP | TTS_ALWAYSTIP | TTS_NOPREFIX
392 }
393
394}
395
396impl Drop for Tooltip {
397 fn drop(&mut self) {
398 self.handle.destroy();
399 }
400}
401pub struct TooltipBuilder<'a> {
402 title: Option<&'a str>,
403 ico: Option<&'a Icon>,
404 default_ico: Option<TooltipIcon>,
405 register: Vec<(ControlHandle, &'a str)>,
406 register_cb: Vec<ControlHandle>,
407}
408
409impl<'a> TooltipBuilder<'a> {
410
411 pub fn register<W: Into<ControlHandle>>(mut self, widget: W, text: &'a str) -> TooltipBuilder<'a> {
412 self.register.push((widget.into(), text));
413 self
414 }
415
416 pub fn register_callback<W: Into<ControlHandle>>(mut self, widget: W) -> TooltipBuilder<'a> {
417 self.register_cb.push(widget.into());
418 self
419 }
420
421 pub fn decoration(mut self, title: Option<&'a str>, ico: Option<&'a Icon>) -> TooltipBuilder<'a> {
422 self.title = title;
423 self.ico = ico;
424 self
425 }
426
427 pub fn default_decoration(mut self, title: Option<&'a str>, ico: Option<TooltipIcon>) -> TooltipBuilder<'a> {
428 self.title = title;
429 self.default_ico = ico;
430 self
431 }
432
433 pub fn build(self, tooltip: &mut Tooltip) -> Result<(), NwgError> {
434 *tooltip = Default::default();
435
436 tooltip.handle = ControlBase::build_hwnd()
437 .class_name(tooltip.class_name())
438 .forced_flags(tooltip.forced_flags())
439 .flags(tooltip.flags())
440 .build()?;
441
442 if self.title.is_some() || self.ico.is_some() || self.default_ico.is_some() {
443 let title = self.title.unwrap_or("");
444 match (self.ico, self.default_ico) {
445 (Some(ico), None) | (Some(ico), _) => tooltip.set_decoration(title, ico),
446 (None, Some(ico)) => tooltip.set_default_decoration(title, ico),
447 (None, None) => tooltip.set_default_decoration(title, TooltipIcon::None),
448 }
449 }
450
451 for (handle, text) in self.register {
452 tooltip.register(&handle, text);
453 }
454
455 for handle in self.register_cb {
456 tooltip.register_callback(&handle);
457 }
458
459 Ok(())
460 }
461
462}