1

Тема: AHK: "Назад" в Chrome

Привет!
В Chrome привычное "Backspace", для возврата к предыдущей странице, заменено на Alt+Left.
Путаешься, потому что в том же проводнике по прежнему "Backspace".
Сделать замену горячей клавишу для Chrome не проблема, но как отличить ситуации, когда вводишь текст в поле формы от других ситуаций?

К Chrome есть расширение, которое помогает, но оно тоже на некоторых сайтах ошибается. На тех же Одноклассниках, например. Изрядно бесит, когда пытаешься исправить ошибку созданную при наборе текста, а тебя перебрасывает на предыдущую страницу.
 
Контролы в Chrome AHK не видит. Практически вся страница "Chrome_RenderWidgetHostHWND1".
Подумал, что можно танцевать от формы курсора, но это только, если он над редактируемым полем. А если нет?
Можно как-то определять, что есть мигающий курсор в поле редактирования? А может есть более простые способы решения проблемы?

2 (изменено: svoboden, 2017-07-08 04:11:12)

Re: AHK: "Назад" в Chrome

Переменная A_Cursor.

3 (изменено: stealzy, 2017-07-08 10:13:08)

Re: AHK: "Назад" в Chrome

В Firefox можно отличать по наличию координат каретки A_CaretX, A_CaretY, впрочем на нем и так BackSpace работает как надо.
Для хромого могу только предложить костыль:

$BackSpace::
	clip := Clipboard
	Clipboard := ""
	Send +{Left}^{Ins}
	ClipWait 0.3
	If ErrorLevel
		Send !{Left}
	Else {
		Send {BackSpace}
		; дальше на случай выделенного текста на странице
		Clipboard := ""
		Sleep 1
		Send ^{Ins}
		ClipWait 0.3
		If !ErrorLevel
			Send !{Left}
	}
	Clipboard := clip
Return

Нормальное решение может быть только изнутри.

4 (изменено: Malcev, 2017-07-08 18:43:45)

Re: AHK: "Назад" в Chrome

Наверняка можно сделать правильней, тут по чуть-чуть память сжирает.
В хроме нужно поставить везде галки на chrome://accessibility/
И в запуске прописать --force-renderer-accessibility
Для работы нужен:
https://github.com/neptercn/UIAutomatio … er/UIA.ahk

global $u:=new IUIAutomation,$e:=new IUIAutomationElement
$u.39(0,uia_OnEvent(pEvent,"OnFocus"))
return
OnFocus(self,sender){
   Critical
   Global control := $e.(sender).21
   return
}

#If WinActive("ahk_class Chrome_WidgetWin_1") and (control != 50003) and (control != 50004)
BackSpace::Send !{Left}
#If

5 (изменено: Malcev, 2017-07-09 16:52:45)

Re: AHK: "Назад" в Chrome

С acc вроде не сжирает память:

#Persistent
ComObjError(false)
OnExit, UnHook

pCallback := RegisterCallback("WinEventProc")
Acc_SetWinEventHook(EVENT_OBJECT_FOCUS := 0x8005, EVENT_OBJECT_FOCUS := 0x8005, pCallBack)
return

#If backspace
BackSpace::Send !{Left}
#If

UnHook:
Acc_UnhookWinEvent(pCallback)
ExitApp

WinEventProc(hHook, event, hWnd, idObject, idChild, eventThread, eventTime)
{
   Critical
   Global backspace := ""
   If WinActive("ahk_class Chrome_WidgetWin_1")
   {
      Acc := Acc_ObjectFromEvent(ByRef _idChild_, hWnd, idObject, idChild)
      Role := Acc.accRole(0)
      Value := Acc.accValue(0)
      if (Role != 42) and (Role != 46) and !InStr(Value, "/chrome/newtab")
         backspace := 1
   }
}



;------------------------------------------------------------------------------
; Acc.ahk Standard Library
; by Sean
; Updated by jethrow:
; 	Modified ComObjEnwrap params from (9,pacc) --> (9,pacc,1)
; 	Changed ComObjUnwrap to ComObjValue in order to avoid AddRef (thanks fincs)
; 	Added Acc_GetRoleText & Acc_GetStateText
; 	Added additional functions - commented below
; 	Removed original Acc_Children function
;	Added Acc_Error, Acc_ChildrenByRole, & Acc_Get functions
; last updated 10/25/2012
;------------------------------------------------------------------------------

