1 (изменено: stealzy, 2018-10-18 11:35:43)

Тема: AHK: Об использовании произвольных клавиш в качестве модификаторов

Модификатор - клавиша, изменяющая действия других клавиш, нажатых вместе с собой. Это клавиши Ctrl, Alt, Fn, Win.
Как правило, действия других клавиш в сочетании с ними предопределены программами и ОС.
AutoHotkey позволяет сделать модификатор из любой клавиши, и определить действия других клавиш в слое вашего модификатора, разумеется.

Если вы хотитие сделать модификатор из некой клавиши, вам придется либо пожертвовать ее оригинальной функцией вовсе, либо перенести ее на момент отжатия этой клавиши.

Для примера возьмем клавишу AppsKey (контекстное меню):


; * нужна, чтобы AppsKey вызывалась / не вызывалась в сочетаниях с модификаторами,
; например Shift+Apps (расширенное контекстное меню в проводнике).
*AppsKey::Return  ; Нажатие клавиши будет посылаться в момент ее отжатияия,
*AppsKey Up::
	If (A_PriorKey = "AppsKey")  ; если в период м/у ее зажатием и отпусканием не были нажаты какие-либо еще клавиши.
		Send {Blind}{AppsKey}  ; Blind сохраняет зажатые клавиши зажатыми.
	; Например, без этой опции вместо при нажатии Shift+Apps будет послано просто Apps.
	Return

; Теперь можно создавать слой действий клавиш с нашим новым модификатором AppsKey:
#If GetKeyState("AppsKey", "P")
	Left::WinMinimize A
	Up::WinMaximize A ; Развернуть окно
	Right::Send #{Right}
	vkBE::Send {Esc} ;.
	vkBF::Send ^{vk5a} ;/ - z
#If
$Esc::ExitApp

*Примечание: в современных USB-клавах (сюда входят и встроенные в ноутбуки) у вас могут быть проблемы с одновременным нажатием некоторых клавиш, последняя клавиша не будет нажиматься. Особенно часто это бывает с рядом расположенными клавишами.
На PS/2 клавах можно зажать хоть все до одной клавиши, и ОС получит сигнал от каждой, но с большинсвом USB это не так.
В целях экономии производители помещают по нескольку клавиш на одну дорожку, что для офисных клавиатур вполне одправдано.
Разумееется к модификаторам Shift, Ctrl, Win проведена отдельная дорожка, чтобы их можно было нажимать в сочетании с любыми другими.
Разводка платы у всех производителей разная, некоторые не позволяют даже нажать одновременно стрелку вверх, вниз и пробел.
Перед покупкой проверить возможности клавы можно с помощью программки AquaKeyTest. Она отображает на экране все нажатые вами клавиши.

Так можно делать, пока вы не захотите создать горячие клавиши (далее - гк) типа (Alt/Shift/Ctrl)+Apps в противовес сочетаниям Apps+(Alt/Shift/Ctrl).
Допустим, вы сделали гк для сочетания Alt+AppsKey и повесили на нее действие.
Однако, в дополнение к вашему действию, при отпускании Apps сработает и гк *AppsKey Up:: (с последующим вызовом контексного меню).
Что же делать? Можно, конечно, убрать звездочку из гк *AppsKey Up::, а для Shift+AppsKey назначить новую гк.
*Примечание: Поскольку уже есть запись *AppsKey::, запись !AppsKey:: скрипт просто проигнорирует. Потребуется запись вида Alt & AppsKey::.

Но правильнее будет воспользоваться тем, что гк Alt & AppsKey:: и *AppsKey:: являются конкурирующими.
Гк Alt & AppsKey:: будет иметь преимущество над конкурирующей *AppsKey::, поэтому при нажатии Alt+AppsKey отработает только Alt & AppsKey::, но не *AppsKey::.
Отметим, что можно сделать и по-другому, использовав запись вида !AppsKey, но поместив *AppsKey:: и *AppsKey Up:: в условный блок [#If !GetKeyState("RAlt", "P")].

Alt & AppsKey::Send #{Right}

*AppsKey::
	AppsKeyHK:=true ; Введем переменную AppsKeyHK, чтобы узнать сработала ли гк *AppsKey::.
	Return
*AppsKey Up::
	if (AppsKeyHK && (A_PriorKey = "AppsKey")) ; Попробуйте заменить AppsKeyHK на 1 и нажать Alt+AppsKey
		Send {Blind}{AppsKey}
	AppsKeyHK:=false
	Return

#If GetKeyState("AppsKey", "P")
	RAlt::MsgBox ; Заметьте, что Alt+AppsKey и AppsKey+Alt выполняют разные действия
	Left::WinMinimize A
	Up::WinMaximize A
	Right::Send #{Right}
	vkBE::Send {Esc} ;.
	*vkBF::Send {Blind}^{vk5a} ;/ - z   Зачем {Blind}? В моем редакторе Ctrl+Z - отмена, а Shift+Ctrl+Z - повтор. Таким образом я убиваю сразу 2 зайцев.
	*vkDC::Send {Blind}^{vk57} ;\ - w   Ctrl+W - закрытие вкладки. Догадайтесь сами, что произойдет по нажатию Apps+Shift+\
	*vkba::Send {Blind}^{vk53} ;; - s
#If
$Esc::ExitApp
+ Тоже самое с использованием Input вместо служебной переменной A_PriorKey:
*AppsKey::
	; Input нужен чтобы узнать, были ли нажатия каких-либо клавиш в период между нажатием и отпусканием нашей клавиши.
	; Однако, Input не получает сведений о нажатии hotkeys, установленных из AutoHotkey.
	; Чтобы обойти эту проблему, hotkeys(включающие в себя нашу клавишу) должны при срабатывании устанавливают переменую KeyInterrupt в true.
	Input keys, L1V, {LControl}{RControl}{LAlt}{RAlt}{LShift}{RShift}{LWin}{RWin}{AppsKey}{F1}{F2}{F3}{F4}{F5}{F6}{F7}{F8}{F9}{F10}{F11}{F12}{Left}{Right}{Up}{Down}{Home}{End}{PgUp}{PgDn}{Del}{Ins}{BS}{Capslock}{Numlock}{PrintScreen}{Pause}
	If ((ErrorLevel = "NewInput") && !KeyInterrupt_AppsKey) ; Если Input прерван отпусканием AppsKey, и не было использовано горячих клавиш
		Send {Blind}{AppsKey} ; Blind сохраняет зажатые клавиши зажатыми. Например, без этой опции вместо Shift+Apps будет послано Apps.
	KeyInterrupt_AppsKey := false
	Return
*AppsKey Up::
	Input
	Return

#If GetKeyState("AppsKey", "P")
	*vkdd:: ;]
		Send {Esc}
		KeyInterrupt_AppsKey:=true
		Return
	*Left::
		WinMinimize A
		KeyInterrupt_AppsKey:=true
		Return
	*Right::
		Send #{Right}
		KeyInterrupt_AppsKey:=true
		Return
	*vkdc:: ;\
		Send ^{vk57} ;w
		KeyInterrupt_AppsKey:=true
		Return
#If GetKeyState("AppsKey", "P") && !TurnOffAltTabSomeCuts
; С помощью TurnOffAltTabSomeCuts, shortcut-ы выключают сами себя до момента отпускания AppsKey
	*Down:: ; Эмуляция AltTab. Вызов - Apps+Down, навигация стрелками при зажатой клавише Apps
		TurnOffAltTabSomeCuts:=true
		Send {AltDown}{Tab}
		KeyWait, AppsKey
		TurnOffAltTabSomeCuts:=false
		Send {AltUp}
		KeyInterrupt_AppsKey:=true
		Return
	*Up:: ; Развернуть окно
		WinMaximize A
		KeyInterrupt_AppsKey:=true
		Return
#If
$Esc::ExitApp

Еще один способ установить гк на Alt+AppsKey, в этот раз в условный блок попадает действие AppsKey при зажатом Alt:
В этом скрипте у нас сразу две эмуляции AltTab - на сочетании RAlt+Apps и на стрелках в слое модификатора AppsKey.

*AppsKey::
	AppsKeyHK:=true
	Return
*AppsKey Up::
	if (AppsKeyHK && (A_PriorKey="AppsKey"))
		Send {Blind}{AppsKey}
	AppsKeyHK:=false
	Return

; Эмуляция AltTab. Вызов - RAlt+Apps, Shift+RAlt+Apps. Навигация повторным нажатием Apps при зажатой клавише RAlt(+Shift)
; Эмуляция CtrlTab аналогично.
#If AppsComeTab:=(GetKeyState("RCtrl", "P") || GetKeyState("RAlt", "P"))
	AppsKey::Tab ; конкурирующая с *AppsKey:: гк, будет иметь преимущество, т.к. устанавливается в #If.
	; Почему я не использовал * здесь? Запись вида клавиша::клавиша автоматически приводится к нужному виду,
	; подробнее об этом - https://autohotkey.com/docs/misc/Remap.htm

; Слой модификатора AppsKey
#If GetKeyState("AppsKey", "P")
	Left::WinMinimize A
	Right::Send #{Right}
	vkBE::Send {Esc} ;.
	*vkBF::Send {Blind}^{vk5a} ;/ - z
	*vkDC::Send {Blind}^{vk57} ;\ - w
	*vkba::Send {Blind}^{vk53} ;; - s
#If (!TurnOffSelfHK && GetKeyState("AppsKey", "P")) ; С помощью TurnOffSelfHK, гк выключают сами себя до момента отпускания AppsKey
	Down:: ; Эмуляция AltTab. Вызов - Apps+Down, навигация стрелками при зажатой клавише Apps
		TurnOffSelfHK:=true
		Send {AltDown}{Tab}
		KeyWait, AppsKey
		TurnOffSelfHK:=false
		Send {AltUp}
		Return
	Up::WinMaximize A ; Развернуть окно
#If
$Esc::ExitApp

В Windows > 7 c эмуляцией AltTab не все так просто.