1

Тема: AHK: Замена Tip на TrayTip

Пытаюсь заменить Menu, Tray, Tip (т.к. текст большой не влазит) на TrayTip (он выводит просто замечательно, красиво и с иконкой).

Menu, Tray, Tip, % "" ; понимаю что неправильно, но как отменить его появление ???

OnMessage(0x404,"WindowProc")  ; м.б. можно вызвать TrayTip как то проще???
Return

WindowProc(wParam,lParam,Msg,hwnd)  { 
	TrayTip, Горячие клавиши, Закрепить окно`t`t`tCtrl Space`nРасчет выделенного выражения`tCtrl Shift =`nВыделенную сумму - прописью`tCtrl Shift W, 2, 1
}

Проблемы:
1. Родной Menu, Tray, Tip при наведении мыши на иконку в трее все равно возникает. Как его убить насовсем???
2. При наведении мыши на иконку в трее заданный TrayTip выскакивает, но не исчезает.

2

Re: AHK: Замена Tip на TrayTip

Подсказка при наведении прописана в NOTIFYICONDATA (параметр TCHAR szTip[64]). Изменить эту структуру можно функцией Shell_NotifyIcon.

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

3 (изменено: mozers, 2017-04-19 22:47:02)

Re: AHK: Замена Tip на TrayTip

teadrinker, спасибо за наводку. Пытаюсь разобраться...
А более простого пути впихнуть в Menu, Tray, Tip мой текст нет? Просто в документации ни о каких ограничениях на текст подсказки не упоминается...

4

Re: AHK: Замена Tip на TrayTip

Вроде ж есть  ограничение.

YMP пишет:

Menu, MenuName, Cmd [, P3, P4, P5]
...
Tip [, Text] Изменяет подсказку значка в трее, которая появляется при наведении на него курсора мыши. Чтобы подсказка состояла из нескольких строк, используйте перевод строки (`n), например, Строка1`nСтрока2. Максимальная длина воспроизводимого текста - 127 символов. Если параметр Text опущен, это приведёт к показу текста по умолчанию. Встроенная переменная A_IconTip содержит текущий текст подсказки (пуста, если текст - по умолчанию).

Или речь про другую документацию?

5

Re: AHK: Замена Tip на TrayTip

В NOTIFYICONDATA тоже есть ограничение по длине. Но скорее всего можно туда отправить пустую строку, в отличие от Menu, Tray, Tip.

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

6

Re: AHK: Замена Tip на TrayTip

ypppu, абсолютно точно! Эксперементально нашёл это ограничение (127 байт), а потом, прочитав твой пост, понял что документацию я читаю невнимательно. Но, правда, там надо еще дописать что табуляторы не допускаются (текст тупо обрезается после первого из них).

teadrinker пишет:

Но скорее всего можно туда отправить пустую строку

Спасибо, понял. Завтра проверю.

Остается вопрос - Как выводить TrayTip при наведении на иконку и скрывать при уходе курсора с иконки?
Т.е. чтобы TrayTip работал идентично Menu, Tray, Tip.

7

Re: AHK: Замена Tip на TrayTip

mozers пишет:

Как выводить TrayTip при наведении на иконку

#Persistent
VarSetCapacity(NOTIFYICONDATA, size := A_PtrSize = 8 ? 848 : A_IsUnicode? 828 : 444, 0)
NumPut(size, NOTIFYICONDATA, "UInt")
NumPut(A_ScriptHwnd, NOTIFYICONDATA, A_PtrSize)
NumPut(0x404, NOTIFYICONDATA, 2*A_PtrSize, "UInt")
NumPut(NIF_TIP := 4, NOTIFYICONDATA, 2*A_PtrSize + 4, "UInt")
NumPut(0, NOTIFYICONDATA, 4*A_PtrSize + 8)
DllCall("shell32\Shell_NotifyIcon", "UInt", NIM_MODIFY := 1, Ptr, &NOTIFYICONDATA)

OnMessage(0x404, Func("AHK_NOTIFYICON").Bind("Заголовок", "Мой текст", 2, 1))
Return

AHK_NOTIFYICON(title, text, seconds, options, wp, lp)  {
   static WM_MOUSEMOVE := 0x200, showtip := []
   if (lp = WM_MOUSEMOVE && !showtip[1])  {
      showtip[1] := true
      TrayTip, % title, % text, 30, % options
      timer := Func("TraytipDel").Bind(showtip)
      SetTimer, % timer, % "-" . (seconds * 1000)
   }
}

TraytipDel(info)  {
   info[1] := false
   TrayTip
}
mozers пишет:

и скрывать при уходе курсора с иконки?

Событие ухода курсора отслеживается системой, но с помощью AHK_NOTIFYICON, вроде, его отследить не получится. Можно с помощью относительно сложного кода по таймеру получать координаты иконки и курсора.

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

8 (изменено: teadrinker, 2017-04-26 20:38:51)

Re: AHK: Замена Tip на TrayTip

Вот так примерно:

#NoEnv
RemoveTip()  ; удаление дефолтной подсказки при наведении на иконку скрипта
OnMessage(0x404, Func("AHK_NOTIFYICON").Bind("Заголовок", "Мой текст", 1))
Return

RemoveTip()  {
   static uID := 0x404, NIF_TIP := 4, NIM_MODIFY := 1
   
   VarSetCapacity(NOTIFYICONDATA, size := A_PtrSize = 8 ? 848 : A_IsUnicode? 828 : 444, 0)
   NumPut(size, NOTIFYICONDATA, "UInt")
   NumPut(A_ScriptHwnd, NOTIFYICONDATA, A_PtrSize)
   NumPut(uID, NOTIFYICONDATA, 2*A_PtrSize, "UInt")
   NumPut(NIF_TIP, NOTIFYICONDATA, 2*A_PtrSize + 4, "UInt")
   NumPut(0, NOTIFYICONDATA, 4*A_PtrSize + 8)
   DllCall("shell32\Shell_NotifyIcon", "UInt", NIM_MODIFY, Ptr, &NOTIFYICONDATA)
}

AHK_NOTIFYICON(title, text, options, wp, lp)  {
   static WM_MOUSEMOVE := 0x200, showtip := []
   if (lp = WM_MOUSEMOVE && !showtip[1])  {
      showtip[1] := true
      MouseGetPos,,,, hToolbar, 2
      TrayTip, % title, % text, 30, % options
      timer := Func("TraytipDel").Bind(showtip, hToolbar)
      SetTimer, % timer, 100
   }
}

TraytipDel(info, hToolbar)  {
   Loop 1  {
      if oPos := GetMousePosOnToolbar()  {
         VarSetCapacity(RECT, 16, 0)
         GetScriptIconPos(hToolbar, RECT)
         WinGetPos, xTB, yTB,,, ahk_id %hToolbar%
         POINT := (oPos.x - xTB) | (oPos.y - yTB) << 32
         if DllCall("PtInRect", Ptr, &RECT, Int64, POINT)
            break
      }
      try SetTimer,, Delete
      info[1] := false
      TrayTip
   }
}

GetMousePosOnToolbar()  {
   CoordMode, Mouse
   MouseGetPos, X, Y, hwnd
   WinGetClass, winClass, ahk_id %hwnd%
   if winClass not in Shell_TrayWnd,NotifyIconOverflowWindow,tooltips_class32
      Return
   
   Return {x: X, y: Y}
}

GetScriptIconPos(hToolbar, ByRef RECT)  {
   static WM_USER := 0x400, TB_BUTTONCOUNT := WM_USER + 24, TB_GETBUTTON := WM_USER + 23, TB_GETITEMRECT := WM_USER + 29
      , PtrSize := A_Is64bitOS ? 8 : 4, szTBBUTTON := 8 + PtrSize*3, szTRAYDATA := 16 + PtrSize*2
      
   WinExist("ahk_id" hToolbar)
   WinGet, PID, PID
   RemoteBuff := New RemoteBuffer(PID, szTBBUTTON)
   
   SendMessage, TB_BUTTONCOUNT
   Loop % ErrorLevel  {
      SendMessage, TB_GETBUTTON, A_Index - 1, RemoteBuff.ptr
      pTBBUTTON := RemoteBuff.Read(szTBBUTTON)
      TraydataOffset := NumGet(pTBBUTTON + 8 + PtrSize) - RemoteBuff.ptr
      pTRAYDATA := RemoteBuff.Read(szTRAYDATA, TraydataOffset)
      hWnd := NumGet(pTRAYDATA + 0, PtrSize = 4 ? "UInt" : "UInt64")
      if (hWnd != A_ScriptHwnd)
         continue
      
      SendMessage, TB_GETITEMRECT, A_Index - 1, RemoteBuff.ptr
      pRECT := RemoteBuff.Read(16)
      DllCall("RtlMoveMemory", Ptr, &RECT, Ptr, pRECT, Ptr, 16)
      break
   }
}

class RemoteBuffer
{
   __New(PID, size)  {
      static PROCESS_VM_OPERATION := 0x8, PROCESS_VM_WRITE := 0x20
           , PROCESS_VM_READ := 0x10, MEM_COMMIT := 0x1000, PAGE_READWRITE := 0x4
         
      if !(this.hProc := DllCall("OpenProcess", UInt, PROCESS_VM_OPERATION|PROCESS_VM_READ|PROCESS_VM_WRITE, Int, 0, UInt, PID, Ptr))
         Return
      
      if !(this.ptr := DllCall("VirtualAllocEx", Ptr, this.hProc, Ptr, 0, Ptr, size, UInt, MEM_COMMIT, UInt, PAGE_READWRITE, Ptr))
         Return, "", DllCall("CloseHandle", Ptr, this.hProc)
      
      this.hHeap := DllCall("GetProcessHeap", Ptr)
   }
   
   __Delete()  {
      DllCall("VirtualFreeEx", Ptr, this.hProc, Ptr, this.ptr, UInt, 0, UInt, MEM_RELEASE := 0x8000)
      DllCall("CloseHandle", Ptr, this.hProc)
      DllCall("HeapFree", Ptr, this.hHeap, UInt, 0, Ptr, this.pHeap)
   }
   
   Read(size, offset = 0)  {
      (this.pHeap && DllCall("HeapFree", Ptr, this.hHeap, UInt, 0, Ptr, this.pHeap))
      this.pHeap := DllCall("HeapAlloc", Ptr, this.hHeap, UInt, HEAP_ZERO_MEMORY := 0x8, Ptr, size, Ptr)
      if !DllCall("ReadProcessMemory", Ptr, this.hProc, Ptr, this.ptr + offset, Ptr, this.pHeap, Ptr, size, Int, 0)
         Return, 0, DllCall("MessageBox", Ptr, 0, Str, "Не удалось прочитать данные`nОшибка " A_LastError, Str, "", UInt, 0)
      Return this.pHeap
   }
   
   Write(pLocalBuff, size, offset = 0)  {
      if !res := DllCall("WriteProcessMemory", Ptr, this.hProc, Ptr, this.ptr + offset, Ptr, pLocalBuff, Ptr, size, PtrP, writtenBytes)
         DllCall("MessageBox", Ptr, 0, Str, "Не удалось записать данные`nОшибка " A_LastError, Str, "", UInt, 0)
      Return writtenBytes
   }
}
Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