Acc_Init()
{
   Static	h
   If Not	h
      h:=DllCall("LoadLibrary","Str","oleacc","Ptr")
}
Acc_ObjectFromEvent(ByRef _idChild_, hWnd, idObject, idChild)
{
   Acc_Init()
   If	DllCall("oleacc\AccessibleObjectFromEvent", "Ptr", hWnd, "UInt", idObject, "UInt", idChild, "Ptr*", pacc, "Ptr", VarSetCapacity(varChild,8+2*A_PtrSize,0)*0+&varChild)=0
   Return	ComObjEnwrap(9,pacc,1), _idChild_:=NumGet(varChild,8,"UInt")
}

Acc_ObjectFromPoint(ByRef _idChild_ = "", x = "", y = "")
{
   Acc_Init()
   If	DllCall("oleacc\AccessibleObjectFromPoint", "Int64", x==""||y==""?0*DllCall("GetCursorPos","Int64*",pt)+pt:x&0xFFFFFFFF|y<<32, "Ptr*", pacc, "Ptr", VarSetCapacity(varChild,8+2*A_PtrSize,0)*0+&varChild)=0
   Return	ComObjEnwrap(9,pacc,1), _idChild_:=NumGet(varChild,8,"UInt")
}

Acc_ObjectFromWindow(hWnd, idObject = 0)
{
   Acc_Init()
   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)
}

Acc_WindowFromObject(pacc)
{
   If	DllCall("oleacc\WindowFromAccessibleObject", "Ptr", IsObject(pacc)?ComObjValue(pacc):pacc, "Ptr*", hWnd)=0
   Return	hWnd
}

Acc_GetRoleText(nRole)
{
   nSize := DllCall("oleacc\GetRoleText", "Uint", nRole, "Ptr", 0, "Uint", 0)
   VarSetCapacity(sRole, (A_IsUnicode?2:1)*nSize)
   DllCall("oleacc\GetRoleText", "Uint", nRole, "str", sRole, "Uint", nSize+1)
   Return	sRole
}

Acc_GetStateText(nState)
{
   nSize := DllCall("oleacc\GetStateText", "Uint", nState, "Ptr", 0, "Uint", 0)
   VarSetCapacity(sState, (A_IsUnicode?2:1)*nSize)
   DllCall("oleacc\GetStateText", "Uint", nState, "str", sState, "Uint", nSize+1)
   Return	sState
}

Acc_SetWinEventHook(eventMin, eventMax, pCallback)
{
   Return	DllCall("SetWinEventHook", "Uint", eventMin, "Uint", eventMax, "Uint", 0, "Ptr", pCallback, "Uint", 0, "Uint", 0, "Uint", 0)
}

Acc_UnhookWinEvent(hHook)
{
   Return	DllCall("UnhookWinEvent", "Ptr", hHook)
}
/*	Win Events:

   pCallback := RegisterCallback("WinEventProc")
   WinEventProc(hHook, event, hWnd, idObject, idChild, eventThread, eventTime)
   {
      Critical
      Acc := Acc_ObjectFromEvent(_idChild_, hWnd, idObject, idChild)
      ; Code Here:

   }
*/

; Written by jethrow
Acc_Role(Acc, ChildId=0) {
   try return ComObjType(Acc,"Name")="IAccessible"?Acc_GetRoleText(Acc.accRole(ChildId)):"invalid object"
}
Acc_State(Acc, ChildId=0) {
   try return ComObjType(Acc,"Name")="IAccessible"?Acc_GetStateText(Acc.accState(ChildId)):"invalid object"
}
Acc_Location(Acc, ChildId=0, byref Position="") { ; adapted from Sean's code
   try Acc.accLocation(ComObj(0x4003,&x:=0), ComObj(0x4003,&y:=0), ComObj(0x4003,&w:=0), ComObj(0x4003,&h:=0), ChildId)
   catch
      return
   Position := "x" NumGet(x,0,"int") " y" NumGet(y,0,"int") " w" NumGet(w,0,"int") " h" NumGet(h,0,"int")
   return	{x:NumGet(x,0,"int"), y:NumGet(y,0,"int"), w:NumGet(w,0,"int"), h:NumGet(h,0,"int")}
}
Acc_Parent(Acc) { 
   try parent:=Acc.accParent
   return parent?Acc_Query(parent):
}
Acc_Child(Acc, ChildId=0) {
   try child:=Acc.accChild(ChildId)
   return child?Acc_Query(child):
}
Acc_Query(Acc) { ; thanks Lexikos - www.autohotkey.com/forum/viewtopic.php?t=81731&p=509530#509530
   try return ComObj(9, ComObjQuery(Acc,"{618736e0-3c3d-11cf-810c-00aa00389b71}"), 1)
}
Acc_Error(p="") {
   static setting:=0
   return p=""?setting:setting:=p
}
Acc_Children(Acc) {
   if ComObjType(Acc,"Name") != "IAccessible"
      ErrorLevel := "Invalid IAccessible Object"
   else {
      Acc_Init(), cChildren:=Acc.accChildCount, Children:=[]
      if DllCall("oleacc\AccessibleChildren", "Ptr",ComObjValue(Acc), "Int",0, "Int",cChildren, "Ptr",VarSetCapacity(varChildren,cChildren*(8+2*A_PtrSize),0)*0+&varChildren, "Int*",cChildren)=0 {
         Loop %cChildren%
            i:=(A_Index-1)*(A_PtrSize*2+8)+8, child:=NumGet(varChildren,i), Children.Insert(NumGet(varChildren,i-8)=9?Acc_Query(child):child), NumGet(varChildren,i-8)=9?ObjRelease(child):
         return Children.MaxIndex()?Children:
      } else
         ErrorLevel := "AccessibleChildren DllCall Failed"
   }
   if Acc_Error()
      throw Exception(ErrorLevel,-1)
}
Acc_ChildrenByRole(Acc, Role) {
   if ComObjType(Acc,"Name")!="IAccessible"
      ErrorLevel := "Invalid IAccessible Object"
   else {
      Acc_Init(), cChildren:=Acc.accChildCount, Children:=[]
      if DllCall("oleacc\AccessibleChildren", "Ptr",ComObjValue(Acc), "Int",0, "Int",cChildren, "Ptr",VarSetCapacity(varChildren,cChildren*(8+2*A_PtrSize),0)*0+&varChildren, "Int*",cChildren)=0 {
         Loop %cChildren% {
            i:=(A_Index-1)*(A_PtrSize*2+8)+8, child:=NumGet(varChildren,i)
            if NumGet(varChildren,i-8)=9
               AccChild:=Acc_Query(child), ObjRelease(child), Acc_Role(AccChild)=Role?Children.Insert(AccChild):
            else
               Acc_Role(Acc, child)=Role?Children.Insert(child):
         }
         return Children.MaxIndex()?Children:, ErrorLevel:=0
      } else
         ErrorLevel := "AccessibleChildren DllCall Failed"
   }
   if Acc_Error()
      throw Exception(ErrorLevel,-1)
}
Acc_Get(Cmd, ChildPath="", ChildID=0, WinTitle="", WinText="", ExcludeTitle="", ExcludeText="") {
   static properties := {Action:"DefaultAction", DoAction:"DoDefaultAction", Keyboard:"KeyboardShortcut"}
   AccObj :=   IsObject(WinTitle)? WinTitle
         :   Acc_ObjectFromWindow( WinExist(WinTitle, WinText, ExcludeTitle, ExcludeText), 0 )
   if ComObjType(AccObj, "Name") != "IAccessible"
      ErrorLevel := "Could not access an IAccessible Object"
   else {
      StringReplace, ChildPath, ChildPath, _, %A_Space%, All
      AccError:=Acc_Error(), Acc_Error(true)
      Loop Parse, ChildPath, ., %A_Space%
         try {
            if A_LoopField is digit
               Children:=Acc_Children(AccObj), m2:=A_LoopField ; mimic "m2" output in else-statement
            else
               RegExMatch(A_LoopField, "(\D*)(\d*)", m), Children:=Acc_ChildrenByRole(AccObj, m1), m2:=(m2?m2:1)
            if Not Children.HasKey(m2)
               throw
            AccObj := Children[m2]
         } catch {
            ErrorLevel:="Cannot access ChildPath Item #" A_Index " -> " A_LoopField, Acc_Error(AccError)
            if Acc_Error()
               throw Exception("Cannot access ChildPath Item", -1, "Item #" A_Index " -> " A_LoopField)
            return
         }
      Acc_Error(AccError)
      StringReplace, Cmd, Cmd, %A_Space%, , All
      properties.HasKey(Cmd)? Cmd:=properties[Cmd]:
      try {
         if (Cmd = "Location")
            AccObj.accLocation(ComObj(0x4003,&x:=0), ComObj(0x4003,&y:=0), ComObj(0x4003,&w:=0), ComObj(0x4003,&h:=0), ChildId)
           , ret_val := "x" NumGet(x,0,"int") " y" NumGet(y,0,"int") " w" NumGet(w,0,"int") " h" NumGet(h,0,"int")
         else if (Cmd = "Object")
            ret_val := AccObj
         else if Cmd in Role,State
            ret_val := Acc_%Cmd%(AccObj, ChildID+0)
         else if Cmd in ChildCount,Selection,Focus
            ret_val := AccObj["acc" Cmd]
         else
            ret_val := AccObj["acc" Cmd](ChildID+0)
      } catch {
         ErrorLevel := """" Cmd """ Cmd Not Implemented"
         if Acc_Error()
            throw Exception("Cmd Not Implemented", -1, Cmd)
         return
      }
      return ret_val, ErrorLevel:=0
   }
   if Acc_Error()
      throw Exception(ErrorLevel,-1)
}

6 (изменено: stealzy, 2017-07-09 18:07:03)

Re: AHK: "Назад" в Chrome

В опере 43 в варианте с Acc из 5 сообщения BS всегда работает как Назад (после запуска с параметром и проставки всех галок).
В варианте с UIA из 4 сообщения: достаточно поставить одну галку - global acc mode.

7

Re: AHK: "Назад" в Chrome

Страшноватые штуки, но попробую.

8 (изменено: Malcev, 2017-07-09 16:54:06)

Re: AHK: "Назад" в Chrome

stealzy, установил оперу 43.
У меня работают оба варианта.
Единственное, из-за глюков на первой странице нужно заменить:

if (Role != 42) and (Role != 46) and !InStr(Value, "/chrome/newtab")

на:

if (Role != 42) and (Role != 46) and !InStr(Value, "chrome://startpage/")

Kurskador, ничего там страшного нету.
Я бы использовал вариант с ACC, так как в UIA я вообще ничего не знаю (писал от балды ), примеров на автохотки практически нету, а в документации мне копаться лень (там ее очень много).

Поправил в примере с acc начальную страницу.

9

Re: AHK: "Назад" в Chrome

Есть простой вариант - нажимает "BackSpace", исходя от вида курсора.

#IfWinActive ahk_class Chrome_WidgetWin_1

loop {
if A_Cursor = IBeam
Hotkey, $BackSpace, Label1
else if Cursor != IBeam
Hotkey, $BackSpace, Label2
Sleep 20
}

Label1:
SendInput {BackSpace}
return

Label2:
SendInput !{Left}
return

10 (изменено: Malcev, 2017-07-10 05:43:37)

Re: AHK: "Назад" в Chrome

svoboden, курсор типа IBeam может быть необязательно только в поле ввода.
Он таким становится при наведении на практически любой текст.

11

Re: AHK: "Назад" в Chrome

Так я и говорю, что вариант простой.

12

Re: AHK: "Назад" в Chrome

Так он не "простой", а "срабатывающий не во всех случаях".
И для того чтобы проверить перед отправкой backspace тип курсора цикл не нужен.

13

Re: AHK: "Назад" в Chrome

Да, можно без loop:

#IfWinActive ahk_class Chrome_WidgetWin_1

Hotkey, BackSpace, Label1
return

Label1:
 If A_Cursor = IBeam
SendInput {BackSpace}
 else If A_Cursor != IBeam
SendInput !{Left}
return

14

Re: AHK: "Назад" в Chrome

svoboden, слишком усложняете:

#If WinActive("ahk_class Chrome_WidgetWin_1") and (A_Cursor != "IBeam")
BackSpace::Send !{Left}
#If

15 (изменено: svoboden, 2017-07-11 00:16:24)

Re: AHK: "Назад" в Chrome

Кстати, была уже похожая тема.