1 (изменено: stealzy, 2017-01-20 07:38:54)

Тема: AHK: ToolTip по наведению на элемент окна

Библиотека для показа тултипов по наведению курсора на контролы.
* Тултипы показываются с задаваемой задержкой.
* Для гарантированного скрытия тултипа на все время его показа создается таймер.
* При движении по контролу тултип скрывается и не показывается снова, пока курсор не покинет контрол.

Gui Add, text, HwndCntrl1Hwnd, Move the mouse cursor here
Gui Add, ComboBox, HwndCntrl2Hwnd, Do not move cursor here!
Gui Show ; ↓ Magic
CntrlToolTipArr := Object(Cntrl1Hwnd, "You did it!", Cntrl2Hwnd, "You don``t have to do that."), showDelay := 700 ;ms
HowerFn := Func("Hower").bind(CntrlToolTipArr, showDelay, false, false)
OnMessage(WM_MOUSEMOVE := 0x200, HowerFn)
Return ; ↑ Magic
GuiClose:
ExitApp

Hower(CntrlsHowerArr, showDelay, showTimer, hideTimer) {
	static OldCntrlHwnd, OldHowerFn_ShowTimer, OldHowerFn_HideTimer, xTT, yTT, ToolTipExist, PreventShowToolTipOnSameCntrl, HideTimerPeriod := 200

	MouseGetPos x, y,, CurrentCntrlHwnd, 2
	if !(CurrentCntrlToolTipText := CntrlsHowerArr[CurrentCntrlHwnd]) {
		parentCntrlHwnd := DllCall("GetParent", "UInt", CurrentCntrlHwnd)
		if CurrentCntrlToolTipText := CntrlsHowerArr[parentCntrlHwnd]
				CurrentCntrlHwnd := parentCntrlHwnd
	}
	if (OldCntrlHwnd != CurrentCntrlHwnd)
		PreventShowToolTipOnSameCntrl:=false
	OldCntrlHwnd := CurrentCntrlHwnd

	if (!hideTimer && !showTimer) { ; WM_MOUSEMOVE
		If OldHowerFn_ShowTimer {
			SetTimer % OldHowerFn_ShowTimer, Delete
			OldHowerFn_ShowTimer := ""
		}
		if (ToolTipExist && ((x != xTT) || (y != yTT))) {
			ToolTipExist := false
			ToolTip
			PreventShowToolTipOnSameCntrl := true
		} else if CurrentCntrlToolTipText {
			HowerFn_ShowTimer := Func("Hower").bind(CntrlsHowerArr, showDelay, true, false)
			SetTimer % HowerFn_ShowTimer, % -showDelay
			OldHowerFn_ShowTimer := HowerFn_ShowTimer
		}
	} else if showTimer {
		if (!PreventShowToolTipOnSameCntrl && CurrentCntrlToolTipText) {
			ToolTipExist := true
			ToolTip % CurrentCntrlToolTipText
			xTT:=x, yTT:=y
			HowerFn_HideTimer := Func("Hower").bind(CntrlsHowerArr, showDelay, false, true)
			SetTimer % HowerFn_HideTimer, % HideTimerPeriod
			OldHowerFn_HideTimer := HowerFn_HideTimer
		}
	} else if hideTimer {
		if (ToolTipExist && !CurrentCntrlToolTipText) {
			ToolTipExist := false
			ToolTip
			SetTimer % OldHowerFn_HideTimer, Delete
		}
	}
}

Если кто подскажет, как сделать код проще и изящнее буду признателен.

2

Re: AHK: ToolTip по наведению на элемент окна

Для начала, попробуй это не с текстом, а всё-таки с ComboBox.

stealzy пишет:

* При движении по контролу тултип скрывается и не показывается снова, пока курсор не покинет контрол.

Конечно, дело вкуса, но по-моему, лучше показывать ToolTip всё время, пока курсор над контролом, только без дёрганья, в одном конкретном месте.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

3 (изменено: serzh82saratov, 2017-01-20 02:26:49)

Re: AHK: ToolTip по наведению на элемент окна

stealzy пишет:

Если кто подскажет, как сделать код проще и изящнее буду признателен.

На счёт изящнее не знаю, но например OnMessage можно перенести в функцию, не вынуждая пользователя прописывать её. В Duration можно указать продолжительность показа, или опустить ключ.



#Persistent
#SingleInstance, Force
#NoEnv

Gui Add, Text, HwndCntrl1Hwnd, Move the mouse cursor here
Gui Add, ComboBox, HwndCntrl2Hwnd, Do not move cursor here! 
Gui Show
Hower({Cntrl1Hwnd:"You did it!", Cntrl2Hwnd:"You don``t have to do that.", "Delay":500, "Duration":2000})
Return

GuiClose:
	ExitApp

