Тема: AHK: Изменить размер шрифта TOOLTIP
Возможно ли изменить размер шрифта именно tooltip ?
Пример
Tooltip, info, ; размер шрифта.
Или какимто другим образом.
Пытался найти по справкам но безуспешно.
Спасибо зарание !
Вы не вошли. Пожалуйста, войдите или зарегистрируйтесь.
Страницы 1
Чтобы отправить ответ, вы должны войти или зарегистрироваться
Возможно ли изменить размер шрифта именно tooltip ?
Пример
Tooltip, info, ; размер шрифта.
Или какимто другим образом.
Пытался найти по справкам но безуспешно.
Спасибо зарание !
Свойства всплывающей подсказки задаются в системе:
rundll32.exe shell32.dll Control_RunDLL desk.cpl,desc,
Понял спасибо!
Шрифт конкретного окна ToolTip можно и скриптом задать, только придётся создавать его вручную через winapi, а не командой.
Пример:
myTrayTip := new ToolTip({ text: "Модифицированный TrayTip"
, title: "AutoHotkey"
, CloseButton: true
, Icon: 1 ; 1 — Info, 2 — Warning, 3 — Error, n > 3 — предполагается hIcon
, TrayTip: true
, FontSize: 18
, FontName: "Verdana" })
myToolTip := new ToolTip({ text: "Это модифицированный ToolTip. `nОн сейчас прозрачен для кликов мыши."
, title: "AutoHotkey"
, Icon: 2
, transparent: true ; если true — окно прозрачно для мыши
, BallonTip: true ; BalloonTip — это ToolTip с хвостиком
, FontSize: 28
, FontName: "Times New Roman"
, FontStyle: "italic" })
Sleep, 2500
myToolTip.Hide() ; скрываем
myToolTip.SetText("Можно менять текст`nи расположение") ; меняем текст
Sleep, 500
myToolTip.Show(50, 50) ; показываем в координатах x = 50, y = 50
Sleep, 2000
myToolTip.Hide()
myToolTip.SetText("I'm near the mouse cursor!")
Sleep, 500
myToolTip.Show() ; без координат — в районе курсора
Sleep, 2500
myToolTip := "" ; уничтожаем ToolTip
myTrayTip.SetText("Goodbye!")
Sleep, 1000
myTrayTip := ""
ExitApp
class ToolTip {
__New( options ) {
/*
Возможные опции:
text, title
icon (1: Info, 2: Warning; 3: Error; n > 3: предполагается hIcon)
CloseButton (true или false)
transparent (true или false)
x, y — координаты, если не указаны, ToolTip появится вблизи курсора
BallonTip (BalloonTip — это ToolTip с хвостиком, true или false)
TrayTip — будет показан BalloonTip у иконки скрипта в трее, параметры x, y, и BalloonTip игнорируются
FontName, FontSize
FontStyle (bold, italic, underline, strikeout в любом сочетании через пробел)
*/
for k, v in options
this[k] := v
((this.icon || this.CloseButton) && this.title = "") && this.title := " "
this._CreateToolTip()
this.Show()
}
_CreateToolTip() {
static WS_POPUP := 0x80000000, WS_EX_TOPMOST := 8, WS_EX_TRANSPARENT := 0x20
, TTS_NOPREFIX := 2, TTS_ALWAYSTIP := 1, TTS_BALLOON := 0x40, TTS_CLOSE := 0x80
, TTF_TRACK := 0x20, TTF_ABSOLUTE := 0x80, WM_GETFONT := 0x31
, mult := A_IsUnicode ? 2 : 1, szTI := A_PtrSize = 4 ? 48 : 72, szLF := 28 + 32 * mult
, LOGPIXELSY := 90, ANTIALIASED_QUALITY := 4
, styles := { bold: {value: 700, offset: 16, size: "Int"}
, italic: {value: 1, offset: 20, size: "Char"}
, underline: {value: 1, offset: 21, size: "Char"}
, strikeout: {value: 1, offset: 22, size: "Char"} }
VarSetCapacity(TOOLINFO, szTI, 0)
this.pTI := &TOOLINFO
NumPut(szTI, TOOLINFO)
(this.TrayTip && this.BallonTip := true)
NumPut(TTF_TRACK|(this.BallonTip ? 0 : TTF_ABSOLUTE), &TOOLINFO + 4)
this.hwnd := DllCall("CreateWindowEx", UInt, WS_EX_TOPMOST|(this.transparent ? WS_EX_TRANSPARENT : 0)
, Str, "tooltips_class32", Str, ""
, UInt, WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP | (this.CloseButton ? TTS_CLOSE : 0) | (this.BallonTip ? TTS_BALLOON : 0)
, Int, 0, Int, 0, Int, 0, Int, 0, Ptr, 0, Ptr, 0, Ptr, 0, Ptr, 0)
hPrevFont := this._SendMessage(WM_GETFONT)
VarSetCapacity(LOGFONT, szLF, 0)
DllCall("GetObject", Ptr, hPrevFont, Int, szLF, Ptr, &LOGFONT)
DllCall("DeleteObject", Ptr, hPrevFont)
if this.FontSize {
hDC := DllCall("GetDC", Ptr, this.hwnd, Ptr)
height := -DllCall("MulDiv", Int, this.FontSize, Int, DllCall("GetDeviceCaps", Ptr, hDC, Int, LOGPIXELSY), Int, 72)
DllCall("ReleaseDC", Ptr, this.hwnd, Ptr, hDC)
NumPut(height, LOGFONT, "Int")
}
FontStyle := this.FontStyle
Loop, parse, FontStyle, %A_Space%
if obj := styles[A_LoopField]
NumPut(obj.value, &LOGFONT + obj.offset, obj.size)
(this.FontSize > 24 && NumPut(ANTIALIASED_QUALITY, &LOGFONT + 26, "Char"))
this.FontName && StrPut(this.FontName, &LOGFONT + 28, StrLen(this.FontName) * mult, A_IsUnicode ? "UTF-16" : "CP0")
this.hFont := DllCall("CreateFontIndirect", Ptr, &LOGFONT, Ptr)
this._SetInfo()
}
_SetInfo() {
static WM_USER := 0x400, WM_SETFONT := 0x30
, TTM_ADDTOOL := WM_USER + (A_IsUnicode ? 50 : 4)
, TTM_SETTITLE := WM_USER + (A_IsUnicode ? 33 : 32)
this._SendMessage(WM_SETFONT, this.hFont, 1)
this._SendMessage(TTM_ADDTOOL, 0, this.pTI)
this._SendMessage(TTM_SETTITLE, this.icon, &(t := this.title))
this.SetText(this.text)
}
SetText(text) {
static WM_USER := 0x400, TTM_UPDATETIPTEXT := WM_USER + (A_IsUnicode ? 57 : 12)
NumPut(&text, this.pTI + (A_PtrSize = 4 ? 36 : 48))
this._SendMessage(TTM_UPDATETIPTEXT, 0, this.pTI)
}
Show(x := "", y := "") {
static WM_USER := 0x400, TTM_TRACKACTIVATE := WM_USER + 17, TTM_TRACKPOSITION := WM_USER + 18
if this.TrayTip
this._GetTrayIconCoords(xTT, yTT)
else {
xTT := x, yTT := y
(xTT = "" && xTT := this.x)
(yTT = "" && yTT := this.y)
if (xTT = "" || yTT = "") {
CoordMode, Mouse
MouseGetPos, xm, ym
(xTT = "" && xTT := xm + 10)
(yTT = "" && yTT := ym + 10)
}
}
this._SendMessage(TTM_TRACKPOSITION, 0, xTT|(yTT<<16))
this._SendMessage(TTM_TRACKACTIVATE, 1, this.pTI)
if this.BallonTip
xMax := A_ScreenWidth, yMax := A_ScreenHeight
else {
WinGetPos,,, W, H, % "ahk_id" this.hwnd
xMax := A_ScreenWidth - W - 10
yMax := A_ScreenHeight - H - 10
}
if (xTT > xMax || yTT > yMax) {
(xTT > xMax && xTT := xMax)
(yTT > yMax && yTT := yMax)
this._SendMessage(TTM_TRACKPOSITION, 0, xTT|(yTT<<16))
}
}
Hide() {
static WM_USER := 0x400, TTM_TRACKACTIVATE := WM_USER + 17
this._SendMessage(TTM_TRACKACTIVATE, 0, this.pTI)
}
_SendMessage(msg, wp := 0, lp := 0) {
Return DllCall("SendMessage", Ptr, this.hwnd, UInt, msg, Ptr, wp, Ptr, lp, Ptr)
}
_GetTrayIconCoords(ByRef x, ByRef y) {
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
ControlGet, hTray, hwnd,, ToolbarWindow321, ahk_class Shell_TrayWnd
WinWait, ahk_id %hTray%
WinGet, PID, PID
WinGetPos, xTB, yTB
if !IsObject(RemoteBuff := new RemoteBuffer(PID, szTBBUTTON)) {
x := xTB, y := yTB
MsgBox, % "Не удалось создать удалённый буфер`nОшибка " A_LastError
Return
}
SendMessage, TB_BUTTONCOUNT
Loop % ErrorLevel {
SendMessage, TB_GETBUTTON, A_Index - 1, RemoteBuff.ptr
pTBBUTTON := RemoteBuff.Read(szTBBUTTON)
pTRAYDATA := RemoteBuff.Read(szTRAYDATA, NumGet(pTBBUTTON + 8 + PtrSize) - RemoteBuff.ptr)
hWnd := NumGet(pTRAYDATA + 0, PtrSize = 4 ? "UInt" : "UInt64")
if (hWnd = A_ScriptHwnd) {
SendMessage, TB_GETITEMRECT, A_Index - 1, RemoteBuff.ptr
pRECT := RemoteBuff.Read(16)
x := NumGet(pRECT + 0, "Int") + xTB + 5, y := NumGet(pRECT + 4, "Int") + yTB + 5
break
}
}
RemoteBuff := "", ((x = "" || y = "") && x := xTB, y := yTB)
}
__Delete() {
DllCall("DeleteObject", Ptr, this.hFont)
DllCall("DestroyWindow", Ptr, this.hwnd)
}
}
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", UInt, this.hProc, UInt, 0, UInt, size, UInt, MEM_COMMIT, UInt, PAGE_READWRITE, Ptr))
Return, "", DllCall("CloseHandle", Ptr, this.hProc)
}
__Delete() {
DllCall("VirtualFreeEx", Ptr, this.hProc, Ptr, this.ptr, UInt, 0, UInt, MEM_RELEASE := 0x8000)
DllCall("CloseHandle", Ptr, this.hProc)
}
Read(size, offset = 0) {
static LocalBuff
VarSetCapacity(LocalBuff, size, 0)
if !DllCall("ReadProcessMemory", Ptr, this.hProc, Ptr, this.ptr + offset, Ptr, &LocalBuff, UInt, size, UInt, 0)
Return, 0, DllCall("MessageBox", Ptr, 0, Str, "Не удалось прочитать данные`nОшибка """ A_LastError """", Str, "", UInt, 0)
VarSetCapacity(LocalBuff, -1)
Return &LocalBuff
}
}
Нужная штука, думаю в коллекции самое место. Добавить ещё опцию через сколько миллисекунд скрыть.
Здесь есть проблема с TrayTip. Я использую недокументированную структуру TRAYDATA для получения hWnd связанного с иконкой окна, а в Windows 10 она, вроде, уже не работает, нужно другой способ искать.
Ну уже ToolTip штука нужная, а про TrayTip написать что он довеском тут, с ограничениями.
Или просто ToolTip выложить, раз везде работает.
Добавить ещё опцию через сколько миллисекунд скрыть.
Кстати, тоже интересный вопрос. А как можно запрограммировать удаление экземпляра объекта изнутри конструктора?
Первой мыслью было создать таймер на метод __Delete(). Но в этом случае окно-то удаляется, но сам объект в памяти остаётся. Пример:
#Persistent
myToolTip := new ToolTip({ FontSize: 30
, text: "Я буду виден 3 секунды"
, time: 3000 })
Sleep, 4000
ExitApp
class ToolTip {
__New( options ) {
/*
Возможные опции:
text, title
icon (1: Info, 2: Warning; 3: Error; n > 3: предполагается hIcon)
CloseButton (true или false)
transparent (true или false)
x, y — координаты, если не указаны, ToolTip появится вблизи курсора
BallonTip (BalloonTip — это ToolTip с хвостиком, true или false)
TrayTip — будет показан BalloonTip у иконки скрипта в трее, параметры x, y, и BalloonTip игнорируются
FontName, FontSize
FontStyle (bold, italic, underline, strikeout в любом сочетании через пробел)
time — время, через которое ToolTip будет уничтожен
*/
for k, v in options
this[k] := v
((this.icon || this.CloseButton) && this.title = "") && this.title := " "
this._CreateToolTip()
this.Show()
}
_CreateToolTip() {
static WS_POPUP := 0x80000000, WS_EX_TOPMOST := 8, WS_EX_TRANSPARENT := 0x20
, TTS_NOPREFIX := 2, TTS_ALWAYSTIP := 1, TTS_BALLOON := 0x40, TTS_CLOSE := 0x80
, TTF_TRACK := 0x20, TTF_ABSOLUTE := 0x80, WM_GETFONT := 0x31
, mult := A_IsUnicode ? 2 : 1, szTI := A_PtrSize = 4 ? 48 : 72, szLF := 28 + 32 * mult
, LOGPIXELSY := 90, ANTIALIASED_QUALITY := 4
, styles := { bold: {value: 700, offset: 16, size: "Int"}
, italic: {value: 1, offset: 20, size: "Char"}
, underline: {value: 1, offset: 21, size: "Char"}
, strikeout: {value: 1, offset: 22, size: "Char"} }
VarSetCapacity(TOOLINFO, szTI, 0)
this.pTI := &TOOLINFO
NumPut(szTI, TOOLINFO)
(this.TrayTip && this.BallonTip := true)
NumPut(TTF_TRACK|(this.BallonTip ? 0 : TTF_ABSOLUTE), &TOOLINFO + 4)
this.hwnd := DllCall("CreateWindowEx", UInt, WS_EX_TOPMOST|(this.transparent ? WS_EX_TRANSPARENT : 0)
, Str, "tooltips_class32", Str, ""
, UInt, WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP | (this.CloseButton ? TTS_CLOSE : 0) | (this.BallonTip ? TTS_BALLOON : 0)
, Int, 0, Int, 0, Int, 0, Int, 0, Ptr, 0, Ptr, 0, Ptr, 0, Ptr, 0)
hPrevFont := this._SendMessage(WM_GETFONT)
VarSetCapacity(LOGFONT, szLF, 0)
DllCall("GetObject", Ptr, hPrevFont, Int, szLF, Ptr, &LOGFONT)
DllCall("DeleteObject", Ptr, hPrevFont)
if this.FontSize {
hDC := DllCall("GetDC", Ptr, this.hwnd, Ptr)
height := -DllCall("MulDiv", Int, this.FontSize, Int, DllCall("GetDeviceCaps", Ptr, hDC, Int, LOGPIXELSY), Int, 72)
DllCall("ReleaseDC", Ptr, this.hwnd, Ptr, hDC)
NumPut(height, LOGFONT, "Int")
}
FontStyle := this.FontStyle
Loop, parse, FontStyle, %A_Space%
if obj := styles[A_LoopField]
NumPut(obj.value, &LOGFONT + obj.offset, obj.size)
(this.FontSize > 24 && NumPut(ANTIALIASED_QUALITY, &LOGFONT + 26, "Char"))
this.FontName && StrPut(this.FontName, &LOGFONT + 28, StrLen(this.FontName) * mult, A_IsUnicode ? "UTF-16" : "CP0")
this.hFont := DllCall("CreateFontIndirect", Ptr, &LOGFONT, Ptr)
if this.time {
timer := ObjBindMethod(this, "__Delete")
SetTimer % timer, % "-" . this.time
}
this._SetInfo()
}
_SetInfo() {
static WM_USER := 0x400, WM_SETFONT := 0x30
, TTM_ADDTOOL := WM_USER + (A_IsUnicode ? 50 : 4)
, TTM_SETTITLE := WM_USER + (A_IsUnicode ? 33 : 32)
this._SendMessage(WM_SETFONT, this.hFont, 1)
this._SendMessage(TTM_ADDTOOL, 0, this.pTI)
this._SendMessage(TTM_SETTITLE, this.icon, &(t := this.title))
this.SetText(this.text)
}
SetText(text) {
static WM_USER := 0x400, TTM_UPDATETIPTEXT := WM_USER + (A_IsUnicode ? 57 : 12)
NumPut(&text, this.pTI + (A_PtrSize = 4 ? 36 : 48))
this._SendMessage(TTM_UPDATETIPTEXT, 0, this.pTI)
}
Show(x := "", y := "") {
static WM_USER := 0x400, TTM_TRACKACTIVATE := WM_USER + 17, TTM_TRACKPOSITION := WM_USER + 18
if this.TrayTip
this._GetTrayIconCoords(xTT, yTT)
else {
xTT := x, yTT := y
(xTT = "" && xTT := this.x)
(yTT = "" && yTT := this.y)
if (xTT = "" || yTT = "") {
CoordMode, Mouse
MouseGetPos, xm, ym
(xTT = "" && xTT := xm + 10)
(yTT = "" && yTT := ym + 10)
}
}
this._SendMessage(TTM_TRACKPOSITION, 0, xTT|(yTT<<16))
this._SendMessage(TTM_TRACKACTIVATE, 1, this.pTI)
if this.BallonTip
xMax := A_ScreenWidth, yMax := A_ScreenHeight
else {
WinGetPos,,, W, H, % "ahk_id" this.hwnd
xMax := A_ScreenWidth - W - 10
yMax := A_ScreenHeight - H - 10
}
if (xTT > xMax || yTT > yMax) {
(xTT > xMax && xTT := xMax)
(yTT > yMax && yTT := yMax)
this._SendMessage(TTM_TRACKPOSITION, 0, xTT|(yTT<<16))
}
}
Hide() {
static WM_USER := 0x400, TTM_TRACKACTIVATE := WM_USER + 17
this._SendMessage(TTM_TRACKACTIVATE, 0, this.pTI)
}
_SendMessage(msg, wp := 0, lp := 0) {
Return DllCall("SendMessage", Ptr, this.hwnd, UInt, msg, Ptr, wp, Ptr, lp, Ptr)
}
_GetTrayIconCoords(ByRef x, ByRef y) {
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, szHWND := PtrSize
ControlGet, hTray, hwnd,, ToolbarWindow321, ahk_class Shell_TrayWnd
WinWait, ahk_id %hTray%
WinGet, PID, PID
WinGetPos, xTB, yTB
if !IsObject(RemoteBuff := new RemoteBuffer(PID, szTBBUTTON)) {
x := xTB, y := yTB
MsgBox, % "Не удалось создать удалённый буфер`nОшибка " A_LastError
Return
}
SendMessage, TB_BUTTONCOUNT
Loop % ErrorLevel {
SendMessage, TB_GETBUTTON, A_Index - 1, RemoteBuff.ptr
pTBBUTTON := RemoteBuff.Read(szTBBUTTON)
pHWND := RemoteBuff.Read(szHWND, NumGet(pTBBUTTON + 8 + PtrSize) - RemoteBuff.ptr)
hWnd := NumGet(pHWND + 0, PtrSize = 4 ? "UInt" : "UInt64")
if (hWnd = A_ScriptHwnd) {
SendMessage, TB_GETITEMRECT, A_Index - 1, RemoteBuff.ptr
pRECT := RemoteBuff.Read(16)
x := NumGet(pRECT + 0, "Int") + xTB + 5, y := NumGet(pRECT + 4, "Int") + yTB + 5
break
}
}
RemoteBuff := "", ((x = "" || y = "") && x := xTB, y := yTB)
}
__Delete() {
SoundBeep
DllCall("DeleteObject", Ptr, this.hFont)
DllCall("DestroyWindow", Ptr, this.hwnd)
}
}
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", UInt, this.hProc, UInt, 0, UInt, size, UInt, MEM_COMMIT, UInt, PAGE_READWRITE, Ptr))
Return, "", DllCall("CloseHandle", Ptr, this.hProc)
}
__Delete() {
DllCall("VirtualFreeEx", Ptr, this.hProc, Ptr, this.ptr, UInt, 0, UInt, MEM_RELEASE := 0x8000)
DllCall("CloseHandle", Ptr, this.hProc)
}
Read(size, offset = 0) {
static LocalBuff
VarSetCapacity(LocalBuff, size, 0)
if !DllCall("ReadProcessMemory", Ptr, this.hProc, Ptr, this.ptr + offset, Ptr, &LocalBuff, UInt, size, UInt, 0)
Return, 0, DllCall("MessageBox", Ptr, 0, Str, "Не удалось прочитать данные`nОшибка """ A_LastError """", Str, "", UInt, 0)
VarSetCapacity(LocalBuff, -1)
Return &LocalBuff
}
}
В метод __Delete() включён SoundBeep для демонстрации. Как видно, он срабатывает два раза — один, когда уничтожается окно, второй, когда завершается скрипт. Получается как-то некрасиво.
Вот вариант от lexikos:
https://autohotkey.com/boards/viewtopic … amp;t=4777
Классный! Он просто изменяет обычный ToolTip. Но с TrayTip не работает. И таким способом нельзя сделать его прозрачным для кликов, будет перехватывать фокус. И BalloonTip не сделать.
как можно запрограммировать удаление экземпляра объекта изнутри конструктора?
Удалось решить.
myTrayTip := new ToolTip({ text: "Модифицированный TrayTip"
, title: "Стандартная иконка «Info»"
, CloseButton: true
, Icon: 1 ; 1 — Info, 2 — Warning, 3 — Error, n > 3 — предполагается hIcon
, TrayTip: true
, FontSize: 18
, FontName: "Verdana" })
myToolTip := new ToolTip({ text: "Это модифицированный ToolTip `nОн прозрачен для кликов мыши."
, title: "Кастомная иконка"
, Icon: hIcon := CreateIconFromBase64(GetIcon(), 32)
, transparent: true ; если true — окно прозрачно для мыши
, BallonTip: true ; BalloonTip — это ToolTip с хвостиком
, FontSize: 28
, FontName: "Times New Roman"
, FontStyle: "italic" })
ColoredTip:= new ToolTip({ text: " Я исчезну через 7 сек "
, x: A_ScreenWidth
, y: 0
, BallonTip: true
, title: " Я цветной ToolTip!"
, TextColor: "Navy"
, BackColor: 0xFFA500
, FontName: "Comic Sans MS"
, FontSize: 25
, TimeOut: 7000 }) ; будет автоматически удалён через 7000 мс
oTimer := Func("Timer").Bind(ColoredTip)
SetTimer, % oTimer, 1000
Sleep, 2500
myToolTip.Hide() ; скрываем
myToolTip.SetText("Можно менять текст`nи расположение") ; меняем текст
Sleep, 500
myToolTip.Show(50, 50) ; показываем в координатах x = 50, y = 50
Sleep, 2000
myToolTip.Hide()
myToolTip.SetText("I'm near the mouse cursor!")
Sleep, 500
myToolTip.Show() ; без координат — в районе курсора
Sleep, 2500
myToolTip := "" ; уничтожаем ToolTip, это можно было сделать автоматически, указав TimeOut
myTrayTip.SetText("Goodbye!")
Sleep, 1000
myTrayTip := ""
ExitApp
class ToolTip {
__New( options ) {
/*
Возможные опции:
text, title
icon (1: Info, 2: Warning; 3: Error; n > 3: предполагается hIcon)
CloseButton (true или false)
transparent (true или false)
x, y — координаты, если не указаны, ToolTip появится вблизи курсора
BallonTip (BalloonTip — это ToolTip с хвостиком, true или false)
TrayTip — будет показан BalloonTip у иконки скрипта в трее, параметры x, y, и BalloonTip игнорируются
FontName, FontSize
FontStyle (bold, italic, underline, strikeout в любом сочетании через пробел)
BackColor — цвет фона
TextColor — цвет текста
TimeOut — время, через которое ToolTip будет уничтожен
*/
for k, v in options
this[k] := v
((this.icon || this.CloseButton) && this.title = "") && this.title := " "
this._CreateToolTip()
this.Show()
}
_CreateToolTip() {
static WS_POPUP := 0x80000000, WS_EX_TOPMOST := 8, WS_EX_TRANSPARENT := 0x20
, TTS_NOPREFIX := 2, TTS_ALWAYSTIP := 1, TTS_BALLOON := 0x40, TTS_CLOSE := 0x80
, TTF_TRACK := 0x20, TTF_ABSOLUTE := 0x80, szTI := A_PtrSize = 4 ? 48 : 72
VarSetCapacity(TOOLINFO, szTI, 0)
this.pTI := &TOOLINFO
NumPut(szTI, TOOLINFO)
(this.TrayTip && this.BallonTip := true)
NumPut(TTF_TRACK|(this.BallonTip ? 0 : TTF_ABSOLUTE), &TOOLINFO + 4)
this.hwnd := DllCall("CreateWindowEx", UInt, WS_EX_TOPMOST|(this.transparent ? WS_EX_TRANSPARENT : 0)
, Str, "tooltips_class32", Str, ""
, UInt, WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP | (this.CloseButton ? TTS_CLOSE : 0) | (this.BallonTip ? TTS_BALLOON : 0)
, Int, 0, Int, 0, Int, 0, Int, 0, Ptr, 0, Ptr, 0, Ptr, 0, Ptr, 0)
if (this.FontName || this.FontSize || this.FontStyle)
this._SetFont()
if (this.TextColor != "" || this.BackColor != "")
this._SetColor()
this._SetInfo()
if this.TimeOut {
timer := ObjBindMethod(this, "_Destroy")
SetTimer % timer, % "-" . this.TimeOut
}
}
_SetFont() {
static WM_GETFONT := 0x31, mult := A_IsUnicode ? 2 : 1, szLF := 28 + 32 * mult
, LOGPIXELSY := 90, ANTIALIASED_QUALITY := 4
, styles := { bold: {value: 700, offset: 16, size: "Int"}
, italic: {value: 1, offset: 20, size: "Char"}
, underline: {value: 1, offset: 21, size: "Char"}
, strikeout: {value: 1, offset: 22, size: "Char"} }
hPrevFont := this._SendMessage(WM_GETFONT)
VarSetCapacity(LOGFONT, szLF, 0)
DllCall("GetObject", Ptr, hPrevFont, Int, szLF, Ptr, &LOGFONT)
DllCall("DeleteObject", Ptr, hPrevFont)
if this.FontSize {
hDC := DllCall("GetDC", Ptr, this.hwnd, Ptr)
height := -DllCall("MulDiv", Int, this.FontSize, Int, DllCall("GetDeviceCaps", Ptr, hDC, Int, LOGPIXELSY), Int, 72)
DllCall("ReleaseDC", Ptr, this.hwnd, Ptr, hDC)
NumPut(height, LOGFONT, "Int")
}
FontStyle := this.FontStyle
Loop, parse, FontStyle, %A_Space%
if obj := styles[A_LoopField]
NumPut(obj.value, &LOGFONT + obj.offset, obj.size)
(this.FontSize > 24 && NumPut(ANTIALIASED_QUALITY, &LOGFONT + 26, "Char"))
this.FontName && StrPut(this.FontName, &LOGFONT + 28, StrLen(this.FontName) * mult, A_IsUnicode ? "UTF-16" : "CP0")
this.hFont := DllCall("CreateFontIndirect", Ptr, &LOGFONT, Ptr)
}
_SetColor() {
static WM_USER := 0x400, TTM_SETTIPBKCOLOR := WM_USER + 19, TTM_SETTIPTEXTCOLOR := WM_USER + 20
VarSetCapacity(empty, 2, 0)
DllCall("UxTheme\SetWindowTheme", Ptr, this.hwnd, Ptr, 0, Ptr, &empty)
( this.TextColor != "" && this._SendMessage(TTM_SETTIPTEXTCOLOR, this._GetColor(this.TextColor)) )
( this.BackColor != "" && this._SendMessage(TTM_SETTIPBKCOLOR, this._GetColor(this.BackColor)) )
}
_GetColor(color) {
static WS_CHILD := 0x40000000, WM_CTLCOLORSTATIC := 0x138
Gui, New, +hwndhGui +%WS_CHILD%
Gui, Color, % color + 0 = "" ? color : Format("{:x}", color)
Gui, Add, Text, hwndhText
hdc := DllCall("GetDC", Ptr, hText, Ptr)
SendMessage, WM_CTLCOLORSTATIC, hdc, hText,, ahk_id %hGui%
clr := DllCall("GetBkColor", Ptr, hdc)
DllCall("ReleaseDC", Ptr, hText, Ptr, hdc)
Gui, Destroy
Return clr
}
_SetInfo() {
static WM_USER := 0x400, WM_SETFONT := 0x30
, TTM_ADDTOOL := WM_USER + (A_IsUnicode ? 50 : 4)
, TTM_SETTITLE := WM_USER + (A_IsUnicode ? 33 : 32)
this._SendMessage(WM_SETFONT, this.hFont, 1)
this._SendMessage(TTM_ADDTOOL, 0, this.pTI)
this._SendMessage(TTM_SETTITLE, this.icon, &(t := this.title))
this.SetText(this.text)
}
SetText(text) {
static WM_USER := 0x400, TTM_UPDATETIPTEXT := WM_USER + (A_IsUnicode ? 57 : 12)
NumPut(&text, this.pTI + (A_PtrSize = 4 ? 36 : 48))
this._SendMessage(TTM_UPDATETIPTEXT, 0, this.pTI)
}
Show(x := "", y := "") {
static WM_USER := 0x400, TTM_TRACKACTIVATE := WM_USER + 17, TTM_TRACKPOSITION := WM_USER + 18
if this.TrayTip
this._GetTrayIconCoords(xTT, yTT)
else {
xTT := x, yTT := y
(xTT = "" && xTT := this.x)
(yTT = "" && yTT := this.y)
if (xTT = "" || yTT = "") {
CoordMode, Mouse
MouseGetPos, xm, ym
(xTT = "" && xTT := xm + 10)
(yTT = "" && yTT := ym + 10)
}
}
this._SendMessage(TTM_TRACKPOSITION, 0, xTT|(yTT<<16))
this._SendMessage(TTM_TRACKACTIVATE, 1, this.pTI)
if this.BallonTip
xMax := A_ScreenWidth, yMax := A_ScreenHeight
else {
WinGetPos,,, W, H, % "ahk_id" this.hwnd
xMax := A_ScreenWidth - W - 10
yMax := A_ScreenHeight - H - 10
}
if (xTT > xMax || yTT > yMax) {
(xTT > xMax && xTT := xMax)
(yTT > yMax && yTT := yMax)
this._SendMessage(TTM_TRACKPOSITION, 0, xTT|(yTT<<16))
}
}
Hide() {
static WM_USER := 0x400, TTM_TRACKACTIVATE := WM_USER + 17
this._SendMessage(TTM_TRACKACTIVATE, 0, this.pTI)
}
_SendMessage(msg, wp := 0, lp := 0) {
Return DllCall("SendMessage", Ptr, this.hwnd, UInt, msg, Ptr, wp, Ptr, lp, Ptr)
}
_GetTrayIconCoords(ByRef x, ByRef y) {
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, szHWND := PtrSize
ControlGet, hTray, hwnd,, ToolbarWindow321, ahk_class Shell_TrayWnd
WinWait, ahk_id %hTray%
WinGet, PID, PID
WinGetPos, xTB, yTB
if !IsObject(RemoteBuff := new RemoteBuffer(PID, szTBBUTTON)) {
x := xTB, y := yTB
MsgBox, % "Не удалось создать удалённый буфер`nОшибка " A_LastError
Return
}
SendMessage, TB_BUTTONCOUNT
Loop % ErrorLevel {
SendMessage, TB_GETBUTTON, A_Index - 1, RemoteBuff.ptr
pTBBUTTON := RemoteBuff.Read(szTBBUTTON)
pHWND := RemoteBuff.Read(szHWND, NumGet(pTBBUTTON + 8 + PtrSize) - RemoteBuff.ptr)
hWnd := NumGet(pHWND + 0, PtrSize = 4 ? "UInt" : "UInt64")
if (hWnd = A_ScriptHwnd) {
SendMessage, TB_GETITEMRECT, A_Index - 1, RemoteBuff.ptr
pRECT := RemoteBuff.Read(16)
x := NumGet(pRECT + 0, "Int") + xTB + 5, y := NumGet(pRECT + 4, "Int") + yTB + 5
break
}
}
RemoteBuff := "", ((x = "" || y = "") && x := xTB, y := yTB)
}
_Destroy() {
this.__Delete()
this.SetCapacity(0)
this.base := ""
}
__Delete() {
(this.hFont && DllCall("DeleteObject", Ptr, this.hFont))
(this.Icon > 3 && DllCall("DestroyIcon", Ptr, this.Icon))
DllCall("DestroyWindow", Ptr, this.hwnd)
}
}
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", UInt, this.hProc, UInt, 0, UInt, size, UInt, MEM_COMMIT, UInt, PAGE_READWRITE, Ptr))
Return, "", DllCall("CloseHandle", Ptr, this.hProc)
}
__Delete() {
DllCall("VirtualFreeEx", Ptr, this.hProc, Ptr, this.ptr, UInt, 0, UInt, MEM_RELEASE := 0x8000)
DllCall("CloseHandle", Ptr, this.hProc)
}
Read(size, offset = 0) {
static LocalBuff
VarSetCapacity(LocalBuff, size, 0)
if !DllCall("ReadProcessMemory", Ptr, this.hProc, Ptr, this.ptr + offset, Ptr, &LocalBuff, UInt, size, UInt, 0)
Return, 0, DllCall("MessageBox", Ptr, 0, Str, "Не удалось прочитать данные`nОшибка """ A_LastError """", Str, "", UInt, 0)
VarSetCapacity(LocalBuff, -1)
Return &LocalBuff
}
}
Timer(oTip) {
static i := 0
oTip.SetText(" Я исчезну через " (7 - ++i) " сек ")
if (i = 7)
SetTimer,, Delete
}
CreateIconFromBase64(StringBASE64, Size)
{
StringBase64ToData(StringBASE64, IconData)
Return DllCall("CreateIconFromResourceEx", Ptr, &IconData + 4
, UInt, NumGet(&IconData, "UInt"), UInt, true, UInt, 0x30000, Int, Size, Int, Size, UInt, 0)
}
StringBase64ToData(StringBase64, ByRef OutData)
{
DllCall("Crypt32.dll\CryptStringToBinary", Ptr, &StringBase64
, UInt, StrLen(StringBase64), UInt, CRYPT_STRING_BASE64 := 1, UInt, 0, UIntP, Bytes, UIntP, 0, UIntP, 0)
VarSetCapacity(OutData, Bytes)
DllCall("Crypt32.dll\CryptStringToBinary", Ptr, &StringBase64
, UInt, StrLen(StringBase64), UInt, CRYPT_STRING_BASE64, Str, OutData, UIntP, Bytes, UIntP, 0, UIntP, 0)
Return Bytes
}
GetIcon() {
Icon32 =
(RTrim Join
qBAAACgAAAAgAAAAQAAAAAEAIAAAAAAAgBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
FRgjABMXIQATFyEAFBchABMXIQAVGCMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAABUYIwAAAAAAAAAAAAAAAAIAAAAAAAAAABUYIwAVGCMAExchABMXIRQUFyETExchABUYIwAVGCMAAAAAAAAAAAAAAAACAAAAAAAAAAAVGCMAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFRgjABQXIgAUFyIAFBciABMXIQATFyEAFRgjABUYIwIUFyIAFBciqxQXIqoUFyIAFRgjAhUYIwAUFyIA
FBgiABQXIgATFyIAExciABUYIwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVGCMAFBciABQXIhAUFiFCExgiABMWIQAAAAAA
FRgjAxUYIwAVFiO4ExghuBUYIwAVGCMDAAAAABQXIgATGiIAExchQhMYIhATGCIAFRgjAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAABUYIwEUFyIAFBchMhQXIv8TFyFDFBciABUXIwIVGCMCFRgjABUXI7sUGCK7FRgjABUYIwIUFyMCFBciABQXIkMUFyL/FBchMhQXIgAVGCMBAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFRgjABUXIgEVFyIAFBciuRQXItoVGSICFRgiARUYIwMUFyIBFBcinRMXIp0UFyIBFRgjAxUYIgEVGCIC
FBci2hQXIroVFyIAFRciARUYIwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFRgjAAAAAAAAAAAAAAAAAgAAAAEVGCMAFBgjARQXIgAUFyIkFBci/xQXImEUFyIC
FRgjAAwOHwD/AAAA/wAAAAoRHQAVGCMAFBciAhQXImAUFyL/FBYiJRQXIgAUGCMBFBgiAAAAAAEAAAACAAAAAAAAAAAVGCMAAAAAAAAAAAAVGCMAFBUiABMVIgAUFyIA
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
+MADH/HgB4/38A/v///////P8///nnn//759///+f////n////////////8=
)
Return Icon32
}
У кого есть возможность запустить на Windows 10, протестируте пожалуйста, появляется ли TrayTip вблизи иконки скрипта в трее, или в стороне от неё.
А чем это лучше?
У кого есть возможность запустить на Windows 10
Попробовал запустить:
Не удалось прочитать данные
Ошибка "299"
После нажатия "Ок" всё работает.
У меня на 10 такое же сообщение в начале. Ни одно из окошек вблизи иконки не появляется.
Спасибо. А на десятке определяется hwnd трея с помощью кода:
ControlGet, hTray, hwnd,, ToolbarWindow321, ahk_class Shell_TrayWnd
MsgBox % hTray
? И как определяется, если нет?
Да, определяется. По кр. мере что-то этот код возвращает.
---------------------------
E00C0A0E363002A9.AHK
---------------------------
0x100de
---------------------------
ОК
---------------------------
YMP, так может глянешь, что там за проблема на десятке? В моём коде метод _GetTrayIconCoords(). Логика такая:
находим хэндл трея (контрол ToolbarWindow321 в окне Shell_TrayWnd);
каждой кнопке поочерёдно отправляем сообщение TB_GETBUTTON, получаем (через удалённый буфер) структуру TBBUTTON, там по смещению 8 + PtrSize (где PtrSize := A_Is64bitOS ? 8 : 4) пойнтер структуры, которая в семёрке выглядит так:
typedef TRAYDATA
{
IntPtr hwnd;
uint uID;
uint uCallbackMessage;
uint Reserved;
uint Reserved2;
IntPtr hIcon;
}
Структура недокументирована и на десятке может быть организована по-другому. Нужно получить hwnd и сравнить его с A_ScriptHwnd скрипта, чтоб понять, принадлежит ли иконка скрипту. Ошибка, которая возникает на десятке при чтении буфера — ERROR_PARTIAL_COPY. Такое может быть, когда 32-битным скриптом пытаешься читать 64-битное значение, но здесь, вроде, не в этом дело.
Хорошо, попробую разобраться.
Указатель на TRAYDATA, получаемый из TBBUTTON, равен 0.
Печально! А хэндл трея точно правильно определяется? TB_BUTTONCOUNT правильное значение возвращает?
Вот код нашёл: Tray Icon Windows 10. Можно с его помощью соотнести иконку с процессом и получить её координаты?
А, кажись нашёл, в чём дело. Тут надо ToolbarWindow323 использовать, тогда работает. TB_BUTTONCOUNT возвращает число тех иконок, которые не скрыты.
Если открыть окошко со скрытыми иконками, оно определяется, как ToolbarWindow321. Но когда в скрипте было это значение, то TB_BUTTONCOUNT возвращало 1, хотя там несколько иконок у меня.
Ага, отлично. А вот таким способом на десятке однозначно нельзя определить?
WinGet, hWnd,, ahk_class Shell_TrayWnd
for k, v in ["TrayNotifyWnd", "SysPager", "ToolbarWindow32"]
hToolbar := DllCall("FindWindowEx", Ptr, k = 1 ? hWnd : hToolbar, Ptr, 0, Str, v, UInt, 0, Ptr)
; для сравнения:
ControlGet, hTray, hwnd,, ToolbarWindow323, ahk_class Shell_TrayWnd
MsgBox, % Format("{:#x}", hToolbar) "`n" hTray
Если нельзя, то как-нибудь подобно можно, чтобы не зависеть от порядкового номера?
Одно и то же находит.
---------------------------
E00C0F04070E0019.AHK
---------------------------
0x100f2
0x100f2
---------------------------
ОК
---------------------------
myTrayTip := new ToolTip({ text: "Модифицированный TrayTip"
, title: "Стандартная иконка «Info»"
, CloseButton: true
, Icon: 1 ; 1 — Info, 2 — Warning, 3 — Error, n > 3 — предполагается hIcon
, TrayTip: true
, FontSize: 18
, FontName: "Verdana" })
myToolTip := new ToolTip({ text: "Это модифицированный ToolTip `nОн прозрачен для кликов мыши."
, title: "Кастомная иконка"
, Icon: hIcon := CreateIconFromBase64(GetIcon(), 32)
, transparent: true ; если true — окно прозрачно для мыши
, BallonTip: true ; BalloonTip — это ToolTip с хвостиком
, FontSize: 28
, FontName: "Times New Roman"
, FontStyle: "italic" })
ColoredTip:= new ToolTip({ text: " Я исчезну через 7 сек "
, x: A_ScreenWidth
, y: 0
, BallonTip: true
, title: " Я цветной ToolTip!"
, TextColor: "Navy"
, BackColor: 0xFFA500
, FontName: "Comic Sans MS"
, FontSize: 25
, TimeOut: 7000 }) ; будет автоматически удалён через 7000 мс
oTimer := Func("Timer").Bind(ColoredTip)
SetTimer, % oTimer, 1000
Sleep, 2500
myToolTip.Hide() ; скрываем
myToolTip.SetText("Можно менять текст`nи расположение") ; меняем текст
Sleep, 500
myToolTip.Show(50, 50) ; показываем в координатах x = 50, y = 50
Sleep, 2000
myToolTip.Hide()
myToolTip.SetText("I'm near the mouse cursor!")
Sleep, 500
myToolTip.Show() ; без координат — в районе курсора
Sleep, 2500
myToolTip := "" ; уничтожаем ToolTip, это можно было сделать автоматически, указав TimeOut
myTrayTip.SetText("Goodbye!")
Sleep, 1000
myTrayTip := ""
ExitApp
class ToolTip {
__New( options ) {
/*
Возможные опции:
text, title
icon (1: Info, 2: Warning; 3: Error; n > 3: предполагается hIcon)
CloseButton (true или false)
transparent (true или false)
x, y — координаты, если не указаны, ToolTip появится вблизи курсора
BallonTip (BalloonTip — это ToolTip с хвостиком, true или false)
TrayTip — будет показан BalloonTip у иконки скрипта в трее, параметры x, y, и BalloonTip игнорируются
FontName, FontSize
FontStyle (bold, italic, underline, strikeout в любом сочетании через пробел)
BackColor — цвет фона
TextColor — цвет текста
TimeOut — время, через которое ToolTip будет уничтожен
*/
for k, v in options
this[k] := v
((this.icon || this.CloseButton) && this.title = "") && this.title := " "
this._CreateToolTip()
this.Show()
}
_CreateToolTip() {
static WS_POPUP := 0x80000000, WS_EX_TOPMOST := 8, WS_EX_TRANSPARENT := 0x20
, TTS_NOPREFIX := 2, TTS_ALWAYSTIP := 1, TTS_BALLOON := 0x40, TTS_CLOSE := 0x80
, TTF_TRACK := 0x20, TTF_ABSOLUTE := 0x80, szTI := A_PtrSize = 4 ? 48 : 72
VarSetCapacity(TOOLINFO, szTI, 0)
this.pTI := &TOOLINFO
NumPut(szTI, TOOLINFO)
(this.TrayTip && this.BallonTip := true)
NumPut(TTF_TRACK|(this.BallonTip ? 0 : TTF_ABSOLUTE), &TOOLINFO + 4)
this.hwnd := DllCall("CreateWindowEx", UInt, WS_EX_TOPMOST|(this.transparent ? WS_EX_TRANSPARENT : 0)
, Str, "tooltips_class32", Str, ""
, UInt, WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP | (this.CloseButton ? TTS_CLOSE : 0) | (this.BallonTip ? TTS_BALLOON : 0)
, Int, 0, Int, 0, Int, 0, Int, 0, Ptr, 0, Ptr, 0, Ptr, 0, Ptr, 0)
if (this.FontName || this.FontSize || this.FontStyle)
this._SetFont()
if (this.TextColor != "" || this.BackColor != "")
this._SetColor()
this._SetInfo()
if this.TimeOut {
timer := ObjBindMethod(this, "_Destroy")
SetTimer % timer, % "-" . this.TimeOut
}
}
_SetFont() {
static WM_GETFONT := 0x31, mult := A_IsUnicode ? 2 : 1, szLF := 28 + 32 * mult
, LOGPIXELSY := 90, ANTIALIASED_QUALITY := 4
, styles := { bold: {value: 700, offset: 16, size: "Int"}
, italic: {value: 1, offset: 20, size: "Char"}
, underline: {value: 1, offset: 21, size: "Char"}
, strikeout: {value: 1, offset: 22, size: "Char"} }
hPrevFont := this._SendMessage(WM_GETFONT)
VarSetCapacity(LOGFONT, szLF, 0)
DllCall("GetObject", Ptr, hPrevFont, Int, szLF, Ptr, &LOGFONT)
DllCall("DeleteObject", Ptr, hPrevFont)
if this.FontSize {
hDC := DllCall("GetDC", Ptr, this.hwnd, Ptr)
height := -DllCall("MulDiv", Int, this.FontSize, Int, DllCall("GetDeviceCaps", Ptr, hDC, Int, LOGPIXELSY), Int, 72)
DllCall("ReleaseDC", Ptr, this.hwnd, Ptr, hDC)
NumPut(height, LOGFONT, "Int")
}
FontStyle := this.FontStyle
Loop, parse, FontStyle, %A_Space%
if obj := styles[A_LoopField]
NumPut(obj.value, &LOGFONT + obj.offset, obj.size)
(this.FontSize > 24 && NumPut(ANTIALIASED_QUALITY, &LOGFONT + 26, "Char"))
this.FontName && StrPut(this.FontName, &LOGFONT + 28, StrLen(this.FontName) * mult, A_IsUnicode ? "UTF-16" : "CP0")
this.hFont := DllCall("CreateFontIndirect", Ptr, &LOGFONT, Ptr)
}
_SetColor() {
static WM_USER := 0x400, TTM_SETTIPBKCOLOR := WM_USER + 19, TTM_SETTIPTEXTCOLOR := WM_USER + 20
VarSetCapacity(empty, 2, 0)
DllCall("UxTheme\SetWindowTheme", Ptr, this.hwnd, Ptr, 0, Ptr, &empty)
( this.TextColor != "" && this._SendMessage(TTM_SETTIPTEXTCOLOR, this._GetColor(this.TextColor)) )
( this.BackColor != "" && this._SendMessage(TTM_SETTIPBKCOLOR, this._GetColor(this.BackColor)) )
}
_GetColor(color) {
static WS_CHILD := 0x40000000, WM_CTLCOLORSTATIC := 0x138
Gui, New, +hwndhGui +%WS_CHILD%
Gui, Color, % color + 0 = "" ? color : Format("{:x}", color)
Gui, Add, Text, hwndhText
hdc := DllCall("GetDC", Ptr, hText, Ptr)
SendMessage, WM_CTLCOLORSTATIC, hdc, hText,, ahk_id %hGui%
clr := DllCall("GetBkColor", Ptr, hdc)
DllCall("ReleaseDC", Ptr, hText, Ptr, hdc)
Gui, Destroy
Return clr
}
_SetInfo() {
static WM_USER := 0x400, WM_SETFONT := 0x30
, TTM_ADDTOOL := WM_USER + (A_IsUnicode ? 50 : 4)
, TTM_SETTITLE := WM_USER + (A_IsUnicode ? 33 : 32)
this._SendMessage(WM_SETFONT, this.hFont, 1)
this._SendMessage(TTM_ADDTOOL, 0, this.pTI)
this._SendMessage(TTM_SETTITLE, this.icon, &(t := this.title))
this.SetText(this.text)
}
SetText(text) {
static WM_USER := 0x400, TTM_UPDATETIPTEXT := WM_USER + (A_IsUnicode ? 57 : 12)
NumPut(&text, this.pTI + (A_PtrSize = 4 ? 36 : 48))
this._SendMessage(TTM_UPDATETIPTEXT, 0, this.pTI)
}
Show(x := "", y := "") {
static WM_USER := 0x400, TTM_TRACKACTIVATE := WM_USER + 17, TTM_TRACKPOSITION := WM_USER + 18
if this.TrayTip
this._GetTrayIconCoords(xTT, yTT)
else {
xTT := x, yTT := y
(xTT = "" && xTT := this.x)
(yTT = "" && yTT := this.y)
if (xTT = "" || yTT = "") {
CoordMode, Mouse
MouseGetPos, xm, ym
(xTT = "" && xTT := xm + 10)
(yTT = "" && yTT := ym + 10)
}
}
this._SendMessage(TTM_TRACKPOSITION, 0, xTT|(yTT<<16))
this._SendMessage(TTM_TRACKACTIVATE, 1, this.pTI)
if this.BallonTip
xMax := A_ScreenWidth, yMax := A_ScreenHeight
else {
WinGetPos,,, W, H, % "ahk_id" this.hwnd
xMax := A_ScreenWidth - W - 10
yMax := A_ScreenHeight - H - 10
}
if (xTT > xMax || yTT > yMax) {
(xTT > xMax && xTT := xMax)
(yTT > yMax && yTT := yMax)
this._SendMessage(TTM_TRACKPOSITION, 0, xTT|(yTT<<16))
}
}
Hide() {
static WM_USER := 0x400, TTM_TRACKACTIVATE := WM_USER + 17
this._SendMessage(TTM_TRACKACTIVATE, 0, this.pTI)
}
_SendMessage(msg, wp := 0, lp := 0) {
Return DllCall("SendMessage", Ptr, this.hwnd, UInt, msg, Ptr, wp, Ptr, lp, Ptr)
}
_GetTrayIconCoords(ByRef x, ByRef y) {
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, szHWND := PtrSize
for k, v in ["TrayNotifyWnd", "SysPager", "ToolbarWindow32"]
hTray := DllCall("FindWindowEx", Ptr, k = 1 ? WinExist("ahk_class Shell_TrayWnd") : hTray, Ptr, 0, Str, v, UInt, 0, Ptr)
WinWait, ahk_id %hTray%
WinGet, PID, PID
WinGetPos, xTB, yTB
if !IsObject(RemoteBuff := new RemoteBuffer(PID, szTBBUTTON)) {
x := xTB, y := yTB
MsgBox, % "Не удалось создать удалённый буфер`nОшибка " A_LastError
Return
}
SendMessage, TB_BUTTONCOUNT
Loop % ErrorLevel {
SendMessage, TB_GETBUTTON, A_Index - 1, RemoteBuff.ptr
pTBBUTTON := RemoteBuff.Read(szTBBUTTON)
pHWND := RemoteBuff.Read(szHWND, NumGet(pTBBUTTON + 8 + PtrSize) - RemoteBuff.ptr)
hWnd := NumGet(pHWND + 0, PtrSize = 4 ? "UInt" : "UInt64")
if (hWnd = A_ScriptHwnd) {
SendMessage, TB_GETITEMRECT, A_Index - 1, RemoteBuff.ptr
pRECT := RemoteBuff.Read(16)
x := NumGet(pRECT + 0, "Int") + xTB + 5, y := NumGet(pRECT + 4, "Int") + yTB + 5
break
}
}
RemoteBuff := "", ((x = "" || y = "") && x := xTB, y := yTB)
}
_Destroy() {
this.__Delete()
this.SetCapacity(0)
this.base := ""
}
__Delete() {
(this.hFont && DllCall("DeleteObject", Ptr, this.hFont))
(this.Icon > 3 && DllCall("DestroyIcon", Ptr, this.Icon))
DllCall("DestroyWindow", Ptr, this.hwnd)
}
}
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", UInt, this.hProc, UInt, 0, UInt, size, UInt, MEM_COMMIT, UInt, PAGE_READWRITE, Ptr))
Return, "", DllCall("CloseHandle", Ptr, this.hProc)
}
__Delete() {
DllCall("VirtualFreeEx", Ptr, this.hProc, Ptr, this.ptr, UInt, 0, UInt, MEM_RELEASE := 0x8000)
DllCall("CloseHandle", Ptr, this.hProc)
}
Read(size, offset = 0) {
static LocalBuff
VarSetCapacity(LocalBuff, size, 0)
if !DllCall("ReadProcessMemory", Ptr, this.hProc, Ptr, this.ptr + offset, Ptr, &LocalBuff, UInt, size, UInt, 0)
Return, 0, DllCall("MessageBox", Ptr, 0, Str, "Не удалось прочитать данные`nОшибка """ A_LastError """", Str, "", UInt, 0)
VarSetCapacity(LocalBuff, -1)
Return &LocalBuff
}
}
Timer(oTip) {
static i := 0
oTip.SetText(" Я исчезну через " (7 - ++i) " сек ")
if (i = 7)
SetTimer,, Delete
}
CreateIconFromBase64(StringBASE64, Size)
{
StringBase64ToData(StringBASE64, IconData)
Return DllCall("CreateIconFromResourceEx", Ptr, &IconData + 4
, UInt, NumGet(&IconData, "UInt"), UInt, true, UInt, 0x30000, Int, Size, Int, Size, UInt, 0)
}
StringBase64ToData(StringBase64, ByRef OutData)
{
DllCall("Crypt32.dll\CryptStringToBinary", Ptr, &StringBase64
, UInt, StrLen(StringBase64), UInt, CRYPT_STRING_BASE64 := 1, UInt, 0, UIntP, Bytes, UIntP, 0, UIntP, 0)
VarSetCapacity(OutData, Bytes)
DllCall("Crypt32.dll\CryptStringToBinary", Ptr, &StringBase64
, UInt, StrLen(StringBase64), UInt, CRYPT_STRING_BASE64, Str, OutData, UIntP, Bytes, UIntP, 0, UIntP, 0)
Return Bytes
}
GetIcon() {
Icon32 =
(RTrim Join
qBAAACgAAAAgAAAAQAAAAAEAIAAAAAAAgBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
FRgjABMXIQATFyEAFBchABMXIQAVGCMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAABUYIwAAAAAAAAAAAAAAAAIAAAAAAAAAABUYIwAVGCMAExchABMXIRQUFyETExchABUYIwAVGCMAAAAAAAAAAAAAAAACAAAAAAAAAAAVGCMAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFRgjABQXIgAUFyIAFBciABMXIQATFyEAFRgjABUYIwIUFyIAFBciqxQXIqoUFyIAFRgjAhUYIwAUFyIA
FBgiABQXIgATFyIAExciABUYIwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVGCMAFBciABQXIhAUFiFCExgiABMWIQAAAAAA
FRgjAxUYIwAVFiO4ExghuBUYIwAVGCMDAAAAABQXIgATGiIAExchQhMYIhATGCIAFRgjAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAABUYIwEUFyIAFBchMhQXIv8TFyFDFBciABUXIwIVGCMCFRgjABUXI7sUGCK7FRgjABUYIwIUFyMCFBciABQXIkMUFyL/FBchMhQXIgAVGCMBAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFRgjABUXIgEVFyIAFBciuRQXItoVGSICFRgiARUYIwMUFyIBFBcinRMXIp0UFyIBFRgjAxUYIgEVGCIC
FBci2hQXIroVFyIAFRciARUYIwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFRgjAAAAAAAAAAAAAAAAAgAAAAEVGCMAFBgjARQXIgAUFyIkFBci/xQXImEUFyIC
FRgjAAwOHwD/AAAA/wAAAAoRHQAVGCMAFBciAhQXImAUFyL/FBYiJRQXIgAUGCMBFBgiAAAAAAEAAAACAAAAAAAAAAAVGCMAAAAAAAAAAAAVGCMAFBUiABMVIgAUFyIA
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
+MADH/HgB4/38A/v///////P8///nnn//759///+f////n////////////8=
)
Return Icon32
}
Вот так на Win 10 корректно с TrayTip работает, как я понимаю?
teadrinker, а нет желания поставить win10 параллельно?
Всё-равно рано или поздно придется переходить.
Да теперь уже только платно можно, вроде. Когда новый девайс на Windows куплю, там уже будет скорее всего Windows 10.
Да теперь уже только платно можно, вроде
Вроде как нет, но не проверял.
https://www.microsoft.com/ru-ru/accessi … s10upgrade
А, да, точно, слышал о такой возможности. Может, как руки дойдут, попробую обновить старую XP до 10.
Это гениально - более того что я ожидал !!!
Добавьте в коллекцию , пригодится к подобному вопросу.
И всем спасибо за поддержку !
Спасибо, только не нужно цитировать пост целиком. Подправленный вариант:
myTrayTip := new ToolTip({ text: "Модифицированный TrayTip"
, title: "Стандартная иконка «Info»"
, CloseButton: true
, Icon: 1 ; 1 — Info, 2 — Warning, 3 — Error, n > 3 — предполагается hIcon
, TrayTip: true
, FontSize: 16
, FontName: "Verdana" })
myToolTip := new ToolTip({ text: "Это модифицированный ToolTip.`nОн прозрачен для кликов мыши."
, title: "Кастомная иконка"
, Icon: hIcon := CreateIconFromBase64(GetIcon(), 32)
, transparent: true ; если true — окно прозрачно для мыши
, BalloonTip: true
, FontSize: 22
, FontName: "Times New Roman"
, FontStyle: "italic" })
ColoredTip:= new ToolTip({ text: "Я цветной ToolTip!`nЯ исчезну через 7 сек"
, x: A_ScreenWidth
, y: 0
, BalloonTip: true
, TextColor: "Navy"
, BackColor: 0xFFA500
, FontName: "Comic Sans MS"
, FontSize: 20
, TimeOut: 7000 }) ; будет автоматически удалён через 7000 мс
oTimer := Func("Timer").Bind(ColoredTip)
SetTimer, % oTimer, 1000 ; этот таймер только для изменения текста
Sleep, 2500
myToolTip.Hide() ; скрываем
myToolTip.SetText("Можно менять`nтекст и расположение") ; меняем текст
Sleep, 500
myToolTip.Show(50, 50) ; показываем в координатах x = 50, y = 50
Sleep, 2000
myToolTip.Hide()
myToolTip.SetText("I'm near the mouse cursor!")
Sleep, 500
myToolTip.Show() ; без координат — в районе курсора
Sleep, 2500
myToolTip := "" ; уничтожаем ToolTip, это можно было сделать автоматически, указав TimeOut
myTrayTip.SetTItle(2, "Изменённый заголовок") ; меняем иконку и заголовок
myTrayTip.SetText("Goodbye!")
Sleep, 1000
myTrayTip.Destroy()
Sleep, 1000
ExitApp
class ToolTip {
/*
Версия: 1.03 — добавлена возможность смены заголовка
При создании экземпляра объекта в конструктор передаётся ассоциативный массив с опциями.
Возможные ключи:
title
text
icon (1: Info, 2: Warning; 3: Error; n > 3: предполагается hIcon)
CloseButton (true или false)
transparent (true или false)
x, y — координаты, если не указаны, ToolTip появится вблизи курсора
BalloonTip (BalloonTip — это ToolTip с хвостиком, true или false)
TrayTip — будет показан BalloonTip у иконки скрипта в трее, параметры x, y, и BalloonTip игнорируются
FontName
FontSize
FontStyle (bold, italic, underline, strikeout в любом сочетании через пробел)
TimeOut — время, через которое ToolTip будет уничтожен
BackColor — цвет фона
TextColor — цвет текста
Для указания цвета можно использовать литеральные названия, перечисленные здесь:
https://autohotkey.com/docs/commands/Progress.htm#colors
В этом случае название должно быть в кавычках.
Ключи можно задавать в любом порядке и в любой комбинации, как показано в примерах использования.
Если указан ключ TrayTip, удалить окно можно только методом Destroy(),
если нет, тогда можно просто прировнять ссылку на объект пустому значению.
После создания экземпляра объекта можно менять:
расположение — метод Show(x, y), x и y — координаты, если пустые значения — ToolTip будет показан возле курсора
видимость — методы Show() и Hide()
текст — метод SetText(text)
иконку и заголовок — метод SetTitle(icon, title)
*/
__New( options ) {
for k, v in options
this[k] := v
this._CreateToolTip()
this.Show()
}
_CreateToolTip() {
static WS_POPUP := 0x80000000, WS_EX_TOPMOST := 8, WS_EX_TRANSPARENT := 0x20
, TTS_NOPREFIX := 2, TTS_ALWAYSTIP := 1, TTS_BALLOON := 0x40, TTS_CLOSE := 0x80
, TTF_TRACK := 0x20, TTF_ABSOLUTE := 0x80, szTI := A_PtrSize = 4 ? 48 : 72
VarSetCapacity(TOOLINFO, szTI, 0)
this.pTI := &TOOLINFO
NumPut(szTI, TOOLINFO)
(this.TrayTip && this.BalloonTip := true)
NumPut(TTF_TRACK|(this.BalloonTip ? 0 : TTF_ABSOLUTE), &TOOLINFO + 4)
this.hwnd := DllCall("CreateWindowEx", UInt, WS_EX_TOPMOST|(this.transparent ? WS_EX_TRANSPARENT : 0)
, Str, "tooltips_class32", Str, ""
, UInt, WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP | (this.CloseButton ? TTS_CLOSE : 0) | (this.BalloonTip ? TTS_BALLOON : 0)
, Int, 0, Int, 0, Int, 0, Int, 0, Ptr, 0, Ptr, 0, Ptr, 0, Ptr, 0)
if (this.FontName || this.FontSize || this.FontStyle)
this._SetFont()
if (this.TextColor != "" || this.BackColor != "")
this._SetColor()
this._SetInfo()
if this.TimeOut {
timer := ObjBindMethod(this, "Destroy")
SetTimer % timer, % "-" . this.TimeOut
}
}
_SetFont() {
static WM_GETFONT := 0x31, mult := A_IsUnicode ? 2 : 1, szLF := 28 + 32 * mult
, LOGPIXELSY := 90, ANTIALIASED_QUALITY := 4
, styles := { bold: {value: 700, offset: 16, size: "Int"}
, italic: {value: 1, offset: 20, size: "Char"}
, underline: {value: 1, offset: 21, size: "Char"}
, strikeout: {value: 1, offset: 22, size: "Char"} }
hPrevFont := this._SendMessage(WM_GETFONT)
VarSetCapacity(LOGFONT, szLF, 0)
DllCall("GetObject", Ptr, hPrevFont, Int, szLF, Ptr, &LOGFONT)
DllCall("DeleteObject", Ptr, hPrevFont)
if this.FontSize {
hDC := DllCall("GetDC", Ptr, this.hwnd, Ptr)
height := -DllCall("MulDiv", Int, this.FontSize, Int, DllCall("GetDeviceCaps", Ptr, hDC, Int, LOGPIXELSY), Int, 72)
DllCall("ReleaseDC", Ptr, this.hwnd, Ptr, hDC)
NumPut(height, LOGFONT, "Int")
}
FontStyle := this.FontStyle
Loop, parse, FontStyle, %A_Space%
if obj := styles[A_LoopField]
NumPut(obj.value, &LOGFONT + obj.offset, obj.size)
(this.FontSize > 24 && NumPut(ANTIALIASED_QUALITY, &LOGFONT + 26, "Char"))
this.FontName && StrPut(this.FontName, &LOGFONT + 28, StrLen(this.FontName) * mult, A_IsUnicode ? "UTF-16" : "CP0")
this.hFont := DllCall("CreateFontIndirect", Ptr, &LOGFONT, Ptr)
}
_SetColor() {
static WM_USER := 0x400, TTM_SETTIPBKCOLOR := WM_USER + 19, TTM_SETTIPTEXTCOLOR := WM_USER + 20
VarSetCapacity(empty, 2, 0)
DllCall("UxTheme\SetWindowTheme", Ptr, this.hwnd, Ptr, 0, Ptr, &empty)
( this.TextColor != "" && this._SendMessage(TTM_SETTIPTEXTCOLOR, this._GetColor(this.TextColor)) )
( this.BackColor != "" && this._SendMessage(TTM_SETTIPBKCOLOR, this._GetColor(this.BackColor)) )
}
_GetColor(color) {
static WS_CHILD := 0x40000000, WM_CTLCOLORSTATIC := 0x138
Gui, New, +hwndhGui +%WS_CHILD%
Gui, Color, % color + 0 = "" ? color : Format("{:x}", color)
Gui, Add, Text, hwndhText
hdc := DllCall("GetDC", Ptr, hText, Ptr)
SendMessage, WM_CTLCOLORSTATIC, hdc, hText,, ahk_id %hGui%
clr := DllCall("GetBkColor", Ptr, hdc)
DllCall("ReleaseDC", Ptr, hText, Ptr, hdc)
Gui, Destroy
Return clr
}
_SetInfo() {
static WM_USER := 0x400, TTM_SETMAXTIPWIDTH := WM_USER + 24
, TTM_ADDTOOL := WM_USER + (A_IsUnicode ? 50 : 4), WM_SETFONT := 0x30
this._SendMessage(WM_SETFONT, this.hFont, 1)
this._SendMessage(TTM_ADDTOOL, 0, this.pTI)
this.SetTitle(this.icon, this.title)
this._SendMessage(TTM_SETMAXTIPWIDTH, 0, A_ScreenWidth)
this.SetText(this.text)
}
SetText(text) {
static WM_USER := 0x400, TTM_UPDATETIPTEXT := WM_USER + (A_IsUnicode ? 57 : 12)
NumPut(&text, this.pTI + (A_PtrSize = 4 ? 36 : 48))
this._SendMessage(TTM_UPDATETIPTEXT, 0, this.pTI)
}
SetTitle(icon := "", title := "") {
static WM_USER := 0x400, TTM_SETTITLE := WM_USER + (A_IsUnicode ? 33 : 32), TTM_UPDATE := WM_USER + 29
((icon || this.CloseButton) && title = "") && title := " "
this._SendMessage(TTM_SETTITLE, icon, &title)
(icon > 3 && DllCall("DestroyIcon", Ptr, icon))
this._SendMessage(TTM_UPDATE)
}
Show(x := "", y := "") {
static WM_USER := 0x400, TTM_TRACKACTIVATE := WM_USER + 17, TTM_TRACKPOSITION := WM_USER + 18
if this.TrayTip {
this._GetTrayIconCoords(xTT, yTT)
if !this.TrayTimer {
timer := ObjBindMethod(this, "Show")
SetTimer, % timer, 1000
this.TrayTimer := timer
}
else {
this._CheckPosAboveTaskBar()
if (this.xTT = xTT && this.yTT = yTT)
Return
else
this.xTT := xTT, this.yTT := yTT
}
}
else {
xTT := x, yTT := y
(xTT = "" && xTT := this.x)
(yTT = "" && yTT := this.y)
if (xTT = "" || yTT = "") {
CoordMode, Mouse
MouseGetPos, xm, ym
(xTT = "" && xTT := xm + 10)
(yTT = "" && yTT := ym + 10)
}
}
this._SendMessage(TTM_TRACKPOSITION, 0, xTT|(yTT<<16))
this._SendMessage(TTM_TRACKACTIVATE, 1, this.pTI)
if this.BalloonTip
xMax := A_ScreenWidth, yMax := A_ScreenHeight
else {
WinGetPos,,, W, H, % "ahk_id" this.hwnd
xMax := A_ScreenWidth - W - 10
yMax := A_ScreenHeight - H - 10
}
if (xTT > xMax || yTT > yMax) {
(xTT > xMax && xTT := xMax)
(yTT > yMax && yTT := yMax)
this._SendMessage(TTM_TRACKPOSITION, 0, xTT|(yTT<<16))
}
}
Hide() {
static WM_USER := 0x400, TTM_TRACKACTIVATE := WM_USER + 17
this._SendMessage(TTM_TRACKACTIVATE, 0, this.pTI)
if timer := this.TrayTimer {
SetTimer, % timer, Off
this.TrayTimer := timer := ""
}
}
Destroy() {
if timer := this.TrayTimer {
SetTimer, % timer, Off
this.TrayTimer := timer := ""
}
this.__Delete()
this.SetCapacity(0)
this.base := ""
}
_CheckPosAboveTaskBar() {
static GW_HWNDNEXT := 2, SWP_NOSIZE := 1, SWP_NOMOVE := 2
hTaskBar := WinExist("ahk_class Shell_TrayWnd")
Loop
hWnd := A_Index = 1 ? DllCall("GetTopWindow", Ptr, 0, Ptr) : DllCall("GetWindow", Ptr, hWnd, UInt, GW_HWNDNEXT, Ptr)
until (hWnd = hTaskBar && TaskBarAbove := true) || hWnd = this.hwnd
if TaskBarAbove
DllCall("SetWindowPos", Ptr, this.hwnd, Ptr, 0
, Int, 0, Int, 0, Int, 0, Int, 0
, UInt, SWP_NOSIZE | SWP_NOMOVE )
}
_SendMessage(msg, wp := 0, lp := 0) {
Return DllCall("SendMessage", Ptr, this.hwnd, UInt, msg, Ptr, wp, Ptr, lp, Ptr)
}
_GetTrayIconCoords(ByRef x, ByRef y) {
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, szHWND := PtrSize
for k, v in ["TrayNotifyWnd", "SysPager", "ToolbarWindow32"]
hTray := DllCall("FindWindowEx", Ptr, k = 1 ? WinExist("ahk_class Shell_TrayWnd") : hTray, Ptr, 0, Str, v, UInt, 0, Ptr)
WinWait, ahk_id %hTray%
WinGet, PID, PID
WinGetPos, xTB, yTB
if !IsObject(RemoteBuff := new this.RemoteBuffer(PID, szTBBUTTON)) {
x := xTB, y := yTB
MsgBox, % "Не удалось создать удалённый буфер`nОшибка " A_LastError
Return
}
SendMessage, TB_BUTTONCOUNT
Loop % ErrorLevel {
SendMessage, TB_GETBUTTON, A_Index - 1, RemoteBuff.ptr
pTBBUTTON := RemoteBuff.Read(szTBBUTTON)
pHWND := RemoteBuff.Read(szHWND, NumGet(pTBBUTTON + 8 + PtrSize) - RemoteBuff.ptr)
hWnd := NumGet(pHWND + 0, PtrSize = 4 ? "UInt" : "UInt64")
if (hWnd = A_ScriptHwnd) {
SendMessage, TB_GETITEMRECT, A_Index - 1, RemoteBuff.ptr
pRECT := RemoteBuff.Read(16)
x := xTB + ( NumGet(pRECT + 0, "Int") + NumGet(pRECT + 8, "Int") )//2
y := yTB + ( NumGet(pRECT + 4, "Int") + NumGet(pRECT + 12, "Int") )//2 - 5
break
}
}
RemoteBuff := "", ((x = "" || y = "") && (x := xTB, y := yTB))
}
__Delete() {
(this.hFont && DllCall("DeleteObject", Ptr, this.hFont))
(this.Icon > 3 && DllCall("DestroyIcon", Ptr, this.Icon))
DllCall("DestroyWindow", Ptr, this.hwnd)
}
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", UInt, this.hProc, UInt, 0, UInt, size, UInt, MEM_COMMIT, UInt, PAGE_READWRITE, Ptr))
Return, "", DllCall("CloseHandle", Ptr, this.hProc)
}
__Delete() {
DllCall("VirtualFreeEx", Ptr, this.hProc, Ptr, this.ptr, UInt, 0, UInt, MEM_RELEASE := 0x8000)
DllCall("CloseHandle", Ptr, this.hProc)
}
Read(size, offset = 0) {
static LocalBuff
VarSetCapacity(LocalBuff, size, 0)
if !DllCall("ReadProcessMemory", Ptr, this.hProc, Ptr, this.ptr + offset, Ptr, &LocalBuff, UInt, size, UInt, 0)
Return, 0, DllCall("MessageBox", Ptr, 0, Str, "Не удалось прочитать данные`nОшибка """ A_LastError """", Str, "", UInt, 0)
VarSetCapacity(LocalBuff, -1)
Return &LocalBuff
}
}
}
Timer(oTip) {
static i := 0
oTip.SetText("Я цветной ToolTip!`r`nЯ исчезну через " (7 - ++i) " сек")
if (i = 7)
SetTimer,, Delete
}
CreateIconFromBase64(StringBASE64, Size)
{
StringBase64ToData(StringBASE64, IconData)
Return DllCall("CreateIconFromResourceEx", Ptr, &IconData + 4
, UInt, NumGet(&IconData, "UInt"), UInt, true, UInt, 0x30000, Int, Size, Int, Size, UInt, 0)
}
StringBase64ToData(StringBase64, ByRef OutData)
{
DllCall("Crypt32.dll\CryptStringToBinary", Ptr, &StringBase64
, UInt, StrLen(StringBase64), UInt, CRYPT_STRING_BASE64 := 1, UInt, 0, UIntP, Bytes, UIntP, 0, UIntP, 0)
VarSetCapacity(OutData, Bytes)
DllCall("Crypt32.dll\CryptStringToBinary", Ptr, &StringBase64
, UInt, StrLen(StringBase64), UInt, CRYPT_STRING_BASE64, Str, OutData, UIntP, Bytes, UIntP, 0, UIntP, 0)
Return Bytes
}
GetIcon() {
Icon32 =
(RTrim Join
qBAAACgAAAAgAAAAQAAAAAEAIAAAAAAAgBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
FRgjABMXIQATFyEAFBchABMXIQAVGCMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAABUYIwAAAAAAAAAAAAAAAAIAAAAAAAAAABUYIwAVGCMAExchABMXIRQUFyETExchABUYIwAVGCMAAAAAAAAAAAAAAAACAAAAAAAAAAAVGCMAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFRgjABQXIgAUFyIAFBciABMXIQATFyEAFRgjABUYIwIUFyIAFBciqxQXIqoUFyIAFRgjAhUYIwAUFyIA
FBgiABQXIgATFyIAExciABUYIwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVGCMAFBciABQXIhAUFiFCExgiABMWIQAAAAAA
FRgjAxUYIwAVFiO4ExghuBUYIwAVGCMDAAAAABQXIgATGiIAExchQhMYIhATGCIAFRgjAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAABUYIwEUFyIAFBchMhQXIv8TFyFDFBciABUXIwIVGCMCFRgjABUXI7sUGCK7FRgjABUYIwIUFyMCFBciABQXIkMUFyL/FBchMhQXIgAVGCMBAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFRgjABUXIgEVFyIAFBciuRQXItoVGSICFRgiARUYIwMUFyIBFBcinRMXIp0UFyIBFRgjAxUYIgEVGCIC
FBci2hQXIroVFyIAFRciARUYIwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFRgjAAAAAAAAAAAAAAAAAgAAAAEVGCMAFBgjARQXIgAUFyIkFBci/xQXImEUFyIC
FRgjAAwOHwD/AAAA/wAAAAoRHQAVGCMAFBciAhQXImAUFyL/FBYiJRQXIgAUGCMBFBgiAAAAAAEAAAACAAAAAAAAAAAVGCMAAAAAAAAAAAAVGCMAFBUiABMVIgAUFyIA
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
+MADH/HgB4/38A/v///////P8///nnn//759///+f////n////////////8=
)
Return Icon32
}
Добавлено отслеживание перемещения иконки скрипта в трее для TrayTip'а и нахождения его поверх панели задач.
Добавлено в Коллекцию.
teadrinker, как устранить ошибку?
Обновить AHK до последней версии.
Версия 1.03 — добавлена возможность смены заголовка.
Есть программа MacroCreator и у нее маленькая задержка ToolTip'a. Как ее увеличить? Спасибо!
svoboden, что за MacroCreator, какое отношение он имеет к теме?
MacroCreator - это макрорегистратор для AutoHotkey. Вопрос не по программе, а в том, как увеличить задержку ToolTip`a.
Это тоже к теме не имеет отношения. У ToolTip'а нет задержки в принципе, он удаляется просто командой с пустыми параметрами.
По-моему, вопрос про ToolTip, вроде по теме все, не хочется создавать новую тему. Вопрос вроде бы простой для вас должен быть. ToolTip быстро исчезает, не удается прочитать даже, что написано.
svoboden, спросите у автора программы.
Или так на 30 сек:
TTM_SETDELAYTIME := 0X403, TTDT_AUTOPOP := 2
loop
{
WinWait ahk_class tooltips_class32
SendMessage, TTM_SETDELAYTIME, TTDT_AUTOPOP, 30000
WinWaitClose
}
Malcev, спасибо, но в Macro Creator не работает. Я в ресурсах нашел настройки ToolTip, гляну, может, получится поменять задержку. Программа вроде на ahk написана, может, сам разберусь.
Если он написан на AutoHotkey, значит нужно взять исходный код и увеличить задержку.
Добавлено:
Вот это наверное:
SetTimer, RemoveToolTip, -3000
ypppu, чтобы я без вашего совета делал.
Только хотел спросить про метод
_Destroy() {
this.__Delete()
this.SetCapacity(0)
this.base := ""
}
, но загуглив __Delete сразу наткнулся на пост Lexikos'a:
You can clear all properties from the object from within the method.
this.Remove("", Chr(255)) would cover most property names (that is, keys; strings with which values are associated).
After removing all properties, this.SetCapacity(0) will ensure any memory associated with the object is freed (but not the allocation of the object itself).
Then you can this.base := "" to disconnect the object from its class (and therefore all user-defined methods).
Как я понял, извне экземпляра удалить экземпляр класса невозможно.
stealzy, тут путаница. В скрипте я пытался запрограммировать удаление экземпляра объекта изнутри конструктора, с чем справился посредством некоторых танцев.
teadrinker
Хорошая штука этот TrayTip из Коллекции, но возникла пара вопросов:
1. Подсказка возникает сразу после объявления new ToolTip. Логичнее было бы после .Show(). Хотя спорить не буду. Я её просто глушу .Hide() сразу же после объявления.
2. Если задан параметр TimeOut, то увидеть подсказку по .Show() получится только 1 раз. Вероятно потому что значение TimeOut загружается в таймер только при инициализации, а надо чтоб всякий раз когда вызывается метод .Show(). А пока приходится убирать этот параметр и гасить TrayTip вручную.
1. Ещё логичнее было бы параметризировать поведение при создании объекта, может, и имеет смысл дополнить.
2. По истечении таймаута текущий экземпляр объекта удаляется, такова концепция. Теоретически, тоже можно было бы параметризировать.
teadrinker
Спасибо за модификацию! На Win7 новые фичи работают как задумано. На Win10 почему то повторный вызов .Show(,, timeout) приводит к тому, что подсказка уже не гасится по истечении timeout-а.
myTrayTip := new ToolTip({ text: "Подсказка"
, TrayTip: true
, ShowNow: false})
myTrayTip.Show(,, 2000)
Sleep, 4000
myTrayTip.Show(,, 2000)
И, наверное, стоит обратить внимание что переход к выполнению следующей строки скрипта происходит не через указанное в .Show(,, timeout) время, а сразу же.
На Win10 почему то повторный вызов .Show(,, timeout) приводит к тому, что подсказка уже не гасится по истечении timeout-а.
mozers, попробуйте в методе Hide() в строке
SetTimer, % timer, Off
заменить Off на Delete.
teadrinker, увы, не помогло.
Ещё можно попробовать в методе Show() стоку
this.timer := timer := ObjBindMethod(this, timeout > 0 ? "Hide" : "Destroy")
заменить на
this.timer := timer := this[timeout > 0 ? "Hide" : "Destroy"].Bind(this)
teadrinker, увы, и это не помогло.
С моим примером, подсказка появляется, через 2 сек исчезает на долю секунды, опять появляется уже навсегда.
Если TrayTip: true убрать, то все работает как положено: 2сек-есть, 2сек-нет, 2сек-есть и исчезает навсегда.
Но проблема в том, что мне нужен именно TrayTip: true.
Если исправление невозможно, то придется ограничить область применения Win7.
С моим примером, подсказка появляется, через 2 сек исчезает на долю секунды, опять появляется
То есть, на десятке с таким примером
myTrayTip := new ToolTip({ text: "Подсказка"
, TrayTip: true
, ShowNow: false})
myTrayTip.Show(,, 2000)
тоже срабатывает неправильно?
teadrinker, точно так. Мигнула после 2сек, но не исчезла насовсем.
Я даже не ожидал (думал сбоит только при втором вызове), а оказалось что сразу...
Кстати, такой код работает безошибочно (даже без всех предложенных исправлений):
myTrayTip := new ToolTip({ text: "Подсказка"
, TrayTip: true
, ShowNow: false})
myTrayTip.Show()
Sleep, 2000
myTrayTip.Hide()
Sleep, 2000
myTrayTip.Show()
Sleep, 2000
myTrayTip.Hide()
Так что можно вообще обойтись и без опции timeout.
Ну, хотя бы понятно, куда копать.
Попробуйте так:
myTrayTip := new ToolTip({ text: "Подсказка"
, TrayTip: true
, ShowNow: false })
myTrayTip.Show(,, 2000)
Sleep, 3000
myTrayTip.Show(,, 2000)
class ToolTip {
/*
Версия: 1.04
Добавлено:
1. Псевдонимы ключей для краткости (можно, например, вместо CloseButton указать close).
Возможные ключи и псевдонимы перечислены ниже.
2. Параметр ShowNow (или now) — показывать или нет ToolTip сразу после создания,
возможные значения — true или false, если не указан, тогда по умолчанию true,
т. е. ToolTip будет показан сразу же.
3. Добавлен параметр "timeout" к методу Show(x, y, timeout).
Если таймаут задан положительным числом, ToolTip будет скрыт через указанное время
в милисекундах, если отрицательным, экземпляр объекта будет удалён без возможности
дальнейшего использования.
Если параметр не указан, ToolTip будет показываться до вызова Hide() или Destroy().
Изменено:
1. Опцию TimeOut при создании объекта можно указать в случае, если нужно показать
ToolTip сразу после создания. Если TimeOut задан положительным числом, ToolTip будет
скрыт через указанное время в милисекундах, если отрицательным, экземпляр объекта
будет удалён без возможности дальнейшего использования.
При создании экземпляра объекта в конструктор передаётся ассоциативный массив с опциями.
Возможные ключи и их псевдонимы:
title
text
icon (1 — Info, 2 — Warning; 3 — Error; n > 3 — предполагается hIcon)
CloseButton (или close) — true или false
transparent (или trans) — true или false, указывает, будет ли ToolTip прозрачен для кликов мыши
ShowNow (или now) — true или false, показывать или не показывать ToolTip при создании экземпляра объекта
Если параметр не указан, ToolTip будет показан сразу же.
x, y — координаты, если не указаны, ToolTip появится вблизи курсора
BalloonTip (или balloon, или ball) — true или false, BalloonTip — это ToolTip с хвостиком
TrayTip (или tray) — будет показан BalloonTip у иконки скрипта в трее, параметры x, y, и BalloonTip игнорируются
Если указан ключ TrayTip, удалить экземпляр объекта можно либо методом Destroy(),
либо указав TimeOut с отрицателным значением.
Если нет, тогда можно просто прировнять ссылку на объект пустому значению.
FontName (или font)
FontSize (или size)
FontStyle (или style) — bold, italic, underline, strikeout в любом сочетании через пробел
TimeOut (или time) — время в милисекундах, через которое ToolTip будет скрыт, если число положительное,
либо уничтожен, если отрицательное
BackColor (или back) — цвет фона
TextColor (или color) — цвет текста
Для указания цвета можно использовать литеральные названия, перечисленные здесь:
https://autohotkey.com/docs/commands/Progress.htm#colors
В этом случае название должно быть в кавычках.
Ключи можно задавать в любом порядке и в любой комбинации, как показано в примерах использования.
Если указан ключ TrayTip, удалить экземпляр объекта можно только методом Destroy(),
если нет, тогда можно просто прировнять ссылку на объект пустому значению.
После создания экземпляра объекта можно:
1. Изменить видимость, расположение и время показа — метод Show(x, y, timeout)
x и y — координаты, если пустые значения — ToolTip будет показан возле курсора
timeout — время в милисекундах, через которое ToolTip будет скрыт, если число положительное,
либо уничтожен, если отрицательное
2. Скрыть ToolTip — метод Hide()
3. Изменить текст — метод SetText(text)
4. Изменить иконку и заголовок — метод SetTitle(icon, title)
5. Уничтожить экземпляр объекта — метод Destroy()
*/
__New( options ) {
this.ShowNow := true
for k, v in options
this[k] := v
this._CreateToolTip()
( this.ShowNow && this.Show(this.x, this.y, this.TimeOut) )
}
__Set(key, value) {
static PsevdoKeys := { close: "CloseButton", trans: "transparent", size: "FontSize"
, balloon: "BalloonTip", ball: "BalloonTip", tray: "TrayTip"
, style: "FontStyle", time: "TimeOut", font: "FontName"
, back: "BackColor", color: "TextColor", now: "ShowNow" }
for k, v in PsevdoKeys
if (key = k)
this[v] := value
}
_CreateToolTip() {
static WS_POPUP := 0x80000000, WS_EX_TOPMOST := 8, WS_EX_TRANSPARENT := 0x20
, TTS_NOPREFIX := 2, TTS_ALWAYSTIP := 1, TTS_BALLOON := 0x40, TTS_CLOSE := 0x80
, TTF_TRACK := 0x20, TTF_ABSOLUTE := 0x80, szTI := A_PtrSize = 4 ? 48 : 72
VarSetCapacity(TOOLINFO, szTI, 0)
this.pTI := &TOOLINFO
NumPut(szTI, TOOLINFO)
(this.TrayTip && this.BalloonTip := true)
NumPut(TTF_TRACK|(this.BalloonTip ? 0 : TTF_ABSOLUTE), &TOOLINFO + 4)
this.hwnd := DllCall("CreateWindowEx", UInt, WS_EX_TOPMOST|(this.transparent ? WS_EX_TRANSPARENT : 0)
, Str, "tooltips_class32", Str, ""
, UInt, WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP | (this.CloseButton ? TTS_CLOSE : 0) | (this.BalloonTip ? TTS_BALLOON : 0)
, Int, 0, Int, 0, Int, 0, Int, 0, Ptr, 0, Ptr, 0, Ptr, 0, Ptr, 0)
if (this.FontName || this.FontSize || this.FontStyle)
this._SetFont()
if (this.TextColor != "" || this.BackColor != "")
this._SetColor()
this._SetInfo()
}
_SetFont() {
static WM_GETFONT := 0x31, mult := A_IsUnicode ? 2 : 1, szLF := 28 + 32 * mult
, LOGPIXELSY := 90, ANTIALIASED_QUALITY := 4
, styles := { bold: {value: 700, offset: 16, size: "Int"}
, italic: {value: 1, offset: 20, size: "Char"}
, underline: {value: 1, offset: 21, size: "Char"}
, strikeout: {value: 1, offset: 22, size: "Char"} }
hPrevFont := this._SendMessage(WM_GETFONT)
VarSetCapacity(LOGFONT, szLF, 0)
DllCall("GetObject", Ptr, hPrevFont, Int, szLF, Ptr, &LOGFONT)
DllCall("DeleteObject", Ptr, hPrevFont)
if this.FontSize {
hDC := DllCall("GetDC", Ptr, this.hwnd, Ptr)
height := -DllCall("MulDiv", Int, this.FontSize, Int, DllCall("GetDeviceCaps", Ptr, hDC, Int, LOGPIXELSY), Int, 72)
DllCall("ReleaseDC", Ptr, this.hwnd, Ptr, hDC)
NumPut(height, LOGFONT, "Int")
}
FontStyle := this.FontStyle
Loop, parse, FontStyle, %A_Space%
if obj := styles[A_LoopField]
NumPut(obj.value, &LOGFONT + obj.offset, obj.size)
(this.FontSize > 24 && NumPut(ANTIALIASED_QUALITY, &LOGFONT + 26, "Char"))
this.FontName && StrPut(this.FontName, &LOGFONT + 28, StrLen(this.FontName) * mult, A_IsUnicode ? "UTF-16" : "CP0")
this.hFont := DllCall("CreateFontIndirect", Ptr, &LOGFONT, Ptr)
}
_SetColor() {
static WM_USER := 0x400, TTM_SETTIPBKCOLOR := WM_USER + 19, TTM_SETTIPTEXTCOLOR := WM_USER + 20
VarSetCapacity(empty, 2, 0)
DllCall("UxTheme\SetWindowTheme", Ptr, this.hwnd, Ptr, 0, Ptr, &empty)
( this.TextColor != "" && this._SendMessage(TTM_SETTIPTEXTCOLOR, this._GetColor(this.TextColor)) )
( this.BackColor != "" && this._SendMessage(TTM_SETTIPBKCOLOR, this._GetColor(this.BackColor)) )
}
_GetColor(color) {
static WS_CHILD := 0x40000000, WM_CTLCOLORSTATIC := 0x138
Gui, New, +hwndhGui +%WS_CHILD%
Gui, Color, % color + 0 = "" ? color : Format("{:x}", color)
Gui, Add, Text, hwndhText
hdc := DllCall("GetDC", Ptr, hText, Ptr)
SendMessage, WM_CTLCOLORSTATIC, hdc, hText,, ahk_id %hGui%
clr := DllCall("GetBkColor", Ptr, hdc)
DllCall("ReleaseDC", Ptr, hText, Ptr, hdc)
Gui, Destroy
Return clr
}
_SetInfo() {
static WM_USER := 0x400, TTM_SETMAXTIPWIDTH := WM_USER + 24
, TTM_ADDTOOL := WM_USER + (A_IsUnicode ? 50 : 4), WM_SETFONT := 0x30
this._SendMessage(WM_SETFONT, this.hFont, 1)
this._SendMessage(TTM_ADDTOOL, 0, this.pTI)
this.SetTitle(this.icon, this.title)
this._SendMessage(TTM_SETMAXTIPWIDTH, 0, A_ScreenWidth)
this.SetText(this.text)
}
SetText(text) {
static WM_USER := 0x400, TTM_UPDATETIPTEXT := WM_USER + (A_IsUnicode ? 57 : 12)
NumPut(&text, this.pTI + (A_PtrSize = 4 ? 36 : 48))
this._SendMessage(TTM_UPDATETIPTEXT, 0, this.pTI)
}
SetTitle(icon := "", title := "") {
static WM_USER := 0x400, TTM_SETTITLE := WM_USER + (A_IsUnicode ? 33 : 32), TTM_UPDATE := WM_USER + 29
((icon || this.CloseButton) && title = "") && title := " "
this._SendMessage(TTM_SETTITLE, icon, &title)
(icon > 3 && DllCall("DestroyIcon", Ptr, icon))
this._SendMessage(TTM_UPDATE)
}
Show(x := "", y := "", timeout := "") {
static WM_USER := 0x400, TTM_TRACKACTIVATE := WM_USER + 17, TTM_TRACKPOSITION := WM_USER + 18
if this.TrayTip {
this._GetTrayIconCoords(xTT, yTT)
if !this.SetTrayTimer {
this.SetTrayTimer := true
this.TrayTimer := timer := ObjBindMethod(this, "Show")
SetTimer, % timer, 1000
}
else {
this._CheckPosAboveTaskBar()
if (this.xTT = xTT && this.yTT = yTT)
Return
else
this.xTT := xTT, this.yTT := yTT
}
}
else {
xTT := x, yTT := y
if (xTT = "" || yTT = "") {
CoordMode, Mouse
MouseGetPos, xm, ym
(xTT = "" && xTT := xm + 10)
(yTT = "" && yTT := ym + 10)
}
}
this._SendMessage(TTM_TRACKPOSITION, 0, xTT|(yTT<<16))
this._SendMessage(TTM_TRACKACTIVATE, 1, this.pTI)
if this.BalloonTip
xMax := A_ScreenWidth, yMax := A_ScreenHeight
else {
WinGetPos,,, W, H, % "ahk_id" this.hwnd
xMax := A_ScreenWidth - W - 10
yMax := A_ScreenHeight - H - 10
}
if (xTT > xMax || yTT > yMax) {
(xTT > xMax && xTT := xMax)
(yTT > yMax && yTT := yMax)
this._SendMessage(TTM_TRACKPOSITION, 0, xTT|(yTT<<16))
}
if timeout {
timer := this.timer
try SetTimer, % timer, Delete
this.timer := timer := ObjBindMethod(this, timeout > 0 ? "Hide" : "Destroy")
SetTimer % timer, % "-" . Abs(timeout)
}
}
Hide() {
static WM_USER := 0x400, TTM_TRACKACTIVATE := WM_USER + 17
this._SendMessage(TTM_TRACKACTIVATE, 0, this.pTI)
if this.SetTrayTimer {
this.SetTrayTimer := false
timer := this.TrayTimer
SetTimer, % timer, Delete
}
}
Destroy() {
this.__Delete()
this.SetCapacity(0)
this.base := ""
}
_CheckPosAboveTaskBar() {
static GW_HWNDNEXT := 2, SWP_NOSIZE := 1, SWP_NOMOVE := 2
hTaskBar := WinExist("ahk_class Shell_TrayWnd")
Loop
hWnd := A_Index = 1 ? DllCall("GetTopWindow", Ptr, 0, Ptr) : DllCall("GetWindow", Ptr, hWnd, UInt, GW_HWNDNEXT, Ptr)
until (hWnd = hTaskBar && TaskBarAbove := true) || hWnd = this.hwnd
if TaskBarAbove
DllCall("SetWindowPos", Ptr, this.hwnd, Ptr, 0
, Int, 0, Int, 0, Int, 0, Int, 0
, UInt, SWP_NOSIZE | SWP_NOMOVE )
}
_SendMessage(msg, wp := 0, lp := 0) {
Return DllCall("SendMessage", Ptr, this.hwnd, UInt, msg, Ptr, wp, Ptr, lp, Ptr)
}
_GetTrayIconCoords(ByRef x, ByRef y) {
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, szHWND := PtrSize
for k, v in ["TrayNotifyWnd", "SysPager", "ToolbarWindow32"]
hTray := DllCall("FindWindowEx", Ptr, k = 1 ? WinExist("ahk_class Shell_TrayWnd") : hTray, Ptr, 0, Str, v, UInt, 0, Ptr)
WinWait, ahk_id %hTray%
WinGet, PID, PID
WinGetPos, xTB, yTB
if !IsObject(RemoteBuff := new this.RemoteBuffer(PID, szTBBUTTON)) {
x := xTB, y := yTB
MsgBox, % "Не удалось создать удалённый буфер`nОшибка " A_LastError
Return
}
SendMessage, TB_BUTTONCOUNT
Loop % ErrorLevel {
SendMessage, TB_GETBUTTON, A_Index - 1, RemoteBuff.ptr
pTBBUTTON := RemoteBuff.Read(szTBBUTTON)
pHWND := RemoteBuff.Read(szHWND, NumGet(pTBBUTTON + 8 + PtrSize) - RemoteBuff.ptr)
hWnd := NumGet(pHWND + 0, PtrSize = 4 ? "UInt" : "UInt64")
if (hWnd = A_ScriptHwnd) {
SendMessage, TB_GETITEMRECT, A_Index - 1, RemoteBuff.ptr
pRECT := RemoteBuff.Read(16)
x := xTB + ( NumGet(pRECT + 0, "Int") + NumGet(pRECT + 8, "Int") )//2
y := yTB + ( NumGet(pRECT + 4, "Int") + NumGet(pRECT + 12, "Int") )//2 - 5
break
}
}
RemoteBuff := "", ((x = "" || y = "") && (x := xTB, y := yTB))
}
__Delete() {
(this.hFont && DllCall("DeleteObject", Ptr, this.hFont))
(this.Icon > 3 && DllCall("DestroyIcon", Ptr, this.Icon))
DllCall("DestroyWindow", Ptr, this.hwnd)
}
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
}
}
}
teadrinker, на Win7 - все четко. На Win10 смогу проверить только вечером.
Попробуйте так:
На моей 10 работает так же, как с классом из Коллекции — моргает и остаётся.
Ничо не понимаю. Почему-то срабатывает уже удалённый таймер. На семёрке тоже удалось воспроизвести проблему, поиграв со значением таймаута.
А так:
myTrayTip := new ToolTip({ text: "Подсказка"
, TrayTip: true
, ShowNow: false })
myTrayTip.Show(,, 3100)
Sleep, 4000
myTrayTip.Show(,, 2000)
class ToolTip {
/*
Версия: 1.04
Добавлено:
1. Псевдонимы ключей для краткости (можно, например, вместо CloseButton указать close).
Возможные ключи и псевдонимы перечислены ниже.
2. Параметр ShowNow (или now) — показывать или нет ToolTip сразу после создания,
возможные значения — true или false, если не указан, тогда по умолчанию true,
т. е. ToolTip будет показан сразу же.
3. Добавлен параметр "timeout" к методу Show(x, y, timeout).
Если таймаут задан положительным числом, ToolTip будет скрыт через указанное время
в милисекундах, если отрицательным, экземпляр объекта будет удалён без возможности
дальнейшего использования.
Если параметр не указан, ToolTip будет показываться до вызова Hide() или Destroy().
Изменено:
1. Опцию TimeOut при создании объекта можно указать в случае, если нужно показать
ToolTip сразу после создания. Если TimeOut задан положительным числом, ToolTip будет
скрыт через указанное время в милисекундах, если отрицательным, экземпляр объекта
будет удалён без возможности дальнейшего использования.
При создании экземпляра объекта в конструктор передаётся ассоциативный массив с опциями.
Возможные ключи и их псевдонимы:
title
text
icon (1 — Info, 2 — Warning; 3 — Error; n > 3 — предполагается hIcon)
CloseButton (или close) — true или false
transparent (или trans) — true или false, указывает, будет ли ToolTip прозрачен для кликов мыши
ShowNow (или now) — true или false, показывать или не показывать ToolTip при создании экземпляра объекта
Если параметр не указан, ToolTip будет показан сразу же.
x, y — координаты, если не указаны, ToolTip появится вблизи курсора
BalloonTip (или balloon, или ball) — true или false, BalloonTip — это ToolTip с хвостиком
TrayTip (или tray) — будет показан BalloonTip у иконки скрипта в трее, параметры x, y, и BalloonTip игнорируются
Если указан ключ TrayTip, удалить экземпляр объекта можно либо методом Destroy(),
либо указав TimeOut с отрицателным значением.
Если нет, тогда можно просто прировнять ссылку на объект пустому значению.
FontName (или font)
FontSize (или size)
FontStyle (или style) — bold, italic, underline, strikeout в любом сочетании через пробел
TimeOut (или time) — время в милисекундах, через которое ToolTip будет скрыт, если число положительное,
либо уничтожен, если отрицательное
BackColor (или back) — цвет фона
TextColor (или color) — цвет текста
Для указания цвета можно использовать литеральные названия, перечисленные здесь:
https://autohotkey.com/docs/commands/Progress.htm#colors
В этом случае название должно быть в кавычках.
Ключи можно задавать в любом порядке и в любой комбинации, как показано в примерах использования.
Если указан ключ TrayTip, удалить экземпляр объекта можно только методом Destroy(),
если нет, тогда можно просто прировнять ссылку на объект пустому значению.
После создания экземпляра объекта можно:
1. Изменить видимость, расположение и время показа — метод Show(x, y, timeout)
x и y — координаты, если пустые значения — ToolTip будет показан возле курсора
timeout — время в милисекундах, через которое ToolTip будет скрыт, если число положительное,
либо уничтожен, если отрицательное
2. Скрыть ToolTip — метод Hide()
3. Изменить текст — метод SetText(text)
4. Изменить иконку и заголовок — метод SetTitle(icon, title)
5. Уничтожить экземпляр объекта — метод Destroy()
*/
__New( options ) {
this.ShowNow := true
for k, v in options
this[k] := v
this._CreateToolTip()
( this.ShowNow && this.Show(this.x, this.y, this.TimeOut) )
}
__Set(key, value) {
static PsevdoKeys := { close: "CloseButton", trans: "transparent", size: "FontSize"
, balloon: "BalloonTip", ball: "BalloonTip", tray: "TrayTip"
, style: "FontStyle", time: "TimeOut", font: "FontName"
, back: "BackColor", color: "TextColor", now: "ShowNow" }
for k, v in PsevdoKeys
if (key = k)
this[v] := value
}
_CreateToolTip() {
static WS_POPUP := 0x80000000, WS_EX_TOPMOST := 8, WS_EX_TRANSPARENT := 0x20
, TTS_NOPREFIX := 2, TTS_ALWAYSTIP := 1, TTS_BALLOON := 0x40, TTS_CLOSE := 0x80
, TTF_TRACK := 0x20, TTF_ABSOLUTE := 0x80, szTI := A_PtrSize = 4 ? 48 : 72
VarSetCapacity(TOOLINFO, szTI, 0)
this.pTI := &TOOLINFO
NumPut(szTI, TOOLINFO)
(this.TrayTip && this.BalloonTip := true)
NumPut(TTF_TRACK|(this.BalloonTip ? 0 : TTF_ABSOLUTE), &TOOLINFO + 4)
this.hwnd := DllCall("CreateWindowEx", UInt, WS_EX_TOPMOST|(this.transparent ? WS_EX_TRANSPARENT : 0)
, Str, "tooltips_class32", Str, ""
, UInt, WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP | (this.CloseButton ? TTS_CLOSE : 0) | (this.BalloonTip ? TTS_BALLOON : 0)
, Int, 0, Int, 0, Int, 0, Int, 0, Ptr, 0, Ptr, 0, Ptr, 0, Ptr, 0)
if (this.FontName || this.FontSize || this.FontStyle)
this._SetFont()
if (this.TextColor != "" || this.BackColor != "")
this._SetColor()
this._SetInfo()
}
_SetFont() {
static WM_GETFONT := 0x31, mult := A_IsUnicode ? 2 : 1, szLF := 28 + 32 * mult
, LOGPIXELSY := 90, ANTIALIASED_QUALITY := 4
, styles := { bold: {value: 700, offset: 16, size: "Int"}
, italic: {value: 1, offset: 20, size: "Char"}
, underline: {value: 1, offset: 21, size: "Char"}
, strikeout: {value: 1, offset: 22, size: "Char"} }
hPrevFont := this._SendMessage(WM_GETFONT)
VarSetCapacity(LOGFONT, szLF, 0)
DllCall("GetObject", Ptr, hPrevFont, Int, szLF, Ptr, &LOGFONT)
DllCall("DeleteObject", Ptr, hPrevFont)
if this.FontSize {
hDC := DllCall("GetDC", Ptr, this.hwnd, Ptr)
height := -DllCall("MulDiv", Int, this.FontSize, Int, DllCall("GetDeviceCaps", Ptr, hDC, Int, LOGPIXELSY), Int, 72)
DllCall("ReleaseDC", Ptr, this.hwnd, Ptr, hDC)
NumPut(height, LOGFONT, "Int")
}
FontStyle := this.FontStyle
Loop, parse, FontStyle, %A_Space%
if obj := styles[A_LoopField]
NumPut(obj.value, &LOGFONT + obj.offset, obj.size)
(this.FontSize > 24 && NumPut(ANTIALIASED_QUALITY, &LOGFONT + 26, "Char"))
this.FontName && StrPut(this.FontName, &LOGFONT + 28, StrLen(this.FontName) * mult, A_IsUnicode ? "UTF-16" : "CP0")
this.hFont := DllCall("CreateFontIndirect", Ptr, &LOGFONT, Ptr)
}
_SetColor() {
static WM_USER := 0x400, TTM_SETTIPBKCOLOR := WM_USER + 19, TTM_SETTIPTEXTCOLOR := WM_USER + 20
VarSetCapacity(empty, 2, 0)
DllCall("UxTheme\SetWindowTheme", Ptr, this.hwnd, Ptr, 0, Ptr, &empty)
( this.TextColor != "" && this._SendMessage(TTM_SETTIPTEXTCOLOR, this._GetColor(this.TextColor)) )
( this.BackColor != "" && this._SendMessage(TTM_SETTIPBKCOLOR, this._GetColor(this.BackColor)) )
}
_GetColor(color) {
static WS_CHILD := 0x40000000, WM_CTLCOLORSTATIC := 0x138
Gui, New, +hwndhGui +%WS_CHILD%
Gui, Color, % color + 0 = "" ? color : Format("{:x}", color)
Gui, Add, Text, hwndhText
hdc := DllCall("GetDC", Ptr, hText, Ptr)
SendMessage, WM_CTLCOLORSTATIC, hdc, hText,, ahk_id %hGui%
clr := DllCall("GetBkColor", Ptr, hdc)
DllCall("ReleaseDC", Ptr, hText, Ptr, hdc)
Gui, Destroy
Return clr
}
_SetInfo() {
static WM_USER := 0x400, TTM_SETMAXTIPWIDTH := WM_USER + 24
, TTM_ADDTOOL := WM_USER + (A_IsUnicode ? 50 : 4), WM_SETFONT := 0x30
this._SendMessage(WM_SETFONT, this.hFont, 1)
this._SendMessage(TTM_ADDTOOL, 0, this.pTI)
this.SetTitle(this.icon, this.title)
this._SendMessage(TTM_SETMAXTIPWIDTH, 0, A_ScreenWidth)
this.SetText(this.text)
}
SetText(text) {
static WM_USER := 0x400, TTM_UPDATETIPTEXT := WM_USER + (A_IsUnicode ? 57 : 12)
NumPut(&text, this.pTI + (A_PtrSize = 4 ? 36 : 48))
this._SendMessage(TTM_UPDATETIPTEXT, 0, this.pTI)
}
SetTitle(icon := "", title := "") {
static WM_USER := 0x400, TTM_SETTITLE := WM_USER + (A_IsUnicode ? 33 : 32), TTM_UPDATE := WM_USER + 29
((icon || this.CloseButton) && title = "") && title := " "
this._SendMessage(TTM_SETTITLE, icon, &title)
(icon > 3 && DllCall("DestroyIcon", Ptr, icon))
this._SendMessage(TTM_UPDATE)
}
Show(x := "", y := "", timeout := "") {
static WM_USER := 0x400, TTM_TRACKACTIVATE := WM_USER + 17, TTM_TRACKPOSITION := WM_USER + 18
if (x = "TrayTimer")
Critical
if this.TrayTip {
this._GetTrayIconCoords(xTT, yTT)
if !this.SetTrayTimer {
this.TrayTimer := timer := ObjBindMethod(this, "Show", "TrayTimer")
SetTimer, % timer, 1000
this.SetTrayTimer := true
}
else {
this._CheckPosAboveTaskBar()
if (this.xTT = xTT && this.yTT = yTT)
Return
else
this.xTT := xTT, this.yTT := yTT
}
}
else {
xTT := x, yTT := y
if (xTT = "" || yTT = "") {
CoordMode, Mouse
MouseGetPos, xm, ym
(xTT = "" && xTT := xm + 10)
(yTT = "" && yTT := ym + 10)
}
}
this._SendMessage(TTM_TRACKPOSITION, 0, xTT|(yTT<<16))
this._SendMessage(TTM_TRACKACTIVATE, 1, this.pTI)
if this.BalloonTip
xMax := A_ScreenWidth, yMax := A_ScreenHeight
else {
WinGetPos,,, W, H, % "ahk_id" this.hwnd
xMax := A_ScreenWidth - W - 10
yMax := A_ScreenHeight - H - 10
}
if (xTT > xMax || yTT > yMax) {
(xTT > xMax && xTT := xMax)
(yTT > yMax && yTT := yMax)
this._SendMessage(TTM_TRACKPOSITION, 0, xTT|(yTT<<16))
}
if timeout {
timer := this.timer
try SetTimer, % timer, Delete
this.timer := timer := ObjBindMethod(this, timeout > 0 ? "Hide" : "Destroy")
SetTimer % timer, % "-" . Abs(timeout)
}
}
Hide() {
static WM_USER := 0x400, TTM_TRACKACTIVATE := WM_USER + 17
this._SendMessage(TTM_TRACKACTIVATE, 0, this.pTI)
if this.SetTrayTimer {
this.SetTrayTimer := false
timer := this.TrayTimer
SetTimer, % timer, Delete
}
}
Destroy() {
this.__Delete()
this.SetCapacity(0)
this.base := ""
}
_CheckPosAboveTaskBar() {
static GW_HWNDNEXT := 2, SWP_NOSIZE := 1, SWP_NOMOVE := 2
hTaskBar := WinExist("ahk_class Shell_TrayWnd")
Loop
hWnd := A_Index = 1 ? DllCall("GetTopWindow", Ptr, 0, Ptr) : DllCall("GetWindow", Ptr, hWnd, UInt, GW_HWNDNEXT, Ptr)
until (hWnd = hTaskBar && TaskBarAbove := true) || hWnd = this.hwnd
if TaskBarAbove
DllCall("SetWindowPos", Ptr, this.hwnd, Ptr, 0
, Int, 0, Int, 0, Int, 0, Int, 0
, UInt, SWP_NOSIZE | SWP_NOMOVE )
}
_SendMessage(msg, wp := 0, lp := 0) {
Return DllCall("SendMessage", Ptr, this.hwnd, UInt, msg, Ptr, wp, Ptr, lp, Ptr)
}
_GetTrayIconCoords(ByRef x, ByRef y) {
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, szHWND := PtrSize
for k, v in ["TrayNotifyWnd", "SysPager", "ToolbarWindow32"]
hTray := DllCall("FindWindowEx", Ptr, k = 1 ? WinExist("ahk_class Shell_TrayWnd") : hTray, Ptr, 0, Str, v, UInt, 0, Ptr)
WinWait, ahk_id %hTray%
WinGet, PID, PID
WinGetPos, xTB, yTB
if !IsObject(RemoteBuff := new this.RemoteBuffer(PID, szTBBUTTON)) {
x := xTB, y := yTB
MsgBox, % "Не удалось создать удалённый буфер`nОшибка " A_LastError
Return
}
SendMessage, TB_BUTTONCOUNT
Loop % ErrorLevel {
SendMessage, TB_GETBUTTON, A_Index - 1, RemoteBuff.ptr
pTBBUTTON := RemoteBuff.Read(szTBBUTTON)
pHWND := RemoteBuff.Read(szHWND, NumGet(pTBBUTTON + 8 + PtrSize) - RemoteBuff.ptr)
hWnd := NumGet(pHWND + 0, PtrSize = 4 ? "UInt" : "UInt64")
if (hWnd = A_ScriptHwnd) {
SendMessage, TB_GETITEMRECT, A_Index - 1, RemoteBuff.ptr
pRECT := RemoteBuff.Read(16)
x := xTB + ( NumGet(pRECT + 0, "Int") + NumGet(pRECT + 8, "Int") )//2
y := yTB + ( NumGet(pRECT + 4, "Int") + NumGet(pRECT + 12, "Int") )//2 - 5
break
}
}
RemoteBuff := "", ((x = "" || y = "") && (x := xTB, y := yTB))
}
__Delete() {
(this.hFont && DllCall("DeleteObject", Ptr, this.hFont))
(this.Icon > 3 && DllCall("DestroyIcon", Ptr, this.Icon))
DllCall("DestroyWindow", Ptr, this.hwnd)
}
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
}
}
}
? Попробуйте разные значения таймаута.
Так нормально. Пробовал также 1, 2, 4 секунды.
Спасибо! Так понял, проблема была в прерывании одного незавершённого потока другим, Critical помогло.
Подтверждаю. Все абсолютно так же. Предпоследний вариант - подсказка не пропадает. Последний - полет нормальный!
teadrinker, спасибо за доработку!
И так уж... Мысль вслух: Тот кто будет использовать TrayTip: true тому родной Tip будет только мешать. Лично я, твою функцию RemoveTip собираюсь засунуть внутрь класса.
mozers, спасибо за тестирование!
Тот кто будет использовать TrayTip: true тому родной Tip будет только мешать.
Почему, не все же будут использовать TrayTip именно при наведении на иконку.
Исправлено в Коллекции.
А можно как-то менять режим отображения как для простого ToolTip
CoordMode,ToolTip, Relative
А то у меня всё время только в экранных координатах выводит. Может я не нашёл чего-то?
Не-а, не предусмотрено, только экранные.
teadrinker
Помогите пожалуйста, советом.
Как использовать Ваш скрипт в таком Gui?
Gui, add, button, w100
Gui, add, button, w100
Gui, add, button, w100
gui, show,w200, Справочник
OnMessage(0x200, "Help")
return
Help(wParam, lParam, Msg) {
MouseGetPos,,,, OutputVarControl
IfEqual, OutputVarControl, Button1
Help := "Кнопка №1"
else IfEqual, OutputVarControl, Button2
Help := "Кнопка №2"
else IfEqual, OutputVarControl, Button3
Help := "Кнопка №3"
ToolTip % Help
}
Тоесть чтобы все работало точно так же: Навел курсор на кнопку в Gui -- всплыла соответствующая этой кнопке подсказка, убрал -- подсказка исчезла. С той лишь разницей, чтобы вместо стандартного ToolTip всплывал например тот, что из Вашего примера:
myToolTip := new ToolTip({ title: "Title"
, text: "I'm the colored TrayTip!"
, icon: 1 ; icon Info
, FontName: "Comic Sans MS"
, FontSize: 20
, TextColor: "Navy"
, BackColor: 0xFFA500 })
#Include %A_ScriptDir%\ToolTip.ahk
Как-то так:
Gui, add, button, w100 vButton1
Gui, add, button, w100 vButton2
Gui, add, button, w100 vButton3
gui, show,w200, Справочник
options := { ShowNow: false
, FontName: "Comic Sans MS"
, FontSize: 20
, TextColor: "Navy"
, BackColor: 0xFFA500 }
options.text := "Кнопка 1", tooltip1 := new ToolTip(options)
options.text := "Кнопка 2", tooltip2 := new ToolTip(options)
options.text := "Кнопка 3", tooltip3 := new ToolTip(options)
ShowToolTip({controls: { Button1: tooltip1, Button2: tooltip2, Button3: tooltip3 }, delay: 500, duration: 2000})
ShowToolTip(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)
}
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") {
MouseGetPos,,, hwnd
WinGet, PID, PID, ahk_id %hwnd%
( MyPID = PID && opt.controls[lp].Show() )
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") {
for k, v in opt.controls
v.Hide()
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))
}
}
#Include %A_ScriptDir%\ToolTip.ahk
teadrinker
Огромное спасибо, то что нужно!
У меня почему-то ничего не отображается. Тултип появляется невидимым и с нулевыми размерами и координатами, даже если принудительно изменить размер, его не видно.
global kkk ; сюда копируется хендл тултипа из _CreateToolTip()
myTrayTip := new ToolTip({ title: " Look!"
, text: " It's a blue TrayTip! "
, FontSize: 18
, icon: 2 ; icon Warning
, TrayTip: true ; будет отображаться возле иконки скрипта в трее
, Timeout: -4000 ; TrayTip будет удалён через 4 секунды
, BackColor: 0x0055aa
, TextColor: "white" })
; ToolTip % kkk " " DllCall("IsWindowVisible", Ptr, kkk)
WinShow % "ahk_id" kkk
WinMove % "ahk_id" kkk,, 200, 100, 200, 100
WinGetPos, x, y, W, H, % "ahk_id" kkk
; ToolTip % x " | " y " | " W " " H
Win7x64, AutoHotkey 1.1.24.
Эта версия устарела слегка, может, в этом проблема.
global kkk ; сюда копируется хендл тултипа из _CreateToolTip()
Вообще, хэндл можно так увидеть:
MsgBox, % myTrayTip.hwnd
У меня на Win7x64 работает с любой версией интерпретатора (64, 32, 32 ANSI), не представляю, в чём может быть проблема. Может, просто где-то опечатка в коде? Попробуйте выложить сюда полный код вместе с классом.
Каюсь, в классе не уделено достаточного внимания обработке ошибок.
Страницы 1
Чтобы отправить ответ, вы должны войти или зарегистрироваться