9 (изменено: mozers, 2017-04-20 22:20:06)

Re: AHK: Замена Tip на TrayTip

teadrinker
Спасибо! Работает как положено.
Но, всё таки, это-ж просто офигеть сколько кода ради моей невинной затеи...
Мой вариант, конечно, примитивен и не так красиво срабатывает, но...

Menu, Tray, Tip, % "`t" ; убираем (почти) оригинальную подсказку

OnMessage(0x404,"WindowProc")

WindowProc(wParam,lParam,Msg,hwnd) {
	static tt
	If (lParam = 512) {
		If (!tt) {
			TrayTip, Горячие клавиши, Закрепить окно`t`t`tCtrl Space`nРасчет выделенного выражения`tCtrl Shift =`nВыделенную сумму - прописью`tCtrl Shift W, 1, 1
			tt := true
		}
	} else {
		tt := false
	}
}

P.S. То, что на Win7 смотрелось вполне прилично, на Win10 не выдерживает никакой критики.
Пожалуй, самым универсальным вариантом будет скрестить код teadrinker из поста выше и его же TrayTip, выложенный в Коллекции.

10 (изменено: mozers, 2017-05-01 22:16:06)

Re: AHK: Замена Tip на TrayTip

Благодаря доработанному teadrinker классу TrayTip стало возможным сделать идеальную всплывающую подсказку к значку в трее:

RemoveTip()  ; удаление дефолтной подсказки

global myTrayTip
myTrayTip := new ToolTip({ text: "Закрепить окно`t`t`tCtrl Space`nРасчет выделенного выражения`tCtrl Shift =`nВыделенную сумму - прописью`tCtrl Shift W"
                        , title: "Горячие клавиши"
                        , CloseButton: true
                        , Icon: 1              ; 1 — Info, 2 — Warning, 3 — Error, n > 3 — предполагается hIcon
                        , TrayTip: true
                        , ShowNow: false})

OnMessage(0x404, "WindowProc")

WindowProc(wParam,lParam,Msg,hwnd) {
   myTrayTip.Show(,, 2000)
}