Hower(wp, lp = "", msg = "", hwnd = "") {
	Static _ := OnMessage(0x200, Func("Hower")), Arr := {}, CBs := {}, Prhwnd
	If IsObject(wp)
	{
		for k, v in wp
		{   
			(%k% + 0) && Arr[%k%] := v
			WinGetClass, CtrlClass, % "ahk_id" %k%
			If CtrlClass = Static
				Control, Style, +0x100, , % "ahk_id" %k%
			Else If CtrlClass = ComboBox 
				SizeOfCBI := 40 + (A_PtrSize * 3)
				, VarSetCapacity(CBI, SizeOfCBI, 0)
				, NumPut(SizeOfCBI, CBI, 0, "UInt")
				, DllCall("GetComboBoxInfo", "Ptr", %k%, "Ptr", &CBI, "Int")
				, h := NumGet(CBI, 40 + A_PtrSize, "UPtr")
				, Arr[h] := v, CBs[h] := %k%, CBs[%k%] := h
		} 
		Arr.Delay := !wp.Delay ? 0 : wp.Delay
		Arr.Duration := wp.Duration
	}
	Else If (Arr.HasKey(hwnd) && (!CBs[hwnd] || CBs[hwnd] != Prhwnd) && hwnd != Prhwnd && (Prhwnd := hwnd))
	{ 
		Gosub Hower_ToolTipHide
		SetTimer Hower_CheckMouse, -50
		SetTimer Hower_ToolTipShow, % "-" Arr.Delay
	}
	Else If Prhwnd && !Arr.HasKey(hwnd) && !(Prhwnd := "")
		GoTo Hower_ToolTipHide
	Return
		
	Hower_ToolTipShow:  
		WinGetPos, x, y, w, h, ahk_id %Prhwnd%
		S_CoordModeToolTip := A_CoordModeToolTip
		CoordMode, ToolTip, Screen
		ToolTip, % Arr[Prhwnd], x + w + 2, y + h + 2, 20
		CoordMode, ToolTip, %S_CoordModeToolTip% 
		If Arr.Duration
			SetTimer Hower_ToolTipHide, % "-" Arr.Duration
		Return
		
	Hower_ToolTipHide:
		SetTimer Hower_ToolTipHide, Off
		SetTimer Hower_ToolTipShow, Off
		SetTimer Hower_CheckMouse, Off
		ToolTip, , , , 20
		Return

	Hower_CheckMouse:
		MouseGetPos, , , , CntrlHwnd, 2
		If !Arr.HasKey(CntrlHwnd) && !(Prhwnd := "")
			GoTo Hower_ToolTipHide
		SetTimer Hower_CheckMouse, -50
		Return 
}
По вопросам возмездной помощи пишите на E-Mail: serzh82saratov@mail.ru Telegram: https://t.me/sergiol982
Win10x64 AhkSpy, Hotkey, ClockGui

4

Re: AHK: ToolTip по наведению на элемент окна

teadrinker, добавил проверку родительского контрола, спасибо.
Алгоритм показа взят из браузера, действительно, в Windows другой подход - тултип показывается все время нахождения курсора на контроле, но зато есть ограничение продолжительности показа.
serzh82saratov, спасибо за код, буду разбираться.
Перенос OnMessage внутрь функции мне не нравится, потому что в основном коде может уже быть OnMessage на WM_MOUSEMOVE, тогда придется изменять код библиотечной, чтобы можно было вызвать оба обработчика.

5 (изменено: teadrinker, 2017-01-20 19:38:22)

Re: AHK: ToolTip по наведению на элемент окна

Вот без хэндлов, используя A_GuiControl, выходит даже проще:

Gui Add, Text, vCtrl1, % " Move the mouse cursor here   "
Gui Add, ComboBox, vCtrl2 wp, Do not move cursor here!||
Gui Show
Hover({controls: {Ctrl1: "You did it!", Ctrl2: "You don``t have to do that."}, delay: 500, duration: 2000})
Return

GuiClose:
   ExitApp

Hover(wp, lp = "", msg = "")  {
   static WM_MOUSEMOVE := 0x200, SS_NOTIFY := 0x100, MyPID := DllCall("GetCurrentProcessId"), prev, opt
   
   fn := A_ThisFunc, GuiCtrl := A_GuiControl
   if (!opt && opt := wp)  {
      OnMessage(WM_MOUSEMOVE, fn)
      for k in opt.controls  {
         GuiControlGet, %k%, hwnd
         WinGetClass, WinClass, % "ahk_id" %k%
         if (WinClass = "Static")
            WinSet, Style, +%SS_NOTIFY%, % "ahk_id" %k%
      }
   }
   else if msg  {
      for k, v in opt.controls  {
         if (GuiCtrl = k && (hover := true) && GuiCtrl != prev && prev := GuiCtrl)  {
            %fn%("hide")
            opt.timers.Push( timer := Func(fn).Bind("show", k) )
            SetTimer, % timer, % "-" . opt.delay
         }
      }
      (!hover && %fn%("hide", true))
   }
   else if (wp = "show")  {
      ToolTip % opt.controls[lp]
      opt.timers.Push( timer := Func(fn).Bind("TrackOut") )
      SetTimer, % timer, 100
      opt.timers.Push( timer := Func(fn).Bind("hide") )
      SetTimer, % timer, % "-" opt.duration
   }
   else if (wp = "hide")  {
      ToolTip
      for key, timer in opt.timers
         SetTimer, % timer, Delete
      opt.timers := [], (lp && prev := "")
   }
   else if (wp = "TrackOut")  {
      MouseGetPos,,, hwnd
      WinGet, PID, PID, ahk_id %hwnd%
      (MyPID != PID && %fn%("hide", true))
   }
}
stealzy пишет:

Перенос OnMessage внутрь функции мне не нравится, потому что в основном коде может уже быть OnMessage на WM_MOUSEMOVE, тогда придется изменять код библиотечной

Нет, OnMessage в этом случае будут добавляться один за другим, последующий не испортит предыдущий.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder