1 (изменено: Drugoy, 2012-01-03 02:06:15)

Тема: AHK: переключение окон скроллом над таскбаром, закрытие через мидлклик

Здравствуйте!
Помогите, пожалуйста, доделать скрипт на основе имеющихся частей.
Очень хочется умный скрипт для работы с таскбаром, который бы облегчил работу в винде.
Примечание: таскбар - это панель задач.

Чего хочется:
1. Чтобы при наведении курсора на таскбар - все посылаемые скроллы вверх/вниз - переключали бы окна в прямом порядке (а не на основе давности с момента последней активации окон, как это происходит при Alt+Tab).
2. Средний клик по окну должен его закрывать, средний клик по кнопке пуск должен вызывать окно завершения работы, а средний клик по пустому место должен делать ничего.

Имеющиеся наработки:
1. Есть код нуждающийся в доработке, который переключает окна, если курсор находится в правом краю экрана (в скрипте нет проверки на положение таскбара, а у меня таскбар справа)

IsClassUnderMouse(class){
   MouseGetPos, , , id
   WinGetClass, this_class, ahk_id %id%
   return this_class=class
}

#if IsClassUnderMouse("Shell_TrayWnd")
{
WheelUp:: ControlSend MSTaskListWClass1, {Up}, ahk_class Shell_TrayWnd
WheelDown:: ControlSend MSTaskListWClass1, {Down}, ahk_class Shell_TrayWnd
MButton:: ControlSend MSTaskListWClass1, {LButton}{Enter}, ahk_class Shell_TrayWnd
}

Что нужно в нём доработать?
А. Нужно как-то избавиться от необходимости мидл-клика по таскбару для активации выбранного окна. В связи с этим вопрос:
Вопрос: есть ли в AHK что-то типа onmouseout?
Если да, то можно было бы избавиться от клика через автоматический посыл Enter при уводе курсора с таскбара.
Если нет, то подозреваю, что потребуется делать через таймер: т.е. посылать Enter если с момента последнего события прокрутка_над_таскбаром прошло, скажем 0.75 сек.

2. Есть код (он работает, но требует доработки), который закрывает окна при мидл-клике по ним в таскбаре:

~MButton::
    MouseGetPos,,, win
    ifWinExist, ahk_id %win% ahk_class Shell_TrayWnd
        Send {Click}!{F4}
return

Что нужно в нём доработать?
А. При мидл-клике по пустому месту в таскбаре он вызывает окно "Завершение работы Windows", а хотелось бы, чтобы он в таком случае ничего не делал.
Б. Он убивает функцию авто-прокрутки в браузере.

Т.к. я собираюсь активно пользоваться таким скриптом - я готов выявлять и докладывать о всех найденных багах, а итоговый вариант скрипта будет достоин, на мой взгляд, помещения в галерею готовых скриптов.

2

Re: AHK: переключение окон скроллом над таскбаром, закрытие через мидлклик

Drugoy пишет:

А. Нужно как-то избавиться от необходимости мидл-клика по таскбару для активации выбранного окна. В связи с этим вопрос:
Вопрос: есть ли в AHK что-то типа onmouseout?
Если да, то можно было бы избавиться от клика через автоматический посыл Enter при уводе курсора с таскбара.

Мне подсказали, что вроде как для этого в AHK используется функция OnMessage(), но я прочитал как она работает и не понял откуда я должен узнать MsgNumber и FunctionName. Помогите, пожалуйста, разобраться.

3

Re: AHK: переключение окон скроллом над таскбаром, закрытие через мидлклик

CoordMode, Mouse
SetNumLockState, Off
SetCapsLockState, Off
SetScrollLockState, Off
VarSetCapacity(IID, 16)
NumPut(0x11CF3C3D618736E0, IID, 0, "Int64")
NumPut(0x719B3800AA000C81, IID, 8, "Int64")
DllCall("kernel32\LoadLibrary","Str","Oleacc")
ControlGet, hWnd, Hwnd,, MSTaskListWClass1
                       , ahk_class Shell_TrayWnd
TaskList:=COM_AccessibleObjectFromWindow(hWnd)
#If MouseIsOver()

MButton::WinClose, % "ahk_id"GetTaskInfo()
*WheelUp::
*WheelDown::
   Hotkey, % A_ThisHotkey, OffKeys, On
   Send, % "{Alt Down}"(A_ThisHotkey="*WheelUp"?"{Shift Up}"
                                               :"{Shift Down}")"{Tab}"
   Sleep, 100
   Hotkey, % A_ThisHotkey, % A_ThisHotkey, On
   SetTimer, UpKeys, -500
   Return

UpKeys:
   Send, {Alt Up}{Shift Up}
OffKeys:
   Return

MouseIsOver()
{
   global
   MouseGetPos, XPosAbs, YPosAbs, WinID, WinControl
   WinGetClass, WinClass, % "ahk_id"WinID
   If % WinClass="Shell_TrayWnd" And WinControl="MSTaskListWClass1"
      Return, True
   Else
      Return, False
}

GetTaskInfo()
{
   global
   TWinID:=
   Loop, % TaskList.accChildCount
   {
      TWinTitle:=TaskList.accName(A_Index)
      TaskList.accLocation(ComObjParameter(0x4003, &L:=0)
                         , ComObjParameter(0x4003, &T:=0)
                         , ComObjParameter(0x4003, &W:=0)
                         , ComObjParameter(0x4003, &H:=0)
                         , A_Index)
      TWinID:=
      WinGet, TWinID, ID, % TWinTitle
      WinGetPos, TWinX, TWinY, TWinW, TWinH, % "ahk_id"TWinID
      TIconLEFTAbs:=NumGet(L), TIconTOPAbs:=NumGet(T)
      TIconWidth:=NumGet(W), TIconHeight:=NumGet(H)
      If (RContains(TIconLEFTAbs, TIconTOPAbs
                  , TIconWidth, TIconHeight
                  , XPosAbs, YPosAbs))
         Break
      Else
         TWinID:=
   }
   Return, TWinID
}

COM_AccessibleObjectFromWindow(Hwnd)
{
   global
   DllCall("Oleacc\AccessibleObjectFromWindow", "Ptr", Hwnd
                                              , "UInt", -4
                                              , "Ptr", &IID
                                              , "Ptr*", ppvObject)
   Return, ComObjEnwrap(ppvObject)
}

RContains(X, Y, W, H, CX, CY)
{
   Return, ((((X<=CX) And (CX<(X+W))) And (Y<=CY)) AND (CY<(Y+H)))
}

4

Re: AHK: переключение окон скроллом над таскбаром, закрытие через мидлклик

Прошу употреблять существующие русские слова, особенно в названии темы.

5 (изменено: Drugoy, 2012-01-03 01:30:02)

Re: AHK: переключение окон скроллом над таскбаром, закрытие через мидлклик

Grey
Спасибо большое за вашу помощь!
К сожалению, я забыл пояснить один очень важный для меня момент (случайно не сохранил его после правки 1-ого сообщения, но на него есть указание в приведённом мной коде): мне важен прямой порядок переключения окон на таскбаре: поэтому посылать Alt+Tab мне не подходит: там порядок окон выстраивается на основе последней их активности (most recently viewed).
Соответственно, необходимо использовать метод, использованный в 1-ом скрипте: посылать Up/Down таскбару, а в конце - посылать Enter, чтобы выбранное окно стало активным.
Собственно, из-за этой необходимости отправки Enter-а - я и обозначил проблему так:

А. Нужно как-то избавиться от необходимости мидл-клика по таскбару для активации выбранного окна.

Возможно из-за того, что вы в своём скрипте использовали иной подход - вы и не столкнулись с проблемой, которую я описал под пунктом В. см. EDIT (проблему удалось исправить).
Во всём остальном - ваш скрипт практически безупречен: он работает прекрасно, есть лишь один маленький нюанс, что при клике по кнопке "Пуск" (которую нельзя расценивать как пустое место на панели задач) - хотелось бы, чтоб всё-таки вылезал запрос о завершении работы.

ypppu пишет:

Прошу употреблять существующие русские слова, особенно в названии темы.

Просьбу вынужден проигнорировать по причине наличия лимита символов на название темы:
"AHK: переключение окон прокруткой над панелью задач, закрытие через среднюю кнопку мыши" просто не влезло.
Но пометку, что таскбар = панель задач - я, пожалуй, добавлю.

EDIT:

Обновил код первого скрипта в 1-ом сообщении так, выполнив 2 собственных изначальных требования к его доработке:
1. теперь скрипт всегда верно определяет находится ли курсор над таскбаром или нет (раньше, он просто считал определённую область на экране таскбаром)
2. теперь нет бага, когда невозможно было переключиться из окна с фокусом на каком-нибудь textarea.

6 (изменено: Grey, 2012-01-03 16:43:51)

Re: AHK: переключение окон скроллом над таскбаром, закрытие через мидлклик

CoordMode, Mouse
VarSetCapacity(IID, 16)
NumPut(0x11CF3C3D618736E0, IID, 0, "Int64")
NumPut(0x719B3800AA000C81, IID, 8, "Int64")
DllCall("kernel32\LoadLibrary","Str","Oleacc")
ControlGet, hWnd, Hwnd,, MSTaskListWClass1, ahk_class Shell_TrayWnd
ControlGet, hWnd2, Hwnd,,, ahk_class Button
TaskList:=COM_AccessibleObjectFromWindow(hWnd)
#If MouseIsOver()

MButton::
   If % WinID=hWnd2
      PostMessage, 0x10,,,, ahk_class Progman ; WM_CLOSE
   Else
      WinClose, % "ahk_id"GetTaskInfo()
   Return
*WheelUp::
*WheelDown::
   If % WinID=hWnd2
   {
      Send, % "{"SubStr(A_ThisHotkey, 2)"}"
      Return
   }
   Hotkey, % A_ThisHotkey, OffKeys, On
   ControlSend, % WinControl
              , % A_ThisHotkey="*WheelUp"?"{Up}":"{Down}"
              , % "ahk_class"WinClass
   Sleep, 150 ; чувствительность прокрутки
   Hotkey, % A_ThisHotkey, % A_ThisHotkey, On
   SetTimer, UpKeys, -500 ; время подтверждения выбора
   Return

UpKeys:
   ControlSend, % WinControl, {Click}{`n}, % "ahk_class"WinClass
OffKeys:
   Return

MouseIsOver()
{
   global
   MouseGetPos, XPosAbs, YPosAbs, WinID, WinControl
   WinGetClass, WinClass, % "ahk_id"WinID
   If % WinClass="Shell_TrayWnd" And WinControl="MSTaskListWClass1"
   Or WinID=hWnd2
      Return, True
   Else
      Return, False
}

GetTaskInfo()
{
   global
   TWinID:=
   Loop, % TaskList.accChildCount
   {
      TWinTitle:=TaskList.accName(A_Index)
      TaskList.accLocation(ComObjParameter(0x4003, &L:=0)
                         , ComObjParameter(0x4003, &T:=0)
                         , ComObjParameter(0x4003, &W:=0)
                         , ComObjParameter(0x4003, &H:=0)
                         , A_Index)
      TWinID:=
      WinGet, TWinID, ID, % TWinTitle
      WinGetPos, TWinX, TWinY, TWinW, TWinH, % "ahk_id"TWinID
      TIconLEFTAbs:=NumGet(L), TIconTOPAbs:=NumGet(T)
      TIconWidth:=NumGet(W), TIconHeight:=NumGet(H)
      If (RContains(TIconLEFTAbs, TIconTOPAbs
                  , TIconWidth, TIconHeight
                  , XPosAbs, YPosAbs))
         Break
      Else
         TWinID:=
   }
   Return, TWinID
}

COM_AccessibleObjectFromWindow(Hwnd)
{
   global
   DllCall("Oleacc\AccessibleObjectFromWindow", "Ptr", Hwnd
                                              , "UInt", -4
                                              , "Ptr", &IID
                                              , "Ptr*", ppvObject)
   Return, ComObjEnwrap(ppvObject)
}

RContains(X, Y, W, H, CX, CY)
{
   Return, ((((X<=CX) And (CX<(X+W))) And (Y<=CY)) AND (CY<(Y+H)))
}

7 (изменено: Drugoy, 2012-01-03 15:40:00)

Re: AHK: переключение окон скроллом над таскбаром, закрытие через мидлклик

Спасибо, но как-то не очень надёжно работает скрипт переключения окон: если крутануть колесо сразу на много пунктов - он переключит только на 1 пункт.
Все эти таймеры - они очень ненадёжно и неудобно работают.
Я дошёл до осознания, что наиболее надёжный и удобный способ возможен только если в AHK есть возможность активировать окно по какому-то признаку (скажем, по ahk_class).
Т.е. вопрос таков: возможно ли в AHK выполнить команду типа "сделать окно с классом class1 активным"?
Если да, то я предлагаю для скрипта сделать такой подход:
1. Получить список всех существующих на данный момент окон с их классами.
2. Определить какое из них сейчас активно.
3. Создать правило, которое при отправке WheelUp/WheelDown над таскбаром будет посылать команду "сделать активным окно следующее из списка"
Такой подход позволил бы сделать мгновенное переключение окон.
Более того, стартовая позиция прокрутки в таком бы случае была бы выставлена всегда на текущее активное окно, а не на кнопку "Пуск", как это сейчас.
Это было бы просто верхом удобства.

И ещё вопрос по вашему коду: вы не могли бы объяснить, зачем вы отключаете NumLock, CapsLock и ScrollLock в начале скрипта?

8

Re: AHK: переключение окон скроллом над таскбаром, закрытие через мидлклик

Drugoy пишет:

...если крутануть колесо сразу на много пунктов - он переключит только на 1 пункт.

Он переключит на последнюю иконку, на которой остановилась прокрутка.

Drugoy пишет:

Все эти таймеры - они очень ненадёжно и неудобно работают.

Отнюдь.

Drugoy пишет:

...наиболее надёжный и удобный способ возможен только если в AHK есть возможность активировать окно по какому-то признаку (скажем, по ahk_class).

У одного класса может быть много окон.

Drugoy пишет:

...возможно ли в AHK выполнить команду типа "сделать окно с классом class1 активным"?

Можно, но если окон будет несколько, то активным будет первое найденое.

Drugoy пишет:

Если да, то я предлагаю...

Можно попробовать...

Drugoy пишет:

...зачем вы отключаете NumLock, CapsLock и ScrollLock в начале скрипта?

Сие требовалось для коректного переключения по средству клавиш альт+таб. В последнем скрипте это балласт.

9

Re: AHK: переключение окон скроллом над таскбаром, закрытие через мидлклик

Grey пишет:

...если крутануть колесо сразу на много пунктов - он переключит только на 1 пункт.

Он переключит на последнюю иконку, на которой остановилась прокрутка.

Нет, если крутить быстро, например сразу на 3 пункта за 1 раз - он переключит лишь на 1 пункт, а не на 3.

Grey пишет:

Все эти таймеры - они очень ненадёжно и неудобно работают.

Отнюдь.

Вот из-за таймера и происходит описанное мною выше поведение, как мне кажется.

Grey пишет:

...наиболее надёжный и удобный способ возможен только если в AHK есть возможность активировать окно по какому-то признаку (скажем, по ahk_class).

У одного класса может быть много окон.
Можно, но если окон будет несколько, то активным будет первое найденое.

Для повышения точности работы скрипта конечно можно брать не только класс, но и ID и PID и Title.

Grey пишет:
Drugoy пишет:

Если да, то я предлагаю...

Можно попробовать...

Начать стоит с составления списка окон, а это я так понимаю

WinGet, id, list,,, Program Manager

да?

10

Re: AHK: переключение окон скроллом над таскбаром, закрытие через мидлклик

Grey пишет:

Можно попробовать...

Я запоролся на этапе сортировки окон.
Использовал код из хэлп файла, чтобы получить список окон:

WinGet, id, list,,, Program Manager
Loop, %id%
{
    this_id := id%A_Index%
    WinActivate, ahk_id %this_id%
    WinGetClass, this_class, ahk_id %this_id%
    WinGetTitle, this_title, ahk_id %this_id%
    MsgBox, 4, , Visiting All Windows`n%a_index% of %id%`nahk_id %this_id%`nahk_class %this_class%`n%this_title%`n`nContinue?
    IfMsgBox, NO, break
}

И он выдаёт окна не в таком порядке, как они расположены на экране, а на основе давности с момента их последней активности.

11 (изменено: Drugoy, 2012-01-05 03:50:37)

Re: AHK: переключение окон скроллом над таскбаром, закрытие через мидлклик

На офф. форуме АНК тоже никто с этим помочь не смог.

12 (изменено: Grey, 2012-01-06 06:06:29)

Re: AHK: переключение окон скроллом над таскбаром, закрытие через мидлклик

Получаю по средству COM Interface список икон на панели задач:

#NoTrayIcon
VarSetCapacity(IID, 16)
NumPut(0x11CF3C3D618736E0, IID, 0, "Int64")
NumPut(0x719B3800AA000C81, IID, 8, "Int64")
DllCall("kernel32\LoadLibrary", "Str", "Oleacc")
ControlGet, hWnd, Hwnd,, MSTaskListWClass1, ahk_class Shell_TrayWnd
taskList:=COM_AccessibleObjectFromWindow(hWnd)
Hotkey, Space, RedrawList
Gui, +AlwaysOnTop +ToolWindow
Gui, Add, ListView, r14 w450, Index|Title
RedrawList:
LV_Delete()
Loop, % taskList.accChildCount
  LV_Add("", A_Index, taskList.accName(A_Index))
Gui, Show,, % (!taskList.accChildCount ? "Nothing was found!"
: ("Found: "taskList.accChildCount " window"
. (taskList.accChildCount>1 ? "s":"  ")))"                     "
. "                        Space: refresh, F5: reload, Esc: exit"
Return

COM_AccessibleObjectFromWindow(Hwnd)
{
   global
   DllCall("Oleacc\AccessibleObjectFromWindow", "Ptr", Hwnd
                                              , "UInt", -4
                                              , "Ptr", &IID
                                              , "Ptr*", ppvObject)
   Return, ComObjEnwrap(ppvObject)
}

F5::Reload
Esc::
GuiClose:
   ExitApp

Вроде всё в порядке, все полученные названия икон приложений располагаются в списке согласно порядку, в каком они располагаются на панели задач, не взирая, на то, что каждое приложение в списке представлено двумя строками записи, можно пробовать активировать окно по его названию, пропуская строку которая похожа на название процесса, но!, опять-таки к примеру, если имеется два/три/много запущенных экземпляров того же Notepad, или Calculator (первый запущенный экземпляр которого в отличии от блокнота/любого другого приложения имеет две строки с одинаковым названием)?, на лицо проблема в виде не возможности получения дескрипторов окон (лично хотел-бы спросить у Стива Балмера, почему отсуствуют эти самые дескрипторы ). Можно получить список приложений другим путём, с дескрипторами, но в том списке не будет порядка соответствующего расположению икон приложений на панели задач:

;DetectHiddenWindows, On
#NoTrayIcon
Gui, +AlwaysOnTop -MinimizeBox +ToolWindow HwndGUIhWnd
Gui, Add, ListView, w450 h259, Index|Handle        |Title
ArrayID:=[]
WinGet, ListID, List,,, Running Applications
Loop, % ListID
{
   IDs:=ListID%A_Index%
   WinGetTitle, WinTitle, % "ahk_id"IDs
   If !WinTitle
   {
      Counter++
      Continue
   }
   ArrayID.Insert(IDs)
}
Gui, Show,, % (!ListID ? "Nothing was found!"
: ("Found all: "ListID " window"(ListID>1 ? "s":"  ")))
. ", with title: "ListID-Counter "                "
. "                           F5: reload, Esc: exit"
For Key, Value In ArrayID
{
   WinGetTitle, WinTitle, % "ahk_id"Value
   LV_Add("", Key, Value, WinTitle)
}
Return

F5::Reload
Esc::
GuiClose:
   ExitApp

13

Re: AHK: переключение окон скроллом над таскбаром, закрытие через мидлклик

Grey,
спасибо большое за ваше участие! Опубликованные коды - очень ценны, но у меня возникло 2 вопроса:
1. Я правильно понимаю, что в первом коде нельзя вызывать окна не по их титулу, а по присвоенному индексу? т.е. нельзя ли в скрипте установить попарные связи между индексом и титулом окна?
Я так понял, что нет, но хочу услышать это от вас.
2. а нельзя ли совместить 2 метода?
Ну то есть создать 2 списка: из первого взять порядок расположения окон, и вызывать по названию окон, но делать проверку: если вызываемое название окна в списке встречается не единократно, то обратиться ко второму списку (где у нас окна с дескрипторами прописаны) в котором сделать выборку записей на основе вызываемого названия окна. Выборка даст опять-таки несколько результатов, но зато каждый из этих результатов можно будет вызвать уже по дескриптору.
Умно?


Вот иллюстрация задуманного:
Допустим, что у нас есть несколько окон, где повторяющимся будет "калькулятор" (у которого название для всех окон одинаковое, но в конце я добавил 1, 2 и 3, чтобы их отличить):
Список 1 (столбцы: Index и Title)

1. миранда
2. фаерфокс
3. ноутпад
4. гуглхром
5. калькулятор1
6. калькулятор2
7. калькулятор3

Список 2 (столбцы: Handle и Title)

0x10188 гуглхром
0x1304a2 калькулятор3
0х230c0c миранда
0x427709 фаерфокс
0x43020c калькулятор1
0xb0138 ноутпад
0xc0c3c калькулятор2

Каков будет порядок переключения окон, если мы захотим проскроллить весь список по кругу, начиная с окна ноутпада:
1. ноутпад (вызов по титулу, т.к. совпадений с другими записями нет).
2. гуглхром (вызов по титулу, т.к. совпадений с другими записями нет).
3. калькулятор (вызов по титулу не производится, т.к. есть ещё 2 совпадения. Вместо этого обращаемся ко 2-ому списку, где просто вызываем 1-ый по списку калькулятор, если список упорядочить по Handle. В нашем случае это будет "0x1304a2 калькулятор3")
4. калькулятор (вызов по титулу не производится, т.к. есть ещё 2 совпадения. Вместо этого обращаемся ко 2-ому списку, где просто вызываем 2-ой по списку калькулятор, если список упорядочить по Handle. В нашем случае это будет "0x43020c калькулятор1")
5. калькулятор (вызов по титулу не производится, т.к. есть ещё 2 совпадения. Вместо этого обращаемся ко 2-ому списку, где просто вызываем 3-ий по списку калькулятор, если список упорядочить по Handle. В нашем случае это будет "0xc0c3c калькулятор2")
6. миранда (вызов по титулу, т.к. совпадений с другими записями нет).
7. фаерфокс (вызов по титулу, т.к. совпадений с другими записями нет).

У меня в винде стоит настройка таскбара "группировать кнопки панели задач", так что для меня переключение между тремя калькуляторами будет выглядеть довольно органично, хоть и порядок их переключения будет 3-1-2 вместо 1-2-3.
Но если снять галку группировки кнопок, то такой порядок переключения окон будет выглядеть, конечно, более дико.

Или может в 1-ый скрипт можно окнам с одинаковым титулом приписывать невидимые символы, чтобы отличать одно окно от другого?

Пожалуйста, не сдавайтесь!

14 (изменено: Grey, 2012-02-23 14:06:47)

Re: AHK: переключение окон скроллом над таскбаром, закрытие через мидлклик

#SingleInstance, Force
CoordMode, Mouse
VarSetCapacity(IID, 16)
NumPut(0x11CF3C3D618736E0, IID, 0, "Int64")
NumPut(0x719B3800AA000C81, IID, 8, "Int64")
DllCall("kernel32\LoadLibrary","Str","oleacc")
ControlGet, hWnd, Hwnd,, MSTaskListWClass1, ahk_class Shell_TrayWnd
ControlGet, hWnd2, Hwnd,,, ahk_class Button
ppTaskList:=COM_AccessibleObjectFromWindow(hWnd)
TaskList:=ComObjEnwrap(ppTaskList)
TCount:=GetSelection()
#If MouseIsOver()

MButton::
   If % WinID=hWnd2
      PostMessage, 0x10,,,, ahk_class Progman
   Else
      WinClose, % "ahk_id"GetTaskInfo()
   Return
WheelUp::SelectStuff(1)
WheelDown::SelectStuff(0)

GetSelection()
{
   global
   Loop, % TaskList.accChildCount
   {
      TaskList.accLocation(ComObjParameter(0x4003, &L:=0)
                         , ComObjParameter(0x4003, &T:=0)
                         , ComObjParameter(0x4003, &W:=0)
                         , ComObjParameter(0x4003, &H:=0)
                         , A_Index)
      If L=0 And T=0 And W=0 And H=0
         Continue
      If TaskList.accState(A_Index)=0x40000000
         Continue
      If TaskList.accState(A_Index)&8
         Return, A_Index
   }
}

MouseIsOver()
{
   global
   MouseGetPos, XPos, YPos, WinID, WinControl
   WinGetClass, WinClass, % "ahk_id"WinID
   Return, % WinClass="Shell_TrayWnd"
         And WinControl="MSTaskListWClass1"
          Or WinID=hWnd2 ? True:False
}

GetTaskInfo()
{
   global
   TWinID:=
   Loop, % TaskList.accChildCount
   {
      TWinTitle:=TaskList.accName(A_Index)
      TaskList.accLocation(ComObjParameter(0x4003, &L:=0)
                         , ComObjParameter(0x4003, &T:=0)
                         , ComObjParameter(0x4003, &W:=0)
                         , ComObjParameter(0x4003, &H:=0)
                         , A_Index)
      If L=0 And T=0 And W=0 And H=0
         Continue
      TWinID:=
      WinGet, TWinID, ID, % TWinTitle
      WinGetPos, TWinX, TWinY, TWinW, TWinH, % "ahk_id"TWinID
      TIconLEFTAbs:=NumGet(L), TIconTOPAbs:=NumGet(T)
      TIconWidth:=NumGet(W), TIconHeight:=NumGet(H)
      If RContains(TIconLEFTAbs, TIconTOPAbs
                  , TIconWidth, TIconHeight
                  , XPos, YPos)
         Break
      Else
         TWinID:=
   }
   Return, TWinID
}

SelectStuff(Dir)
{
   global
   If (TCount=2 And Dir=1)
   Or (TCount=TaskList.accChildCount And Dir=0)
   {
      TrayTip,, % "достигнут "
   . (Dir=1 ? "верхний":"нижний")
           . " край списка икон"
      Return
   }
   Sleep, 300
   If Dir=0
      TCount++
   Else
      TCount--
   If TCount>TaskList.accChildCount
      TCount:=TaskList.accChildCount
   If TCount<2
      TCount:=2
   State:=TaskList.accState(TCount)
   TaskList.accLocation(ComObjParameter(0x4003, &L:=0)
                      , ComObjParameter(0x4003, &T:=0)
                      , ComObjParameter(0x4003, &W:=0)
                      , ComObjParameter(0x4003, &H:=0)
                      , TCount)
   If !L And !T And !W And !H
   {
      If Dir=0
         TCount++
      Else
         TCount--
      If TCount>TaskList.accChildCount
         TCount:=TaskList.accChildCount
      Else If TCount<2
         TCount:=2
   }
   acc_Select(ppTaskList, TCount, 3)
   TaskList.accDoDefaultAction(TCount)
}

COM_AccessibleObjectFromWindow(Hwnd)
{
   global
   DllCall("oleacc\AccessibleObjectFromWindow", "Ptr", Hwnd
                                              , "UInt", -4
                                              , "Ptr", &IID
                                              , "Ptr*", ppValueObject)
   Return, ppValueObject
}

acc_Select(pacc, idChild=0, nFlags=3)
{
   Return, DllCall(NumGet(NumGet(1*pacc)+84), "UInt", pacc
                                            , "Int", nFlags
                                            , "Int64", 3
                                            , "Int64", idChild)
}

RContains(X, Y, W, H, CX, CY)
{
   Return, ((((X<=CX) And (CX<(X+W))) And (Y<=CY)) AND (CY<(Y+H)))
}

Скрипт корректно не работает с группированными окнами.

P.S.: Вы меня извините, но далее я эту тему разрабатывать не буду.

15 (изменено: Drugoy, 2012-01-09 05:53:43)

Re: AHK: переключение окон скроллом над таскбаром, закрытие через мидлклик

Ок, спасибо за ваши старания, если получить список окон в прямом порядке с дескрипторами окон - невозможно, то и чёрт с ним.
Но помогите, пожалуйста, последнюю мелкую фигню доделать.
Я остановился на этом варианте скрипта:

IsClassUnderMouse(class) {
   MouseGetPos, , , id
   WinGetClass, this_class, ahk_id %id%
   return this_class=class
}

#IF IsClassUnderMouse("Shell_TrayWnd")

WheelUp::
ControlSend MSTaskListWClass1, {Up}, ahk_class Shell_TrayWnd
SetTimer, Check, 50
return

WheelDown::
ControlSend MSTaskListWClass1, {Down}, ahk_class Shell_TrayWnd
SetTimer, Check, 50
return

#IF

Check:
If !IsClassUnderMouse("Shell_TrayWnd") {
ControlSend MSTaskListWClass1, {LButton}{enter}, ahk_class Shell_TrayWnd
SetTimer, Check, Off
} return

Но он настроен под вертикальный таскбар, для горизонтального вместо Up надо посылать Right, а вместо Down - Left.
Хотелось бы авто-определения позиции таскбара:
мне думается, что это проще всего сделать добавив WinGetPos Shell_TrayWnd определив его width и height, а затем сравнив их: если height больше width - то таскбар вертикален, если наоборот - то горизонтален.

16

Re: AHK: переключение окон скроллом над таскбаром, закрытие через мидлклик

В таком виде скрипт перестаёт работать должным образом: прокрутка перестаёт переключать окна по порядку, вместо этого она одно и то же выбирает.

Пробовал и так:

IsClassUnderMouse(class) {
   MouseGetPos, , , id
   WinGetClass, this_class, ahk_id %id%
   return this_class=class
}

#IF IsClassUnderMouse("Shell_TrayWnd")

WheelUp::
   WinGetPos,,, W, H, ahk_class Shell_TrayWnd
if W>H
{
ControlSend MSTaskListWClass1, {Up}, ahk_class Shell_TrayWnd
SetTimer, Check, 50
return
}
else
{
ControlSend MSTaskListWClass1, {Right}, ahk_class Shell_TrayWnd
SetTimer, Check, 50
return
}

WheelDown::
   WinGetPos,,, W, H, ahk_class Shell_TrayWnd
if W>H
{
ControlSend MSTaskListWClass1, {Down}, ahk_class Shell_TrayWnd
SetTimer, Check, 50
return
}
else
{
ControlSend MSTaskListWClass1, {Left}, ahk_class Shell_TrayWnd
SetTimer, Check, 50
return
}

#IF

Check:
If !IsClassUnderMouse("Shell_TrayWnd") {
ControlSend MSTaskListWClass1, {LButton}{enter}, ahk_class Shell_TrayWnd
SetTimer, Check, Off
} return

Тоже перестаёт работать.

17

Re: AHK: переключение окон скроллом над таскбаром, закрытие через мидлклик

IsClassUnderMouse(class) {
   MouseGetPos, , , id
   WinGetClass, this_class, ahk_id %id%
   return this_class=class
}

#IF IsClassUnderMouse("Shell_TrayWnd")

WheelUp::
   WinGetPos,,, W, H, ahk_class Shell_TrayWnd
   ControlSend, MSTaskListWClass1, % W>H ? "{Right}":"{Up}", ahk_class Shell_TrayWnd
   SetTimer, Check, 50
   Return
 
WheelDown::
   WinGetPos,,, W, H, ahk_class Shell_TrayWnd
   ControlSend, MSTaskListWClass1, % W>H ? "{Left}":"{Down}", ahk_class Shell_TrayWnd
   SetTimer, Check, 50
   Return

#IF

Check:
If !IsClassUnderMouse("Shell_TrayWnd") {
ControlSend MSTaskListWClass1, {LButton}{enter}, ahk_class Shell_TrayWnd
SetTimer, Check, Off
} return

18

Re: AHK: переключение окон скроллом над таскбаром, закрытие через мидлклик

Огромное вам спасибо!

19 (изменено: Drugoy, 2012-05-27 18:50:06)

Re: AHK: переключение окон скроллом над таскбаром, закрытие через мидлклик

Проблема с этим скриптом, что почему-то не выбираются окна из групп окон.
Т.е. если открыть несколько окон Проводника - то они на панели задач (ПЗ) объединяться в группу.
Допустим, у нас вертикальная ПЗ справа.
Без скрипта мы делаем клик по ПЗ, нажимаем Tab, выбираем стрелками вверх/вниз нужное нам окно, прикреплённый ярлык или группу окон и дальше активируем выбранное посыланием Space или Enter для первых двух вариантов, либо двойным посыланием этих же клавишь, в случае группы окон (если хотим выделить хотя бы просто тупо первое из группы, а не выбирать ещё и внутри группы окно).

В последней части скрипта имеется:

Check:
If !IsClassUnderMouse("Shell_TrayWnd") {
ControlSend MSTaskListWClass1, {LButton}{enter}, ahk_class Shell_TrayWnd
SetTimer, Check, Off
} return

Вопрос 1: зачем вообще там сначала посылается {LButton}?
Вопрос 2: если {LButton} заменить (или просто добавить после него) на {Left} (который по идее ничего не должен сломать ни для 1 из случаев) - скрипт всё равно не срабатывает на группах окон. Почему?

20

Re: AHK: переключение окон скроллом над таскбаром, закрытие через мидлклик

Кажется, ответы такие:
Ответ 1: Он там не нужен, т.к. посылает клик туда, где находится курсор.
Ответ 2: потому что посылаемая кнопка отправляется в окно с ahk_class Shell_TrayWnd, а у групп окон ahk_class TaskListThumbnailWnd

21

Re: AHK: переключение окон скроллом над таскбаром, закрытие через мидлклик

Улучшенный скрипт:
1. Ему не важна позиция панели задач (слева, справа, снизу, сверху).
2. Теперь при выборе группы окон - активирует первое из них, а не виснет на половине процесса, как это было раньше.
3. Переключение окон происходит от текущего активного, а не от кнопки "Пуск".
4. Средний клик мышью по окну на панели задач закрывает его нежно (т.е. если имеется несохранённая информация - окно выдаст запрос на подтверждение операции).

; Script to work with Windows Taskbar
; 1. Hover taskbar
; 2. Scroll up/down to pre-select any window or pinned task.
; 3. Move cursor away from taskbar to select the pre-selected window/task.
  
#SingleInstance, Force
SetKeyDelay, 30
  
IsClassUnderMouse(class) {
   MouseGetPos, , , id
   WinGetClass, this_class, ahk_id %id%
   return this_class=class
}
  
#If IsClassUnderMouse("Shell_TrayWnd")
  
WheelUp::
   ControlSend, MSTaskListWClass1, % TrayKey("list") TrayKey("previous") TrayKey("thumbs"), ahk_class Shell_TrayWnd
   SetTimer, Check, 200
Return
  
WheelDown::
   ControlSend, MSTaskListWClass1, % TrayKey("list") TrayKey("next") TrayKey("thumbs"), ahk_class Shell_TrayWnd
   SetTimer, Check, 200
Return
  
MButton::
    WinActivate, ahk_class Shell_TrayWnd
    Click
    WinWaitNotActive, , , 0.5
    If !ErrorLevel
        WinClose, A
return
  
Check:
    If !IsClassUnderMouse("Shell_TrayWnd")
    {
        ControlSend, , {Enter}, ahk_class TaskListThumbnailWnd
        SetTimer, , Off
    }
return
  
TrayKey(action) {
    WinGetPos, X, Y, W, H, ahk_class Shell_TrayWnd
    if (action = "next")
        return (W > H) ? "{Right}" : "{Down}"
    if (action = "previous")
        return (W > H) ? "{Left}" : "{Up}"
    if (action = "thumbs")
        return (X = 0 && Y = 0) ? (W > H ? "{Down}" : "{Right}") : (X = 0) ? "{Up}" : "{Left}"
    if (action = "list")
        return (X = 0 && Y = 0) ? (W > H ? "{Up}" : "{Left}") : (X = 0) ? "{Down}" : "{Right}"
}

22 (изменено: Irbis, 2012-05-28 13:45:50)

Re: AHK: переключение окон скроллом над таскбаром, закрытие через мидлклик

Скрипт в win7 "пролистывает" иконки быстрого запуска, даже если приложение не запущено. То есть, на рисунке видно, что от Оперы до Тотал Коммандера надо сделать 3 поворота колесика, а не одно.
Так и задумано? В остальном идея неплоха.
http://savepic.su/2077178.jpg

Upd: Не всегда срабатывает переключение - к примеру, активна Опера, колесом выбираю ТС, но при возвращении курсора за пределы таскбара опять активой становится Опера.

23

Re: AHK: переключение окон скроллом над таскбаром, закрытие через мидлклик

Да, так и задумано. Можно конечно их и в исключения наверно добавить, но у меня там всё равно ничего не прикреплено.

24

Re: AHK: переключение окон скроллом над таскбаром, закрытие через мидлклик

Irbis пишет:

Upd: Не всегда срабатывает переключение - к примеру, активна Опера, колесом выбираю ТС, но при возвращении курсора за пределы таскбара опять активой становится Опера.

У меня тоже не всегда (Win 7).

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

25

Re: AHK: переключение окон скроллом над таскбаром, закрытие через мидлклик

teadrinker пишет:

У меня тоже не всегда (Win 7).

Подтверждаю баг. Пытались коллективно определить его причину - не смогли.
Можете попробовать увеличить SetKeyDelay до 50-100.


Вот другая версия этого же скрипта.
В ней можно переключаться между окнами внутри групп окон (если у вас включена группировка схожих задач).
Немного кривоватый скрипт, нуждается в множестве мелких доработок, но как основа - прекрасен.

Count=0
SetKeyDelay,20
IsActive() {        
   MouseGetPos, , , id
   return id=WinExist("ahk_class Shell_TrayWnd") ? 1 : 0
}
TaskBarPos() {
    WinGetPos,X,Y,W,H,ahk_class Shell_TrayWnd
    return x=0 ? y=0 ? h<w ? "Top" : "Left" : "Bottom" : "Right"
}
#If IsActive()
WheelDown::
WheelUp::
Max := Num()
Count := Count <= 1 ? Max : Count -1
if (Count<=Max&& Count>1)
ControlSend, , % (A_ThisHotkey="WheelDown" ? TaskBarPos()="Top" || TaskBarPos()="Bottom" ? "{Right}" : "{Down}" : TaskBarPos()="Top" || TaskBarPos()="Bottom" ? "{Left}" : "{Up}" ) ,ahk_class TaskListThumbnailWnd
else
ControlSend, MSTaskListWClass1, % "{PGND}" (A_ThisHotkey="WheelDown" ? TaskBarPos()="Top" || TaskBarPos()="Bottom" ? "{Right}{Up}" : "{Down}{Left}" : TaskBarPos()="Top" || TaskBarPos()="Bottom" ? "{Left}{Up}" : "{Up}{Left}" ) ,ahk_class Shell_TrayWnd
SetTimer, Check, 50
return
#If
Check:
    If !IsActive() {
       ControlSend, , {Enter}, ahk_class TaskListThumbnailWnd
        SetTimer, Check, Off
    }
return
F3::ExitApp
Num() {
  return Acc_ObjectFromWindow(WinExist("ahk_class TaskListThumbnailWnd")).accChildCount//3
}
Acc_ObjectFromWindow(hWnd, idObject = -4){
    Static  h
    If Not  h
        h:=DllCall("LoadLibrary","Str","oleacc","Ptr")
    If  DllCall("oleacc\AccessibleObjectFromWindow", "Ptr", hWnd, "UInt", idObject&=0xFFFFFFFF, "Ptr", -VarSetCapacity(IID,16)+NumPut(idObject==0xFFFFFFF0?0x46000000000000C0:0x719B3800AA000C81,NumPut(idObject==0xFFFFFFF0?0x0000000000020400:0x11CF3C3D618736E0,IID,"Int64"),"Int64"), "Ptr*", pacc)=0
    Return  ComObjEnwrap(9,pacc,1)
}