An Alternative Menu (A Quick Access To Various Features)

Post your working scripts, libraries and tools for AHK v1.1 and older
SundayProgrammer
Posts: 143
Joined: 25 Dec 2020, 12:26

An Alternative Menu (A Quick Access To Various Features)

26 Sep 2021, 23:30

this is a gui which shows a bunch of buttons for various tasks. it reads from a piece of text and base on the items listed in the text to construct itself. each item on the list has at least three fields which specify the label/name of the button, a description of its purpose (to be shown in a tool tip), and the type of this item (which can be "send", "gosub", "function", or "func+param"). buttons are organized in a very simple way, just one by one in the sequence as how they are laid in the text. the fields are separated by a "tab" character, and assumed one line one item. for lines without "tab" character, they will be treated as plain text.

a movable spot is created as a trigger for the gui. so, i call it spotmenu. spotmenu is scrollable if its content is more than a screen space can cope with. i think it's touch friendly as the buttons are relatively big.

spotmenu comes with some functions i copied from others, such as, SetCtrlFont, GuiControlTips, UpdateScrollBars, and OnScroll. in which, i have made some changes to GuiControlTips and OnScroll.

Code: Select all

_menu_=
(
= = = Top = = =

^#{Left}	Switch Desktop	(Send)
^#{Right}	Switch Desktop	(Send)
^z	Undo	(Send)
^w	Close Tab	(Send)
^a	Select All	(Send)
{Esc}	Escape	(Send)
{Enter}	Enter	(Send)
f	Fullscreen Toggle	(Send)
m{Enter}	MyActivity.Google	(Send)
- - - - - - - - - - - - - - - -
menu.Gui_OnEscape	Return To mSpot	(Function)
demo1	hello world	(Gosub)
demo2	foobar	(Gosub)
demo3	blablabla	(Function)
demo4	bla bla	(Func+Param)	_menu_
demo5	bla bla bla	(Func+Param)	_menu_,dummy

= = = Bottom = = =
)
dummy = i am param the second
menu.Spot()
Return
demo1:
	msgbox, this is demo1
	return
demo2:
	msgbox, this is demo2 this is demo2
	return
demo3(){
	msgbox, this is demo3 this is demo3 this is demo3
}
demo4(p){
	msgbox, % p
}
demo5(p1, p2){
	msgbox, % p1 "`n`n=========================`n`n" p2
}

#IfWinActive ahk_group SpotMenu
	WheelUp::
	WheelDown::
	+WheelUp::
	+WheelDown::OnScroll(InStr(A_ThisHotkey, "Down") ? 1 : 0, 0, GetKeyState("Shift") ? 0x114 : 0x115, WinExist())
#If

WM_DISPLAYCHANGE() {
	WinGetPos, x, y,,, mSpot
	If (x > A_ScreenWidth - 50)
		x := A_ScreenWidth - 100
	If (y > A_ScreenHeight - 125)
		y := A_ScreenHeight - 175
	Gui, mSpot:Show, x%x% y%y% NoActivate
}

WM_ACTIVATE() {
	IfWinActive, mSpot
		WinSet, TransColor, White, mSpot
	Else WinSet, TransColor, White 64, mSpot
}

Class menu {
Spot() {
	Gui, mSpot:New
	Gui, +LastFound -Caption +AlwaysOnTop +ToolWindow
	Gui, Color, White
	Gui, Margin, 0, 0
	Gui, Font, s150, Consolas
	Gui, Add, Text, x0 y0 cB2D6F3 BackgroundTrans, ●
	OnSuch := this.HitEvent.Bind(this)
	GuiControl +g, Static1, % OnSuch
	WinSet, TransColor, White 64
	x := A_ScreenWidth - 100, y := A_ScreenHeight - 530
	Gui, Show, x%x% y%y% NoActivate, mSpot
	this.HitEvent() ; Init sPos
	OnMessage(0x7E, "WM_DISPLAYCHANGE")
	OnMessage(0x06, "WM_ACTIVATE")
}
HitEvent(hwnd := "") {
	static sPos
	global _menu_
	If not StrLen(sPos)
	{	WinGetPos, x, y,,, mSpot
		sPos := x "," y
	}If not StrLen(hwnd)
		Return sPos
	SendMessage, 0xA1, 2,,, A ; WM_NCLBUTTONDOWN
	GuiControlGet, wHwnd, Hwnd, %hwnd%
	WinGetPos, x, y,,, ahk_id %wHwnd%
	If x "," y = sPos
	{	Gui, mSpot:Hide
		this.SpotMenu(_menu_)
	}Else
	{	sPos := x "," y
		SetTimer, Deactivate, -500
	}Return
	Deactivate:
		Gui, mSpot:Hide
		Gui, mSpot:Show, NoActivate
		WinSet, TransColor, White 64, mSpot
		Return
}
SpotMenu(t) {
	global TT
	Loop, Parse, t, `n
		If RegExMatch(A_LoopField, "[^\t]+(?=\t)", m)
			w%A_Index% := this.GetButtonWidth(m, "30 bold", "Consolas")
	OnMessage(0x115, "OnScroll") ; WM_VSCROLL
	OnMessage(0x114, "OnScroll") ; WM_HSCROLL
	OnMessage(0x20A, "OnScroll") ; WM_MOUSEWHEEL
	OnMessage(0x20E, "OnScroll") ; WM_MOUSEHWHEEL
	OnMessage(0x119, "gHandler") ; WM_GESTURE
	Gui, menu:New, hwndHmenu +Labelmenu.Gui_On
	Gui, +Resize +0x300000	; WS_VSCROLL | WS_HSCROLL
	TT := New GuiControlTips(Hmenu), TT.SetDelayTimes(1000, 15000, -1)
	Gui, Font, s30 bold, Consolas
	ci := nl := 1
	Loop, Parse, t, `n
		If RegExMatch(A_LoopField, "[^\t]+(?=\t)", m)
		{	If not nl
				nl := (gx + gw + 25 + w%A_Index%) > A_ScreenWidth - 40
			p := nl ? "" : "+", y := nl ? "y+28" : ""
			Gui, Add, Button, HwndhBtn%ci% x%p%25 %y% h57, % (m, pi := ci, ci++)
			OnSuch := this.MenuClick.Bind(this)
			GuiControl +g, Button%pi%, % OnSuch
			GuiControlGet, g, Pos, Button%pi%
			TT.Attach(hBtn%pi%, RegExReplace(RegExReplace(SubStr(A_LoopField, 0) = "`r" ? SubStr(A_LoopField, 1, -1) : A_LoopField, "[^\t]+\t",,, 1), "\t", "`n"))
			nl := (gx + gw) > (A_ScreenWidth - 116)
		}Else
		{	Gui, Add, Text, x25 cWhite, % SubStr(A_LoopField, 0) = "`r" ? SubStr(A_LoopField, 1, -1) : A_LoopField
			nl := True
		}
	Gui, Color, Black
	TT.SetTitle("Descriptions", LoadPicture("shell32.dll", "Icon222", ImageType))
	SetCtrlFont(TT.HTIP, "s20", "Arial New")
	Gui, +LastFound
	WinSet, Transparent, 180
	Gui, Show,, Touch Friendly Menu
	GroupAdd, SpotMenu, % "ahk_id " . WinExist()
}
Gui_OnSize() {
	UpdateScrollBars(A_Gui, A_GuiWidth, A_GuiHeight)
}
Gui_OnEscape() {
	menu.OnClose()
	Gui, mSpot:Show, NoActivate
	WinSet, TransColor, White 64, mSpot
}
Gui_OnClose() {
	ExitApp
}
OnClose() {
	OnMessage(0x115, "") ; WM_VSCROLL
	OnMessage(0x114, "") ; WM_HSCROLL
	OnMessage(0x20A, "") ; WM_MOUSEWHEEL
	OnMessage(0x20E, "") ; WM_MOUSEHWHEEL
	OnMessage(0x119, "") ; WM_GESTURE
	Gui, %A_Gui%:Destroy
}
GetButtonWidth(t, s, f) {
	Gui, New
	Gui, Font, s%s%, % f
	Gui, Add, Button,, % t
	GuiControlGet, g, Pos, Button1
	Gui, Destroy
	Return gW
}
MenuClick(h) {
	global TT
	theText := A_GuiControl
	this.OnClose()
	WinWaitNotActive, Touch Friendly Menu
	Sleep, 500
	If InStr(t := TT.GetText(h), "(Send)")
		Send, %theText%
	Else If InStr(t, "(Gosub)")
		Gosub, %theText%
	Else If InStr(t, "(Function)")
		If pos := InStr(theText, ".")
			className := SubStr(theText, 1, pos - 1), method := SubStr(theText, pos + 1), %className%[method]()
		Else %theText%()
	Else If InStr(t, "(Func+Param)")
	{	RegExMatch(t, "[^\n]+$", m)
		If InStr(m, ",")
		{	Loop, Parse, m, `,
				p%A_Index% := A_LoopField
			StrReplace(m, ",",, c)
			If c = 1
				%theText%(%p1%, %p2%)
			Else If c = 2
				%theText%(%p1%, %p2%, %p3%)
			Else If c = 3
				%theText%(%p1%, %p2%, %p3%, %p4%)
			Else If c = 4
				%theText%(%p1%, %p2%, %p3%, %p4%, %p5%)
			Else If c = 5
				%theText%(%p1%, %p2%, %p3%, %p4%, %p5%, %p6%)
			Else If c = 6
				%theText%(%p1%, %p2%, %p3%, %p4%, %p5%, %p6%, %p7%)
			Else If c = 7
				%theText%(%p1%, %p2%, %p3%, %p4%, %p5%, %p6%, %p7%, %p8%)
			Else If c = 8
				%theText%(%p1%, %p2%, %p3%, %p4%, %p5%, %p6%, %p7%, %p8%, %p9%)
			Else If c = 9
				%theText%(%p1%, %p2%, %p3%, %p4%, %p5%, %p6%, %p7%, %p8%, %p9%, %p10%)
			Else{}	;more than 10 parameters is not supported
		}Else %theText%(%m%)
	}
	Gui, mSpot:Show, NoActivate
	WinSet, TransColor, White 64, mSpot
}
}	;end of class

SetCtrlFont(CtrlHwnd, FontOptions := "", FontName := "") {	;written by iPhilip
   static WM_SETFONT := 0x0030, WM_GETFONT := 0x0031
   DefaultGui := A_DefaultGui
   Gui, New
   Gui, Font, % FontOptions, % FontName
   Gui, Add, Text, hwndhText, Text
   hFont := DllCall("SendMessage", "Ptr", hText, "UInt", WM_GETFONT, "Ptr", 0, "Ptr", 0, "Ptr")
   Gui, Destroy
   Gui, %DefaultGui%:Default
   DllCall("SendMessage", "Ptr", CtrlHwnd, "UInt", WM_SETFONT, "Ptr", hFont, "Ptr", true)
   Return hFont
}

; ======================================================================================================================
; Namespace:      GuiControlTips
; AHK version:    AHK 1.1.14.03
; Function:       Helper object to simply assign ToolTips for GUI controls
; Tested on:      Win 7 (x64)
; Change history:
;                 1.1.00.01/2020-06-03/just me - fixed missing Static WS_EX_TOPMOST
;                 1.1.00.00/2014-03-06/just me - Added SetDelayTimes()
;                 1.0.01.00/2012-07-29/just me
; ======================================================================================================================
; CLASS GuiControlTips
;
; The class provides four public methods to register (Attach), unregister (Detach), update (Update), and
; disable/enable (Suspend) common ToolTips for GUI controls.
;
; Usage:
; To assign ToolTips to GUI controls you have to create a new instance of GuiControlTips per GUI with
;     MyToolTipObject := New GuiControlTips(HGUI)
; passing the HWND of the GUI.
;
; After this you may assign ToolTips to your GUI controls by calling
;     MyToolTipObject.Attach(HCTRL, "ToolTip text")
; passing the HWND of the control and the ToolTip's text. Pass True/1 for the optional third parameter if you
; want the ToolTip to be shown centered below the control.
;
; To remove a ToolTip call
;     MyToolTipObject.Detach(HCTRL)
; passing the HWND of the control.
;
; To update the ToolTip's text call
;     MyToolTipObject.Update(HCTRL, "New text!")
; passing the HWND of the control and the new text.
;
; To deactivate the ToolTips call
;     MyToolTipObject.Suspend(True),
; to activate them again afterwards call
;     MyToolTipObject.Suspend(False).
;
; To adjust the ToolTips delay times call
;     MyToolTipObject.SetDelayTimesd(),
; specifying the delay times in milliseconds.
;
; That's all you can / have to do!
; ======================================================================================================================
Class GuiControlTips {	;written by @"just me"
   ; ===================================================================================================================
   ; INSTANCE variables
   ; ===================================================================================================================
   HTIP := 0
   HGUI := 0
   CTRL := {}
   ; ===================================================================================================================
   ; CONSTRUCTOR           __New()
   ; ===================================================================================================================
   __New(HGUI) {
      Static CLASS_TOOLTIP      := "tooltips_class32"
      Static CW_USEDEFAULT      := 0x80000000
      Static TTM_SETMAXTIPWIDTH := 0x0418
      Static TTM_SETMARGIN      := 0x041A
      Static WS_EX_TOPMOST      := 0x00000008
      Static WS_STYLES          := 0x80000002 ; WS_POPUP | TTS_NOPREFIX
      ; Create a Tooltip control ...
      HTIP := DllCall("User32.dll\CreateWindowEx", "UInt", WS_EX_TOPMOST, "Str", CLASS_TOOLTIP, "Ptr", 0
                    , "UInt", WS_STYLES
                    , "Int", CW_USEDEFAULT, "Int", CW_USEDEFAULT, "Int", CW_USEDEFAULT, "Int", CW_USEDEFAULT
                    , "Ptr", HGUI, "Ptr", 0, "Ptr", 0, "Ptr", 0, "Ptr")
      If ((ErrorLevel) || !(HTIP))
         Return False
      ; ... prepare it to display multiple lines if required
      DllCall("User32.dll\SendMessage", "Ptr", HTIP, "Int", TTM_SETMAXTIPWIDTH, "Ptr", 0, "Ptr", A_ScreenWidth*96//A_ScreenDPI)	;touched by SundayProgrammer who took it from iPhilip response for AddTooltip v2.0
      ; ... set the instance variables
      This.HTIP := HTIP
      This.HGUI := HGUI
      If (DllCall("Kernel32.dll\GetVersion", "UInt") & 0xFF) < 6 ; to avoid some XP issues ...
         This.Attach(HGUI, "") ; ... register the GUI with an empty tiptext
   }
   ; ===================================================================================================================
   ; DESTRUCTOR            __Delete()
   ; ===================================================================================================================
   __Delete() {
      If (This.HTIP) {
         DllCall("User32.dll\DestroyWindow", "Ptr", This.HTIP)
      }
   }
   ; ===================================================================================================================
   ; PRIVATE METHOD        SetToolInfo - Create and fill a TOOLINFO structure
   ; ===================================================================================================================
   SetToolInfo(ByRef TOOLINFO, HCTRL, TipTextAddr, CenterTip = 0) {
      Static TTF_IDISHWND  := 0x0001
      Static TTF_CENTERTIP := 0x0002
      Static TTF_SUBCLASS  := 0x0010
      Static OffsetSize  := 0
      Static OffsetFlags := 4
      Static OffsetHwnd  := 8
      Static OffsetID    := OffsetHwnd + A_PtrSize
      Static OffsetRect  := OffsetID + A_PtrSize
      Static OffsetInst  := OffsetRect + 16
      Static OffsetText  := OffsetInst + A_PtrSize
      Static StructSize  := (4 * 6) + (A_PtrSize * 6)
      Flags := TTF_IDISHWND | TTF_SUBCLASS
      If (CenterTip)
         Flags |= TTF_CENTERTIP
      VarSetCapacity(TOOLINFO, StructSize, 0)
      NumPut(StructSize, TOOLINFO, OffsetSize, "UInt")
      NumPut(Flags, TOOLINFO, OffsetFlags, "UInt")
      NumPut(This.HGUI, TOOLINFO, OffsetHwnd, "Ptr")
      NumPut(HCTRL, TOOLINFO, OffsetID, "Ptr")
      NumPut(TipTextAddr, TOOLINFO, OffsetText, "Ptr")
      Return True
   }
   ; ===================================================================================================================
   ; PUBLIC METHOD         Attach         -  Assign a ToolTip to a certain control
   ; Parameters:           HWND           -  Control's HWND
   ;                       TipText        -  ToolTip's text
   ;                       Optional:      ------------------------------------------------------------------------------
   ;                       CenterTip      -  Centers the tooltip window below the control
   ;                                         Values:  True/False
   ;                                         Default: False
   ; Return values:        On success: True
   ;                       On failure: False
   ; ===================================================================================================================
   Attach(HCTRL, TipText, CenterTip = False) {
      Static TTM_ADDTOOL  := A_IsUnicode ? 0x0432 : 0x0404 ; TTM_ADDTOOLW : TTM_ADDTOOLA
      If !(This.HTIP) {
         Return False
      }
      If This.CTRL.HasKey(HCTRL)
         Return False
      TOOLINFO := ""
      This.SetToolInfo(TOOLINFO, HCTRL, &TipText, CenterTip)
      If DllCall("User32.dll\SendMessage", "Ptr", This.HTIP, "Int", TTM_ADDTOOL, "Ptr", 0, "Ptr", &TOOLINFO) {
         This.CTRL[HCTRL] := 1
		 This.TTT[HCTRL] := TipText	;added by SundayProgrammer
         Return True
      } Else {
        Return False
      }
   }
   ; ===================================================================================================================
   ; PUBLIC METHOD         Detach         -  Remove the ToolTip for a certain control
   ; Parameters:           HWND           -  Control's HWND
   ; Return values:        On success: True
   ;                       On failure: False
   ; ===================================================================================================================
   Detach(HCTRL) {
      Static TTM_DELTOOL  := A_IsUnicode ? 0x0433 : 0x0405 ; TTM_DELTOOLW : TTM_DELTOOLA
      If !This.CTRL.HasKey(HCTRL)
         Return False
      TOOLINFO := ""
      This.SetToolInfo(TOOLINFO, HCTRL, 0)
      DllCall("User32.dll\SendMessage", "Ptr", This.HTIP, "Int", TTM_DELTOOL, "Ptr", 0, "Ptr", &TOOLINFO)
      This.CTRL.Remove(HCTRL, "")
	  This.TTT.Remove(HCTRL, "")	;added by SundayProgrammer
      Return True
   }
   ; ===================================================================================================================
   ; PUBLIC METHOD         Update         -  Update the ToolTip's text for a certain control
   ; Parameters:           HWND           -  Control's HWND
   ;                       TipText        -  New text                                                      
   ; Return values:        On success: True
   ;                       On failure: False
   ; ===================================================================================================================
   Update(HCTRL, TipText) {
      Static TTM_UPDATETIPTEXT  := A_IsUnicode ? 0x0439 : 0x040C ; TTM_UPDATETIPTEXTW : TTM_UPDATETIPTEXTA
      If !This.CTRL.HasKey(HCTRL)
         Return False
      TOOLINFO := ""
      This.SetToolInfo(TOOLINFO, HCTRL, &TipText)
      DllCall("SendMessage", "Ptr", This.HTIP, "Int", TTM_UPDATETIPTEXT, "Ptr", 0, "Ptr", &TOOLINFO)
	  This.TTT[HCTRL] := TipText	;added by SundayProgrammer
      Return True
   }
   ;added by SundayProgrammer
   GetText(HCTRL) {
      If !This.CTRL.HasKey(HCTRL)
         Return ""
      Return This.TTT[HCTRL]
   }
   ; ===================================================================================================================
   ; PUBLIC METHOD         Suspend        -  Disable/enable the ToolTip control (don't show / show ToolTips)
   ; Parameters:           Mode           -  True/False (1/0)
   ;                                         Default: True/1
   ; Return values:        On success: True
   ;                       On failure: False
   ; Remarks:              ToolTips are enabled automatically on creation.
   ; ===================================================================================================================
   Suspend(Mode = True) {
      Static TTM_ACTIVATE := 0x0401
      If !(This.HTIP)
         Return False
      DllCall("SendMessage", "Ptr", This.HTIP, "Int", TTM_ACTIVATE, "Ptr", !Mode, "Ptr", 0)
      Return True
   }
   ; ===================================================================================================================
   ; PUBLIC METHOD         SetDelayTimes  -  Set the initial, pop-up, and reshow durations for a tooltip control.
   ; Parameters:           Init           -  Amount of time, in milliseconds, a pointer must remain stationary within
   ;                                         a tool's bounding rectangle before the tooltip window appears.
   ;                                         Default: -1 (system default time)
   ;                       PopUp          -  Amount of time, in milliseconds, a tooltip window remains visible if the
   ;                                         pointer is stationary within a tool's bounding rectangle.
   ;                                         Default: -1 (system default time)
   ;                       ReShow         -  Amount of time, in milliseconds, it takes for subsequent tooltip windows
   ;                                         to appear as the pointer moves from one tool to another.
   ;                                         Default: -1 (system default time)
   ; Return values:        On success: True
   ;                       On failure: False
   ; Remarks:              Times are set per ToolTip control and applied to all added tools.
   ; ===================================================================================================================
   SetDelayTimes(Init = -1, PopUp = -1, ReShow = -1) {
      Static TTM_SETDELAYTIME   := 0x0403
      Static TTDT_RESHOW   := 1
      Static TTDT_AUTOPOP  := 2
      Static TTDT_INITIAL  := 3
      DllCall("SendMessage", "Ptr", This.HTIP, "Int", TTM_SETDELAYTIME, "Ptr", TTDT_INITIAL, "Ptr", Init)
      DllCall("SendMessage", "Ptr", This.HTIP, "Int", TTM_SETDELAYTIME, "Ptr", TTDT_AUTOPOP, "Ptr", PopUp)
      DllCall("SendMessage", "Ptr", This.HTIP, "Int", TTM_SETDELAYTIME, "Ptr", TTDT_RESHOW , "Ptr", ReShow)
   }
   ;added by SundayProgrammer who largely took it from AddTooltip v2.0
   SetTitle(theTitle := "", theIcon := 0) {
      Static TTM_SETTITLE := A_IsUnicode ? 0x421 : 0x420 ; TTM_SETTITLEW : TTM_SETTITLEA
      If StrLen(theTitle) > 99
         theTitle := SubStr(theTitle, 1, 99)
      If theIcon is not Integer
         theIcon := 0
      DllCall("SendMessage", "Ptr", This.HTIP, "Int", TTM_SETTITLE, "Ptr", theIcon, "Ptr", &theTitle)
   }
}

UpdateScrollBars(GuiNum, GuiWidth, GuiHeight) {	;written by lexikos
    static SIF_RANGE=0x1, SIF_PAGE=0x2, SIF_DISABLENOSCROLL=0x8, SB_HORZ=0, SB_VERT=1
   
    Gui, %GuiNum%:Default
    Gui, +LastFound
   
    ; Calculate scrolling area.
    Left := Top := 9999
    Right := Bottom := 0
    WinGet, ControlList, ControlList
    Loop, Parse, ControlList, `n
    {
        GuiControlGet, c, Pos, %A_LoopField%
        if (cX < Left)
            Left := cX
        if (cY < Top)
            Top := cY
        if (cX + cW > Right)
            Right := cX + cW
        if (cY + cH > Bottom)
            Bottom := cY + cH
    }
    Left -= 8
    Top -= 8
    Right += 8
    Bottom += 8
    ScrollWidth := Right-Left
    ScrollHeight := Bottom-Top
   
    ; Initialize SCROLLINFO.
    VarSetCapacity(si, 28, 0)
    NumPut(28, si) ; cbSize
    NumPut(SIF_RANGE | SIF_PAGE, si, 4) ; fMask
   
    ; Update horizontal scroll bar.
    NumPut(ScrollWidth, si, 12) ; nMax
    NumPut(GuiWidth, si, 16) ; nPage
    DllCall("SetScrollInfo", "uint", WinExist(), "uint", SB_HORZ, "uint", &si, "int", 1)
   
    ; Update vertical scroll bar.
;     NumPut(SIF_RANGE | SIF_PAGE | SIF_DISABLENOSCROLL, si, 4) ; fMask
    NumPut(ScrollHeight, si, 12) ; nMax
    NumPut(GuiHeight, si, 16) ; nPage
    DllCall("SetScrollInfo", "uint", WinExist(), "uint", SB_VERT, "uint", &si, "int", 1)
   
    if (Left < 0 && Right < GuiWidth)
        x := Abs(Left) > GuiWidth-Right ? GuiWidth-Right : Abs(Left)
    if (Top < 0 && Bottom < GuiHeight)
        y := Abs(Top) > GuiHeight-Bottom ? GuiHeight-Bottom : Abs(Top)
    if (x || y)
        DllCall("ScrollWindow", "uint", WinExist(), "int", x, "int", y, "uint", 0, "uint", 0)
}

OnScroll(wParam, lParam, msg, hwnd) {	;written by lexikos
    static SIF_ALL=0x17, SCROLL_STEP=85 ;changed by SundayProgrammer from 10 to 85 for a more practical outcome
	static xpos := 0, ypos := 0	;added by SundayProgrammer - for touch gesture scrolling
	global gFlag	;added by SundayProgrammer - for touch gesture scrolling
if DllCall("GetParent", "uint", hwnd)	;added by SundayProgrammer - a quick fix for the scenario when any scrollable control is involved
	return	;added by SundayProgrammer - a quick fix for the scenario when any scrollable control is involved
   
    bar := (msg=0x115) or (msg=0x20A) ; (SB_HORZ=0, SB_VERT=1) or (WM_MOUSEHWHEEL=0, WM_MOUSEWHEEL=1)	;changed by SundayProgrammer - for WM_MOUSEWHEEL and WM_MOUSEHWHEEL
   
	if gFlag	;added by SundayProgrammer - for touch gesture scrolling
	{	gAction(xpos, ypos, bar, hwnd)	;added by SundayProgrammer - for touch gesture scrolling
		return	;added by SundayProgrammer - for touch gesture scrolling
	}	;added by SundayProgrammer - for touch gesture scrolling
   
    VarSetCapacity(si, 28, 0)
    NumPut(28, si) ; cbSize
    NumPut(SIF_ALL, si, 4) ; fMask
    if !DllCall("GetScrollInfo", "uint", hwnd, "int", bar, "uint", &si)
        return
   
    VarSetCapacity(rect, 16)
    DllCall("GetClientRect", "uint", hwnd, "uint", &rect)
   
    new_pos := NumGet(si, 20) ; nPos (saw "25" in another version, which exhibited a bug in my testing, whereas "20" (this version) worked fine so far)
   
    if msg=0x20A	;added by SundayProgrammer - for WM_MOUSEWHEEL
        wParam := wParam>0x780000	;added by SundayProgrammer - for WM_MOUSEWHEEL
    else if msg=0x20E	;added by SundayProgrammer - for WM_MOUSEHWHEEL
        wParam := wParam=0x780000	;added by SundayProgrammer - for WM_MOUSEHWHEEL
    action := wParam & 0xFFFF
    if action = 0 ; SB_LINEUP
        new_pos -= SCROLL_STEP
    else if action = 1 ; SB_LINEDOWN
        new_pos += SCROLL_STEP
    else if action = 2 ; SB_PAGEUP
        new_pos -= NumGet(rect, 12, "int") - SCROLL_STEP
    else if action = 3 ; SB_PAGEDOWN
        new_pos += NumGet(rect, 12, "int") - SCROLL_STEP
    else if (action = 5 || action = 4) ; SB_THUMBTRACK || SB_THUMBPOSITION
        new_pos := wParam>>16
    else if action = 6 ; SB_TOP
        new_pos := NumGet(si, 8, "int") ; nMin
    else if action = 7 ; SB_BOTTOM
        new_pos := NumGet(si, 12, "int") ; nMax
    else
        return
   
    min := NumGet(si, 8, "int") ; nMin
    max := NumGet(si, 12, "int") - NumGet(si, 16) ; nMax-nPage
    new_pos := new_pos > max ? max : new_pos
    new_pos := new_pos < min ? min : new_pos
   
    old_pos := NumGet(si, 20, "int") ; nPos (saw "25" in another version, which exhibited a bug in my testing, whereas "20" (this version) worked fine so far)
   
    x := y := 0
    if bar = 0 ; SB_HORZ
        x := old_pos-new_pos
    else
        y := old_pos-new_pos
    ; Scroll contents of window and invalidate uncovered area.
    DllCall("ScrollWindow", "uint", hwnd, "int", x, "int", y, "uint", 0, "uint", 0)
   
    ; Update scroll bar.
    NumPut(new_pos, si, 20, "int") ; nPos (saw "25" in another version, which exhibited a bug in my testing, whereas "20" (this version) worked fine so far)
    DllCall("SetScrollInfo", "uint", hwnd, "int", bar, "uint", &si, "int", 1)

	z := bar ? "y" : "x", %z%pos := new_pos	;added by SundayProgrammer - for touch gesture scrolling
}
gAction(byref xpos, byref ypos, bar, hwnd) {	;written by SundayProgrammer - for touch gesture scrolling
	VarSetCapacity(si, 28, 0), NumPut(28, si), NumPut(0x17, si, 4), DllCall("GetScrollInfo", "uint", hwnd, "int", bar, "uint", &si), aPos := NumGet(si, 20, "int"), z := bar ? "y" : "x"
	if not (%z%pos = aPos)
		x := y := 0, %z% := %z%pos - aPos, DllCall("ScrollWindow", "uint", hwnd, "int", x, "int", y, "uint", 0, "uint", 0), %z%pos := aPos
}
gHandler(wParam, lParam, msg, hwnd) {	;written by SundayProgrammer - for touch gesture scrolling
	global gFlag
	gFlag := true
	settimer, Reset_gFlag, -100
	return
	Reset_gFlag:
		gFlag := false
		return
}
2021-9-28 made some minor adjustments
SundayProgrammer
Posts: 143
Joined: 25 Dec 2020, 12:26

Re: An Alternative Menu (A Quick Access To Various Features)

16 Oct 2021, 10:34

what's new in this version:

(1) avoided to use global variables. all stored in the pool as a "global" variables repository

(2) added one more item type, the "script", which makes use of ObjRegisterActive and "run dynamic script through a pipe" to achieve the result. and, in order to enable the dynamic script not only can call "remote" functions through the registered object, a "var" function was created to get/set "remote" variables as well.

Code: Select all

pool()	;init variables
menu.Spot()	;show mSpot
Return
EditMenu:
	Run, Notepad MenuText.txt
	Return
demo1:
	msgbox, this is demo1
	return
demo2:
	msgbox, this is demo2 this is demo2
	return
demo3(){
	msgbox, this is demo3 this is demo3 this is demo3
}
demo4(p){
	msgbox, % p
}
demo5(p1, p2){
	msgbox, % p1 "`n`n=========================`n`n" p2
}

pool(param = "") {	;written by SundayProgrammer
	static
	static clsid1 := "{A3C04B39-0465-4460-8CA0-7BFFF481FF98}", s1l := "a := ComObjActive(""" clsid1 """)`n"
	static dummy1 := "i am param the first"
	static dummy2 := "i am param the second"
If StrLen(param)
	If IsGetSet(param, action, varname, value)
	{	If (action = "Set:")
			%varname% := value
		v := %varname%
		Return v
	}Else{}
Else{
	ObjRegisterActive(agent, clsid1)
	IfExist, MenuText.txt
		FileRead, _menu_, MenuText.txt
	Else{
_menu_=
(
= = = Top = = =
^#{Left}	Switch Desktop	(Send)
^#{Right}	Switch Desktop	(Send)
#{Tab}	Windows Switcher	(Send)
m{Enter}	MyActivity.Google	(Send)
+^u	Rald Toggle	(Send)
^f	Find	(Send)
^r	Reload Web Page	(Send)
^w	Close Tab	(Send)
j	10s Backward	(Send)
!{Left}	Previous Web Page	(Send)
!{Right}	Next Web Page	(Send)
{Enter}	Enter	(Send)
f	Fullscreen Toggle	(Send)
{Space}	Space	(Send)
{Del}	Delete	(Send)
{Esc}	Escape	(Send)
^g	Go To Line	(Send)
!d	Duplicate	(Send)
^z	Undo	(Send)
^x	Cut	(Send)
^c	Copy	(Send)
^v	Paste	(Send)
^s	Save File	(Send)
^a	Select All	(Send)
- - - - - - - - - - - - - - - -
Close Menu	{menu.Gui_OnEscape}	Return To mSpot	(Function)
Reload Menu	{menu.Reload}	Reflect The Latest Content	(Function)
Edit Menu	{EditMenu}	(Gosub)
Demo One	{demo1}	hello world	(Gosub)
Demo Two	{demo2}	foobar	(Gosub)
Demo Three	{demo3}	blablabla	(Function)
Demo Four	{demo4}	bla bla	(Func+Param)	dummy1
Demo Five	{demo5}	bla bla bla	(Func+Param)	dummy1,dummy2
Script I	(Script)	msgbox `% "remote: " a.call("var","dummy1@pool")
Script II	(Script)	msgbox `% "remote: " a.call("var","dummy2@pool")
Script III	(Script)	a.call("var","Set:dummy3@pool",a_tickcount)|| ||msgbox `% "remote: " a.call("var","dummy3@pool")
Verify	{demo4}	(Func+Param)	dummy3
= = = Bottom = = =
)
FileAppend, %_menu_%, MenuText.txt, UTF-8
		}
	}
}

IsGetSet(Param, ByRef a, ByRef v, ByRef value) {	;written by SundayProgrammer
	a := SubStr(Param, 1, 4)
	If (a = "Set:") or (a = "Get:")
	{	v := Trim((p := InStr(Param, "=")) ? (SubStr(Param, 5, p - 5), value := SubStr(Param, p + 1)) : SubStr(Param, 5))
		Return True
	}Return False
}
var(name, value = "") {	;written by SundayProgrammer
	global
	local p, a, v, m1, m2, m3, segc
	If p := InStr(name, "@")
	{	a := SubStr(name, 1, 4), v := Trim(SubStr(name, 1, p - 1)), name := Trim(SubStr(name, p + 1))
		If (a = "Set:") or (a = "Get:")
			v := Trim(SubStr(v, 5))
		Else a := StrLen(value) ? "Set:" : "Get:"
		If StrLen(v) and StrLen(name)
		{	v := a v, v .= (a = "Set:") ? "=" value : ""
			If InStr(name, ".")
			{	Loop, Parse, name, .
					m%A_Index% := A_LoopField, segc := A_Index
				If segc = 2
					v := %m1%[m2](v)
				Else If segc = 3
					v := %m1%[m2][m3](v)
				Else v = Error: Segments More Than Allowed.
			}Else v := %name%(v)
		}Else v = Error: Invalid Parameter "%v%@%name%"
	}Else
	{	a := SubStr(name, 1, 4)
		If (a = "Set:") or (a = "Get:")
			name := Trim(SubStr(name, 5))
		If InStr(name, ".")
		{	Loop, Parse, name, .
				m%A_Index% := A_LoopField, segc := A_Index
			If segc = 2
			{	If (a = "Set:") or StrLen(value)
					%m1%[m2] := value
				v := %m1%[m2]
			}Else If segc = 3
			{	If (a = "Set:") or StrLen(value)
					%m1%[m2][m3] := value
				v := %m1%[m2][m3]
			}Else v = Error: Segments More Than Allowed.
		}Else
		{	If (a = "Set:") or StrLen(value)
				%name% := value
			v := %name%
		}
	}
	Return v
}

Class agent {	;written by SundayProgrammer who got the idea from this post https://www.autohotkey.com/boards/viewtopic.php?p=200597#p200597
	Call(name, p*) {	;allows you to call any function in this script
		If InStr(name, ".")
		{	Loop, Parse, name, .
				m%A_Index% := A_LoopField, segc := A_Index
			If segc = 2
				Return %m1%[m2](p*)
			Else If segc = 3
				Return %m1%[m2][m3](p*)
			Else Return "Error: Segments More Than Allowed."
		}Return %name%(p*)
	}
}

#IfWinActive ahk_group SpotMenu
	WheelUp::
	WheelDown::
	+WheelUp::
	+WheelDown::OnScroll(InStr(A_ThisHotkey, "Down") ? 1 : 0, 0, GetKeyState("Shift") ? 0x114 : 0x115, WinExist())
#If

WM_DISPLAYCHANGE() {
	WinGetPos, x, y,,, mSpot
	If (x > A_ScreenWidth - 50)
		x := A_ScreenWidth - 100
	If (y > A_ScreenHeight - 125)
		y := A_ScreenHeight - 175
	Gui, mSpot:Show, x%x% y%y% NoActivate
}

WM_ACTIVATE() {
	IfWinActive, mSpot
		WinSet, TransColor, White, mSpot
	Else WinSet, TransColor, White 64, mSpot
}

Class menu {	;written by SundayProgrammer
Spot() {
	Gui, mSpot:New
	Gui, +LastFound -Caption +AlwaysOnTop +ToolWindow
	Gui, Color, White
	Gui, Margin, 0, 0
	Gui, Font, s150, Consolas
	Gui, Add, Text, x0 y0 cFFB10F BackgroundTrans, ●
	OnSuch := this.HitEvent.Bind(this)
	GuiControl +g, Static1, % OnSuch
	WinSet, TransColor, White 64
	x := A_ScreenWidth - 100, y := A_ScreenHeight - 530
	Gui, Show, x%x% y%y% NoActivate, mSpot
	this.HitEvent() ; Init sPos
	OnMessage(0x7E, "WM_DISPLAYCHANGE")
	OnMessage(0x06, "WM_ACTIVATE")
}
HitEvent(hwnd := "") {
	static sPos
	If not StrLen(sPos)
	{	WinGetPos, x, y,,, mSpot
		sPos := x "," y
	}If not StrLen(hwnd)
		Return sPos
	SendMessage, 0xA1, 2,,, A ; WM_NCLBUTTONDOWN
	GuiControlGet, wHwnd, Hwnd, %hwnd%
	WinGetPos, x, y,,, ahk_id %wHwnd%
	If x "," y = sPos
	{	Gui, mSpot:Hide
		this.SpotMenu(var("_menu_@pool"))
	}Else
	{	sPos := x "," y
		SetTimer, Deactivate, -500
	}Return
	Deactivate:
		Gui, mSpot:Hide
		Gui, mSpot:Show, NoActivate
		WinSet, TransColor, White 64, mSpot
		Return
}
SpotMenu(t) {
	global TT
	Loop, Parse, t, `n
		If RegExMatch(A_LoopField, "[^\t]+(?=\t)", m)
			w%A_Index% := this.GetButtonWidth(m, "30 bold", "Consolas")
	OnMessage(0x115, "OnScroll") ; WM_VSCROLL
	OnMessage(0x114, "OnScroll") ; WM_HSCROLL
	OnMessage(0x20A, "OnScroll") ; WM_MOUSEWHEEL
	OnMessage(0x20E, "OnScroll") ; WM_MOUSEHWHEEL
	OnMessage(0x119, "gHandler") ; WM_GESTURE
	Gui, menu:New, hwndHmenu +Labelmenu.Gui_On
	Gui, +Resize +0x300000	; WS_VSCROLL | WS_HSCROLL
	TT := New GuiControlTips(Hmenu), TT.SetDelayTimes(1000, 15000, -1)
	Gui, Font, s30 bold, Consolas
	ci := nl := 1
	Loop, Parse, t, `n
		If RegExMatch(A_LoopField, "[^\t]+(?=\t)", m)
		{	If not nl
				nl := (gx + gw + 25 + w%A_Index%) > A_ScreenWidth - 40
			p := nl ? "" : "+", y := nl ? "y+28" : ""
			Gui, Add, Button, HwndhBtn%ci% x%p%25 %y% h57, % (m, pi := ci, ci++)
			OnSuch := this.MenuClick.Bind(this)
			GuiControl +g, Button%pi%, % OnSuch
			GuiControlGet, g, Pos, Button%pi%
			TT.Attach(hBtn%pi%, RegExReplace(RegExReplace(SubStr(A_LoopField, 0) = "`r" ? SubStr(A_LoopField, 1, -1) : A_LoopField, "[^\t]+\t",,, 1), "\t", "`n"))
			nl := (gx + gw) > (A_ScreenWidth - 116)
		}Else
		{	Gui, Add, Text, x25 cWhite, % SubStr(A_LoopField, 0) = "`r" ? SubStr(A_LoopField, 1, -1) : A_LoopField
			nl := True
		}
	Gui, Color, Black
	TT.SetTitle("Descriptions", LoadPicture("shell32.dll", "Icon222", ImageType))
	SetCtrlFont(TT.HTIP, "s20", "Arial New")
	Gui, +LastFound
	WinSet, Transparent, 180
	Gui, Show,, Touch Friendly Menu
	GroupAdd, SpotMenu, % "ahk_id " . WinExist()
}
Gui_OnSize() {
	UpdateScrollBars(A_Gui, A_GuiWidth, A_GuiHeight)
}
Gui_OnEscape() {
	menu.OnClose()
	Gui, mSpot:Show, NoActivate
	WinSet, TransColor, White 64, mSpot
}
Gui_OnClose() {
	ObjRegisterActive(agent, "")
	ExitApp
}
OnClose() {
	OnMessage(0x115, "") ; WM_VSCROLL
	OnMessage(0x114, "") ; WM_HSCROLL
	OnMessage(0x20A, "") ; WM_MOUSEWHEEL
	OnMessage(0x20E, "") ; WM_MOUSEHWHEEL
	OnMessage(0x119, "") ; WM_GESTURE
	Gui, menu:Destroy
}
GetButtonWidth(t, s, f) {
	Gui, New
	Gui, Font, s%s%, % f
	Gui, Add, Button,, % t
	GuiControlGet, g, Pos, Button1
	Gui, Destroy
	Return gW
}
MenuClick(h) {
	global TT
	theText := A_GuiControl
	this.OnClose()
	WinWaitNotActive, Touch Friendly Menu
	Sleep, 500
	If RegExMatch(t := TT.GetText(h), "{\K[^}]+(?=})", m)
		If not InStr(t, "(Send)")
			theText := m
	If InStr(t, "(Send)")
		Send, %theText%
	Else If InStr(t, "(Gosub)")
		Gosub, %theText%
	Else If InStr(t, "(Function)")
		If pos := InStr(theText, ".")
			className := SubStr(theText, 1, pos - 1), method := SubStr(theText, pos + 1), %className%[method]()
		Else %theText%()
	Else If InStr(t, "(Func+Param)")
	{	RegExMatch(t, "[^\n]+$", m)
		If InStr(m, ",")
		{	Loop, Parse, m, `,
				p%A_Index% := var(A_LoopField "@pool")
			StrReplace(m, ",",, c)
			If c = 1
				%theText%(p1, p2)
			Else If c = 2
				%theText%(p1, p2, p3)
			Else If c = 3
				%theText%(p1, p2, p3, p4)
			Else If c = 4
				%theText%(p1, p2, p3, p4, p5)
			Else If c = 5
				%theText%(p1, p2, p3, p4, p5, p6)
			Else If c = 6
				%theText%(p1, p2, p3, p4, p5, p6, p7)
			Else If c = 7
				%theText%(p1, p2, p3, p4, p5, p6, p7, p8)
			Else If c = 8
				%theText%(p1, p2, p3, p4, p5, p6, p7, p8, p9)
			Else If c = 9
				%theText%(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10)
			Else{}	;more than 10 parameters is not supported
		}Else %theText%(var(m "@pool"))
	}Else If InStr(t, "(Script)")
		RegExMatch(t, "\(Script\)[\n\t]+\K.*$", m), m := StrReplace(m, "|| ||", "`n"), ExecScript(var("s1l@pool") m,, A_AhkPath)
	Gui, mSpot:Show, NoActivate
	WinSet, TransColor, White 64, mSpot
}
Reload() {
	FileRead, _menu_, MenuText.txt
	var("Set:_menu_@pool", _menu_)
	SetTimer, ReloadMenu, -500
	Return
	ReloadMenu:
		Gui, mSpot:Hide
		menu.SpotMenu(var("_menu_@pool"))
		Return
}
}	;end of class

SetCtrlFont(CtrlHwnd, FontOptions := "", FontName := "") {	;written by iPhilip
   static WM_SETFONT := 0x0030, WM_GETFONT := 0x0031
   DefaultGui := A_DefaultGui
   Gui, New
   Gui, Font, % FontOptions, % FontName
   Gui, Add, Text, hwndhText, Text
   hFont := DllCall("SendMessage", "Ptr", hText, "UInt", WM_GETFONT, "Ptr", 0, "Ptr", 0, "Ptr")
   Gui, Destroy
   Gui, %DefaultGui%:Default
   DllCall("SendMessage", "Ptr", CtrlHwnd, "UInt", WM_SETFONT, "Ptr", hFont, "Ptr", true)
   Return hFont
}

; ======================================================================================================================
; Namespace:      GuiControlTips
; AHK version:    AHK 1.1.14.03
; Function:       Helper object to simply assign ToolTips for GUI controls
; Tested on:      Win 7 (x64)
; Change history:
;                 1.1.00.01/2020-06-03/just me - fixed missing Static WS_EX_TOPMOST
;                 1.1.00.00/2014-03-06/just me - Added SetDelayTimes()
;                 1.0.01.00/2012-07-29/just me
; ======================================================================================================================
; CLASS GuiControlTips
;
; The class provides four public methods to register (Attach), unregister (Detach), update (Update), and
; disable/enable (Suspend) common ToolTips for GUI controls.
;
; Usage:
; To assign ToolTips to GUI controls you have to create a new instance of GuiControlTips per GUI with
;     MyToolTipObject := New GuiControlTips(HGUI)
; passing the HWND of the GUI.
;
; After this you may assign ToolTips to your GUI controls by calling
;     MyToolTipObject.Attach(HCTRL, "ToolTip text")
; passing the HWND of the control and the ToolTip's text. Pass True/1 for the optional third parameter if you
; want the ToolTip to be shown centered below the control.
;
; To remove a ToolTip call
;     MyToolTipObject.Detach(HCTRL)
; passing the HWND of the control.
;
; To update the ToolTip's text call
;     MyToolTipObject.Update(HCTRL, "New text!")
; passing the HWND of the control and the new text.
;
; To deactivate the ToolTips call
;     MyToolTipObject.Suspend(True),
; to activate them again afterwards call
;     MyToolTipObject.Suspend(False).
;
; To adjust the ToolTips delay times call
;     MyToolTipObject.SetDelayTimesd(),
; specifying the delay times in milliseconds.
;
; That's all you can / have to do!
; ======================================================================================================================
Class GuiControlTips {	;written by @"just me"
   ; ===================================================================================================================
   ; INSTANCE variables
   ; ===================================================================================================================
   HTIP := 0
   HGUI := 0
   CTRL := {}
   ; ===================================================================================================================
   ; CONSTRUCTOR           __New()
   ; ===================================================================================================================
   __New(HGUI) {
      Static CLASS_TOOLTIP      := "tooltips_class32"
      Static CW_USEDEFAULT      := 0x80000000
      Static TTM_SETMAXTIPWIDTH := 0x0418
      Static TTM_SETMARGIN      := 0x041A
      Static WS_EX_TOPMOST      := 0x00000008
      Static WS_STYLES          := 0x80000002 ; WS_POPUP | TTS_NOPREFIX
      ; Create a Tooltip control ...
      HTIP := DllCall("User32.dll\CreateWindowEx", "UInt", WS_EX_TOPMOST, "Str", CLASS_TOOLTIP, "Ptr", 0
                    , "UInt", WS_STYLES
                    , "Int", CW_USEDEFAULT, "Int", CW_USEDEFAULT, "Int", CW_USEDEFAULT, "Int", CW_USEDEFAULT
                    , "Ptr", HGUI, "Ptr", 0, "Ptr", 0, "Ptr", 0, "Ptr")
      If ((ErrorLevel) || !(HTIP))
         Return False
      ; ... prepare it to display multiple lines if required
      DllCall("User32.dll\SendMessage", "Ptr", HTIP, "Int", TTM_SETMAXTIPWIDTH, "Ptr", 0, "Ptr", A_ScreenWidth*96//A_ScreenDPI)	;touched by SundayProgrammer who took it from iPhilip response for AddTooltip v2.0
      ; ... set the instance variables
      This.HTIP := HTIP
      This.HGUI := HGUI
      If (DllCall("Kernel32.dll\GetVersion", "UInt") & 0xFF) < 6 ; to avoid some XP issues ...
         This.Attach(HGUI, "") ; ... register the GUI with an empty tiptext
   }
   ; ===================================================================================================================
   ; DESTRUCTOR            __Delete()
   ; ===================================================================================================================
   __Delete() {
      If (This.HTIP) {
         DllCall("User32.dll\DestroyWindow", "Ptr", This.HTIP)
      }
   }
   ; ===================================================================================================================
   ; PRIVATE METHOD        SetToolInfo - Create and fill a TOOLINFO structure
   ; ===================================================================================================================
   SetToolInfo(ByRef TOOLINFO, HCTRL, TipTextAddr, CenterTip = 0) {
      Static TTF_IDISHWND  := 0x0001
      Static TTF_CENTERTIP := 0x0002
      Static TTF_SUBCLASS  := 0x0010
      Static OffsetSize  := 0
      Static OffsetFlags := 4
      Static OffsetHwnd  := 8
      Static OffsetID    := OffsetHwnd + A_PtrSize
      Static OffsetRect  := OffsetID + A_PtrSize
      Static OffsetInst  := OffsetRect + 16
      Static OffsetText  := OffsetInst + A_PtrSize
      Static StructSize  := (4 * 6) + (A_PtrSize * 6)
      Flags := TTF_IDISHWND | TTF_SUBCLASS
      If (CenterTip)
         Flags |= TTF_CENTERTIP
      VarSetCapacity(TOOLINFO, StructSize, 0)
      NumPut(StructSize, TOOLINFO, OffsetSize, "UInt")
      NumPut(Flags, TOOLINFO, OffsetFlags, "UInt")
      NumPut(This.HGUI, TOOLINFO, OffsetHwnd, "Ptr")
      NumPut(HCTRL, TOOLINFO, OffsetID, "Ptr")
      NumPut(TipTextAddr, TOOLINFO, OffsetText, "Ptr")
      Return True
   }
   ; ===================================================================================================================
   ; PUBLIC METHOD         Attach         -  Assign a ToolTip to a certain control
   ; Parameters:           HWND           -  Control's HWND
   ;                       TipText        -  ToolTip's text
   ;                       Optional:      ------------------------------------------------------------------------------
   ;                       CenterTip      -  Centers the tooltip window below the control
   ;                                         Values:  True/False
   ;                                         Default: False
   ; Return values:        On success: True
   ;                       On failure: False
   ; ===================================================================================================================
   Attach(HCTRL, TipText, CenterTip = False) {
      Static TTM_ADDTOOL  := A_IsUnicode ? 0x0432 : 0x0404 ; TTM_ADDTOOLW : TTM_ADDTOOLA
      If !(This.HTIP) {
         Return False
      }
      If This.CTRL.HasKey(HCTRL)
         Return False
      TOOLINFO := ""
      This.SetToolInfo(TOOLINFO, HCTRL, &TipText, CenterTip)
      If DllCall("User32.dll\SendMessage", "Ptr", This.HTIP, "Int", TTM_ADDTOOL, "Ptr", 0, "Ptr", &TOOLINFO) {
         This.CTRL[HCTRL] := 1
		 This.TTT[HCTRL] := TipText	;added by SundayProgrammer
         Return True
      } Else {
        Return False
      }
   }
   ; ===================================================================================================================
   ; PUBLIC METHOD         Detach         -  Remove the ToolTip for a certain control
   ; Parameters:           HWND           -  Control's HWND
   ; Return values:        On success: True
   ;                       On failure: False
   ; ===================================================================================================================
   Detach(HCTRL) {
      Static TTM_DELTOOL  := A_IsUnicode ? 0x0433 : 0x0405 ; TTM_DELTOOLW : TTM_DELTOOLA
      If !This.CTRL.HasKey(HCTRL)
         Return False
      TOOLINFO := ""
      This.SetToolInfo(TOOLINFO, HCTRL, 0)
      DllCall("User32.dll\SendMessage", "Ptr", This.HTIP, "Int", TTM_DELTOOL, "Ptr", 0, "Ptr", &TOOLINFO)
      This.CTRL.Remove(HCTRL, "")
	  This.TTT.Remove(HCTRL, "")	;added by SundayProgrammer
      Return True
   }
   ; ===================================================================================================================
   ; PUBLIC METHOD         Update         -  Update the ToolTip's text for a certain control
   ; Parameters:           HWND           -  Control's HWND
   ;                       TipText        -  New text                                                      
   ; Return values:        On success: True
   ;                       On failure: False
   ; ===================================================================================================================
   Update(HCTRL, TipText) {
      Static TTM_UPDATETIPTEXT  := A_IsUnicode ? 0x0439 : 0x040C ; TTM_UPDATETIPTEXTW : TTM_UPDATETIPTEXTA
      If !This.CTRL.HasKey(HCTRL)
         Return False
      TOOLINFO := ""
      This.SetToolInfo(TOOLINFO, HCTRL, &TipText)
      DllCall("SendMessage", "Ptr", This.HTIP, "Int", TTM_UPDATETIPTEXT, "Ptr", 0, "Ptr", &TOOLINFO)
	  This.TTT[HCTRL] := TipText	;added by SundayProgrammer
      Return True
   }
   ;added by SundayProgrammer
   GetText(HCTRL) {
      If !This.CTRL.HasKey(HCTRL)
         Return ""
      Return This.TTT[HCTRL]
   }
   ; ===================================================================================================================
   ; PUBLIC METHOD         Suspend        -  Disable/enable the ToolTip control (don't show / show ToolTips)
   ; Parameters:           Mode           -  True/False (1/0)
   ;                                         Default: True/1
   ; Return values:        On success: True
   ;                       On failure: False
   ; Remarks:              ToolTips are enabled automatically on creation.
   ; ===================================================================================================================
   Suspend(Mode = True) {
      Static TTM_ACTIVATE := 0x0401
      If !(This.HTIP)
         Return False
      DllCall("SendMessage", "Ptr", This.HTIP, "Int", TTM_ACTIVATE, "Ptr", !Mode, "Ptr", 0)
      Return True
   }
   ; ===================================================================================================================
   ; PUBLIC METHOD         SetDelayTimes  -  Set the initial, pop-up, and reshow durations for a tooltip control.
   ; Parameters:           Init           -  Amount of time, in milliseconds, a pointer must remain stationary within
   ;                                         a tool's bounding rectangle before the tooltip window appears.
   ;                                         Default: -1 (system default time)
   ;                       PopUp          -  Amount of time, in milliseconds, a tooltip window remains visible if the
   ;                                         pointer is stationary within a tool's bounding rectangle.
   ;                                         Default: -1 (system default time)
   ;                       ReShow         -  Amount of time, in milliseconds, it takes for subsequent tooltip windows
   ;                                         to appear as the pointer moves from one tool to another.
   ;                                         Default: -1 (system default time)
   ; Return values:        On success: True
   ;                       On failure: False
   ; Remarks:              Times are set per ToolTip control and applied to all added tools.
   ; ===================================================================================================================
   SetDelayTimes(Init = -1, PopUp = -1, ReShow = -1) {
      Static TTM_SETDELAYTIME   := 0x0403
      Static TTDT_RESHOW   := 1
      Static TTDT_AUTOPOP  := 2
      Static TTDT_INITIAL  := 3
      DllCall("SendMessage", "Ptr", This.HTIP, "Int", TTM_SETDELAYTIME, "Ptr", TTDT_INITIAL, "Ptr", Init)
      DllCall("SendMessage", "Ptr", This.HTIP, "Int", TTM_SETDELAYTIME, "Ptr", TTDT_AUTOPOP, "Ptr", PopUp)
      DllCall("SendMessage", "Ptr", This.HTIP, "Int", TTM_SETDELAYTIME, "Ptr", TTDT_RESHOW , "Ptr", ReShow)
   }
   ;added by SundayProgrammer who largely took it from AddTooltip v2.0
   SetTitle(theTitle := "", theIcon := 0) {
      Static TTM_SETTITLE := A_IsUnicode ? 0x421 : 0x420 ; TTM_SETTITLEW : TTM_SETTITLEA
      If StrLen(theTitle) > 99
         theTitle := SubStr(theTitle, 1, 99)
      If theIcon is not Integer
         theIcon := 0
      DllCall("SendMessage", "Ptr", This.HTIP, "Int", TTM_SETTITLE, "Ptr", theIcon, "Ptr", &theTitle)
   }
}

UpdateScrollBars(GuiNum, GuiWidth, GuiHeight) {	;written by lexikos
    static SIF_RANGE=0x1, SIF_PAGE=0x2, SIF_DISABLENOSCROLL=0x8, SB_HORZ=0, SB_VERT=1
   
    Gui, %GuiNum%:Default
    Gui, +LastFound
   
    ; Calculate scrolling area.
    Left := Top := 9999
    Right := Bottom := 0
    WinGet, ControlList, ControlList
    Loop, Parse, ControlList, `n
    {
        GuiControlGet, c, Pos, %A_LoopField%
        if (cX < Left)
            Left := cX
        if (cY < Top)
            Top := cY
        if (cX + cW > Right)
            Right := cX + cW
        if (cY + cH > Bottom)
            Bottom := cY + cH
    }
    Left -= 8
    Top -= 8
    Right += 8
    Bottom += 8
    ScrollWidth := Right-Left
    ScrollHeight := Bottom-Top
   
    ; Initialize SCROLLINFO.
    VarSetCapacity(si, 28, 0)
    NumPut(28, si) ; cbSize
    NumPut(SIF_RANGE | SIF_PAGE, si, 4) ; fMask
   
    ; Update horizontal scroll bar.
    NumPut(ScrollWidth, si, 12) ; nMax
    NumPut(GuiWidth, si, 16) ; nPage
    DllCall("SetScrollInfo", "uint", WinExist(), "uint", SB_HORZ, "uint", &si, "int", 1)
   
    ; Update vertical scroll bar.
;     NumPut(SIF_RANGE | SIF_PAGE | SIF_DISABLENOSCROLL, si, 4) ; fMask
    NumPut(ScrollHeight, si, 12) ; nMax
    NumPut(GuiHeight, si, 16) ; nPage
    DllCall("SetScrollInfo", "uint", WinExist(), "uint", SB_VERT, "uint", &si, "int", 1)
   
    if (Left < 0 && Right < GuiWidth)
        x := Abs(Left) > GuiWidth-Right ? GuiWidth-Right : Abs(Left)
    if (Top < 0 && Bottom < GuiHeight)
        y := Abs(Top) > GuiHeight-Bottom ? GuiHeight-Bottom : Abs(Top)
    if (x || y)
        DllCall("ScrollWindow", "uint", WinExist(), "int", x, "int", y, "uint", 0, "uint", 0)
}

OnScroll(wParam, lParam, msg, hwnd) {	;written by lexikos
    static SIF_ALL=0x17, SCROLL_STEP=85 ;changed by SundayProgrammer from 10 to 85 for a more practical outcome
	static xpos := 0, ypos := 0	;added by SundayProgrammer - for touch gesture scrolling
	global gFlag	;added by SundayProgrammer - for touch gesture scrolling
if DllCall("GetParent", "uint", hwnd)	;added by SundayProgrammer - a quick fix for the scenario when any scrollable control is involved
	return	;added by SundayProgrammer - a quick fix for the scenario when any scrollable control is involved
   
    bar := (msg=0x115) or (msg=0x20A) ; (SB_HORZ=0, SB_VERT=1) or (WM_MOUSEHWHEEL=0, WM_MOUSEWHEEL=1)	;changed by SundayProgrammer - for WM_MOUSEWHEEL and WM_MOUSEHWHEEL
   
	if gFlag	;added by SundayProgrammer - for touch gesture scrolling
	{	gAction(xpos, ypos, bar, hwnd)	;added by SundayProgrammer - for touch gesture scrolling
		return	;added by SundayProgrammer - for touch gesture scrolling
	}	;added by SundayProgrammer - for touch gesture scrolling
   
    VarSetCapacity(si, 28, 0)
    NumPut(28, si) ; cbSize
    NumPut(SIF_ALL, si, 4) ; fMask
    if !DllCall("GetScrollInfo", "uint", hwnd, "int", bar, "uint", &si)
        return
   
    VarSetCapacity(rect, 16)
    DllCall("GetClientRect", "uint", hwnd, "uint", &rect)
   
    new_pos := NumGet(si, 20) ; nPos (saw "25" in another version, which exhibited a bug in my testing, whereas "20" (this version) worked fine so far)
   
    if msg=0x20A	;added by SundayProgrammer - for WM_MOUSEWHEEL
        wParam := wParam>0x780000	;added by SundayProgrammer - for WM_MOUSEWHEEL
    else if msg=0x20E	;added by SundayProgrammer - for WM_MOUSEHWHEEL
        wParam := wParam=0x780000	;added by SundayProgrammer - for WM_MOUSEHWHEEL
    action := wParam & 0xFFFF
    if action = 0 ; SB_LINEUP
        new_pos -= SCROLL_STEP
    else if action = 1 ; SB_LINEDOWN
        new_pos += SCROLL_STEP
    else if action = 2 ; SB_PAGEUP
        new_pos -= NumGet(rect, 12, "int") - SCROLL_STEP
    else if action = 3 ; SB_PAGEDOWN
        new_pos += NumGet(rect, 12, "int") - SCROLL_STEP
    else if (action = 5 || action = 4) ; SB_THUMBTRACK || SB_THUMBPOSITION
        new_pos := wParam>>16
    else if action = 6 ; SB_TOP
        new_pos := NumGet(si, 8, "int") ; nMin
    else if action = 7 ; SB_BOTTOM
        new_pos := NumGet(si, 12, "int") ; nMax
    else
        return
   
    min := NumGet(si, 8, "int") ; nMin
    max := NumGet(si, 12, "int") - NumGet(si, 16) ; nMax-nPage
    new_pos := new_pos > max ? max : new_pos
    new_pos := new_pos < min ? min : new_pos
   
    old_pos := NumGet(si, 20, "int") ; nPos (saw "25" in another version, which exhibited a bug in my testing, whereas "20" (this version) worked fine so far)
   
    x := y := 0
    if bar = 0 ; SB_HORZ
        x := old_pos-new_pos
    else
        y := old_pos-new_pos
    ; Scroll contents of window and invalidate uncovered area.
    DllCall("ScrollWindow", "uint", hwnd, "int", x, "int", y, "uint", 0, "uint", 0)
   
    ; Update scroll bar.
    NumPut(new_pos, si, 20, "int") ; nPos (saw "25" in another version, which exhibited a bug in my testing, whereas "20" (this version) worked fine so far)
    DllCall("SetScrollInfo", "uint", hwnd, "int", bar, "uint", &si, "int", 1)

	z := bar ? "y" : "x", %z%pos := new_pos	;added by SundayProgrammer - for touch gesture scrolling
}
gAction(byref xpos, byref ypos, bar, hwnd) {	;written by SundayProgrammer - for touch gesture scrolling
	VarSetCapacity(si, 28, 0), NumPut(28, si), NumPut(0x17, si, 4), DllCall("GetScrollInfo", "uint", hwnd, "int", bar, "uint", &si), aPos := NumGet(si, 20, "int"), z := bar ? "y" : "x"
	if not (%z%pos = aPos)
		x := y := 0, %z% := %z%pos - aPos, DllCall("ScrollWindow", "uint", hwnd, "int", x, "int", y, "uint", 0, "uint", 0), %z%pos := aPos
}
gHandler(wParam, lParam, msg, hwnd) {	;written by SundayProgrammer - for touch gesture scrolling
	global gFlag
	gFlag := true
	settimer, Reset_gFlag, -100
	return
	Reset_gFlag:
		gFlag := false
		return
}

ObjRegisterActive(Object, CLSID, Flags:=0) {	;written by Lexikos
    static cookieJar := {}
    if (!CLSID) {
        if (cookie := cookieJar.Remove(Object)) != ""
            DllCall("oleaut32\RevokeActiveObject", "uint", cookie, "ptr", 0)
        return
    }
    if cookieJar[Object]
        throw Exception("Object is already registered", -1)
    VarSetCapacity(_clsid, 16, 0)
    if (hr := DllCall("ole32\CLSIDFromString", "wstr", CLSID, "ptr", &_clsid)) < 0
        throw Exception("Invalid CLSID", -1, CLSID)
    hr := DllCall("oleaut32\RegisterActiveObject"
        , "ptr", &Object, "ptr", &_clsid, "uint", Flags, "uint*", cookie
        , "uint")
    if hr < 0
        throw Exception(format("Error 0x{:x}", hr), -1)
    cookieJar[Object] := cookie
}

;Function Ripped out of CodeQuickTester written by GeekDude https://github.com/G33kDude/CodeQuickTester
ExecScript(Script, Params="", AhkPath="")	;copy from "Execute code stored in a variable (dynamic variable?)" https://www.reddit.com/r/AutoHotkey/comments/ebwora/comment/fbcwvuy/?utm_source=share&utm_medium=web2x&context=3
{
	static Shell := ComObjCreate("WScript.Shell")
	Name := "\\.\pipe\AHK_CQT_" A_TickCount
	Pipe := []
	Loop, 3
	{
		Pipe[A_Index] := DllCall("CreateNamedPipe"
		, "Str", Name
		, "UInt", 2, "UInt", 0
		, "UInt", 255, "UInt", 0
		, "UInt", 0, "UPtr", 0
		, "UPtr", 0, "UPtr")
	}
	if !FileExist(AhkPath)
		throw Exception("AutoHotkey runtime not found: " AhkPath)
	if (A_IsCompiled && AhkPath == A_ScriptFullPath)
		AhkPath .= " /E"
	if FileExist(Name)
	{
		Exec := Shell.Exec(AhkPath " /CP65001 " Name " " Params)
		DllCall("ConnectNamedPipe", "UPtr", Pipe[2], "UPtr", 0)
		DllCall("ConnectNamedPipe", "UPtr", Pipe[3], "UPtr", 0)
		FileOpen(Pipe[3], "h", "UTF-8").Write(Script)
	}
	else ; Running under WINE with improperly implemented pipes
	{
		FileOpen(Name := "AHK_CQT_TMP.ahk", "w").Write(Script)
		Exec := Shell.Exec(AhkPath " /CP65001 " Name " " Params)
	}
	Loop, 3
		DllCall("CloseHandle", "UPtr", Pipe[A_Index])
	return Exec
}
User avatar
Thoughtfu1Tux
Posts: 125
Joined: 31 May 2018, 23:26

Re: An Alternative Menu (A Quick Access To Various Features)

21 Oct 2021, 20:47

You should attach an image/video of what the script does. There's a much higher barrier for people to try out your script when they don't see what it looks like/what it's capable of.
SundayProgrammer
Posts: 143
Joined: 25 Dec 2020, 12:26

Re: An Alternative Menu (A Quick Access To Various Features)

23 Oct 2021, 14:52

Thoughtfu1Tux wrote:
21 Oct 2021, 20:47
You should attach an image/video of what the script does. There's a much higher barrier for people to try out your script when they don't see what it looks like/what it's capable of.
thank you, and indeed. im hell lazy though.
SundayProgrammer
Posts: 143
Joined: 25 Dec 2020, 12:26

Re: An Alternative Menu (A Quick Access To Various Features)

23 Oct 2021, 15:30

what's new in this version:

(1) a preceding semi-colon can have the menu item bypassed

(2) a preceding declaration can limit the menu item to only appear when active window and focused control are matched. syntax: [:window:control:] or just [:window:] (to be matched by regex)

(3) "script" type content can be separated and placed after the menu (or in file). syntax: {script the linking text string}...{/script} (this syntax only needed if the script is placed in the same file with the menu, i.e. if it's in its own ahk file, no need.)

please also see the examples included.

Code: Select all

pool()	;init variables
menu.Spot()	;show mSpot
Return
EditMenu:
	Run, Notepad MenuText.txt
	Return
demo1:
	msgbox, this is demo1
	return
demo2:
	msgbox, this is demo2 this is demo2
	return
demo3(){
	msgbox, this is demo3 this is demo3 this is demo3
}
demo4(p){
	msgbox, % p
}
demo5(p1, p2){
	msgbox, % p1 "`n`n=========================`n`n" p2
}

pool(param = "") {	;written by SundayProgrammer
	static
	static clsid1 := "{A3C04B39-0465-4460-8CA0-7BFFF481FF98}", s1l := "a := ComObjActive(""" clsid1 """)`n"
	static dummy1 := "i am param the first"
	static dummy2 := "i am param the second"
If StrLen(param)
	If IsGetSet(param, action, varname, value)
	{	If (action = "Set:")
			%varname% := value
		v := %varname%
		Return v
	}Else{}
Else{
	ObjRegisterActive(agent, clsid1)
	IfExist, MenuText.txt
		FileRead, _menu_, MenuText.txt
	Else{
_menu_=
(
= = = Top = = =
^#{Left}	Switch Desktop	(Send)
^#{Right}	Switch Desktop	(Send)
#{Tab}	Windows Switcher	(Send)
[:\s- Microsoft.+?Edge:] m{Enter}	MyActivity.Google	(Send)
[:\s- Microsoft.+?Edge:] +^u	Rald Toggle	(Send)
^f	Find	(Send)
[:\s- Microsoft.+?Edge:] ^r	Reload Web Page	(Send)
^w	Close Tab	(Send)
[:\s- YouTube:] j	10s Backward	(Send)
[:\s- Microsoft.+?Edge:] !{Left}	Previous Web Page	(Send)
[:\s- Microsoft.+?Edge:] !{Right}	Next Web Page	(Send)
{Enter}	Enter	(Send)
[:\s- YouTube:] f	Fullscreen Toggle	(Send)
{Space}	Space	(Send)
{Del}	Delete	(Send)
{Esc}	Escape	(Send)
[:\s- Sublime Text:] ^g	Go To Line	(Send)
[:\s- Sublime Text:] !d	Duplicate	(Send)
^z	Undo	(Send)
^x	Cut	(Send)
^c	Copy	(Send)
^v	Paste	(Send)
^s	Save File	(Send)
^a	Select All	(Send)
- - - - - - - - - - - - - - - -
Close Menu	{menu.Gui_OnEscape}	Return To mSpot	(Function)
Reload Menu	{menu.Reload}	Reflect The Latest Content	(Function)
Edit Menu	{EditMenu}	(Gosub)
Demo One	{demo1}	hello world	(Gosub)
Demo Two	{demo2}	foobar	(Gosub)
Demo Three	{demo3}	blablabla	(Function)
Demo Four	{demo4}	bla bla	(Func+Param)	dummy1
Demo Five	{demo5}	bla bla bla	(Func+Param)	dummy1,dummy2
Script I	(Script)	msgbox `% "remote: " a.call("var","dummy1@pool")
Script II	(Script)	msgbox `% "remote: " a.call("var","dummy2@pool")
Script III	(Script)	a.call("var","Set:dummy3@pool",a_tickcount)|| ||msgbox `% "remote: " a.call("var","dummy3@pool")
Verify	{demo4}	(Func+Param)	dummy3
Script IV	(Script)	{Apart}
[:Text Filter:RICHEDIT50W1:] Script V	(Script)	{fghij}
Script VI	(Script)	{menuDemo}
= = = Bottom = = =
;
{Script Apart}
a.call("var","Set:dummy3@pool",a_tickcount "(a)")
msgbox `% "remote: " a.call("var","dummy3@pool")
{/Script}

{Script fghij}
a.call("demo5", "p1p", "p2p")
If InStr(a.call("var","dummy3@pool"), "(a)")
	msgbox found "(a)" in dummy3
{/Script}
)
FileAppend, %_menu_%, MenuText.txt, UTF-8
		}
	IfNotExist, menuDemo.ahk
FileAppend, msgbox im an island, menuDemo.ahk, UTF-8
	}
}

IsGetSet(Param, ByRef a, ByRef v, ByRef value) {	;written by SundayProgrammer
	a := SubStr(Param, 1, 4)
	If (a = "Set:") or (a = "Get:")
	{	v := Trim((p := InStr(Param, "=")) ? (SubStr(Param, 5, p - 5), value := SubStr(Param, p + 1)) : SubStr(Param, 5))
		Return True
	}Return False
}
var(name, value = "") {	;written by SundayProgrammer
	global
	local p, a, v, m1, m2, m3, segc
	If p := InStr(name, "@")
	{	a := SubStr(name, 1, 4), v := Trim(SubStr(name, 1, p - 1)), name := Trim(SubStr(name, p + 1))
		If (a = "Set:") or (a = "Get:")
			v := Trim(SubStr(v, 5))
		Else a := StrLen(value) ? "Set:" : "Get:"
		If StrLen(v) and StrLen(name)
		{	v := a v, v .= (a = "Set:") ? "=" value : ""
			If InStr(name, ".")
			{	Loop, Parse, name, .
					m%A_Index% := A_LoopField, segc := A_Index
				If segc = 2
					v := %m1%[m2](v)
				Else If segc = 3
					v := %m1%[m2][m3](v)
				Else v = Error: Segments More Than Allowed.
			}Else v := %name%(v)
		}Else v = Error: Invalid Parameter "%v%@%name%"
	}Else
	{	a := SubStr(name, 1, 4)
		If (a = "Set:") or (a = "Get:")
			name := Trim(SubStr(name, 5))
		If InStr(name, ".")
		{	Loop, Parse, name, .
				m%A_Index% := A_LoopField, segc := A_Index
			If segc = 2
			{	If (a = "Set:") or StrLen(value)
					%m1%[m2] := value
				v := %m1%[m2]
			}Else If segc = 3
			{	If (a = "Set:") or StrLen(value)
					%m1%[m2][m3] := value
				v := %m1%[m2][m3]
			}Else v = Error: Segments More Than Allowed.
		}Else
		{	If (a = "Set:") or StrLen(value)
				%name% := value
			v := %name%
		}
	}
	Return v
}

Class agent {	;written by SundayProgrammer who got the idea from this post https://www.autohotkey.com/boards/viewtopic.php?p=200597#p200597
	Call(name, p*) {	;allows you to call any function in this script
		If InStr(name, ".")
		{	Loop, Parse, name, .
				m%A_Index% := A_LoopField, segc := A_Index
			If segc = 2
				Return %m1%[m2](p*)
			Else If segc = 3
				Return %m1%[m2][m3](p*)
			Else Return "Error: Segments More Than Allowed."
		}Return %name%(p*)
	}
}

#IfWinActive ahk_group SpotMenu
	WheelUp::
	WheelDown::
	+WheelUp::
	+WheelDown::OnScroll(InStr(A_ThisHotkey, "Down") ? 1 : 0, 0, GetKeyState("Shift") ? 0x114 : 0x115, WinExist())
#If

WM_DISPLAYCHANGE() {
	WinGetPos, x, y,,, mSpot
	If (x > A_ScreenWidth - 50)
		x := A_ScreenWidth - 100
	If (y > A_ScreenHeight - 125)
		y := A_ScreenHeight - 175
	Gui, mSpot:Show, x%x% y%y% NoActivate
}

WM_ACTIVATE() {
	IfWinActive, mSpot
		WinSet, TransColor, White, mSpot
	Else WinSet, TransColor, White 64, mSpot
}

Class menu {	;written by SundayProgrammer
Spot() {
	Gui, mSpot:New
	Gui, +LastFound -Caption +AlwaysOnTop +ToolWindow
	Gui, Color, White
	Gui, Margin, 0, 0
	Gui, Font, s150, Consolas
	Gui, Add, Text, x0 y0 cFFB10F BackgroundTrans, ●
	OnSuch := this.HitEvent.Bind(this)
	GuiControl +g, Static1, % OnSuch
	WinSet, TransColor, White 64
	x := A_ScreenWidth - 100, y := A_ScreenHeight - 530
	Gui, Show, x%x% y%y% NoActivate, mSpot
	this.HitEvent() ; Init sPos
	OnMessage(0x7E, "WM_DISPLAYCHANGE")
	OnMessage(0x06, "WM_ACTIVATE")
}
HitEvent(hwnd := "") {
	static sPos
	If not StrLen(sPos)
	{	WinGetPos, x, y,,, mSpot
		sPos := x "," y
	}If not StrLen(hwnd)
		Return sPos
	SendMessage, 0xA1, 2,,, A ; WM_NCLBUTTONDOWN
	GuiControlGet, wHwnd, Hwnd, %hwnd%
	WinGetPos, x, y,,, ahk_id %wHwnd%
	If x "," y = sPos
	{	Gui, mSpot:Hide
		this.SpotMenu(var("_menu_@pool"))
	}Else
	{	sPos := x "," y
		SetTimer, Deactivate, -500
	}Return
	Deactivate:
		Gui, mSpot:Hide
		Gui, mSpot:Show, NoActivate
		WinSet, TransColor, White 64, mSpot
		Return
}
SpotMenu(t) {
	global TT
	WinWaitNotActive, mSpot
	WinGetActiveTitle, sWin
	ControlGetFocus, sCtl, A
	Loop, Parse, t, `n
		If SubStr(Trim(A_LoopField), 1, 1) not = ";"
		If RegExMatch(A_LoopField, "i)^\s*{\s*Script\s+[^}]+?\s*}")
			Break
		Else If RegExMatch(A_LoopField, "[^\t]+(?=\t)", m)
			w%A_Index% := this.GetButtonWidth(Trim(RegExReplace(m, "\s*\[([^\]]+)]")), "30 bold", "Consolas")
	OnMessage(0x115, "OnScroll") ; WM_VSCROLL
	OnMessage(0x114, "OnScroll") ; WM_HSCROLL
	OnMessage(0x20A, "OnScroll") ; WM_MOUSEWHEEL
	OnMessage(0x20E, "OnScroll") ; WM_MOUSEHWHEEL
	OnMessage(0x119, "gHandler") ; WM_GESTURE
	Gui, menu:New, hwndHmenu +Labelmenu.Gui_On
	Gui, +Resize +0x300000	; WS_VSCROLL | WS_HSCROLL
	TT := New GuiControlTips(Hmenu), TT.SetDelayTimes(1000, 15000, -1)
	Gui, Font, s30 bold, Consolas
	ci := nl := 1
	Loop, Parse, t, `n
		If SubStr(Trim(A_LoopField), 1, 1) not = ";"
		If RegExMatch(A_LoopField, "[^\t]+(?=\t)", m)
		{	Passed := False
			If RegExMatch(m, "\s*\[([^\]]+)]", mm)
			If RegExMatch(Trim(mm1), ":([^:]+(:[^:]+)?):", mm)
			If pos := InStr(mm1, ":")
				If not RegExMatch(sWin, Trim(SubStr(mm1, 1, pos - 1))) or not RegExMatch(sCtl, Trim(SubStr(mm1, pos + 1)))
					Continue
				Else Passed := True
			Else If not RegExMatch(sWin, Trim(mm1))
				Continue
			Else Passed := True
			If Passed
				m := Trim(RegExReplace(m, "\s*\[([^\]]+)]"))
			If not nl
				nl := (gx + gw + 25 + w%A_Index%) > A_ScreenWidth - 40
			p := nl ? "" : "+", y := nl ? "y+28" : ""
			Gui, Add, Button, HwndhBtn%ci% x%p%25 %y% h57, % (m, pi := ci, ci++)
			OnSuch := this.MenuClick.Bind(this)
			GuiControl +g, Button%pi%, % OnSuch
			GuiControlGet, g, Pos, Button%pi%
			TT.Attach(hBtn%pi%, RegExReplace(RegExReplace(SubStr(A_LoopField, 0) = "`r" ? SubStr(A_LoopField, 1, -1) : A_LoopField, "[^\t]+\t",,, 1), "\t", "`n"))
			nl := (gx + gw) > (A_ScreenWidth - 116)
		}Else If RegExMatch(A_LoopField, "i)^\s*{\s*Script\s+[^}]+?\s*}")
			Break
		Else
		{	Gui, Add, Text, x25 cWhite, % SubStr(A_LoopField, 0) = "`r" ? SubStr(A_LoopField, 1, -1) : A_LoopField
			nl := True
		}
	Gui, Color, Black
	TT.SetTitle("Descriptions", LoadPicture("shell32.dll", "Icon222", ImageType))
	SetCtrlFont(TT.HTIP, "s20", "Arial New")
	Gui, +LastFound
	WinSet, Transparent, 180
	Gui, Show,, Touch Friendly Menu
	GroupAdd, SpotMenu, % "ahk_id " . WinExist()
	this.t := t
}
Gui_OnSize() {
	UpdateScrollBars(A_Gui, A_GuiWidth, A_GuiHeight)
}
Gui_OnEscape() {
	menu.OnClose()
	Gui, mSpot:Show, NoActivate
	WinSet, TransColor, White 64, mSpot
}
Gui_OnClose() {
	ObjRegisterActive(agent, "")
	ExitApp
}
OnClose() {
	OnMessage(0x115, "") ; WM_VSCROLL
	OnMessage(0x114, "") ; WM_HSCROLL
	OnMessage(0x20A, "") ; WM_MOUSEWHEEL
	OnMessage(0x20E, "") ; WM_MOUSEHWHEEL
	OnMessage(0x119, "") ; WM_GESTURE
	Gui, menu:Destroy
}
GetButtonWidth(t, s, f) {
	Gui, New
	Gui, Font, s%s%, % f
	Gui, Add, Button,, % t
	GuiControlGet, g, Pos, Button1
	Gui, Destroy
	Return gW
}
MenuClick(h) {
	global TT
	theText := A_GuiControl
	this.OnClose()
	WinWaitNotActive, Touch Friendly Menu
	Sleep, 500
	If RegExMatch(t := TT.GetText(h), "{\K[^}]+(?=})", m)
		If not InStr(t, "(Send)")
			theText := m
	If InStr(t, "(Send)")
		Send, %theText%
	Else If InStr(t, "(Gosub)")
		Gosub, %theText%
	Else If InStr(t, "(Function)")
		If pos := InStr(theText, ".")
			className := SubStr(theText, 1, pos - 1), method := SubStr(theText, pos + 1), %className%[method]()
		Else %theText%()
	Else If InStr(t, "(Func+Param)")
	{	RegExMatch(t, "[^\n]+$", m)
		If InStr(m, ",")
		{	Loop, Parse, m, `,
				p%A_Index% := var(A_LoopField "@pool")
			StrReplace(m, ",",, c)
			If c = 1
				%theText%(p1, p2)
			Else If c = 2
				%theText%(p1, p2, p3)
			Else If c = 3
				%theText%(p1, p2, p3, p4)
			Else If c = 4
				%theText%(p1, p2, p3, p4, p5)
			Else If c = 5
				%theText%(p1, p2, p3, p4, p5, p6)
			Else If c = 6
				%theText%(p1, p2, p3, p4, p5, p6, p7)
			Else If c = 7
				%theText%(p1, p2, p3, p4, p5, p6, p7, p8)
			Else If c = 8
				%theText%(p1, p2, p3, p4, p5, p6, p7, p8, p9)
			Else If c = 9
				%theText%(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10)
			Else{}	;more than 10 parameters is not supported
		}Else %theText%(var(m "@pool"))
	}Else If InStr(t, "(Script)")
		If RegExMatch(t, "{\K[^}]+(?=})", m)
			If RegExMatch(this.t, "is){\s*Script\s+" m "\s*}(.+?){\s*/\s*Script\s*}", mm)
				ExecScript(var("s1l@pool") mm1,, A_AhkPath)
			Else IfExist, % m := (SubStr(Trim(m), -3) = ".ahk" ? m : Trim(m) ".ahk")
			{	FileRead, mm1, %m%
				ExecScript(var("s1l@pool") mm1,, A_AhkPath)
			}Else{}
		Else RegExMatch(t, "\(Script\)[\n\t]+\K.*$", m), m := StrReplace(m, "|| ||", "`n"), ExecScript(var("s1l@pool") m,, A_AhkPath)
	Gui, mSpot:Show, NoActivate
	WinSet, TransColor, White 64, mSpot
}
Reload() {
	FileRead, _menu_, MenuText.txt
	var("Set:_menu_@pool", _menu_)
	SetTimer, ReloadMenu, -500
	Return
	ReloadMenu:
		Gui, mSpot:Hide
		menu.SpotMenu(var("_menu_@pool"))
		Return
}
}	;end of class

SetCtrlFont(CtrlHwnd, FontOptions := "", FontName := "") {	;written by iPhilip
   static WM_SETFONT := 0x0030, WM_GETFONT := 0x0031
   DefaultGui := A_DefaultGui
   Gui, New
   Gui, Font, % FontOptions, % FontName
   Gui, Add, Text, hwndhText, Text
   hFont := DllCall("SendMessage", "Ptr", hText, "UInt", WM_GETFONT, "Ptr", 0, "Ptr", 0, "Ptr")
   Gui, Destroy
   Gui, %DefaultGui%:Default
   DllCall("SendMessage", "Ptr", CtrlHwnd, "UInt", WM_SETFONT, "Ptr", hFont, "Ptr", true)
   Return hFont
}

; ======================================================================================================================
; Namespace:      GuiControlTips
; AHK version:    AHK 1.1.14.03
; Function:       Helper object to simply assign ToolTips for GUI controls
; Tested on:      Win 7 (x64)
; Change history:
;                 1.1.00.01/2020-06-03/just me - fixed missing Static WS_EX_TOPMOST
;                 1.1.00.00/2014-03-06/just me - Added SetDelayTimes()
;                 1.0.01.00/2012-07-29/just me
; ======================================================================================================================
; CLASS GuiControlTips
;
; The class provides four public methods to register (Attach), unregister (Detach), update (Update), and
; disable/enable (Suspend) common ToolTips for GUI controls.
;
; Usage:
; To assign ToolTips to GUI controls you have to create a new instance of GuiControlTips per GUI with
;     MyToolTipObject := New GuiControlTips(HGUI)
; passing the HWND of the GUI.
;
; After this you may assign ToolTips to your GUI controls by calling
;     MyToolTipObject.Attach(HCTRL, "ToolTip text")
; passing the HWND of the control and the ToolTip's text. Pass True/1 for the optional third parameter if you
; want the ToolTip to be shown centered below the control.
;
; To remove a ToolTip call
;     MyToolTipObject.Detach(HCTRL)
; passing the HWND of the control.
;
; To update the ToolTip's text call
;     MyToolTipObject.Update(HCTRL, "New text!")
; passing the HWND of the control and the new text.
;
; To deactivate the ToolTips call
;     MyToolTipObject.Suspend(True),
; to activate them again afterwards call
;     MyToolTipObject.Suspend(False).
;
; To adjust the ToolTips delay times call
;     MyToolTipObject.SetDelayTimesd(),
; specifying the delay times in milliseconds.
;
; That's all you can / have to do!
; ======================================================================================================================
Class GuiControlTips {	;written by @"just me"
   ; ===================================================================================================================
   ; INSTANCE variables
   ; ===================================================================================================================
   HTIP := 0
   HGUI := 0
   CTRL := {}
   ; ===================================================================================================================
   ; CONSTRUCTOR           __New()
   ; ===================================================================================================================
   __New(HGUI) {
      Static CLASS_TOOLTIP      := "tooltips_class32"
      Static CW_USEDEFAULT      := 0x80000000
      Static TTM_SETMAXTIPWIDTH := 0x0418
      Static TTM_SETMARGIN      := 0x041A
      Static WS_EX_TOPMOST      := 0x00000008
      Static WS_STYLES          := 0x80000002 ; WS_POPUP | TTS_NOPREFIX
      ; Create a Tooltip control ...
      HTIP := DllCall("User32.dll\CreateWindowEx", "UInt", WS_EX_TOPMOST, "Str", CLASS_TOOLTIP, "Ptr", 0
                    , "UInt", WS_STYLES
                    , "Int", CW_USEDEFAULT, "Int", CW_USEDEFAULT, "Int", CW_USEDEFAULT, "Int", CW_USEDEFAULT
                    , "Ptr", HGUI, "Ptr", 0, "Ptr", 0, "Ptr", 0, "Ptr")
      If ((ErrorLevel) || !(HTIP))
         Return False
      ; ... prepare it to display multiple lines if required
      DllCall("User32.dll\SendMessage", "Ptr", HTIP, "Int", TTM_SETMAXTIPWIDTH, "Ptr", 0, "Ptr", A_ScreenWidth*96//A_ScreenDPI)	;touched by SundayProgrammer who took it from iPhilip response for AddTooltip v2.0
      ; ... set the instance variables
      This.HTIP := HTIP
      This.HGUI := HGUI
      If (DllCall("Kernel32.dll\GetVersion", "UInt") & 0xFF) < 6 ; to avoid some XP issues ...
         This.Attach(HGUI, "") ; ... register the GUI with an empty tiptext
   }
   ; ===================================================================================================================
   ; DESTRUCTOR            __Delete()
   ; ===================================================================================================================
   __Delete() {
      If (This.HTIP) {
         DllCall("User32.dll\DestroyWindow", "Ptr", This.HTIP)
      }
   }
   ; ===================================================================================================================
   ; PRIVATE METHOD        SetToolInfo - Create and fill a TOOLINFO structure
   ; ===================================================================================================================
   SetToolInfo(ByRef TOOLINFO, HCTRL, TipTextAddr, CenterTip = 0) {
      Static TTF_IDISHWND  := 0x0001
      Static TTF_CENTERTIP := 0x0002
      Static TTF_SUBCLASS  := 0x0010
      Static OffsetSize  := 0
      Static OffsetFlags := 4
      Static OffsetHwnd  := 8
      Static OffsetID    := OffsetHwnd + A_PtrSize
      Static OffsetRect  := OffsetID + A_PtrSize
      Static OffsetInst  := OffsetRect + 16
      Static OffsetText  := OffsetInst + A_PtrSize
      Static StructSize  := (4 * 6) + (A_PtrSize * 6)
      Flags := TTF_IDISHWND | TTF_SUBCLASS
      If (CenterTip)
         Flags |= TTF_CENTERTIP
      VarSetCapacity(TOOLINFO, StructSize, 0)
      NumPut(StructSize, TOOLINFO, OffsetSize, "UInt")
      NumPut(Flags, TOOLINFO, OffsetFlags, "UInt")
      NumPut(This.HGUI, TOOLINFO, OffsetHwnd, "Ptr")
      NumPut(HCTRL, TOOLINFO, OffsetID, "Ptr")
      NumPut(TipTextAddr, TOOLINFO, OffsetText, "Ptr")
      Return True
   }
   ; ===================================================================================================================
   ; PUBLIC METHOD         Attach         -  Assign a ToolTip to a certain control
   ; Parameters:           HWND           -  Control's HWND
   ;                       TipText        -  ToolTip's text
   ;                       Optional:      ------------------------------------------------------------------------------
   ;                       CenterTip      -  Centers the tooltip window below the control
   ;                                         Values:  True/False
   ;                                         Default: False
   ; Return values:        On success: True
   ;                       On failure: False
   ; ===================================================================================================================
   Attach(HCTRL, TipText, CenterTip = False) {
      Static TTM_ADDTOOL  := A_IsUnicode ? 0x0432 : 0x0404 ; TTM_ADDTOOLW : TTM_ADDTOOLA
      If !(This.HTIP) {
         Return False
      }
      If This.CTRL.HasKey(HCTRL)
         Return False
      TOOLINFO := ""
      This.SetToolInfo(TOOLINFO, HCTRL, &TipText, CenterTip)
      If DllCall("User32.dll\SendMessage", "Ptr", This.HTIP, "Int", TTM_ADDTOOL, "Ptr", 0, "Ptr", &TOOLINFO) {
         This.CTRL[HCTRL] := 1
		 This.TTT[HCTRL] := TipText	;added by SundayProgrammer
         Return True
      } Else {
        Return False
      }
   }
   ; ===================================================================================================================
   ; PUBLIC METHOD         Detach         -  Remove the ToolTip for a certain control
   ; Parameters:           HWND           -  Control's HWND
   ; Return values:        On success: True
   ;                       On failure: False
   ; ===================================================================================================================
   Detach(HCTRL) {
      Static TTM_DELTOOL  := A_IsUnicode ? 0x0433 : 0x0405 ; TTM_DELTOOLW : TTM_DELTOOLA
      If !This.CTRL.HasKey(HCTRL)
         Return False
      TOOLINFO := ""
      This.SetToolInfo(TOOLINFO, HCTRL, 0)
      DllCall("User32.dll\SendMessage", "Ptr", This.HTIP, "Int", TTM_DELTOOL, "Ptr", 0, "Ptr", &TOOLINFO)
      This.CTRL.Remove(HCTRL, "")
	  This.TTT.Remove(HCTRL, "")	;added by SundayProgrammer
      Return True
   }
   ; ===================================================================================================================
   ; PUBLIC METHOD         Update         -  Update the ToolTip's text for a certain control
   ; Parameters:           HWND           -  Control's HWND
   ;                       TipText        -  New text                                                      
   ; Return values:        On success: True
   ;                       On failure: False
   ; ===================================================================================================================
   Update(HCTRL, TipText) {
      Static TTM_UPDATETIPTEXT  := A_IsUnicode ? 0x0439 : 0x040C ; TTM_UPDATETIPTEXTW : TTM_UPDATETIPTEXTA
      If !This.CTRL.HasKey(HCTRL)
         Return False
      TOOLINFO := ""
      This.SetToolInfo(TOOLINFO, HCTRL, &TipText)
      DllCall("SendMessage", "Ptr", This.HTIP, "Int", TTM_UPDATETIPTEXT, "Ptr", 0, "Ptr", &TOOLINFO)
	  This.TTT[HCTRL] := TipText	;added by SundayProgrammer
      Return True
   }
   ;added by SundayProgrammer
   GetText(HCTRL) {
      If !This.CTRL.HasKey(HCTRL)
         Return ""
      Return This.TTT[HCTRL]
   }
   ; ===================================================================================================================
   ; PUBLIC METHOD         Suspend        -  Disable/enable the ToolTip control (don't show / show ToolTips)
   ; Parameters:           Mode           -  True/False (1/0)
   ;                                         Default: True/1
   ; Return values:        On success: True
   ;                       On failure: False
   ; Remarks:              ToolTips are enabled automatically on creation.
   ; ===================================================================================================================
   Suspend(Mode = True) {
      Static TTM_ACTIVATE := 0x0401
      If !(This.HTIP)
         Return False
      DllCall("SendMessage", "Ptr", This.HTIP, "Int", TTM_ACTIVATE, "Ptr", !Mode, "Ptr", 0)
      Return True
   }
   ; ===================================================================================================================
   ; PUBLIC METHOD         SetDelayTimes  -  Set the initial, pop-up, and reshow durations for a tooltip control.
   ; Parameters:           Init           -  Amount of time, in milliseconds, a pointer must remain stationary within
   ;                                         a tool's bounding rectangle before the tooltip window appears.
   ;                                         Default: -1 (system default time)
   ;                       PopUp          -  Amount of time, in milliseconds, a tooltip window remains visible if the
   ;                                         pointer is stationary within a tool's bounding rectangle.
   ;                                         Default: -1 (system default time)
   ;                       ReShow         -  Amount of time, in milliseconds, it takes for subsequent tooltip windows
   ;                                         to appear as the pointer moves from one tool to another.
   ;                                         Default: -1 (system default time)
   ; Return values:        On success: True
   ;                       On failure: False
   ; Remarks:              Times are set per ToolTip control and applied to all added tools.
   ; ===================================================================================================================
   SetDelayTimes(Init = -1, PopUp = -1, ReShow = -1) {
      Static TTM_SETDELAYTIME   := 0x0403
      Static TTDT_RESHOW   := 1
      Static TTDT_AUTOPOP  := 2
      Static TTDT_INITIAL  := 3
      DllCall("SendMessage", "Ptr", This.HTIP, "Int", TTM_SETDELAYTIME, "Ptr", TTDT_INITIAL, "Ptr", Init)
      DllCall("SendMessage", "Ptr", This.HTIP, "Int", TTM_SETDELAYTIME, "Ptr", TTDT_AUTOPOP, "Ptr", PopUp)
      DllCall("SendMessage", "Ptr", This.HTIP, "Int", TTM_SETDELAYTIME, "Ptr", TTDT_RESHOW , "Ptr", ReShow)
   }
   ;added by SundayProgrammer who largely took it from AddTooltip v2.0
   SetTitle(theTitle := "", theIcon := 0) {
      Static TTM_SETTITLE := A_IsUnicode ? 0x421 : 0x420 ; TTM_SETTITLEW : TTM_SETTITLEA
      If StrLen(theTitle) > 99
         theTitle := SubStr(theTitle, 1, 99)
      If theIcon is not Integer
         theIcon := 0
      DllCall("SendMessage", "Ptr", This.HTIP, "Int", TTM_SETTITLE, "Ptr", theIcon, "Ptr", &theTitle)
   }
}

UpdateScrollBars(GuiNum, GuiWidth, GuiHeight) {	;written by lexikos
    static SIF_RANGE=0x1, SIF_PAGE=0x2, SIF_DISABLENOSCROLL=0x8, SB_HORZ=0, SB_VERT=1
   
    Gui, %GuiNum%:Default
    Gui, +LastFound
   
    ; Calculate scrolling area.
    Left := Top := 9999
    Right := Bottom := 0
    WinGet, ControlList, ControlList
    Loop, Parse, ControlList, `n
    {
        GuiControlGet, c, Pos, %A_LoopField%
        if (cX < Left)
            Left := cX
        if (cY < Top)
            Top := cY
        if (cX + cW > Right)
            Right := cX + cW
        if (cY + cH > Bottom)
            Bottom := cY + cH
    }
    Left -= 8
    Top -= 8
    Right += 8
    Bottom += 8
    ScrollWidth := Right-Left
    ScrollHeight := Bottom-Top
   
    ; Initialize SCROLLINFO.
    VarSetCapacity(si, 28, 0)
    NumPut(28, si) ; cbSize
    NumPut(SIF_RANGE | SIF_PAGE, si, 4) ; fMask
   
    ; Update horizontal scroll bar.
    NumPut(ScrollWidth, si, 12) ; nMax
    NumPut(GuiWidth, si, 16) ; nPage
    DllCall("SetScrollInfo", "uint", WinExist(), "uint", SB_HORZ, "uint", &si, "int", 1)
   
    ; Update vertical scroll bar.
;     NumPut(SIF_RANGE | SIF_PAGE | SIF_DISABLENOSCROLL, si, 4) ; fMask
    NumPut(ScrollHeight, si, 12) ; nMax
    NumPut(GuiHeight, si, 16) ; nPage
    DllCall("SetScrollInfo", "uint", WinExist(), "uint", SB_VERT, "uint", &si, "int", 1)
   
    if (Left < 0 && Right < GuiWidth)
        x := Abs(Left) > GuiWidth-Right ? GuiWidth-Right : Abs(Left)
    if (Top < 0 && Bottom < GuiHeight)
        y := Abs(Top) > GuiHeight-Bottom ? GuiHeight-Bottom : Abs(Top)
    if (x || y)
        DllCall("ScrollWindow", "uint", WinExist(), "int", x, "int", y, "uint", 0, "uint", 0)
}

OnScroll(wParam, lParam, msg, hwnd) {	;written by lexikos
    static SIF_ALL=0x17, SCROLL_STEP=85 ;changed by SundayProgrammer from 10 to 85 for a more practical outcome
	static xpos := 0, ypos := 0	;added by SundayProgrammer - for touch gesture scrolling
	global gFlag	;added by SundayProgrammer - for touch gesture scrolling
if DllCall("GetParent", "uint", hwnd)	;added by SundayProgrammer - a quick fix for the scenario when any scrollable control is involved
	return	;added by SundayProgrammer - a quick fix for the scenario when any scrollable control is involved
   
    bar := (msg=0x115) or (msg=0x20A) ; (SB_HORZ=0, SB_VERT=1) or (WM_MOUSEHWHEEL=0, WM_MOUSEWHEEL=1)	;changed by SundayProgrammer - for WM_MOUSEWHEEL and WM_MOUSEHWHEEL
   
	if gFlag	;added by SundayProgrammer - for touch gesture scrolling
	{	gAction(xpos, ypos, bar, hwnd)	;added by SundayProgrammer - for touch gesture scrolling
		return	;added by SundayProgrammer - for touch gesture scrolling
	}	;added by SundayProgrammer - for touch gesture scrolling
   
    VarSetCapacity(si, 28, 0)
    NumPut(28, si) ; cbSize
    NumPut(SIF_ALL, si, 4) ; fMask
    if !DllCall("GetScrollInfo", "uint", hwnd, "int", bar, "uint", &si)
        return
   
    VarSetCapacity(rect, 16)
    DllCall("GetClientRect", "uint", hwnd, "uint", &rect)
   
    new_pos := NumGet(si, 20) ; nPos (saw "25" in another version, which exhibited a bug in my testing, whereas "20" (this version) worked fine so far)
   
    if msg=0x20A	;added by SundayProgrammer - for WM_MOUSEWHEEL
        wParam := wParam>0x780000	;added by SundayProgrammer - for WM_MOUSEWHEEL
    else if msg=0x20E	;added by SundayProgrammer - for WM_MOUSEHWHEEL
        wParam := wParam=0x780000	;added by SundayProgrammer - for WM_MOUSEHWHEEL
    action := wParam & 0xFFFF
    if action = 0 ; SB_LINEUP
        new_pos -= SCROLL_STEP
    else if action = 1 ; SB_LINEDOWN
        new_pos += SCROLL_STEP
    else if action = 2 ; SB_PAGEUP
        new_pos -= NumGet(rect, 12, "int") - SCROLL_STEP
    else if action = 3 ; SB_PAGEDOWN
        new_pos += NumGet(rect, 12, "int") - SCROLL_STEP
    else if (action = 5 || action = 4) ; SB_THUMBTRACK || SB_THUMBPOSITION
        new_pos := wParam>>16
    else if action = 6 ; SB_TOP
        new_pos := NumGet(si, 8, "int") ; nMin
    else if action = 7 ; SB_BOTTOM
        new_pos := NumGet(si, 12, "int") ; nMax
    else
        return
   
    min := NumGet(si, 8, "int") ; nMin
    max := NumGet(si, 12, "int") - NumGet(si, 16) ; nMax-nPage
    new_pos := new_pos > max ? max : new_pos
    new_pos := new_pos < min ? min : new_pos
   
    old_pos := NumGet(si, 20, "int") ; nPos (saw "25" in another version, which exhibited a bug in my testing, whereas "20" (this version) worked fine so far)
   
    x := y := 0
    if bar = 0 ; SB_HORZ
        x := old_pos-new_pos
    else
        y := old_pos-new_pos
    ; Scroll contents of window and invalidate uncovered area.
    DllCall("ScrollWindow", "uint", hwnd, "int", x, "int", y, "uint", 0, "uint", 0)
   
    ; Update scroll bar.
    NumPut(new_pos, si, 20, "int") ; nPos (saw "25" in another version, which exhibited a bug in my testing, whereas "20" (this version) worked fine so far)
    DllCall("SetScrollInfo", "uint", hwnd, "int", bar, "uint", &si, "int", 1)

	z := bar ? "y" : "x", %z%pos := new_pos	;added by SundayProgrammer - for touch gesture scrolling
}
gAction(byref xpos, byref ypos, bar, hwnd) {	;written by SundayProgrammer - for touch gesture scrolling
	VarSetCapacity(si, 28, 0), NumPut(28, si), NumPut(0x17, si, 4), DllCall("GetScrollInfo", "uint", hwnd, "int", bar, "uint", &si), aPos := NumGet(si, 20, "int"), z := bar ? "y" : "x"
	if not (%z%pos = aPos)
		x := y := 0, %z% := %z%pos - aPos, DllCall("ScrollWindow", "uint", hwnd, "int", x, "int", y, "uint", 0, "uint", 0), %z%pos := aPos
}
gHandler(wParam, lParam, msg, hwnd) {	;written by SundayProgrammer - for touch gesture scrolling
	global gFlag
	gFlag := true
	settimer, Reset_gFlag, -100
	return
	Reset_gFlag:
		gFlag := false
		return
}

ObjRegisterActive(Object, CLSID, Flags:=0) {	;written by Lexikos
    static cookieJar := {}
    if (!CLSID) {
        if (cookie := cookieJar.Remove(Object)) != ""
            DllCall("oleaut32\RevokeActiveObject", "uint", cookie, "ptr", 0)
        return
    }
    if cookieJar[Object]
        throw Exception("Object is already registered", -1)
    VarSetCapacity(_clsid, 16, 0)
    if (hr := DllCall("ole32\CLSIDFromString", "wstr", CLSID, "ptr", &_clsid)) < 0
        throw Exception("Invalid CLSID", -1, CLSID)
    hr := DllCall("oleaut32\RegisterActiveObject"
        , "ptr", &Object, "ptr", &_clsid, "uint", Flags, "uint*", cookie
        , "uint")
    if hr < 0
        throw Exception(format("Error 0x{:x}", hr), -1)
    cookieJar[Object] := cookie
}

;Function Ripped out of CodeQuickTester written by GeekDude https://github.com/G33kDude/CodeQuickTester
ExecScript(Script, Params="", AhkPath="")	;copy from "Execute code stored in a variable (dynamic variable?)" https://www.reddit.com/r/AutoHotkey/comments/ebwora/comment/fbcwvuy/?utm_source=share&utm_medium=web2x&context=3
{
	static Shell := ComObjCreate("WScript.Shell")
	Name := "\\.\pipe\AHK_CQT_" A_TickCount
	Pipe := []
	Loop, 3
	{
		Pipe[A_Index] := DllCall("CreateNamedPipe"
		, "Str", Name
		, "UInt", 2, "UInt", 0
		, "UInt", 255, "UInt", 0
		, "UInt", 0, "UPtr", 0
		, "UPtr", 0, "UPtr")
	}
	if !FileExist(AhkPath)
		throw Exception("AutoHotkey runtime not found: " AhkPath)
	if (A_IsCompiled && AhkPath == A_ScriptFullPath)
		AhkPath .= " /E"
	if FileExist(Name)
	{
		Exec := Shell.Exec(AhkPath " /CP65001 " Name " " Params)
		DllCall("ConnectNamedPipe", "UPtr", Pipe[2], "UPtr", 0)
		DllCall("ConnectNamedPipe", "UPtr", Pipe[3], "UPtr", 0)
		FileOpen(Pipe[3], "h", "UTF-8").Write(Script)
	}
	else ; Running under WINE with improperly implemented pipes
	{
		FileOpen(Name := "AHK_CQT_TMP.ahk", "w").Write(Script)
		Exec := Shell.Exec(AhkPath " /CP65001 " Name " " Params)
	}
	Loop, 3
		DllCall("CloseHandle", "UPtr", Pipe[A_Index])
	return Exec
}
Stavencross
Posts: 90
Joined: 24 May 2016, 16:42

Re: An Alternative Menu (A Quick Access To Various Features)

29 Oct 2021, 09:41

without reading all your code, I have no idea what this script does, and more importantly, I have no idea how to use it. Can i suggest some pics and a how to use?
Lepes
Posts: 141
Joined: 06 May 2021, 07:32
Location: Spain

Re: An Alternative Menu (A Quick Access To Various Features)

29 Oct 2021, 11:50

It draws a dot in the screen and when you click on it, a menu appears.

That menu can do several customized task, send keys, execute scripts and so on.

The question is: What kind of resolution SundayProggrammer are using? I'm using 1920x1080
Attachments
screen_20211029_184418.png
Menu on top of Adventure 3. Traslucent Menu
screen_20211029_184418.png (114.45 KiB) Viewed 6686 times
screen_20211029_184354.png
dot on screen you click to get access to the menu
screen_20211029_184354.png (3.74 KiB) Viewed 6686 times
SundayProgrammer
Posts: 143
Joined: 25 Dec 2020, 12:26

Re: An Alternative Menu (A Quick Access To Various Features)

30 Oct 2021, 00:09

Stavencross wrote:
29 Oct 2021, 09:41
without reading all your code, I have no idea what this script does, and more importantly, I have no idea how to use it. Can i suggest some pics and a how to use?
thank you. and please feel free to do whatever you deemed vital for it.
SundayProgrammer
Posts: 143
Joined: 25 Dec 2020, 12:26

Re: An Alternative Menu (A Quick Access To Various Features)

30 Oct 2021, 00:10

Lepes wrote:
29 Oct 2021, 11:50
It draws a dot in the screen and when you click on it, a menu appears.

That menu can do several customized task, send keys, execute scripts and so on.

The question is: What kind of resolution SundayProggrammer are using? I'm using 1920x1080
thank you. mines 800x1280, mostly.
SundayProgrammer
Posts: 143
Joined: 25 Dec 2020, 12:26

Re: An Alternative Menu (A Quick Access To Various Features)

30 Oct 2021, 00:13

here is what i can supplement.

whenever you think of making a hotkey (or some hotkeys) to trigger something, you may use a menu (the ahk menu command) instead. and when menu is a right choice (or it can be an addition to the hotkeys) for the thing you are working on, spotmenu as an alternative could also be used.

it may be good to provide a comparison among the three, to show their advantages/disadvantages in different aspects and cases, but then im too lazy for that.
gya
Posts: 25
Joined: 04 Nov 2021, 01:22

Re: An Alternative Menu (A Quick Access To Various Features)

06 Nov 2021, 03:31

Hello,

Having an easily accessible menu at all times is a very good idea and I use it.
Unfortunately you lack the courage to give clear explanations of how to use it properly and so I can only use it in a basic way.

That said, I find that the buttons and text font could be halved and still be very readable.

Once again, I regret the lack of effort for the explanations which are sometimes totally incomprehensible,.
But after all it is your free choice.

Best regards.
Translated with https://www.deepl.com/translator
SundayProgrammer
Posts: 143
Joined: 25 Dec 2020, 12:26

Re: An Alternative Menu (A Quick Access To Various Features)

06 Nov 2021, 04:31

gya wrote:
06 Nov 2021, 03:31
Hello,

Having an easily accessible menu at all times is a very good idea and I use it.
Unfortunately you lack the courage to give clear explanations of how to use it properly and so I can only use it in a basic way.

That said, I find that the buttons and text font could be halved and still be very readable.

Once again, I regret the lack of effort for the explanations which are sometimes totally incomprehensible,.
But after all it is your free choice.

Best regards.
Translated with https://www.deepl.com/translator
oh, well, thank you. let's put it this way. if you can ask specific question(s) about what you want to know in particular, or what obstacles you encountered, ill try my best to provide as much detail as i can, or provide a solution for the issue you came across.

anyhow, to give one more opinion in general, ill say, just read the existing posts above few more times, to grasp the readily available information to the fullest extent, and then edit the menu to the way you like, try different entries and see the results. feel free to play it upside down, and confirm/clarify your understanding/assumptions throughout the process. how about that?
SundayProgrammer
Posts: 143
Joined: 25 Dec 2020, 12:26

Re: An Alternative Menu (A Quick Access To Various Features)

06 Nov 2021, 04:58

gya wrote:
06 Nov 2021, 03:31
That said, I find that the buttons and text font could be halved and still be very readable.
the button (thus text font) size is mainly concerned about "touch friendly", not readability. even though my eyesight is not very good, and tend to like things relatively bigger.

you may easily change it yourself. if you need help in this, just raise.
SundayProgrammer
Posts: 143
Joined: 25 Dec 2020, 12:26

Re: An Alternative Menu (A Quick Access To Various Features)

07 Nov 2021, 13:58

what's new in this version:

added this syntax: #include the linking text string#

it's only for those scripts reside in the menu text (i.e. for those in their own ahk file, do not use this syntax. just use the normal ahk syntax please.)

example: (menu text)

Code: Select all

[:\s- YouTube:] Single Play	(Script)	SinglePlay(3)|| ||#Include Play a Single Item out of a Youtube Playlist#
[:\s- YouTube:] Toggle SP Spot	(Script)	{Toggle Single Play Spot}
- - - - - - - - - - - - - - - -
Close Menu	{menu.Gui_OnEscape}	Return To mSpot	(Function)
Reload Menu	{menu.Reload}	Reflect The Latest Content	(Function)
Edit Menu	{EditMenu}	(Gosub)
;
{Script Play a Single Item out of a Youtube Playlist}
	SinglePlay(n) {
		Points := GetMouseMovePoints(n)
		For Each, Point In Points
			x%A_Index% := Point.X, y%A_Index% := Point.Y
		CoordMode, Mouse, Screen
		MouseMove, x%n%, y%n%
		CoordMode, Mouse, Window
		Send, {RButton}
		Sleep, 1000
		Send, o{Enter}
		Sleep, 1000
		Clipboard := RegExReplace(Clipboard, "&list=.+$")
		Send, +^l
	}
	#Include Get Mouse Move Points#
{/Script}

{Script Get Mouse Move Points}
	GetMouseMovePoints(Points := 16) {	;written by @"just me"
		Static SizeOfMMP := 8 + A_PtrSize + A_PtrSize
		If Points Is Not Integer
			Points := 16
		Else If (Points < 2)
			Points := 2
		Else If (Points > 64)
			Points := 64
		VarSetCapacity(POINT, 8, 0)
		DllCall("GetCursorPos", "Ptr", &POINT)
		VarSetCapacity(MMPIN, SizeOfMMP, 0)
		X := NumGet(POINT, 0, "Int")
		Y := NumGet(POINT, 4, "Int")
		NumPut(X & 0xFFFF, MMPIN, 0, "Int")
		NumPut(Y & 0xFFFF, MMPIN, 4, "Int")
		VarSetCapacity(MMPOUT, SizeOfMMP * Points, 0)
		Points := DllCall("GetMouseMovePointsEx", "UInt", SizeOfMMP, "Ptr", &MMPIN, "Ptr", &MMPOUT, "Int", Points, "UInt", 1, "Int")
		If (Points < 1)
			Return 0
		Offset := 0
		MouseArray := []
		Loop, %Points% {
			X := NumGet(MMPOUT, Offset, "Int")
			Y := NumGet(MMPOUT, Offset + 4, "Int")
			If (X > 32767)
				X -= 65536
			If (Y > 32767)
				Y -= 65536
			MouseArray.Push({X: X, Y: Y})
			Offset += SizeOfMMP
		}
		Return MouseArray
	}
{/Script}

{Script Toggle Single Play Spot}
	IfWinExist, SPspot
	{	WinGet, PID, PID, SPspot
		Process, Close, %PID%
		ExitApp
	}Gui, SPs:New
	Gui, +LastFound -Caption +AlwaysOnTop +ToolWindow
	Gui, Color, White
	Gui, Margin, 0, 0
	Gui, Font, s150, Consolas
	Gui, Add, Text, x0 y0 cB2D6F3 BackgroundTrans gHit, ●
	WinSet, TransColor, White 64
	x := A_ScreenWidth - 100, y := A_ScreenHeight - 700
	Gui, Show, x%x% y%y% NoActivate, SPspot
	Hit() {
		SinglePlay(2)
	}
	#Include Play a Single Item out of a Youtube Playlist#
{/Script}
the scripts inside this example are tested working under msedge win10 and require touchscreen. it demonstrates stepping back mouse positions, and an extra spot for the task which reduced the process from two taps to just one. you first open a youtube playlist and then move your mouse pointer on top of the video you want to play. tap on the mspot to show the menu, then tap on the button "single play" which will play the video alone (will not continue to play the rest of the playlist.) for the sp spot way, tap on the mspot to show the menu first, then tap on the button "toggle sp spot" to bring up the sp spot, therefore open a youtube playlist and move your mouse pointer on top of the video you want to play, then tap on the sp spot to play it.

here is the updated script:

Code: Select all

pool()	;init variables
menu.Spot()	;show mSpot
Return
EditMenu:
	Run, Notepad MenuText.txt
	Return
demo1:
	msgbox, this is demo1
	return
demo2:
	msgbox, this is demo2 this is demo2
	return
demo3(){
	msgbox, this is demo3 this is demo3 this is demo3
}
demo4(p){
	msgbox, % p
}
demo5(p1, p2){
	msgbox, % p1 "`n`n=========================`n`n" p2
}

pool(param = "") {	;written by SundayProgrammer
	static
	static clsid1 := "{A3C04B39-0465-4460-8CA0-7BFFF481FF98}", s1l := "a := ComObjActive(""" clsid1 """)`n"
	static dummy1 := "i am param the first"
	static dummy2 := "i am param the second"
If StrLen(param)
	If IsGetSet(param, action, varname, value)
	{	If (action = "Set:")
			%varname% := value
		v := %varname%
		Return v
	}Else{}
Else{
	ObjRegisterActive(agent, clsid1)
	IfExist, MenuText.txt
		FileRead, _menu_, MenuText.txt
	Else{
_menu_=
(
= = = Top = = =
^#{Left}	Switch Desktop	(Send)
^#{Right}	Switch Desktop	(Send)
#{Tab}	Windows Switcher	(Send)
[:\s- Microsoft.+?Edge:] m{Enter}	MyActivity.Google	(Send)
[:\s- Microsoft.+?Edge:] +^u	Rald Toggle	(Send)
^f	Find	(Send)
[:\s- Microsoft.+?Edge:] ^r	Reload Web Page	(Send)
^w	Close Tab	(Send)
[:\s- YouTube:] j	10s Backward	(Send)
[:\s- Microsoft.+?Edge:] !{Left}	Previous Web Page	(Send)
[:\s- Microsoft.+?Edge:] !{Right}	Next Web Page	(Send)
{Enter}	Enter	(Send)
[:\s- YouTube:] f	Fullscreen Toggle	(Send)
{Space}	Space	(Send)
{Del}	Delete	(Send)
{Esc}	Escape	(Send)
[:\s- Sublime Text:] ^g	Go To Line	(Send)
[:\s- Sublime Text:] !d	Duplicate	(Send)
^z	Undo	(Send)
^x	Cut	(Send)
^c	Copy	(Send)
^v	Paste	(Send)
^s	Save File	(Send)
^a	Select All	(Send)
- - - - - - - - - - - - - - - -
Close Menu	{menu.Gui_OnEscape}	Return To mSpot	(Function)
Reload Menu	{menu.Reload}	Reflect The Latest Content	(Function)
Edit Menu	{EditMenu}	(Gosub)
Demo One	{demo1}	hello world	(Gosub)
Demo Two	{demo2}	foobar	(Gosub)
Demo Three	{demo3}	blablabla	(Function)
Demo Four	{demo4}	bla bla	(Func+Param)	dummy1
Demo Five	{demo5}	bla bla bla	(Func+Param)	dummy1,dummy2
Script I	(Script)	msgbox `% "remote: " a.call("var","dummy1@pool")
Script II	(Script)	msgbox `% "remote: " a.call("var","dummy2@pool")
Script III	(Script)	a.call("var","Set:dummy3@pool",a_tickcount)|| ||msgbox `% "remote: " a.call("var","dummy3@pool")
Verify	{demo4}	(Func+Param)	dummy3
Script IV	(Script)	{Apart}
[:Text Filter:RICHEDIT50W1:] Script V	(Script)	{fghij}
Script VI	(Script)	{menuDemo}
= = = Bottom = = =
;
{Script Apart}
a.call("var","Set:dummy3@pool",a_tickcount "(a)")
msgbox `% "remote: " a.call("var","dummy3@pool")
{/Script}

{Script fghij}
a.call("demo5", "p1p", "p2p")
If InStr(a.call("var","dummy3@pool"), "(a)")
	msgbox found "(a)" in dummy3
{/Script}
)
FileAppend, %_menu_%, MenuText.txt, UTF-8
		}
	IfNotExist, menuDemo.ahk
FileAppend, msgbox im an island, menuDemo.ahk, UTF-8
	}
}

IsGetSet(Param, ByRef a, ByRef v, ByRef value) {	;written by SundayProgrammer
	a := SubStr(Param, 1, 4)
	If (a = "Set:") or (a = "Get:")
	{	v := Trim((p := InStr(Param, "=")) ? (SubStr(Param, 5, p - 5), value := SubStr(Param, p + 1)) : SubStr(Param, 5))
		Return True
	}Return False
}
var(name, value = "") {	;written by SundayProgrammer
	global
	local p, a, v, m1, m2, m3, segc
	If p := InStr(name, "@")
	{	a := SubStr(name, 1, 4), v := Trim(SubStr(name, 1, p - 1)), name := Trim(SubStr(name, p + 1))
		If (a = "Set:") or (a = "Get:")
			v := Trim(SubStr(v, 5))
		Else a := StrLen(value) ? "Set:" : "Get:"
		If StrLen(v) and StrLen(name)
		{	v := a v, v .= (a = "Set:") ? "=" value : ""
			If InStr(name, ".")
			{	Loop, Parse, name, .
					m%A_Index% := A_LoopField, segc := A_Index
				If segc = 2
					v := %m1%[m2](v)
				Else If segc = 3
					v := %m1%[m2][m3](v)
				Else v = Error: Segments More Than Allowed.
			}Else v := %name%(v)
		}Else v = Error: Invalid Parameter "%v%@%name%"
	}Else
	{	a := SubStr(name, 1, 4)
		If (a = "Set:") or (a = "Get:")
			name := Trim(SubStr(name, 5))
		If InStr(name, ".")
		{	Loop, Parse, name, .
				m%A_Index% := A_LoopField, segc := A_Index
			If segc = 2
			{	If (a = "Set:") or StrLen(value)
					%m1%[m2] := value
				v := %m1%[m2]
			}Else If segc = 3
			{	If (a = "Set:") or StrLen(value)
					%m1%[m2][m3] := value
				v := %m1%[m2][m3]
			}Else v = Error: Segments More Than Allowed.
		}Else
		{	If (a = "Set:") or StrLen(value)
				%name% := value
			v := %name%
		}
	}
	Return v
}

Class agent {	;written by SundayProgrammer who got the idea from this post https://www.autohotkey.com/boards/viewtopic.php?p=200597#p200597
	Call(name, p*) {	;allows you to call any function in this script
		If InStr(name, ".")
		{	Loop, Parse, name, .
				m%A_Index% := A_LoopField, segc := A_Index
			If segc = 2
				Return %m1%[m2](p*)
			Else If segc = 3
				Return %m1%[m2][m3](p*)
			Else Return "Error: Segments More Than Allowed."
		}Return %name%(p*)
	}
}

#IfWinActive ahk_group SpotMenu
	WheelUp::
	WheelDown::
	+WheelUp::
	+WheelDown::OnScroll(InStr(A_ThisHotkey, "Down") ? 1 : 0, 0, GetKeyState("Shift") ? 0x114 : 0x115, WinExist())
#If

WM_DISPLAYCHANGE() {
	WinGetPos, x, y,,, mSpot
	If (x > A_ScreenWidth - 50)
		x := A_ScreenWidth - 100
	If (y > A_ScreenHeight - 125)
		y := A_ScreenHeight - 175
	Gui, mSpot:Show, x%x% y%y% NoActivate
}

WM_ACTIVATE() {
	IfWinActive, mSpot
		WinSet, TransColor, White, mSpot
	Else WinSet, TransColor, White 64, mSpot
}

Class menu {	;written by SundayProgrammer
Spot() {
	Gui, mSpot:New
	Gui, +LastFound -Caption +AlwaysOnTop +ToolWindow
	Gui, Color, White
	Gui, Margin, 0, 0
	Gui, Font, s150, Consolas
	Gui, Add, Text, x0 y0 cFFB10F BackgroundTrans, ●
	OnSuch := this.HitEvent.Bind(this)
	GuiControl +g, Static1, % OnSuch
	WinSet, TransColor, White 64
	x := A_ScreenWidth - 100, y := A_ScreenHeight - 530
	Gui, Show, x%x% y%y% NoActivate, mSpot
	this.HitEvent() ; Init sPos
	OnMessage(0x7E, "WM_DISPLAYCHANGE")
	OnMessage(0x06, "WM_ACTIVATE")
}
HitEvent(hwnd := "") {
	static sPos
	If not StrLen(sPos)
	{	WinGetPos, x, y,,, mSpot
		sPos := x "," y
	}If not StrLen(hwnd)
		Return sPos
	SendMessage, 0xA1, 2,,, A ; WM_NCLBUTTONDOWN
	GuiControlGet, wHwnd, Hwnd, %hwnd%
	WinGetPos, x, y,,, ahk_id %wHwnd%
	If x "," y = sPos
	{	Gui, mSpot:Hide
		this.SpotMenu(var("_menu_@pool"))
	}Else
	{	sPos := x "," y
		SetTimer, Deactivate, -500
	}Return
	Deactivate:
		Gui, mSpot:Hide
		Gui, mSpot:Show, NoActivate
		WinSet, TransColor, White 64, mSpot
		Return
}
SpotMenu(t) {
	global TT
	WinWaitNotActive, mSpot
	WinGetActiveTitle, sWin
	ControlGetFocus, sCtl, A
	Loop, Parse, t, `n
		If SubStr(Trim(A_LoopField), 1, 1) not = ";"
		If RegExMatch(A_LoopField, "i)^\s*{\s*Script\s+[^}]+?\s*}")
			Break
		Else If RegExMatch(A_LoopField, "[^\t]+(?=\t)", m)
			w%A_Index% := this.GetButtonWidth(Trim(RegExReplace(m, "\s*\[([^\]]+)]")), "30 bold", "Consolas")
	OnMessage(0x115, "OnScroll") ; WM_VSCROLL
	OnMessage(0x114, "OnScroll") ; WM_HSCROLL
	OnMessage(0x20A, "OnScroll") ; WM_MOUSEWHEEL
	OnMessage(0x20E, "OnScroll") ; WM_MOUSEHWHEEL
	OnMessage(0x119, "gHandler") ; WM_GESTURE
	Gui, menu:New, hwndHmenu +Labelmenu.Gui_On
	Gui, +Resize +0x300000	; WS_VSCROLL | WS_HSCROLL
	TT := New GuiControlTips(Hmenu), TT.SetDelayTimes(1000, 15000, -1)
	Gui, Font, s30 bold, Consolas
	ci := nl := 1
	Loop, Parse, t, `n
		If SubStr(Trim(A_LoopField), 1, 1) not = ";"
		If RegExMatch(A_LoopField, "[^\t]+(?=\t)", m)
		{	Passed := False
			If RegExMatch(m, "\s*\[([^\]]+)]", mm)
			If RegExMatch(Trim(mm1), ":([^:]+(:[^:]+)?):", mm)
			If pos := InStr(mm1, ":")
				If not RegExMatch(sWin, Trim(SubStr(mm1, 1, pos - 1))) or not RegExMatch(sCtl, Trim(SubStr(mm1, pos + 1)))
					Continue
				Else Passed := True
			Else If not RegExMatch(sWin, Trim(mm1))
				Continue
			Else Passed := True
			If Passed
				m := Trim(RegExReplace(m, "\s*\[([^\]]+)]"))
			If not nl
				nl := (gx + gw + 25 + w%A_Index%) > A_ScreenWidth - 40
			p := nl ? "" : "+", y := nl ? "y+28" : ""
			Gui, Add, Button, HwndhBtn%ci% x%p%25 %y% h57, % (m, pi := ci, ci++)
			OnSuch := this.MenuClick.Bind(this)
			GuiControl +g, Button%pi%, % OnSuch
			GuiControlGet, g, Pos, Button%pi%
			TT.Attach(hBtn%pi%, RegExReplace(RegExReplace(SubStr(A_LoopField, 0) = "`r" ? SubStr(A_LoopField, 1, -1) : A_LoopField, "[^\t]+\t",,, 1), "\t", "`n"))
			nl := (gx + gw) > (A_ScreenWidth - 116)
		}Else If RegExMatch(A_LoopField, "i)^\s*{\s*Script\s+[^}]+?\s*}")
			Break
		Else
		{	Gui, Add, Text, x25 cWhite, % SubStr(A_LoopField, 0) = "`r" ? SubStr(A_LoopField, 1, -1) : A_LoopField
			nl := True
		}
	Gui, Color, Black
	TT.SetTitle("Descriptions", LoadPicture("shell32.dll", "Icon222", ImageType))
	SetCtrlFont(TT.HTIP, "s20", "Arial New")
	Gui, +LastFound
	WinSet, Transparent, 180
	Gui, Show,, Touch Friendly Menu
	GroupAdd, SpotMenu, % "ahk_id " . WinExist()
	this.t := t
}
Gui_OnSize() {
	UpdateScrollBars(A_Gui, A_GuiWidth, A_GuiHeight)
}
Gui_OnEscape() {
	menu.OnClose()
	Gui, mSpot:Show, NoActivate
	WinSet, TransColor, White 64, mSpot
}
Gui_OnClose() {
	ObjRegisterActive(agent, "")
	ExitApp
}
OnClose() {
	OnMessage(0x115, "") ; WM_VSCROLL
	OnMessage(0x114, "") ; WM_HSCROLL
	OnMessage(0x20A, "") ; WM_MOUSEWHEEL
	OnMessage(0x20E, "") ; WM_MOUSEHWHEEL
	OnMessage(0x119, "") ; WM_GESTURE
	Gui, menu:Destroy
}
GetButtonWidth(t, s, f) {
	Gui, New
	Gui, Font, s%s%, % f
	Gui, Add, Button,, % t
	GuiControlGet, g, Pos, Button1
	Gui, Destroy
	Return gW
}
MenuClick(h) {
	global TT
	theText := A_GuiControl
	this.OnClose()
	WinWaitNotActive, Touch Friendly Menu
	Sleep, 500
	If RegExMatch(t := TT.GetText(h), "{\K[^}]+(?=})", m)
		If not InStr(t, "(Send)")
			theText := m
	If InStr(t, "(Send)")
		Send, %theText%
	Else If InStr(t, "(Gosub)")
		Gosub, %theText%
	Else If InStr(t, "(Function)")
		If pos := InStr(theText, ".")
			className := SubStr(theText, 1, pos - 1), method := SubStr(theText, pos + 1), %className%[method]()
		Else %theText%()
	Else If InStr(t, "(Func+Param)")
	{	RegExMatch(t, "[^\n]+$", m)
		If InStr(m, ",")
		{	Loop, Parse, m, `,
				p%A_Index% := var(A_LoopField "@pool")
			StrReplace(m, ",",, c)
			If c = 1
				%theText%(p1, p2)
			Else If c = 2
				%theText%(p1, p2, p3)
			Else If c = 3
				%theText%(p1, p2, p3, p4)
			Else If c = 4
				%theText%(p1, p2, p3, p4, p5)
			Else If c = 5
				%theText%(p1, p2, p3, p4, p5, p6)
			Else If c = 6
				%theText%(p1, p2, p3, p4, p5, p6, p7)
			Else If c = 7
				%theText%(p1, p2, p3, p4, p5, p6, p7, p8)
			Else If c = 8
				%theText%(p1, p2, p3, p4, p5, p6, p7, p8, p9)
			Else If c = 9
				%theText%(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10)
			Else{}	;more than 10 parameters is not supported
		}Else %theText%(var(m "@pool"))
	}Else If InStr(t, "(Script)")
		If RegExMatch(t, "{\K[^}]+(?=})", m)
			If RegExMatch(this.t, "is){\s*Script\s+" m "\s*}(.+?){\s*/\s*Script\s*}", mm)
				ExecScript(var("s1l@pool") this.HandleInclude(mm1),, A_AhkPath)
			Else IfExist, % m := (SubStr(Trim(m), -3) = ".ahk" ? m : Trim(m) ".ahk")
			{	FileRead, mm1, %m%
				ExecScript(var("s1l@pool") mm1,, A_AhkPath)
			}Else{}
		Else RegExMatch(t, "\(Script\)[\n\t]+\K.*$", m), m := StrReplace(m, "|| ||", "`n"), ExecScript(var("s1l@pool") this.HandleInclude(m),, A_AhkPath)
	Gui, mSpot:Show, NoActivate
	WinSet, TransColor, White 64, mSpot
}
HandleInclude(s) {
	If RegExMatch(s, "is)#\s*Include\s+[^#]+#")
	{	RegExReplace(s, "is)#\s*Include\s+[^#]+#",, n), pos := 1, m := ""
		Loop, %n%
			pos := RegExMatch(s, "is)#\s*Include\s+\K[^#]+(?=#)", m, pos + StrLen(m)), RegExMatch(this.t, "is){\s*Script\s+" Trim(m) "\s*}(.+?){\s*/\s*Script\s*}", mm), s := RegExReplace(s, "is)#\s*Include\s+" Trim(m) "\s*#", RegExMatch(mm1, "is)#\s*Include\s+[^#]+#") ? this.HandleInclude(mm1) : mm1)
	}
	Return s
}
Reload() {
	FileRead, _menu_, MenuText.txt
	var("Set:_menu_@pool", _menu_)
	SetTimer, ReloadMenu, -500
	Return
	ReloadMenu:
		Gui, mSpot:Hide
		menu.SpotMenu(var("_menu_@pool"))
		Return
}
}	;end of class

SetCtrlFont(CtrlHwnd, FontOptions := "", FontName := "") {	;written by iPhilip
   static WM_SETFONT := 0x0030, WM_GETFONT := 0x0031
   DefaultGui := A_DefaultGui
   Gui, New
   Gui, Font, % FontOptions, % FontName
   Gui, Add, Text, hwndhText, Text
   hFont := DllCall("SendMessage", "Ptr", hText, "UInt", WM_GETFONT, "Ptr", 0, "Ptr", 0, "Ptr")
   Gui, Destroy
   Gui, %DefaultGui%:Default
   DllCall("SendMessage", "Ptr", CtrlHwnd, "UInt", WM_SETFONT, "Ptr", hFont, "Ptr", true)
   Return hFont
}

; ======================================================================================================================
; Namespace:      GuiControlTips
; AHK version:    AHK 1.1.14.03
; Function:       Helper object to simply assign ToolTips for GUI controls
; Tested on:      Win 7 (x64)
; Change history:
;                 1.1.00.01/2020-06-03/just me - fixed missing Static WS_EX_TOPMOST
;                 1.1.00.00/2014-03-06/just me - Added SetDelayTimes()
;                 1.0.01.00/2012-07-29/just me
; ======================================================================================================================
; CLASS GuiControlTips
;
; The class provides four public methods to register (Attach), unregister (Detach), update (Update), and
; disable/enable (Suspend) common ToolTips for GUI controls.
;
; Usage:
; To assign ToolTips to GUI controls you have to create a new instance of GuiControlTips per GUI with
;     MyToolTipObject := New GuiControlTips(HGUI)
; passing the HWND of the GUI.
;
; After this you may assign ToolTips to your GUI controls by calling
;     MyToolTipObject.Attach(HCTRL, "ToolTip text")
; passing the HWND of the control and the ToolTip's text. Pass True/1 for the optional third parameter if you
; want the ToolTip to be shown centered below the control.
;
; To remove a ToolTip call
;     MyToolTipObject.Detach(HCTRL)
; passing the HWND of the control.
;
; To update the ToolTip's text call
;     MyToolTipObject.Update(HCTRL, "New text!")
; passing the HWND of the control and the new text.
;
; To deactivate the ToolTips call
;     MyToolTipObject.Suspend(True),
; to activate them again afterwards call
;     MyToolTipObject.Suspend(False).
;
; To adjust the ToolTips delay times call
;     MyToolTipObject.SetDelayTimesd(),
; specifying the delay times in milliseconds.
;
; That's all you can / have to do!
; ======================================================================================================================
Class GuiControlTips {	;written by @"just me"
   ; ===================================================================================================================
   ; INSTANCE variables
   ; ===================================================================================================================
   HTIP := 0
   HGUI := 0
   CTRL := {}
   ; ===================================================================================================================
   ; CONSTRUCTOR           __New()
   ; ===================================================================================================================
   __New(HGUI) {
      Static CLASS_TOOLTIP      := "tooltips_class32"
      Static CW_USEDEFAULT      := 0x80000000
      Static TTM_SETMAXTIPWIDTH := 0x0418
      Static TTM_SETMARGIN      := 0x041A
      Static WS_EX_TOPMOST      := 0x00000008
      Static WS_STYLES          := 0x80000002 ; WS_POPUP | TTS_NOPREFIX
      ; Create a Tooltip control ...
      HTIP := DllCall("User32.dll\CreateWindowEx", "UInt", WS_EX_TOPMOST, "Str", CLASS_TOOLTIP, "Ptr", 0
                    , "UInt", WS_STYLES
                    , "Int", CW_USEDEFAULT, "Int", CW_USEDEFAULT, "Int", CW_USEDEFAULT, "Int", CW_USEDEFAULT
                    , "Ptr", HGUI, "Ptr", 0, "Ptr", 0, "Ptr", 0, "Ptr")
      If ((ErrorLevel) || !(HTIP))
         Return False
      ; ... prepare it to display multiple lines if required
      DllCall("User32.dll\SendMessage", "Ptr", HTIP, "Int", TTM_SETMAXTIPWIDTH, "Ptr", 0, "Ptr", A_ScreenWidth*96//A_ScreenDPI)	;touched by SundayProgrammer who took it from iPhilip response for AddTooltip v2.0
      ; ... set the instance variables
      This.HTIP := HTIP
      This.HGUI := HGUI
      If (DllCall("Kernel32.dll\GetVersion", "UInt") & 0xFF) < 6 ; to avoid some XP issues ...
         This.Attach(HGUI, "") ; ... register the GUI with an empty tiptext
   }
   ; ===================================================================================================================
   ; DESTRUCTOR            __Delete()
   ; ===================================================================================================================
   __Delete() {
      If (This.HTIP) {
         DllCall("User32.dll\DestroyWindow", "Ptr", This.HTIP)
      }
   }
   ; ===================================================================================================================
   ; PRIVATE METHOD        SetToolInfo - Create and fill a TOOLINFO structure
   ; ===================================================================================================================
   SetToolInfo(ByRef TOOLINFO, HCTRL, TipTextAddr, CenterTip = 0) {
      Static TTF_IDISHWND  := 0x0001
      Static TTF_CENTERTIP := 0x0002
      Static TTF_SUBCLASS  := 0x0010
      Static OffsetSize  := 0
      Static OffsetFlags := 4
      Static OffsetHwnd  := 8
      Static OffsetID    := OffsetHwnd + A_PtrSize
      Static OffsetRect  := OffsetID + A_PtrSize
      Static OffsetInst  := OffsetRect + 16
      Static OffsetText  := OffsetInst + A_PtrSize
      Static StructSize  := (4 * 6) + (A_PtrSize * 6)
      Flags := TTF_IDISHWND | TTF_SUBCLASS
      If (CenterTip)
         Flags |= TTF_CENTERTIP
      VarSetCapacity(TOOLINFO, StructSize, 0)
      NumPut(StructSize, TOOLINFO, OffsetSize, "UInt")
      NumPut(Flags, TOOLINFO, OffsetFlags, "UInt")
      NumPut(This.HGUI, TOOLINFO, OffsetHwnd, "Ptr")
      NumPut(HCTRL, TOOLINFO, OffsetID, "Ptr")
      NumPut(TipTextAddr, TOOLINFO, OffsetText, "Ptr")
      Return True
   }
   ; ===================================================================================================================
   ; PUBLIC METHOD         Attach         -  Assign a ToolTip to a certain control
   ; Parameters:           HWND           -  Control's HWND
   ;                       TipText        -  ToolTip's text
   ;                       Optional:      ------------------------------------------------------------------------------
   ;                       CenterTip      -  Centers the tooltip window below the control
   ;                                         Values:  True/False
   ;                                         Default: False
   ; Return values:        On success: True
   ;                       On failure: False
   ; ===================================================================================================================
   Attach(HCTRL, TipText, CenterTip = False) {
      Static TTM_ADDTOOL  := A_IsUnicode ? 0x0432 : 0x0404 ; TTM_ADDTOOLW : TTM_ADDTOOLA
      If !(This.HTIP) {
         Return False
      }
      If This.CTRL.HasKey(HCTRL)
         Return False
      TOOLINFO := ""
      This.SetToolInfo(TOOLINFO, HCTRL, &TipText, CenterTip)
      If DllCall("User32.dll\SendMessage", "Ptr", This.HTIP, "Int", TTM_ADDTOOL, "Ptr", 0, "Ptr", &TOOLINFO) {
         This.CTRL[HCTRL] := 1
		 This.TTT[HCTRL] := TipText	;added by SundayProgrammer
         Return True
      } Else {
        Return False
      }
   }
   ; ===================================================================================================================
   ; PUBLIC METHOD         Detach         -  Remove the ToolTip for a certain control
   ; Parameters:           HWND           -  Control's HWND
   ; Return values:        On success: True
   ;                       On failure: False
   ; ===================================================================================================================
   Detach(HCTRL) {
      Static TTM_DELTOOL  := A_IsUnicode ? 0x0433 : 0x0405 ; TTM_DELTOOLW : TTM_DELTOOLA
      If !This.CTRL.HasKey(HCTRL)
         Return False
      TOOLINFO := ""
      This.SetToolInfo(TOOLINFO, HCTRL, 0)
      DllCall("User32.dll\SendMessage", "Ptr", This.HTIP, "Int", TTM_DELTOOL, "Ptr", 0, "Ptr", &TOOLINFO)
      This.CTRL.Remove(HCTRL, "")
	  This.TTT.Remove(HCTRL, "")	;added by SundayProgrammer
      Return True
   }
   ; ===================================================================================================================
   ; PUBLIC METHOD         Update         -  Update the ToolTip's text for a certain control
   ; Parameters:           HWND           -  Control's HWND
   ;                       TipText        -  New text                                                      
   ; Return values:        On success: True
   ;                       On failure: False
   ; ===================================================================================================================
   Update(HCTRL, TipText) {
      Static TTM_UPDATETIPTEXT  := A_IsUnicode ? 0x0439 : 0x040C ; TTM_UPDATETIPTEXTW : TTM_UPDATETIPTEXTA
      If !This.CTRL.HasKey(HCTRL)
         Return False
      TOOLINFO := ""
      This.SetToolInfo(TOOLINFO, HCTRL, &TipText)
      DllCall("SendMessage", "Ptr", This.HTIP, "Int", TTM_UPDATETIPTEXT, "Ptr", 0, "Ptr", &TOOLINFO)
	  This.TTT[HCTRL] := TipText	;added by SundayProgrammer
      Return True
   }
   ;added by SundayProgrammer
   GetText(HCTRL) {
      If !This.CTRL.HasKey(HCTRL)
         Return ""
      Return This.TTT[HCTRL]
   }
   ; ===================================================================================================================
   ; PUBLIC METHOD         Suspend        -  Disable/enable the ToolTip control (don't show / show ToolTips)
   ; Parameters:           Mode           -  True/False (1/0)
   ;                                         Default: True/1
   ; Return values:        On success: True
   ;                       On failure: False
   ; Remarks:              ToolTips are enabled automatically on creation.
   ; ===================================================================================================================
   Suspend(Mode = True) {
      Static TTM_ACTIVATE := 0x0401
      If !(This.HTIP)
         Return False
      DllCall("SendMessage", "Ptr", This.HTIP, "Int", TTM_ACTIVATE, "Ptr", !Mode, "Ptr", 0)
      Return True
   }
   ; ===================================================================================================================
   ; PUBLIC METHOD         SetDelayTimes  -  Set the initial, pop-up, and reshow durations for a tooltip control.
   ; Parameters:           Init           -  Amount of time, in milliseconds, a pointer must remain stationary within
   ;                                         a tool's bounding rectangle before the tooltip window appears.
   ;                                         Default: -1 (system default time)
   ;                       PopUp          -  Amount of time, in milliseconds, a tooltip window remains visible if the
   ;                                         pointer is stationary within a tool's bounding rectangle.
   ;                                         Default: -1 (system default time)
   ;                       ReShow         -  Amount of time, in milliseconds, it takes for subsequent tooltip windows
   ;                                         to appear as the pointer moves from one tool to another.
   ;                                         Default: -1 (system default time)
   ; Return values:        On success: True
   ;                       On failure: False
   ; Remarks:              Times are set per ToolTip control and applied to all added tools.
   ; ===================================================================================================================
   SetDelayTimes(Init = -1, PopUp = -1, ReShow = -1) {
      Static TTM_SETDELAYTIME   := 0x0403
      Static TTDT_RESHOW   := 1
      Static TTDT_AUTOPOP  := 2
      Static TTDT_INITIAL  := 3
      DllCall("SendMessage", "Ptr", This.HTIP, "Int", TTM_SETDELAYTIME, "Ptr", TTDT_INITIAL, "Ptr", Init)
      DllCall("SendMessage", "Ptr", This.HTIP, "Int", TTM_SETDELAYTIME, "Ptr", TTDT_AUTOPOP, "Ptr", PopUp)
      DllCall("SendMessage", "Ptr", This.HTIP, "Int", TTM_SETDELAYTIME, "Ptr", TTDT_RESHOW , "Ptr", ReShow)
   }
   ;added by SundayProgrammer who largely took it from AddTooltip v2.0
   SetTitle(theTitle := "", theIcon := 0) {
      Static TTM_SETTITLE := A_IsUnicode ? 0x421 : 0x420 ; TTM_SETTITLEW : TTM_SETTITLEA
      If StrLen(theTitle) > 99
         theTitle := SubStr(theTitle, 1, 99)
      If theIcon is not Integer
         theIcon := 0
      DllCall("SendMessage", "Ptr", This.HTIP, "Int", TTM_SETTITLE, "Ptr", theIcon, "Ptr", &theTitle)
   }
}

UpdateScrollBars(GuiNum, GuiWidth, GuiHeight) {	;written by lexikos
    static SIF_RANGE=0x1, SIF_PAGE=0x2, SIF_DISABLENOSCROLL=0x8, SB_HORZ=0, SB_VERT=1
   
    Gui, %GuiNum%:Default
    Gui, +LastFound
   
    ; Calculate scrolling area.
    Left := Top := 9999
    Right := Bottom := 0
    WinGet, ControlList, ControlList
    Loop, Parse, ControlList, `n
    {
        GuiControlGet, c, Pos, %A_LoopField%
        if (cX < Left)
            Left := cX
        if (cY < Top)
            Top := cY
        if (cX + cW > Right)
            Right := cX + cW
        if (cY + cH > Bottom)
            Bottom := cY + cH
    }
    Left -= 8
    Top -= 8
    Right += 8
    Bottom += 8
    ScrollWidth := Right-Left
    ScrollHeight := Bottom-Top
   
    ; Initialize SCROLLINFO.
    VarSetCapacity(si, 28, 0)
    NumPut(28, si) ; cbSize
    NumPut(SIF_RANGE | SIF_PAGE, si, 4) ; fMask
   
    ; Update horizontal scroll bar.
    NumPut(ScrollWidth, si, 12) ; nMax
    NumPut(GuiWidth, si, 16) ; nPage
    DllCall("SetScrollInfo", "uint", WinExist(), "uint", SB_HORZ, "uint", &si, "int", 1)
   
    ; Update vertical scroll bar.
;     NumPut(SIF_RANGE | SIF_PAGE | SIF_DISABLENOSCROLL, si, 4) ; fMask
    NumPut(ScrollHeight, si, 12) ; nMax
    NumPut(GuiHeight, si, 16) ; nPage
    DllCall("SetScrollInfo", "uint", WinExist(), "uint", SB_VERT, "uint", &si, "int", 1)
   
    if (Left < 0 && Right < GuiWidth)
        x := Abs(Left) > GuiWidth-Right ? GuiWidth-Right : Abs(Left)
    if (Top < 0 && Bottom < GuiHeight)
        y := Abs(Top) > GuiHeight-Bottom ? GuiHeight-Bottom : Abs(Top)
    if (x || y)
        DllCall("ScrollWindow", "uint", WinExist(), "int", x, "int", y, "uint", 0, "uint", 0)
}

OnScroll(wParam, lParam, msg, hwnd) {	;written by lexikos
    static SIF_ALL=0x17, SCROLL_STEP=85 ;changed by SundayProgrammer from 10 to 85 for a more practical outcome
	static xpos := 0, ypos := 0	;added by SundayProgrammer - for touch gesture scrolling
	global gFlag	;added by SundayProgrammer - for touch gesture scrolling
if DllCall("GetParent", "uint", hwnd)	;added by SundayProgrammer - a quick fix for the scenario when any scrollable control is involved
	return	;added by SundayProgrammer - a quick fix for the scenario when any scrollable control is involved
   
    bar := (msg=0x115) or (msg=0x20A) ; (SB_HORZ=0, SB_VERT=1) or (WM_MOUSEHWHEEL=0, WM_MOUSEWHEEL=1)	;changed by SundayProgrammer - for WM_MOUSEWHEEL and WM_MOUSEHWHEEL
   
	if gFlag	;added by SundayProgrammer - for touch gesture scrolling
	{	gAction(xpos, ypos, bar, hwnd)	;added by SundayProgrammer - for touch gesture scrolling
		return	;added by SundayProgrammer - for touch gesture scrolling
	}	;added by SundayProgrammer - for touch gesture scrolling
   
    VarSetCapacity(si, 28, 0)
    NumPut(28, si) ; cbSize
    NumPut(SIF_ALL, si, 4) ; fMask
    if !DllCall("GetScrollInfo", "uint", hwnd, "int", bar, "uint", &si)
        return
   
    VarSetCapacity(rect, 16)
    DllCall("GetClientRect", "uint", hwnd, "uint", &rect)
   
    new_pos := NumGet(si, 20) ; nPos (saw "25" in another version, which exhibited a bug in my testing, whereas "20" (this version) worked fine so far)
   
    if msg=0x20A	;added by SundayProgrammer - for WM_MOUSEWHEEL
        wParam := wParam>0x780000	;added by SundayProgrammer - for WM_MOUSEWHEEL
    else if msg=0x20E	;added by SundayProgrammer - for WM_MOUSEHWHEEL
        wParam := wParam=0x780000	;added by SundayProgrammer - for WM_MOUSEHWHEEL
    action := wParam & 0xFFFF
    if action = 0 ; SB_LINEUP
        new_pos -= SCROLL_STEP
    else if action = 1 ; SB_LINEDOWN
        new_pos += SCROLL_STEP
    else if action = 2 ; SB_PAGEUP
        new_pos -= NumGet(rect, 12, "int") - SCROLL_STEP
    else if action = 3 ; SB_PAGEDOWN
        new_pos += NumGet(rect, 12, "int") - SCROLL_STEP
    else if (action = 5 || action = 4) ; SB_THUMBTRACK || SB_THUMBPOSITION
        new_pos := wParam>>16
    else if action = 6 ; SB_TOP
        new_pos := NumGet(si, 8, "int") ; nMin
    else if action = 7 ; SB_BOTTOM
        new_pos := NumGet(si, 12, "int") ; nMax
    else
        return
   
    min := NumGet(si, 8, "int") ; nMin
    max := NumGet(si, 12, "int") - NumGet(si, 16) ; nMax-nPage
    new_pos := new_pos > max ? max : new_pos
    new_pos := new_pos < min ? min : new_pos
   
    old_pos := NumGet(si, 20, "int") ; nPos (saw "25" in another version, which exhibited a bug in my testing, whereas "20" (this version) worked fine so far)
   
    x := y := 0
    if bar = 0 ; SB_HORZ
        x := old_pos-new_pos
    else
        y := old_pos-new_pos
    ; Scroll contents of window and invalidate uncovered area.
    DllCall("ScrollWindow", "uint", hwnd, "int", x, "int", y, "uint", 0, "uint", 0)
   
    ; Update scroll bar.
    NumPut(new_pos, si, 20, "int") ; nPos (saw "25" in another version, which exhibited a bug in my testing, whereas "20" (this version) worked fine so far)
    DllCall("SetScrollInfo", "uint", hwnd, "int", bar, "uint", &si, "int", 1)

	z := bar ? "y" : "x", %z%pos := new_pos	;added by SundayProgrammer - for touch gesture scrolling
}
gAction(byref xpos, byref ypos, bar, hwnd) {	;written by SundayProgrammer - for touch gesture scrolling
	VarSetCapacity(si, 28, 0), NumPut(28, si), NumPut(0x17, si, 4), DllCall("GetScrollInfo", "uint", hwnd, "int", bar, "uint", &si), aPos := NumGet(si, 20, "int"), z := bar ? "y" : "x"
	if not (%z%pos = aPos)
		x := y := 0, %z% := %z%pos - aPos, DllCall("ScrollWindow", "uint", hwnd, "int", x, "int", y, "uint", 0, "uint", 0), %z%pos := aPos
}
gHandler(wParam, lParam, msg, hwnd) {	;written by SundayProgrammer - for touch gesture scrolling
	global gFlag
	gFlag := true
	settimer, Reset_gFlag, -100
	return
	Reset_gFlag:
		gFlag := false
		return
}

ObjRegisterActive(Object, CLSID, Flags:=0) {	;written by Lexikos
    static cookieJar := {}
    if (!CLSID) {
        if (cookie := cookieJar.Remove(Object)) != ""
            DllCall("oleaut32\RevokeActiveObject", "uint", cookie, "ptr", 0)
        return
    }
    if cookieJar[Object]
        throw Exception("Object is already registered", -1)
    VarSetCapacity(_clsid, 16, 0)
    if (hr := DllCall("ole32\CLSIDFromString", "wstr", CLSID, "ptr", &_clsid)) < 0
        throw Exception("Invalid CLSID", -1, CLSID)
    hr := DllCall("oleaut32\RegisterActiveObject"
        , "ptr", &Object, "ptr", &_clsid, "uint", Flags, "uint*", cookie
        , "uint")
    if hr < 0
        throw Exception(format("Error 0x{:x}", hr), -1)
    cookieJar[Object] := cookie
}

;Function Ripped out of CodeQuickTester written by GeekDude https://github.com/G33kDude/CodeQuickTester
ExecScript(Script, Params="", AhkPath="")	;copy from "Execute code stored in a variable (dynamic variable?)" https://www.reddit.com/r/AutoHotkey/comments/ebwora/comment/fbcwvuy/?utm_source=share&utm_medium=web2x&context=3
{
	static Shell := ComObjCreate("WScript.Shell")
	Name := "\\.\pipe\AHK_CQT_" A_TickCount
	Pipe := []
	Loop, 3
	{
		Pipe[A_Index] := DllCall("CreateNamedPipe"
		, "Str", Name
		, "UInt", 2, "UInt", 0
		, "UInt", 255, "UInt", 0
		, "UInt", 0, "UPtr", 0
		, "UPtr", 0, "UPtr")
	}
	if !FileExist(AhkPath)
		throw Exception("AutoHotkey runtime not found: " AhkPath)
	if (A_IsCompiled && AhkPath == A_ScriptFullPath)
		AhkPath .= " /E"
	if FileExist(Name)
	{
		Exec := Shell.Exec(AhkPath " /CP65001 " Name " " Params)
		DllCall("ConnectNamedPipe", "UPtr", Pipe[2], "UPtr", 0)
		DllCall("ConnectNamedPipe", "UPtr", Pipe[3], "UPtr", 0)
		FileOpen(Pipe[3], "h", "UTF-8").Write(Script)
	}
	else ; Running under WINE with improperly implemented pipes
	{
		FileOpen(Name := "AHK_CQT_TMP.ahk", "w").Write(Script)
		Exec := Shell.Exec(AhkPath " /CP65001 " Name " " Params)
	}
	Loop, 3
		DllCall("CloseHandle", "UPtr", Pipe[A_Index])
	return Exec
}
that's it for now.
Last edited by SundayProgrammer on 17 Nov 2021, 19:15, edited 1 time in total.
gya
Posts: 25
Joined: 04 Nov 2021, 01:22

Re: An Alternative Menu (A Quick Access To Various Features)

12 Nov 2021, 00:50

@SundayProgrammer
Thank you for the changes to your script which I understand better but not all.
I appreciate that it is very accessible with the mouse because with the years (76 years) passing by I sometimes find it difficult to remember some things. Goodbye
SundayProgrammer
Posts: 143
Joined: 25 Dec 2020, 12:26

Re: An Alternative Menu (A Quick Access To Various Features)

13 Nov 2021, 07:38

what's new in this version:

a rightclick feature.

there could have time when a particular menu button is going to be used repeatedly, and hence it might be useful if it can become a standalone spot ("widget") on screen to make the operation more convenient. that's why i made a menu button to toggle such thing in the previous example. however, making toggles for every button (or many buttons) is apparently not a smart way to go. so, i arranged the rightclick to take up the job, as it's much more simple. you just need to rightclick the button to create an extra spot.

and a rightclick on the spot can dismiss it on the other hand.

the mspot can also be dismissed by rightclick, and you can bring it back by a long press.

one thing to note though, these extra spots (may also be referred as "shapes" (as there are totally 15 different shapes for you to choose from)) may all stack up at the same place if you don't move them elsewhere. so, once an extra spot is created, move it somewhere could be the first thing you want to do.

here is the updated script.

Code: Select all

pool()	;init variables
menu.Spot()	;show mSpot
Return
EditMenu:
	Run, Notepad MenuText.txt
	Return
demo1:
	msgbox, this is demo1
	return
demo2:
	msgbox, this is demo2 this is demo2
	return
demo3(){
	msgbox, this is demo3 this is demo3 this is demo3
}
demo4(p){
	msgbox, % p
}
demo5(p1, p2){
	msgbox, % p1 "`n`n=========================`n`n" p2
}

$RButton::keyRButton()
$RButton up::Return
#IfWinActive, Select A Shape Please
	Space::
	Enter::
	`::menu.Pick()
#If

pool(param = "") {	;written by SundayProgrammer
	static
	static clsid1 := "{A3C04B39-0465-4460-8CA0-7BFFF481FF98}", s1l := "a := ComObjActive(""" clsid1 """)`n"
	static dummy1 := "i am param the first"
	static dummy2 := "i am param the second"
If StrLen(param)
	If IsGetSet(param, action, varname, value)
	{	If (action = "Set:")
			%varname% := value
		v := %varname%
		Return v
	}Else{}
Else{
	ObjRegisterActive(agent, clsid1)
	IfExist, MenuText.txt
		FileRead, _menu_, MenuText.txt
	Else{
_menu_=
(
= = = Top = = =
^#{Left}	Switch Desktop	(Send)
^#{Right}	Switch Desktop	(Send)
#{Tab}	Windows Switcher	(Send)
[:\s- Microsoft.+?Edge:] m{Enter}	MyActivity.Google	(Send)
[:\s- Microsoft.+?Edge:] +^u	Rald Toggle	(Send)
^f	Find	(Send)
[:\s- Microsoft.+?Edge:] ^r	Reload Web Page	(Send)
^w	Close Tab	(Send)
[:\s- YouTube:] j	10s Backward	(Send)
[:\s- Microsoft.+?Edge:] !{Left}	Previous Web Page	(Send)
[:\s- Microsoft.+?Edge:] !{Right}	Next Web Page	(Send)
{Enter}	Enter	(Send)
[:\s- YouTube:] f	Fullscreen Toggle	(Send)
{Space}	Space	(Send)
{Del}	Delete	(Send)
{Esc}	Escape	(Send)
[:\s- Sublime Text:] ^g	Go To Line	(Send)
[:\s- Sublime Text:] !d	Duplicate	(Send)
^z	Undo	(Send)
^x	Cut	(Send)
^c	Copy	(Send)
^v	Paste	(Send)
^s	Save File	(Send)
^a	Select All	(Send)
- - - - - - - - - - - - - - - -
Close Menu	{menu.Gui_OnEscape}	Return To mSpot	(Function)
Reload Menu	{menu.Reload}	Reflect The Latest Content	(Function)
Edit Menu	{EditMenu}	(Gosub)
Demo One	{demo1}	hello world	(Gosub)
Demo Two	{demo2}	foobar	(Gosub)
Demo Three	{demo3}	blablabla	(Function)
Demo Four	{demo4}	bla bla	(Func+Param)	dummy1
Demo Five	{demo5}	bla bla bla	(Func+Param)	dummy1,dummy2
Script I	(Script)	msgbox `% "remote: " a.call("var","dummy1@pool")
Script II	(Script)	msgbox `% "remote: " a.call("var","dummy2@pool")
Script III	(Script)	a.call("var","Set:dummy3@pool",a_tickcount)|| ||msgbox `% "remote: " a.call("var","dummy3@pool")
Verify	{demo4}	(Func+Param)	dummy3
Script IV	(Script)	{Apart}
[:Text Filter:RICHEDIT50W1:] Script V	(Script)	{fghij}
Script VI	(Script)	{menuDemo}
= = = Bottom = = =
;
{Script Apart}
a.call("var","Set:dummy3@pool",a_tickcount "(a)")
msgbox `% "remote: " a.call("var","dummy3@pool")
{/Script}

{Script fghij}
a.call("demo5", "p1p", "p2p")
If InStr(a.call("var","dummy3@pool"), "(a)")
	msgbox found "(a)" in dummy3
{/Script}
)
FileAppend, %_menu_%, MenuText.txt, UTF-8
		}
	IfNotExist, menuDemo.ahk
FileAppend, msgbox im an island, menuDemo.ahk, UTF-8
	}
}

IsGetSet(Param, ByRef a, ByRef v, ByRef value) {	;written by SundayProgrammer
	a := SubStr(Param, 1, 4)
	If (a = "Set:") or (a = "Get:")
	{	v := Trim((p := InStr(Param, "=")) ? (SubStr(Param, 5, p - 5), value := SubStr(Param, p + 1)) : SubStr(Param, 5))
		Return True
	}Return False
}
var(name, value = "") {	;written by SundayProgrammer
	global
	local p, a, v, m1, m2, m3, segc
	If p := InStr(name, "@")
	{	a := SubStr(name, 1, 4), v := Trim(SubStr(name, 1, p - 1)), name := Trim(SubStr(name, p + 1))
		If (a = "Set:") or (a = "Get:")
			v := Trim(SubStr(v, 5))
		Else a := StrLen(value) ? "Set:" : "Get:"
		If StrLen(v) and StrLen(name)
		{	v := a v, v .= (a = "Set:") ? "=" value : ""
			If InStr(name, ".")
			{	Loop, Parse, name, .
					m%A_Index% := A_LoopField, segc := A_Index
				If segc = 2
					v := %m1%[m2](v)
				Else If segc = 3
					v := %m1%[m2][m3](v)
				Else v = Error: Segments More Than Allowed.
			}Else v := %name%(v)
		}Else v = Error: Invalid Parameter "%v%@%name%"
	}Else
	{	a := SubStr(name, 1, 4)
		If (a = "Set:") or (a = "Get:")
			name := Trim(SubStr(name, 5))
		If InStr(name, ".")
		{	Loop, Parse, name, .
				m%A_Index% := A_LoopField, segc := A_Index
			If segc = 2
			{	If (a = "Set:") or StrLen(value)
					%m1%[m2] := value
				v := %m1%[m2]
			}Else If segc = 3
			{	If (a = "Set:") or StrLen(value)
					%m1%[m2][m3] := value
				v := %m1%[m2][m3]
			}Else v = Error: Segments More Than Allowed.
		}Else
		{	If (a = "Set:") or StrLen(value)
				%name% := value
			v := %name%
		}
	}
	Return v
}

Class agent {	;written by SundayProgrammer who got the idea from this post https://www.autohotkey.com/boards/viewtopic.php?p=200597#p200597
	Call(name, p*) {	;allows you to call any function in this script
		If InStr(name, ".")
		{	Loop, Parse, name, .
				m%A_Index% := A_LoopField, segc := A_Index
			If segc = 2
				Return %m1%[m2](p*)
			Else If segc = 3
				Return %m1%[m2][m3](p*)
			Else Return "Error: Segments More Than Allowed."
		}Return %name%(p*)
	}
}

#IfWinActive ahk_group SpotMenu
	WheelUp::
	WheelDown::
	+WheelUp::
	+WheelDown::OnScroll(InStr(A_ThisHotkey, "Down") ? 1 : 0, 0, GetKeyState("Shift") ? 0x114 : 0x115, WinExist())
#If

WM_DISPLAYCHANGE() {
	WinGetPos, x, y,,, mSpot
	If (x > A_ScreenWidth - 50)
		x := A_ScreenWidth - 100
	If (y > A_ScreenHeight - 125)
		y := A_ScreenHeight - 175
	Gui, mSpot:Show, x%x% y%y% NoActivate
}

WM_ACTIVATE() {
	IfWinActive, mSpot
		WinSet, TransColor, White, mSpot
	Else WinSet, TransColor, White 64, mSpot
}

Class menu {	;written by SundayProgrammer
Spot() {
	Gui, mSpot:New
	Gui, +LastFound -Caption +AlwaysOnTop +ToolWindow
	Gui, Color, White
	Gui, Margin, 0, 0
	Gui, Font, s150, Consolas
	Gui, Add, Text, x0 y0 cFFB10F BackgroundTrans, ●
	OnSuch := this.HitEvent.Bind(this)
	GuiControl +g, Static1, % OnSuch
	WinSet, TransColor, White 64
	x := A_ScreenWidth - 100, y := A_ScreenHeight - 530
	Gui, Show, x%x% y%y% NoActivate, mSpot
	this.HitEvent() ; Init sPos
	OnMessage(0x7E, "WM_DISPLAYCHANGE")
	OnMessage(0x06, "WM_ACTIVATE")
}
HitEvent(hwnd := "") {
	static sPos
	If not StrLen(sPos)
	{	WinGetPos, x, y,,, mSpot
		sPos := x "," y
	}If not StrLen(hwnd)
		Return sPos
	SendMessage, 0xA1, 2,,, A ; WM_NCLBUTTONDOWN
	GuiControlGet, wHwnd, Hwnd, %hwnd%
	WinGetPos, x, y,,, ahk_id %wHwnd%
	If x "," y = sPos
	{	Gui, mSpot:Hide
		this.SpotMenu(var("_menu_@pool"))
	}Else
	{	sPos := x "," y
		SetTimer, Deactivate, -500
	}Return
	Deactivate:
		Gui, mSpot:Hide
		Gui, mSpot:Show, NoActivate
		WinSet, TransColor, White 64, mSpot
		Return
}
SpotMenu(t) {
	global TT
	WinWaitNotActive, mSpot
	WinGetActiveTitle, sWin
	ControlGetFocus, sCtl, A
	Loop, Parse, t, `n
		If SubStr(Trim(A_LoopField), 1, 1) not = ";"
		If RegExMatch(A_LoopField, "i)^\s*{\s*Script\s+[^}]+?\s*}")
			Break
		Else If RegExMatch(A_LoopField, "[^\t]+(?=\t)", m)
			w%A_Index% := this.GetButtonWidth(Trim(RegExReplace(m, "\s*\[([^\]]+)]")), "30 bold", "Consolas")
	OnMessage(0x115, "OnScroll") ; WM_VSCROLL
	OnMessage(0x114, "OnScroll") ; WM_HSCROLL
	OnMessage(0x20A, "OnScroll") ; WM_MOUSEWHEEL
	OnMessage(0x20E, "OnScroll") ; WM_MOUSEHWHEEL
	OnMessage(0x119, "gHandler") ; WM_GESTURE
	Gui, menu:New, hwndHmenu +Labelmenu.Gui_On
	Gui, +Resize +0x300000	; WS_VSCROLL | WS_HSCROLL
	TT := New GuiControlTips(Hmenu), TT.SetDelayTimes(1000, 15000, -1)
	Gui, Font, s30 bold, Consolas
	ci := nl := 1
	Loop, Parse, t, `n
		If SubStr(Trim(A_LoopField), 1, 1) not = ";"
		If RegExMatch(A_LoopField, "[^\t]+(?=\t)", m)
		{	Passed := False
			If RegExMatch(m, "\s*\[([^\]]+)]", mm)
			If RegExMatch(Trim(mm1), ":([^:]+(:[^:]+)?):", mm)
			If pos := InStr(mm1, ":")
				If not RegExMatch(sWin, Trim(SubStr(mm1, 1, pos - 1))) or not RegExMatch(sCtl, Trim(SubStr(mm1, pos + 1)))
					Continue
				Else Passed := True
			Else If not RegExMatch(sWin, Trim(mm1))
				Continue
			Else Passed := True
			If Passed
				m := Trim(RegExReplace(m, "\s*\[([^\]]+)]"))
			If not nl
				nl := (gx + gw + 25 + w%A_Index%) > A_ScreenWidth - 40
			p := nl ? "" : "+", y := nl ? "y+28" : ""
			Gui, Add, Button, HwndhBtn%ci% x%p%25 %y% h57, % (m, pi := ci, ci++)
			OnSuch := this.MenuClick.Bind(this)
			GuiControl +g, Button%pi%, % OnSuch
			GuiControlGet, g, Pos, Button%pi%
			TT.Attach(hBtn%pi%, RegExReplace(RegExReplace(SubStr(A_LoopField, 0) = "`r" ? SubStr(A_LoopField, 1, -1) : A_LoopField, "[^\t]+\t",,, 1), "\t", "`n"))
			nl := (gx + gw) > (A_ScreenWidth - 116)
		}Else If RegExMatch(A_LoopField, "i)^\s*{\s*Script\s+[^}]+?\s*}")
			Break
		Else
		{	Gui, Add, Text, x25 cWhite, % SubStr(A_LoopField, 0) = "`r" ? SubStr(A_LoopField, 1, -1) : A_LoopField
			nl := True
		}
	Gui, Color, Black
	TT.SetTitle("Descriptions", LoadPicture("shell32.dll", "Icon222", ImageType))
	SetCtrlFont(TT.HTIP, "s20", "Arial New")
	Gui, +LastFound
	WinSet, Transparent, 180
	Gui, Show,, Touch Friendly Menu
	GroupAdd, SpotMenu, % "ahk_id " . WinExist()
	this.t := t
}
Gui_OnSize() {
	UpdateScrollBars(A_Gui, A_GuiWidth, A_GuiHeight)
}
Gui_OnEscape() {
	menu.OnClose()
	Gui, mSpot:Show, NoActivate
	WinSet, TransColor, White 64, mSpot
}
Gui_OnClose() {
	ObjRegisterActive(agent, "")
	ExitApp
}
OnClose() {
	OnMessage(0x115, "") ; WM_VSCROLL
	OnMessage(0x114, "") ; WM_HSCROLL
	OnMessage(0x20A, "") ; WM_MOUSEWHEEL
	OnMessage(0x20E, "") ; WM_MOUSEHWHEEL
	OnMessage(0x119, "") ; WM_GESTURE
	Gui, menu:Destroy
}
GetButtonWidth(t, s, f) {
	Gui, New
	Gui, Font, s%s%, % f
	Gui, Add, Button,, % t
	GuiControlGet, g, Pos, Button1
	Gui, Destroy
	Return gW
}
MenuClick(h) {
	global TT
	theText := A_GuiControl
	this.OnClose()
	WinWaitNotActive, Touch Friendly Menu
	Sleep, 500
	this.do(theText, TT.GetText(h))
	Gui, mSpot:Show, NoActivate
	WinSet, TransColor, White 64, mSpot
}
do(theText, t) {
	If RegExMatch(t, "{\K[^}]+(?=})", m)
		If not InStr(t, "(Send)")
			theText := m
	If InStr(t, "(Send)")
		Send, %theText%
	Else If InStr(t, "(Gosub)")
		Gosub, %theText%
	Else If InStr(t, "(Function)")
		If pos := InStr(theText, ".")
			className := SubStr(theText, 1, pos - 1), method := SubStr(theText, pos + 1), %className%[method]()
		Else %theText%()
	Else If InStr(t, "(Func+Param)")
	{	RegExMatch(t, "[^\n]+$", m)
		If InStr(m, ",")
		{	Loop, Parse, m, `,
				p%A_Index% := var(A_LoopField "@pool")
			StrReplace(m, ",",, c)
			If c = 1
				%theText%(p1, p2)
			Else If c = 2
				%theText%(p1, p2, p3)
			Else If c = 3
				%theText%(p1, p2, p3, p4)
			Else If c = 4
				%theText%(p1, p2, p3, p4, p5)
			Else If c = 5
				%theText%(p1, p2, p3, p4, p5, p6)
			Else If c = 6
				%theText%(p1, p2, p3, p4, p5, p6, p7)
			Else If c = 7
				%theText%(p1, p2, p3, p4, p5, p6, p7, p8)
			Else If c = 8
				%theText%(p1, p2, p3, p4, p5, p6, p7, p8, p9)
			Else If c = 9
				%theText%(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10)
			Else{}	;more than 10 parameters is not supported
		}Else %theText%(var(m "@pool"))
	}Else If InStr(t, "(Script)")
		If RegExMatch(t, "{\K[^}]+(?=})", m)
			If RegExMatch(this.t, "is){\s*Script\s+" m "\s*}(.+?){\s*/\s*Script\s*}", mm)
				ExecScript(var("s1l@pool") this.HandleInclude(mm1),, A_AhkPath)
			Else IfExist, % m := (SubStr(Trim(m), -3) = ".ahk" ? m : Trim(m) ".ahk")
			{	FileRead, mm1, %m%
				ExecScript(var("s1l@pool") mm1,, A_AhkPath)
			}Else{}
		Else RegExMatch(t, "\(Script\)[\n\t]+\K.*$", m), m := StrReplace(m, "|| ||", "`n"), ExecScript(var("s1l@pool") this.HandleInclude(m),, A_AhkPath)
}
HandleInclude(s) {
	If RegExMatch(s, "is)#\s*Include\s+[^#]+#")
	{	RegExReplace(s, "is)#\s*Include\s+[^#]+#",, n), pos := 1, m := ""
		Loop, %n%
			pos := RegExMatch(s, "is)#\s*Include\s+\K[^#]+(?=#)", m, pos + StrLen(m)), RegExMatch(this.t, "is){\s*Script\s+" Trim(m) "\s*}(.+?){\s*/\s*Script\s*}", mm), s := RegExReplace(s, "is)#\s*Include\s+" Trim(m) "\s*#", RegExMatch(mm1, "is)#\s*Include\s+[^#]+#") ? this.HandleInclude(mm1) : mm1)
	}
	Return s
}
ExtraSpot() {
	btn := "B" A_TickCount
	MouseGetPos,,, mow, moc
	WinGetTitle, mow, ahk_id %mow%
	ControlGetText, m, %moc%, %mow%
	MouseGetPos,,,, h, 2
	t := agent.Call("TT.GetText", h)
	menu.Gui_OnEscape()
	Gui, shape:New, +ToolWindow
	Gui, Font, s30, Consolas
	Gui, Add, ListBox, r15, ●|◆|◼|◉|◈|▣|◙|▲|▼|◀|▶|◢|◥|◣|◤
	OnSuch := this.Pick.Bind(this)
	GuiControl +g, ListBox1, % OnSuch
	Gui, Show,, Select A Shape Please
	var("Set:Shape@pool", False), this.CurItem := "●"
	While, not var("Shape@pool") and WinExist("Select A Shape Please")
		Sleep, 100
	If not var("Shape@pool")
		var("Set:Shape@pool", this.CurItem)
	Gui, shape:Destroy
	Gui, %btn%:New, hwndHspot
	Gui, +LastFound -Caption +AlwaysOnTop +ToolWindow
	Gui, Color, White
	Gui, Margin, 0, 0
	Gui, Font, s120, Consolas
	Gui, Add, Text, x0 y0 cB2D6F3 BackgroundTrans, % var("Shape@pool")
	OnSuch := this.xHit.Bind(this)
	GuiControl +g, Static1, % OnSuch
	Gui, Add, Text, Hidden, %m%
	Gui, Add, Text, Hidden, %t%
	WinSet, TransColor, White 64
	x := A_ScreenWidth - 100
	Gui, Show, x%x% NoActivate, %btn%spot
	this.wt := btn "spot"
	ControlGet, sid, hwnd,, Static1, %btn%spot
	this.xHit(sid + 0) ; Init sPos
}
xHit(hwnd := "") {
	static sPos := []
	If not StrLen(sPos[hwnd])
	{	WinGetPos, x, y,,, % this.wt
		sPos[hwnd] := x "," y
		Return
	}SendMessage, 0xA1, 2,,, A ; WM_NCLBUTTONDOWN
	GuiControlGet, wHwnd, Hwnd, %hwnd%
	WinGetPos, x, y,,, ahk_id %wHwnd%
	WinGetActiveTitle, wt
	var("Set:wName@pool", wName := SubStr(wt, 1, -4))
	If x "," y = sPos[hwnd]
	{	GuiControlGet, theText,, Static2
		GuiControlGet, t,, Static3
		Gui, %wName%:Hide
		menu.do(theText, t)
	}Else sPos[hwnd] := x "," y
	SetTimer, DeactivateIt, -500
	Return
	DeactivateIt:
		wName := var("wName@pool")
		Gui, %wName%:Hide
		Gui, %wName%:Show, NoActivate
		Return
}
Pick(hwnd := "") {
	GuiControlGet, Choice,, ListBox1
	If StrLen(Choice)
	{	this.CurItem := Choice
		If not InStr(A_PriorKey, "LButton")
			Return
	}Else If A_ThisHotkey in Space,Enter,``
		Choice := this.CurItem
	var("Set:Shape@pool", Choice)
}
Reload() {
	FileRead, _menu_, MenuText.txt
	var("Set:_menu_@pool", _menu_)
	SetTimer, ReloadMenu, -500
	Return
	ReloadMenu:
		Gui, mSpot:Hide
		menu.SpotMenu(var("_menu_@pool"))
		Return
}
}	;end of class

keyRButton() {
	IfWinNotExist, mSpot
	{	beg := A_TickCount
		While, GetKeyState("RButton", "P")
			If A_TickCount - beg > 1000
			{	IfWinNotExist, Touch Friendly Menu
				{	menu.Spot(), beg := False
					SoundBeep
				}Break
			}
		If not beg
			Return
	}
	MouseGetPos,,, mow, moc
	WinGetTitle, mow, ahk_id %mow%
	If SubStr(mow, -3) = "Spot" and moc = "Static1"
		WinClose, %mow%
	Else If mow = Touch Friendly Menu
		If SubStr(moc, 1, 6) = "Button"
			menu.ExtraSpot()
		Else{}
	Else Send, {RButton}
}

SetCtrlFont(CtrlHwnd, FontOptions := "", FontName := "") {	;written by iPhilip
   static WM_SETFONT := 0x0030, WM_GETFONT := 0x0031
   DefaultGui := A_DefaultGui
   Gui, New
   Gui, Font, % FontOptions, % FontName
   Gui, Add, Text, hwndhText, Text
   hFont := DllCall("SendMessage", "Ptr", hText, "UInt", WM_GETFONT, "Ptr", 0, "Ptr", 0, "Ptr")
   Gui, Destroy
   Gui, %DefaultGui%:Default
   DllCall("SendMessage", "Ptr", CtrlHwnd, "UInt", WM_SETFONT, "Ptr", hFont, "Ptr", true)
   Return hFont
}

; ======================================================================================================================
; Namespace:      GuiControlTips
; AHK version:    AHK 1.1.14.03
; Function:       Helper object to simply assign ToolTips for GUI controls
; Tested on:      Win 7 (x64)
; Change history:
;                 1.1.00.01/2020-06-03/just me - fixed missing Static WS_EX_TOPMOST
;                 1.1.00.00/2014-03-06/just me - Added SetDelayTimes()
;                 1.0.01.00/2012-07-29/just me
; ======================================================================================================================
; CLASS GuiControlTips
;
; The class provides four public methods to register (Attach), unregister (Detach), update (Update), and
; disable/enable (Suspend) common ToolTips for GUI controls.
;
; Usage:
; To assign ToolTips to GUI controls you have to create a new instance of GuiControlTips per GUI with
;     MyToolTipObject := New GuiControlTips(HGUI)
; passing the HWND of the GUI.
;
; After this you may assign ToolTips to your GUI controls by calling
;     MyToolTipObject.Attach(HCTRL, "ToolTip text")
; passing the HWND of the control and the ToolTip's text. Pass True/1 for the optional third parameter if you
; want the ToolTip to be shown centered below the control.
;
; To remove a ToolTip call
;     MyToolTipObject.Detach(HCTRL)
; passing the HWND of the control.
;
; To update the ToolTip's text call
;     MyToolTipObject.Update(HCTRL, "New text!")
; passing the HWND of the control and the new text.
;
; To deactivate the ToolTips call
;     MyToolTipObject.Suspend(True),
; to activate them again afterwards call
;     MyToolTipObject.Suspend(False).
;
; To adjust the ToolTips delay times call
;     MyToolTipObject.SetDelayTimesd(),
; specifying the delay times in milliseconds.
;
; That's all you can / have to do!
; ======================================================================================================================
Class GuiControlTips {	;written by @"just me"
   ; ===================================================================================================================
   ; INSTANCE variables
   ; ===================================================================================================================
   HTIP := 0
   HGUI := 0
   CTRL := {}
   ; ===================================================================================================================
   ; CONSTRUCTOR           __New()
   ; ===================================================================================================================
   __New(HGUI) {
      Static CLASS_TOOLTIP      := "tooltips_class32"
      Static CW_USEDEFAULT      := 0x80000000
      Static TTM_SETMAXTIPWIDTH := 0x0418
      Static TTM_SETMARGIN      := 0x041A
      Static WS_EX_TOPMOST      := 0x00000008
      Static WS_STYLES          := 0x80000002 ; WS_POPUP | TTS_NOPREFIX
      ; Create a Tooltip control ...
      HTIP := DllCall("User32.dll\CreateWindowEx", "UInt", WS_EX_TOPMOST, "Str", CLASS_TOOLTIP, "Ptr", 0
                    , "UInt", WS_STYLES
                    , "Int", CW_USEDEFAULT, "Int", CW_USEDEFAULT, "Int", CW_USEDEFAULT, "Int", CW_USEDEFAULT
                    , "Ptr", HGUI, "Ptr", 0, "Ptr", 0, "Ptr", 0, "Ptr")
      If ((ErrorLevel) || !(HTIP))
         Return False
      ; ... prepare it to display multiple lines if required
      DllCall("User32.dll\SendMessage", "Ptr", HTIP, "Int", TTM_SETMAXTIPWIDTH, "Ptr", 0, "Ptr", A_ScreenWidth*96//A_ScreenDPI)	;touched by SundayProgrammer who took it from iPhilip response for AddTooltip v2.0
      ; ... set the instance variables
      This.HTIP := HTIP
      This.HGUI := HGUI
      If (DllCall("Kernel32.dll\GetVersion", "UInt") & 0xFF) < 6 ; to avoid some XP issues ...
         This.Attach(HGUI, "") ; ... register the GUI with an empty tiptext
   }
   ; ===================================================================================================================
   ; DESTRUCTOR            __Delete()
   ; ===================================================================================================================
   __Delete() {
      If (This.HTIP) {
         DllCall("User32.dll\DestroyWindow", "Ptr", This.HTIP)
      }
   }
   ; ===================================================================================================================
   ; PRIVATE METHOD        SetToolInfo - Create and fill a TOOLINFO structure
   ; ===================================================================================================================
   SetToolInfo(ByRef TOOLINFO, HCTRL, TipTextAddr, CenterTip = 0) {
      Static TTF_IDISHWND  := 0x0001
      Static TTF_CENTERTIP := 0x0002
      Static TTF_SUBCLASS  := 0x0010
      Static OffsetSize  := 0
      Static OffsetFlags := 4
      Static OffsetHwnd  := 8
      Static OffsetID    := OffsetHwnd + A_PtrSize
      Static OffsetRect  := OffsetID + A_PtrSize
      Static OffsetInst  := OffsetRect + 16
      Static OffsetText  := OffsetInst + A_PtrSize
      Static StructSize  := (4 * 6) + (A_PtrSize * 6)
      Flags := TTF_IDISHWND | TTF_SUBCLASS
      If (CenterTip)
         Flags |= TTF_CENTERTIP
      VarSetCapacity(TOOLINFO, StructSize, 0)
      NumPut(StructSize, TOOLINFO, OffsetSize, "UInt")
      NumPut(Flags, TOOLINFO, OffsetFlags, "UInt")
      NumPut(This.HGUI, TOOLINFO, OffsetHwnd, "Ptr")
      NumPut(HCTRL, TOOLINFO, OffsetID, "Ptr")
      NumPut(TipTextAddr, TOOLINFO, OffsetText, "Ptr")
      Return True
   }
   ; ===================================================================================================================
   ; PUBLIC METHOD         Attach         -  Assign a ToolTip to a certain control
   ; Parameters:           HWND           -  Control's HWND
   ;                       TipText        -  ToolTip's text
   ;                       Optional:      ------------------------------------------------------------------------------
   ;                       CenterTip      -  Centers the tooltip window below the control
   ;                                         Values:  True/False
   ;                                         Default: False
   ; Return values:        On success: True
   ;                       On failure: False
   ; ===================================================================================================================
   Attach(HCTRL, TipText, CenterTip = False) {
      Static TTM_ADDTOOL  := A_IsUnicode ? 0x0432 : 0x0404 ; TTM_ADDTOOLW : TTM_ADDTOOLA
      If !(This.HTIP) {
         Return False
      }
      If This.CTRL.HasKey(HCTRL)
         Return False
      TOOLINFO := ""
      This.SetToolInfo(TOOLINFO, HCTRL, &TipText, CenterTip)
      If DllCall("User32.dll\SendMessage", "Ptr", This.HTIP, "Int", TTM_ADDTOOL, "Ptr", 0, "Ptr", &TOOLINFO) {
         This.CTRL[HCTRL] := 1
		 This.TTT[HCTRL] := TipText	;added by SundayProgrammer
         Return True
      } Else {
        Return False
      }
   }
   ; ===================================================================================================================
   ; PUBLIC METHOD         Detach         -  Remove the ToolTip for a certain control
   ; Parameters:           HWND           -  Control's HWND
   ; Return values:        On success: True
   ;                       On failure: False
   ; ===================================================================================================================
   Detach(HCTRL) {
      Static TTM_DELTOOL  := A_IsUnicode ? 0x0433 : 0x0405 ; TTM_DELTOOLW : TTM_DELTOOLA
      If !This.CTRL.HasKey(HCTRL)
         Return False
      TOOLINFO := ""
      This.SetToolInfo(TOOLINFO, HCTRL, 0)
      DllCall("User32.dll\SendMessage", "Ptr", This.HTIP, "Int", TTM_DELTOOL, "Ptr", 0, "Ptr", &TOOLINFO)
      This.CTRL.Remove(HCTRL, "")
	  This.TTT.Remove(HCTRL, "")	;added by SundayProgrammer
      Return True
   }
   ; ===================================================================================================================
   ; PUBLIC METHOD         Update         -  Update the ToolTip's text for a certain control
   ; Parameters:           HWND           -  Control's HWND
   ;                       TipText        -  New text                                                      
   ; Return values:        On success: True
   ;                       On failure: False
   ; ===================================================================================================================
   Update(HCTRL, TipText) {
      Static TTM_UPDATETIPTEXT  := A_IsUnicode ? 0x0439 : 0x040C ; TTM_UPDATETIPTEXTW : TTM_UPDATETIPTEXTA
      If !This.CTRL.HasKey(HCTRL)
         Return False
      TOOLINFO := ""
      This.SetToolInfo(TOOLINFO, HCTRL, &TipText)
      DllCall("SendMessage", "Ptr", This.HTIP, "Int", TTM_UPDATETIPTEXT, "Ptr", 0, "Ptr", &TOOLINFO)
	  This.TTT[HCTRL] := TipText	;added by SundayProgrammer
      Return True
   }
   ;added by SundayProgrammer
   GetText(HCTRL) {
      If !This.CTRL.HasKey(HCTRL)
         Return ""
      Return This.TTT[HCTRL]
   }
   ; ===================================================================================================================
   ; PUBLIC METHOD         Suspend        -  Disable/enable the ToolTip control (don't show / show ToolTips)
   ; Parameters:           Mode           -  True/False (1/0)
   ;                                         Default: True/1
   ; Return values:        On success: True
   ;                       On failure: False
   ; Remarks:              ToolTips are enabled automatically on creation.
   ; ===================================================================================================================
   Suspend(Mode = True) {
      Static TTM_ACTIVATE := 0x0401
      If !(This.HTIP)
         Return False
      DllCall("SendMessage", "Ptr", This.HTIP, "Int", TTM_ACTIVATE, "Ptr", !Mode, "Ptr", 0)
      Return True
   }
   ; ===================================================================================================================
   ; PUBLIC METHOD         SetDelayTimes  -  Set the initial, pop-up, and reshow durations for a tooltip control.
   ; Parameters:           Init           -  Amount of time, in milliseconds, a pointer must remain stationary within
   ;                                         a tool's bounding rectangle before the tooltip window appears.
   ;                                         Default: -1 (system default time)
   ;                       PopUp          -  Amount of time, in milliseconds, a tooltip window remains visible if the
   ;                                         pointer is stationary within a tool's bounding rectangle.
   ;                                         Default: -1 (system default time)
   ;                       ReShow         -  Amount of time, in milliseconds, it takes for subsequent tooltip windows
   ;                                         to appear as the pointer moves from one tool to another.
   ;                                         Default: -1 (system default time)
   ; Return values:        On success: True
   ;                       On failure: False
   ; Remarks:              Times are set per ToolTip control and applied to all added tools.
   ; ===================================================================================================================
   SetDelayTimes(Init = -1, PopUp = -1, ReShow = -1) {
      Static TTM_SETDELAYTIME   := 0x0403
      Static TTDT_RESHOW   := 1
      Static TTDT_AUTOPOP  := 2
      Static TTDT_INITIAL  := 3
      DllCall("SendMessage", "Ptr", This.HTIP, "Int", TTM_SETDELAYTIME, "Ptr", TTDT_INITIAL, "Ptr", Init)
      DllCall("SendMessage", "Ptr", This.HTIP, "Int", TTM_SETDELAYTIME, "Ptr", TTDT_AUTOPOP, "Ptr", PopUp)
      DllCall("SendMessage", "Ptr", This.HTIP, "Int", TTM_SETDELAYTIME, "Ptr", TTDT_RESHOW , "Ptr", ReShow)
   }
   ;added by SundayProgrammer who largely took it from AddTooltip v2.0
   SetTitle(theTitle := "", theIcon := 0) {
      Static TTM_SETTITLE := A_IsUnicode ? 0x421 : 0x420 ; TTM_SETTITLEW : TTM_SETTITLEA
      If StrLen(theTitle) > 99
         theTitle := SubStr(theTitle, 1, 99)
      If theIcon is not Integer
         theIcon := 0
      DllCall("SendMessage", "Ptr", This.HTIP, "Int", TTM_SETTITLE, "Ptr", theIcon, "Ptr", &theTitle)
   }
}

UpdateScrollBars(GuiNum, GuiWidth, GuiHeight) {	;written by lexikos
    static SIF_RANGE=0x1, SIF_PAGE=0x2, SIF_DISABLENOSCROLL=0x8, SB_HORZ=0, SB_VERT=1
   
    Gui, %GuiNum%:Default
    Gui, +LastFound
   
    ; Calculate scrolling area.
    Left := Top := 9999
    Right := Bottom := 0
    WinGet, ControlList, ControlList
    Loop, Parse, ControlList, `n
    {
        GuiControlGet, c, Pos, %A_LoopField%
        if (cX < Left)
            Left := cX
        if (cY < Top)
            Top := cY
        if (cX + cW > Right)
            Right := cX + cW
        if (cY + cH > Bottom)
            Bottom := cY + cH
    }
    Left -= 8
    Top -= 8
    Right += 8
    Bottom += 8
    ScrollWidth := Right-Left
    ScrollHeight := Bottom-Top
   
    ; Initialize SCROLLINFO.
    VarSetCapacity(si, 28, 0)
    NumPut(28, si) ; cbSize
    NumPut(SIF_RANGE | SIF_PAGE, si, 4) ; fMask
   
    ; Update horizontal scroll bar.
    NumPut(ScrollWidth, si, 12) ; nMax
    NumPut(GuiWidth, si, 16) ; nPage
    DllCall("SetScrollInfo", "uint", WinExist(), "uint", SB_HORZ, "uint", &si, "int", 1)
   
    ; Update vertical scroll bar.
;     NumPut(SIF_RANGE | SIF_PAGE | SIF_DISABLENOSCROLL, si, 4) ; fMask
    NumPut(ScrollHeight, si, 12) ; nMax
    NumPut(GuiHeight, si, 16) ; nPage
    DllCall("SetScrollInfo", "uint", WinExist(), "uint", SB_VERT, "uint", &si, "int", 1)
   
    if (Left < 0 && Right < GuiWidth)
        x := Abs(Left) > GuiWidth-Right ? GuiWidth-Right : Abs(Left)
    if (Top < 0 && Bottom < GuiHeight)
        y := Abs(Top) > GuiHeight-Bottom ? GuiHeight-Bottom : Abs(Top)
    if (x || y)
        DllCall("ScrollWindow", "uint", WinExist(), "int", x, "int", y, "uint", 0, "uint", 0)
}

OnScroll(wParam, lParam, msg, hwnd) {	;written by lexikos
    static SIF_ALL=0x17, SCROLL_STEP=85 ;changed by SundayProgrammer from 10 to 85 for a more practical outcome
	static xpos := 0, ypos := 0	;added by SundayProgrammer - for touch gesture scrolling
	global gFlag	;added by SundayProgrammer - for touch gesture scrolling
if DllCall("GetParent", "uint", hwnd)	;added by SundayProgrammer - a quick fix for the scenario when any scrollable control is involved
	return	;added by SundayProgrammer - a quick fix for the scenario when any scrollable control is involved
   
    bar := (msg=0x115) or (msg=0x20A) ; (SB_HORZ=0, SB_VERT=1) or (WM_MOUSEHWHEEL=0, WM_MOUSEWHEEL=1)	;changed by SundayProgrammer - for WM_MOUSEWHEEL and WM_MOUSEHWHEEL
   
	if gFlag	;added by SundayProgrammer - for touch gesture scrolling
	{	gAction(xpos, ypos, bar, hwnd)	;added by SundayProgrammer - for touch gesture scrolling
		return	;added by SundayProgrammer - for touch gesture scrolling
	}	;added by SundayProgrammer - for touch gesture scrolling
   
    VarSetCapacity(si, 28, 0)
    NumPut(28, si) ; cbSize
    NumPut(SIF_ALL, si, 4) ; fMask
    if !DllCall("GetScrollInfo", "uint", hwnd, "int", bar, "uint", &si)
        return
   
    VarSetCapacity(rect, 16)
    DllCall("GetClientRect", "uint", hwnd, "uint", &rect)
   
    new_pos := NumGet(si, 20) ; nPos (saw "25" in another version, which exhibited a bug in my testing, whereas "20" (this version) worked fine so far)
   
    if msg=0x20A	;added by SundayProgrammer - for WM_MOUSEWHEEL
        wParam := wParam>0x780000	;added by SundayProgrammer - for WM_MOUSEWHEEL
    else if msg=0x20E	;added by SundayProgrammer - for WM_MOUSEHWHEEL
        wParam := wParam=0x780000	;added by SundayProgrammer - for WM_MOUSEHWHEEL
    action := wParam & 0xFFFF
    if action = 0 ; SB_LINEUP
        new_pos -= SCROLL_STEP
    else if action = 1 ; SB_LINEDOWN
        new_pos += SCROLL_STEP
    else if action = 2 ; SB_PAGEUP
        new_pos -= NumGet(rect, 12, "int") - SCROLL_STEP
    else if action = 3 ; SB_PAGEDOWN
        new_pos += NumGet(rect, 12, "int") - SCROLL_STEP
    else if (action = 5 || action = 4) ; SB_THUMBTRACK || SB_THUMBPOSITION
        new_pos := wParam>>16
    else if action = 6 ; SB_TOP
        new_pos := NumGet(si, 8, "int") ; nMin
    else if action = 7 ; SB_BOTTOM
        new_pos := NumGet(si, 12, "int") ; nMax
    else
        return
   
    min := NumGet(si, 8, "int") ; nMin
    max := NumGet(si, 12, "int") - NumGet(si, 16) ; nMax-nPage
    new_pos := new_pos > max ? max : new_pos
    new_pos := new_pos < min ? min : new_pos
   
    old_pos := NumGet(si, 20, "int") ; nPos (saw "25" in another version, which exhibited a bug in my testing, whereas "20" (this version) worked fine so far)
   
    x := y := 0
    if bar = 0 ; SB_HORZ
        x := old_pos-new_pos
    else
        y := old_pos-new_pos
    ; Scroll contents of window and invalidate uncovered area.
    DllCall("ScrollWindow", "uint", hwnd, "int", x, "int", y, "uint", 0, "uint", 0)
   
    ; Update scroll bar.
    NumPut(new_pos, si, 20, "int") ; nPos (saw "25" in another version, which exhibited a bug in my testing, whereas "20" (this version) worked fine so far)
    DllCall("SetScrollInfo", "uint", hwnd, "int", bar, "uint", &si, "int", 1)

	z := bar ? "y" : "x", %z%pos := new_pos	;added by SundayProgrammer - for touch gesture scrolling
}
gAction(byref xpos, byref ypos, bar, hwnd) {	;written by SundayProgrammer - for touch gesture scrolling
	VarSetCapacity(si, 28, 0), NumPut(28, si), NumPut(0x17, si, 4), DllCall("GetScrollInfo", "uint", hwnd, "int", bar, "uint", &si), aPos := NumGet(si, 20, "int"), z := bar ? "y" : "x"
	if not (%z%pos = aPos)
		x := y := 0, %z% := %z%pos - aPos, DllCall("ScrollWindow", "uint", hwnd, "int", x, "int", y, "uint", 0, "uint", 0), %z%pos := aPos
}
gHandler(wParam, lParam, msg, hwnd) {	;written by SundayProgrammer - for touch gesture scrolling
	global gFlag
	gFlag := true
	settimer, Reset_gFlag, -100
	return
	Reset_gFlag:
		gFlag := false
		return
}

ObjRegisterActive(Object, CLSID, Flags:=0) {	;written by Lexikos
    static cookieJar := {}
    if (!CLSID) {
        if (cookie := cookieJar.Remove(Object)) != ""
            DllCall("oleaut32\RevokeActiveObject", "uint", cookie, "ptr", 0)
        return
    }
    if cookieJar[Object]
        throw Exception("Object is already registered", -1)
    VarSetCapacity(_clsid, 16, 0)
    if (hr := DllCall("ole32\CLSIDFromString", "wstr", CLSID, "ptr", &_clsid)) < 0
        throw Exception("Invalid CLSID", -1, CLSID)
    hr := DllCall("oleaut32\RegisterActiveObject"
        , "ptr", &Object, "ptr", &_clsid, "uint", Flags, "uint*", cookie
        , "uint")
    if hr < 0
        throw Exception(format("Error 0x{:x}", hr), -1)
    cookieJar[Object] := cookie
}

;Function Ripped out of CodeQuickTester written by GeekDude https://github.com/G33kDude/CodeQuickTester
ExecScript(Script, Params="", AhkPath="")	;copy from "Execute code stored in a variable (dynamic variable?)" https://www.reddit.com/r/AutoHotkey/comments/ebwora/comment/fbcwvuy/?utm_source=share&utm_medium=web2x&context=3
{
	static Shell := ComObjCreate("WScript.Shell")
	Name := "\\.\pipe\AHK_CQT_" A_TickCount
	Pipe := []
	Loop, 3
	{
		Pipe[A_Index] := DllCall("CreateNamedPipe"
		, "Str", Name
		, "UInt", 2, "UInt", 0
		, "UInt", 255, "UInt", 0
		, "UInt", 0, "UPtr", 0
		, "UPtr", 0, "UPtr")
	}
	if !FileExist(AhkPath)
		throw Exception("AutoHotkey runtime not found: " AhkPath)
	if (A_IsCompiled && AhkPath == A_ScriptFullPath)
		AhkPath .= " /E"
	if FileExist(Name)
	{
		Exec := Shell.Exec(AhkPath " /CP65001 " Name " " Params)
		DllCall("ConnectNamedPipe", "UPtr", Pipe[2], "UPtr", 0)
		DllCall("ConnectNamedPipe", "UPtr", Pipe[3], "UPtr", 0)
		FileOpen(Pipe[3], "h", "UTF-8").Write(Script)
	}
	else ; Running under WINE with improperly implemented pipes
	{
		FileOpen(Name := "AHK_CQT_TMP.ahk", "w").Write(Script)
		Exec := Shell.Exec(AhkPath " /CP65001 " Name " " Params)
	}
	Loop, 3
		DllCall("CloseHandle", "UPtr", Pipe[A_Index])
	return Exec
}
Last edited by SundayProgrammer on 17 Nov 2021, 19:17, edited 1 time in total.
SundayProgrammer
Posts: 143
Joined: 25 Dec 2020, 12:26

Re: An Alternative Menu (A Quick Access To Various Features)

13 Nov 2021, 07:42

gya wrote:
12 Nov 2021, 00:50
Thank you for the changes to your script
i don't really know what are you talking about, im afraid.
SundayProgrammer
Posts: 143
Joined: 25 Dec 2020, 12:26

Re: An Alternative Menu (A Quick Access To Various Features)

17 Nov 2021, 19:24

found a bug in the function "handleinclude", and fixed. it was there since the "include" syntax was added. sorry for didn't notice it beforehand. now, both versions of the script are updated.
SundayProgrammer
Posts: 143
Joined: 25 Dec 2020, 12:26

Re: An Alternative Menu (A Quick Access To Various Features)

20 Nov 2021, 05:07

what's new in this version:

tweaked the rightclick feature to achieve group (of "all" extra spots) hide/show.

however, rightclick for any extra spots remain unchange, that is, a rightclick on any individual extra spot is, still, to dismiss it.

the only changes are: (1) rightclick mspot to toggle the group hide/show, (2) long press to dismiss mspot or bring it back.

remark: the words "all" extra spots mentioned above means all of the extra spots under the same group name.

two variables are added in this version, they are menu.spots and menu.color. in which, the former is the group name. the existence of these two variables enabled the possibility of multiple groups and multiple colors.

you may use the menu buttons in the example (menu text) below to edit these two variables.

Code: Select all

Spots Group	(Script)	{Change Spots Group Name}
Spots Color	(Script)	{Change Spots Color}
;
{Script Change Spots Group Name}
	Gui, sgn:New
	Gui, +Delimiter`n
	Gui, Font, s30, Consolas
	Gui, Add, ComboBox, hwndHEdit
	GuiControl,, ComboBox1, % "`n" (hist := a.Call("var", "menu.sgnHistory"))
	ControlSetText,, % a.Call("var", "menu.Spots"), ahk_id %HEdit%
	Gui, Add, Button, h55 ys Default, Go
	Gui, Show,, Future Spots Group Name
	Return
sgnGuiEscape:
sgnGuiClose:
	ExitApp
sgnButtonGo:
	GuiControlGet, GroupName,, Edit1
	If StrLen(GroupName)
	{	If not StrLen(hist)
			hist := GroupName, Added := True
		Else If not RegExMatch(hist, "im`a)^\Q" GroupName "\E$")
			hist := GroupName "`n" hist, Added := True
		Else Added := False
		If Added
			a.Call("var", "Set:menu.sgnHistory", hist)
		a.Call("var", "Set:menu.Spots", GroupName)
		ExitApp
	}Return
{/Script}

{Script Change Spots Color}
	Gui, fsc:New
	Gui, +Delimiter`n
	Gui, Font, s30, Consolas
	Gui, Add, ComboBox, hwndHEdit
	GuiControl,, ComboBox1, % "`n" (hist := a.Call("var", "menu.fscHistory"))
	ControlSetText,, % a.Call("var", "menu.Color"), ahk_id %HEdit%
	Gui, Add, Button, h55 ys Default, Go
	Gui, Show,, Future Spot Color
	Return
fscGuiEscape:
fscGuiClose:
	ExitApp
fscButtonGo:
	GuiControlGet, ColorCode,, Edit1
	If StrLen(ColorCode)
	{	If not StrLen(hist)
			hist := ColorCode, Added := True
		Else If not RegExMatch(hist, "im`a)^\Q" ColorCode "\E$")
			hist := ColorCode "`n" hist, Added := True
		Else Added := False
		If Added
			a.Call("var", "Set:menu.fscHistory", hist)
		a.Call("var", "Set:menu.Color", ColorCode)
		ExitApp
	}Return
{/Script}



here is the updated script.

Code: Select all

pool()	;init variables
menu.Spot()	;show mSpot
Return
EditMenu:
	Run, Notepad MenuText.txt
	Return
demo1:
	msgbox, this is demo1
	return
demo2:
	msgbox, this is demo2 this is demo2
	return
demo3(){
	msgbox, this is demo3 this is demo3 this is demo3
}
demo4(p){
	msgbox, % p
}
demo5(p1, p2){
	msgbox, % p1 "`n`n=========================`n`n" p2
}

$RButton::keyRButton()
$RButton up::Return
#IfWinActive, Select A Shape Please
	Space::
	Enter::
	`::menu.Pick()
#If

pool(param = "") {	;written by SundayProgrammer
	static
	static clsid1 := "{A3C04B39-0465-4460-8CA0-7BFFF481FF98}", s1l := "a := ComObjActive(""" clsid1 """)`n"
	static dummy1 := "i am param the first"
	static dummy2 := "i am param the second"
If StrLen(param)
	If IsGetSet(param, action, varname, value)
	{	If (action = "Set:")
			%varname% := value
		v := %varname%
		Return v
	}Else{}
Else{
	ObjRegisterActive(agent, clsid1)
	IfExist, MenuText.txt
		FileRead, _menu_, MenuText.txt
	Else{
_menu_=
(
= = = Top = = =
^#{Left}	Switch Desktop	(Send)
^#{Right}	Switch Desktop	(Send)
#{Tab}	Windows Switcher	(Send)
[:\s- Microsoft.+?Edge:] m{Enter}	MyActivity.Google	(Send)
[:\s- Microsoft.+?Edge:] +^u	Rald Toggle	(Send)
^f	Find	(Send)
[:\s- Microsoft.+?Edge:] ^r	Reload Web Page	(Send)
^w	Close Tab	(Send)
[:\s- YouTube:] j	10s Backward	(Send)
[:\s- Microsoft.+?Edge:] !{Left}	Previous Web Page	(Send)
[:\s- Microsoft.+?Edge:] !{Right}	Next Web Page	(Send)
{Enter}	Enter	(Send)
[:\s- YouTube:] f	Fullscreen Toggle	(Send)
{Space}	Space	(Send)
{Del}	Delete	(Send)
{Esc}	Escape	(Send)
[:\s- Sublime Text:] ^g	Go To Line	(Send)
[:\s- Sublime Text:] !d	Duplicate	(Send)
^z	Undo	(Send)
^x	Cut	(Send)
^c	Copy	(Send)
^v	Paste	(Send)
^s	Save File	(Send)
^a	Select All	(Send)
- - - - - - - - - - - - - - - -
Close Menu	{menu.Gui_OnEscape}	Return To mSpot	(Function)
Reload Menu	{menu.Reload}	Reflect The Latest Content	(Function)
Edit Menu	{EditMenu}	(Gosub)
Demo One	{demo1}	hello world	(Gosub)
Demo Two	{demo2}	foobar	(Gosub)
Demo Three	{demo3}	blablabla	(Function)
Demo Four	{demo4}	bla bla	(Func+Param)	dummy1
Demo Five	{demo5}	bla bla bla	(Func+Param)	dummy1,dummy2
Script I	(Script)	msgbox `% "remote: " a.call("var","dummy1@pool")
Script II	(Script)	msgbox `% "remote: " a.call("var","dummy2@pool")
Script III	(Script)	a.call("var","Set:dummy3@pool",a_tickcount)|| ||msgbox `% "remote: " a.call("var","dummy3@pool")
Verify	{demo4}	(Func+Param)	dummy3
Script IV	(Script)	{Apart}
[:Text Filter:RICHEDIT50W1:] Script V	(Script)	{fghij}
Script VI	(Script)	{menuDemo}
= = = Bottom = = =
;
{Script Apart}
a.call("var","Set:dummy3@pool",a_tickcount "(a)")
msgbox `% "remote: " a.call("var","dummy3@pool")
{/Script}

{Script fghij}
a.call("demo5", "p1p", "p2p")
If InStr(a.call("var","dummy3@pool"), "(a)")
	msgbox found "(a)" in dummy3
{/Script}
)
FileAppend, %_menu_%, MenuText.txt, UTF-8
		}
	IfNotExist, menuDemo.ahk
FileAppend, msgbox im an island, menuDemo.ahk, UTF-8
	}
}

IsGetSet(Param, ByRef a, ByRef v, ByRef value) {	;written by SundayProgrammer
	a := SubStr(Param, 1, 4)
	If (a = "Set:") or (a = "Get:")
	{	v := Trim((p := InStr(Param, "=")) ? (SubStr(Param, 5, p - 5), value := SubStr(Param, p + 1)) : SubStr(Param, 5))
		Return True
	}Return False
}
var(name, value = "") {	;written by SundayProgrammer
	global
	local p, a, v, m1, m2, m3, segc
	If p := InStr(name, "@")
	{	a := SubStr(name, 1, 4), v := Trim(SubStr(name, 1, p - 1)), name := Trim(SubStr(name, p + 1))
		If (a = "Set:") or (a = "Get:")
			v := Trim(SubStr(v, 5))
		Else a := StrLen(value) ? "Set:" : "Get:"
		If StrLen(v) and StrLen(name)
		{	v := a v, v .= (a = "Set:") ? "=" value : ""
			If InStr(name, ".")
			{	Loop, Parse, name, .
					m%A_Index% := A_LoopField, segc := A_Index
				If segc = 2
					v := %m1%[m2](v)
				Else If segc = 3
					v := %m1%[m2][m3](v)
				Else v = Error: Segments More Than Allowed.
			}Else v := %name%(v)
		}Else v = Error: Invalid Parameter "%v%@%name%"
	}Else
	{	a := SubStr(name, 1, 4)
		If (a = "Set:") or (a = "Get:")
			name := Trim(SubStr(name, 5))
		If InStr(name, ".")
		{	Loop, Parse, name, .
				m%A_Index% := A_LoopField, segc := A_Index
			If segc = 2
			{	If (a = "Set:") or StrLen(value)
					%m1%[m2] := value
				v := %m1%[m2]
			}Else If segc = 3
			{	If (a = "Set:") or StrLen(value)
					%m1%[m2][m3] := value
				v := %m1%[m2][m3]
			}Else v = Error: Segments More Than Allowed.
		}Else
		{	If (a = "Set:") or StrLen(value)
				%name% := value
			v := %name%
		}
	}
	Return v
}

Class agent {	;written by SundayProgrammer who got the idea from this post https://www.autohotkey.com/boards/viewtopic.php?p=200597#p200597
	Call(name, p*) {	;allows you to call any function in this script
		If InStr(name, ".")
		{	Loop, Parse, name, .
				m%A_Index% := A_LoopField, segc := A_Index
			If segc = 2
				Return %m1%[m2](p*)
			Else If segc = 3
				Return %m1%[m2][m3](p*)
			Else Return "Error: Segments More Than Allowed."
		}Return %name%(p*)
	}
}

#IfWinActive ahk_group SpotMenu
	WheelUp::
	WheelDown::
	+WheelUp::
	+WheelDown::OnScroll(InStr(A_ThisHotkey, "Down") ? 1 : 0, 0, GetKeyState("Shift") ? 0x114 : 0x115, WinExist())
#If

WM_DISPLAYCHANGE() {
	WinGetPos, x, y,,, mSpot
	If (x > A_ScreenWidth - 50)
		x := A_ScreenWidth - 100
	If (y > A_ScreenHeight - 125)
		y := A_ScreenHeight - 175
	Gui, mSpot:Show, x%x% y%y% NoActivate
}

WM_ACTIVATE() {
	IfWinActive, mSpot
		WinSet, TransColor, White, mSpot
	Else WinSet, TransColor, White 64, mSpot
}

Class menu {	;written by SundayProgrammer
Spot() {
	If not StrLen(this.Spots)
		this.Spots := this.sgnHistory := "Spots"
	If not StrLen(this.Color)
		this.Color := this.fscHistory := "B2D6F3"
	Gui, mSpot:New
	Gui, +LastFound -Caption +AlwaysOnTop +ToolWindow
	Gui, Color, White
	Gui, Margin, 0, 0
	Gui, Font, s150, Consolas
	Gui, Add, Text, x0 y0 cFFB10F BackgroundTrans, ●
	OnSuch := this.HitEvent.Bind(this)
	GuiControl +g, Static1, % OnSuch
	WinSet, TransColor, White 64
	x := A_ScreenWidth - 100, y := A_ScreenHeight - 530
	Gui, Show, x%x% y%y% NoActivate, mSpot
	this.HitEvent() ; Init sPos
	OnMessage(0x7E, "WM_DISPLAYCHANGE")
	OnMessage(0x06, "WM_ACTIVATE")
}
HitEvent(hwnd := "") {
	static sPos
	If not StrLen(sPos)
	{	WinGetPos, x, y,,, mSpot
		sPos := x "," y
	}If not StrLen(hwnd)
		Return sPos
	SendMessage, 0xA1, 2,,, A ; WM_NCLBUTTONDOWN
	GuiControlGet, wHwnd, Hwnd, %hwnd%
	WinGetPos, x, y,,, ahk_id %wHwnd%
	If x "," y = sPos
	{	Gui, mSpot:Hide
		this.SpotMenu(var("_menu_@pool"))
	}Else
	{	sPos := x "," y
		SetTimer, Deactivate, -500
	}Return
	Deactivate:
		Gui, mSpot:Hide
		Gui, mSpot:Show, NoActivate
		WinSet, TransColor, White 64, mSpot
		Return
}
SpotMenu(t) {
	global TT
	WinWaitNotActive, mSpot
	WinGetActiveTitle, sWin
	ControlGetFocus, sCtl, A
	Loop, Parse, t, `n
		If SubStr(Trim(A_LoopField), 1, 1) not = ";"
		If RegExMatch(A_LoopField, "i)^\s*{\s*Script\s+[^}]+?\s*}")
			Break
		Else If RegExMatch(A_LoopField, "[^\t]+(?=\t)", m)
			w%A_Index% := this.GetButtonWidth(Trim(RegExReplace(m, "\s*\[([^\]]+)]")), "30 bold", "Consolas")
	OnMessage(0x115, "OnScroll") ; WM_VSCROLL
	OnMessage(0x114, "OnScroll") ; WM_HSCROLL
	OnMessage(0x20A, "OnScroll") ; WM_MOUSEWHEEL
	OnMessage(0x20E, "OnScroll") ; WM_MOUSEHWHEEL
	OnMessage(0x119, "gHandler") ; WM_GESTURE
	Gui, menu:New, hwndHmenu +Labelmenu.Gui_On
	Gui, +Resize +0x300000	; WS_VSCROLL | WS_HSCROLL
	TT := New GuiControlTips(Hmenu), TT.SetDelayTimes(1000, 15000, -1)
	Gui, Font, s30 bold, Consolas
	ci := nl := 1
	Loop, Parse, t, `n
		If SubStr(Trim(A_LoopField), 1, 1) not = ";"
		If RegExMatch(A_LoopField, "[^\t]+(?=\t)", m)
		{	Passed := False
			If RegExMatch(m, "\s*\[([^\]]+)]", mm)
			If RegExMatch(Trim(mm1), ":([^:]+(:[^:]+)?):", mm)
			If pos := InStr(mm1, ":")
				If not RegExMatch(sWin, Trim(SubStr(mm1, 1, pos - 1))) or not RegExMatch(sCtl, Trim(SubStr(mm1, pos + 1)))
					Continue
				Else Passed := True
			Else If not RegExMatch(sWin, Trim(mm1))
				Continue
			Else Passed := True
			If Passed
				m := Trim(RegExReplace(m, "\s*\[([^\]]+)]"))
			If not nl
				nl := (gx + gw + 25 + w%A_Index%) > A_ScreenWidth - 40
			p := nl ? "" : "+", y := nl ? "y+28" : ""
			Gui, Add, Button, HwndhBtn%ci% x%p%25 %y% h57, % (m, pi := ci, ci++)
			OnSuch := this.MenuClick.Bind(this)
			GuiControl +g, Button%pi%, % OnSuch
			GuiControlGet, g, Pos, Button%pi%
			TT.Attach(hBtn%pi%, RegExReplace(RegExReplace(SubStr(A_LoopField, 0) = "`r" ? SubStr(A_LoopField, 1, -1) : A_LoopField, "[^\t]+\t",,, 1), "\t", "`n"))
			nl := (gx + gw) > (A_ScreenWidth - 116)
		}Else If RegExMatch(A_LoopField, "i)^\s*{\s*Script\s+[^}]+?\s*}")
			Break
		Else
		{	Gui, Add, Text, x25 cWhite, % SubStr(A_LoopField, 0) = "`r" ? SubStr(A_LoopField, 1, -1) : A_LoopField
			nl := True
		}
	Gui, Color, Black
	TT.SetTitle("Descriptions", LoadPicture("shell32.dll", "Icon222", ImageType))
	SetCtrlFont(TT.HTIP, "s20", "Arial New")
	Gui, +LastFound
	WinSet, Transparent, 180
	Gui, Show,, Touch Friendly Menu
	GroupAdd, SpotMenu, % "ahk_id " . WinExist()
	this.t := t
}
Gui_OnSize() {
	UpdateScrollBars(A_Gui, A_GuiWidth, A_GuiHeight)
}
Gui_OnEscape() {
	menu.OnClose()
	Gui, mSpot:Show, NoActivate
	WinSet, TransColor, White 64, mSpot
}
Gui_OnClose() {
	ObjRegisterActive(agent, "")
	ExitApp
}
OnClose() {
	OnMessage(0x115, "") ; WM_VSCROLL
	OnMessage(0x114, "") ; WM_HSCROLL
	OnMessage(0x20A, "") ; WM_MOUSEWHEEL
	OnMessage(0x20E, "") ; WM_MOUSEHWHEEL
	OnMessage(0x119, "") ; WM_GESTURE
	Gui, menu:Destroy
}
GetButtonWidth(t, s, f) {
	Gui, New
	Gui, Font, s%s%, % f
	Gui, Add, Button,, % t
	GuiControlGet, g, Pos, Button1
	Gui, Destroy
	Return gW
}
MenuClick(h) {
	global TT
	theText := A_GuiControl
	this.OnClose()
	WinWaitNotActive, Touch Friendly Menu
	Sleep, 500
	this.do(theText, TT.GetText(h))
	Gui, mSpot:Show, NoActivate
	WinSet, TransColor, White 64, mSpot
}
do(theText, t) {
	If RegExMatch(t, "{\K[^}]+(?=})", m)
		If not InStr(t, "(Send)")
			theText := m
	If InStr(t, "(Send)")
		Send, %theText%
	Else If InStr(t, "(Gosub)")
		Gosub, %theText%
	Else If InStr(t, "(Function)")
		If pos := InStr(theText, ".")
			className := SubStr(theText, 1, pos - 1), method := SubStr(theText, pos + 1), %className%[method]()
		Else %theText%()
	Else If InStr(t, "(Func+Param)")
	{	RegExMatch(t, "[^\n]+$", m)
		If InStr(m, ",")
		{	Loop, Parse, m, `,
				p%A_Index% := var(A_LoopField "@pool")
			StrReplace(m, ",",, c)
			If c = 1
				%theText%(p1, p2)
			Else If c = 2
				%theText%(p1, p2, p3)
			Else If c = 3
				%theText%(p1, p2, p3, p4)
			Else If c = 4
				%theText%(p1, p2, p3, p4, p5)
			Else If c = 5
				%theText%(p1, p2, p3, p4, p5, p6)
			Else If c = 6
				%theText%(p1, p2, p3, p4, p5, p6, p7)
			Else If c = 7
				%theText%(p1, p2, p3, p4, p5, p6, p7, p8)
			Else If c = 8
				%theText%(p1, p2, p3, p4, p5, p6, p7, p8, p9)
			Else If c = 9
				%theText%(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10)
			Else{}	;more than 10 parameters is not supported
		}Else %theText%(var(m "@pool"))
	}Else If InStr(t, "(Script)")
		If RegExMatch(t, "{\K[^}]+(?=})", m)
			If RegExMatch(this.t, "is){\s*Script\s+" m "\s*}(.+?){\s*/\s*Script\s*}", mm)
				ExecScript(var("s1l@pool") this.HandleInclude(mm1),, A_AhkPath)
			Else IfExist, % m := (SubStr(Trim(m), -3) = ".ahk" ? m : Trim(m) ".ahk")
			{	FileRead, mm1, %m%
				ExecScript(var("s1l@pool") mm1,, A_AhkPath)
			}Else{}
		Else RegExMatch(t, "\(Script\)[\n\t]+\K.*$", m), m := StrReplace(m, "|| ||", "`n"), ExecScript(var("s1l@pool") this.HandleInclude(m),, A_AhkPath)
}
HandleInclude(s) {
	If RegExMatch(s, "is)#\s*Include\s+[^#]+#")
	{	RegExReplace(s, "is)#\s*Include\s+[^#]+#",, n), pos := 1, m := ""
		Loop, %n%
			pos := RegExMatch(s, "is)#\s*Include\s+\K[^#]+(?=#)", m, pos + StrLen(m)), RegExMatch(this.t, "is){\s*Script\s+" Trim(m) "\s*}(.+?){\s*/\s*Script\s*}", mm), s := RegExReplace(s, "is)#\s*Include\s+" Trim(m) "\s*#", RegExMatch(mm1, "is)#\s*Include\s+[^#]+#") ? this.HandleInclude(mm1) : mm1)
	}
	Return s
}
ExtraSpot() {
	btn := "B" A_TickCount
	MouseGetPos,,, mow, moc
	WinGetTitle, mow, ahk_id %mow%
	ControlGetText, m, %moc%, %mow%
	MouseGetPos,,,, h, 2
	t := agent.Call("TT.GetText", h)
	menu.Gui_OnEscape()
	Gui, shape:New, +ToolWindow
	Gui, Font, s30, Consolas
	Gui, Add, ListBox, r15, ●|◆|◼|◉|◈|▣|◙|▲|▼|◀|▶|◢|◥|◣|◤
	OnSuch := this.Pick.Bind(this)
	GuiControl +g, ListBox1, % OnSuch
	Gui, Show,, Select A Shape Please
	var("Set:Shape@pool", False), this.CurItem := "●"
	While, not var("Shape@pool") and WinExist("Select A Shape Please")
		Sleep, 100
	If not var("Shape@pool")
		var("Set:Shape@pool", this.CurItem)
	Gui, shape:Destroy
	Gui, %btn%:New, hwndHspot
	Gui, +LastFound -Caption +AlwaysOnTop +ToolWindow
	Gui, Color, White
	Gui, Margin, 0, 0
	Gui, Font, s120, Consolas
	Color := this.Color
	Gui, Add, Text, x0 y0 c%Color% BackgroundTrans, % var("Shape@pool")
	OnSuch := this.xHit.Bind(this)
	GuiControl +g, Static1, % OnSuch
	Gui, Add, Text, Hidden, %m%
	Gui, Add, Text, Hidden, %t%
	Gui, Add, Text, Hidden, % this.Spots
	WinSet, TransColor, White 64
	x := A_ScreenWidth - 100
	Gui, Show, x%x% NoActivate, %btn%spot
	GroupAdd, % this.Spots, % "ahk_id " . WinExist()
	this.wt := btn "spot"
	ControlGet, sid, hwnd,, Static1, %btn%spot
	this.xHit(sid + 0) ; Init sPos
}
xHit(hwnd := "") {
	static sPos := []
	If not StrLen(sPos[hwnd])
	{	WinGetPos, x, y,,, % this.wt
		sPos[hwnd] := x "," y
		Return
	}SendMessage, 0xA1, 2,,, A ; WM_NCLBUTTONDOWN
	GuiControlGet, wHwnd, Hwnd, %hwnd%
	WinGetPos, x, y,,, ahk_id %wHwnd%
	WinGetActiveTitle, wt
	var("Set:wName@pool", wName := SubStr(wt, 1, -4))
	If x "," y = sPos[hwnd]
	{	GuiControlGet, theText,, Static2
		GuiControlGet, t,, Static3
		Gui, %wName%:Hide
		menu.do(theText, t)
	}Else sPos[hwnd] := x "," y
	SetTimer, DeactivateIt, -500
	Return
	DeactivateIt:
		wName := var("wName@pool")
		Gui, %wName%:Hide
		Gui, %wName%:Show, NoActivate
		Return
}
Pick(hwnd := "") {
	GuiControlGet, Choice,, ListBox1
	If StrLen(Choice)
	{	this.CurItem := Choice
		If not InStr(A_PriorKey, "LButton")
			Return
	}Else If A_ThisHotkey in Space,Enter,``
		Choice := this.CurItem
	var("Set:Shape@pool", Choice)
}
Reload() {
	FileRead, _menu_, MenuText.txt
	var("Set:_menu_@pool", _menu_)
	SetTimer, ReloadMenu, -500
	Return
	ReloadMenu:
		Gui, mSpot:Hide
		menu.SpotMenu(var("_menu_@pool"))
		Return
}
}	;end of class

keyRButton() {
	beg := A_TickCount
	While, GetKeyState("RButton", "P")
		If A_TickCount - beg > 1000
		{	IfWinNotExist, Touch Friendly Menu
			IfWinExist, mSpot
			{	MouseGetPos,,, mow, moc
				WinGetTitle, mow, ahk_id %mow%
				If (mow = "mSpot") and (moc = "Static1")
				{	Gui, mSpot:Destroy
					beg := False
				}
			}Else menu.Spot(), beg := False
			SoundBeep
			Break
		}
	If not beg
		Return
	MouseGetPos,,, mow, moc
	WinGetTitle, mow, ahk_id %mow%
	If SubStr(mow, -3) = "Spot" and moc = "Static1"
		If mow = mSpot
		{	Spots := menu.Spots
			WinGet, c, Count, ahk_group %Spots%
			dhw := A_DetectHiddenWindows
			DetectHiddenWindows, On
			WinGet, ca, Count, ahk_group %Spots%
			DetectHiddenWindows, %dhw%
			If (ca > c)
			{	aw := WinExist("A")
				WinShow, ahk_group %Spots%
				WinActivate, ahk_id %aw%
				WinWaitActive
			}Else If ca
				WinHide, ahk_group %Spots%
			Else Gui, mSpot:Destroy
		}Else ;WinClose, %mow%
		{	GuiName := SubStr(mow, 1, -4)
			Gui, %GuiName%:Destroy
		}
	Else If mow = Touch Friendly Menu
		If SubStr(moc, 1, 6) = "Button"
			menu.ExtraSpot()
		Else{}
	Else Send, {RButton}
}

SetCtrlFont(CtrlHwnd, FontOptions := "", FontName := "") {	;written by iPhilip
   static WM_SETFONT := 0x0030, WM_GETFONT := 0x0031
   DefaultGui := A_DefaultGui
   Gui, New
   Gui, Font, % FontOptions, % FontName
   Gui, Add, Text, hwndhText, Text
   hFont := DllCall("SendMessage", "Ptr", hText, "UInt", WM_GETFONT, "Ptr", 0, "Ptr", 0, "Ptr")
   Gui, Destroy
   Gui, %DefaultGui%:Default
   DllCall("SendMessage", "Ptr", CtrlHwnd, "UInt", WM_SETFONT, "Ptr", hFont, "Ptr", true)
   Return hFont
}

; ======================================================================================================================
; Namespace:      GuiControlTips
; AHK version:    AHK 1.1.14.03
; Function:       Helper object to simply assign ToolTips for GUI controls
; Tested on:      Win 7 (x64)
; Change history:
;                 1.1.00.01/2020-06-03/just me - fixed missing Static WS_EX_TOPMOST
;                 1.1.00.00/2014-03-06/just me - Added SetDelayTimes()
;                 1.0.01.00/2012-07-29/just me
; ======================================================================================================================
; CLASS GuiControlTips
;
; The class provides four public methods to register (Attach), unregister (Detach), update (Update), and
; disable/enable (Suspend) common ToolTips for GUI controls.
;
; Usage:
; To assign ToolTips to GUI controls you have to create a new instance of GuiControlTips per GUI with
;     MyToolTipObject := New GuiControlTips(HGUI)
; passing the HWND of the GUI.
;
; After this you may assign ToolTips to your GUI controls by calling
;     MyToolTipObject.Attach(HCTRL, "ToolTip text")
; passing the HWND of the control and the ToolTip's text. Pass True/1 for the optional third parameter if you
; want the ToolTip to be shown centered below the control.
;
; To remove a ToolTip call
;     MyToolTipObject.Detach(HCTRL)
; passing the HWND of the control.
;
; To update the ToolTip's text call
;     MyToolTipObject.Update(HCTRL, "New text!")
; passing the HWND of the control and the new text.
;
; To deactivate the ToolTips call
;     MyToolTipObject.Suspend(True),
; to activate them again afterwards call
;     MyToolTipObject.Suspend(False).
;
; To adjust the ToolTips delay times call
;     MyToolTipObject.SetDelayTimesd(),
; specifying the delay times in milliseconds.
;
; That's all you can / have to do!
; ======================================================================================================================
Class GuiControlTips {	;written by @"just me"
   ; ===================================================================================================================
   ; INSTANCE variables
   ; ===================================================================================================================
   HTIP := 0
   HGUI := 0
   CTRL := {}
   ; ===================================================================================================================
   ; CONSTRUCTOR           __New()
   ; ===================================================================================================================
   __New(HGUI) {
      Static CLASS_TOOLTIP      := "tooltips_class32"
      Static CW_USEDEFAULT      := 0x80000000
      Static TTM_SETMAXTIPWIDTH := 0x0418
      Static TTM_SETMARGIN      := 0x041A
      Static WS_EX_TOPMOST      := 0x00000008
      Static WS_STYLES          := 0x80000002 ; WS_POPUP | TTS_NOPREFIX
      ; Create a Tooltip control ...
      HTIP := DllCall("User32.dll\CreateWindowEx", "UInt", WS_EX_TOPMOST, "Str", CLASS_TOOLTIP, "Ptr", 0
                    , "UInt", WS_STYLES
                    , "Int", CW_USEDEFAULT, "Int", CW_USEDEFAULT, "Int", CW_USEDEFAULT, "Int", CW_USEDEFAULT
                    , "Ptr", HGUI, "Ptr", 0, "Ptr", 0, "Ptr", 0, "Ptr")
      If ((ErrorLevel) || !(HTIP))
         Return False
      ; ... prepare it to display multiple lines if required
      DllCall("User32.dll\SendMessage", "Ptr", HTIP, "Int", TTM_SETMAXTIPWIDTH, "Ptr", 0, "Ptr", A_ScreenWidth*96//A_ScreenDPI)	;touched by SundayProgrammer who took it from iPhilip response for AddTooltip v2.0
      ; ... set the instance variables
      This.HTIP := HTIP
      This.HGUI := HGUI
      If (DllCall("Kernel32.dll\GetVersion", "UInt") & 0xFF) < 6 ; to avoid some XP issues ...
         This.Attach(HGUI, "") ; ... register the GUI with an empty tiptext
   }
   ; ===================================================================================================================
   ; DESTRUCTOR            __Delete()
   ; ===================================================================================================================
   __Delete() {
      If (This.HTIP) {
         DllCall("User32.dll\DestroyWindow", "Ptr", This.HTIP)
      }
   }
   ; ===================================================================================================================
   ; PRIVATE METHOD        SetToolInfo - Create and fill a TOOLINFO structure
   ; ===================================================================================================================
   SetToolInfo(ByRef TOOLINFO, HCTRL, TipTextAddr, CenterTip = 0) {
      Static TTF_IDISHWND  := 0x0001
      Static TTF_CENTERTIP := 0x0002
      Static TTF_SUBCLASS  := 0x0010
      Static OffsetSize  := 0
      Static OffsetFlags := 4
      Static OffsetHwnd  := 8
      Static OffsetID    := OffsetHwnd + A_PtrSize
      Static OffsetRect  := OffsetID + A_PtrSize
      Static OffsetInst  := OffsetRect + 16
      Static OffsetText  := OffsetInst + A_PtrSize
      Static StructSize  := (4 * 6) + (A_PtrSize * 6)
      Flags := TTF_IDISHWND | TTF_SUBCLASS
      If (CenterTip)
         Flags |= TTF_CENTERTIP
      VarSetCapacity(TOOLINFO, StructSize, 0)
      NumPut(StructSize, TOOLINFO, OffsetSize, "UInt")
      NumPut(Flags, TOOLINFO, OffsetFlags, "UInt")
      NumPut(This.HGUI, TOOLINFO, OffsetHwnd, "Ptr")
      NumPut(HCTRL, TOOLINFO, OffsetID, "Ptr")
      NumPut(TipTextAddr, TOOLINFO, OffsetText, "Ptr")
      Return True
   }
   ; ===================================================================================================================
   ; PUBLIC METHOD         Attach         -  Assign a ToolTip to a certain control
   ; Parameters:           HWND           -  Control's HWND
   ;                       TipText        -  ToolTip's text
   ;                       Optional:      ------------------------------------------------------------------------------
   ;                       CenterTip      -  Centers the tooltip window below the control
   ;                                         Values:  True/False
   ;                                         Default: False
   ; Return values:        On success: True
   ;                       On failure: False
   ; ===================================================================================================================
   Attach(HCTRL, TipText, CenterTip = False) {
      Static TTM_ADDTOOL  := A_IsUnicode ? 0x0432 : 0x0404 ; TTM_ADDTOOLW : TTM_ADDTOOLA
      If !(This.HTIP) {
         Return False
      }
      If This.CTRL.HasKey(HCTRL)
         Return False
      TOOLINFO := ""
      This.SetToolInfo(TOOLINFO, HCTRL, &TipText, CenterTip)
      If DllCall("User32.dll\SendMessage", "Ptr", This.HTIP, "Int", TTM_ADDTOOL, "Ptr", 0, "Ptr", &TOOLINFO) {
         This.CTRL[HCTRL] := 1
		 This.TTT[HCTRL] := TipText	;added by SundayProgrammer
         Return True
      } Else {
        Return False
      }
   }
   ; ===================================================================================================================
   ; PUBLIC METHOD         Detach         -  Remove the ToolTip for a certain control
   ; Parameters:           HWND           -  Control's HWND
   ; Return values:        On success: True
   ;                       On failure: False
   ; ===================================================================================================================
   Detach(HCTRL) {
      Static TTM_DELTOOL  := A_IsUnicode ? 0x0433 : 0x0405 ; TTM_DELTOOLW : TTM_DELTOOLA
      If !This.CTRL.HasKey(HCTRL)
         Return False
      TOOLINFO := ""
      This.SetToolInfo(TOOLINFO, HCTRL, 0)
      DllCall("User32.dll\SendMessage", "Ptr", This.HTIP, "Int", TTM_DELTOOL, "Ptr", 0, "Ptr", &TOOLINFO)
      This.CTRL.Remove(HCTRL, "")
	  This.TTT.Remove(HCTRL, "")	;added by SundayProgrammer
      Return True
   }
   ; ===================================================================================================================
   ; PUBLIC METHOD         Update         -  Update the ToolTip's text for a certain control
   ; Parameters:           HWND           -  Control's HWND
   ;                       TipText        -  New text                                                      
   ; Return values:        On success: True
   ;                       On failure: False
   ; ===================================================================================================================
   Update(HCTRL, TipText) {
      Static TTM_UPDATETIPTEXT  := A_IsUnicode ? 0x0439 : 0x040C ; TTM_UPDATETIPTEXTW : TTM_UPDATETIPTEXTA
      If !This.CTRL.HasKey(HCTRL)
         Return False
      TOOLINFO := ""
      This.SetToolInfo(TOOLINFO, HCTRL, &TipText)
      DllCall("SendMessage", "Ptr", This.HTIP, "Int", TTM_UPDATETIPTEXT, "Ptr", 0, "Ptr", &TOOLINFO)
	  This.TTT[HCTRL] := TipText	;added by SundayProgrammer
      Return True
   }
   ;added by SundayProgrammer
   GetText(HCTRL) {
      If !This.CTRL.HasKey(HCTRL)
         Return ""
      Return This.TTT[HCTRL]
   }
   ; ===================================================================================================================
   ; PUBLIC METHOD         Suspend        -  Disable/enable the ToolTip control (don't show / show ToolTips)
   ; Parameters:           Mode           -  True/False (1/0)
   ;                                         Default: True/1
   ; Return values:        On success: True
   ;                       On failure: False
   ; Remarks:              ToolTips are enabled automatically on creation.
   ; ===================================================================================================================
   Suspend(Mode = True) {
      Static TTM_ACTIVATE := 0x0401
      If !(This.HTIP)
         Return False
      DllCall("SendMessage", "Ptr", This.HTIP, "Int", TTM_ACTIVATE, "Ptr", !Mode, "Ptr", 0)
      Return True
   }
   ; ===================================================================================================================
   ; PUBLIC METHOD         SetDelayTimes  -  Set the initial, pop-up, and reshow durations for a tooltip control.
   ; Parameters:           Init           -  Amount of time, in milliseconds, a pointer must remain stationary within
   ;                                         a tool's bounding rectangle before the tooltip window appears.
   ;                                         Default: -1 (system default time)
   ;                       PopUp          -  Amount of time, in milliseconds, a tooltip window remains visible if the
   ;                                         pointer is stationary within a tool's bounding rectangle.
   ;                                         Default: -1 (system default time)
   ;                       ReShow         -  Amount of time, in milliseconds, it takes for subsequent tooltip windows
   ;                                         to appear as the pointer moves from one tool to another.
   ;                                         Default: -1 (system default time)
   ; Return values:        On success: True
   ;                       On failure: False
   ; Remarks:              Times are set per ToolTip control and applied to all added tools.
   ; ===================================================================================================================
   SetDelayTimes(Init = -1, PopUp = -1, ReShow = -1) {
      Static TTM_SETDELAYTIME   := 0x0403
      Static TTDT_RESHOW   := 1
      Static TTDT_AUTOPOP  := 2
      Static TTDT_INITIAL  := 3
      DllCall("SendMessage", "Ptr", This.HTIP, "Int", TTM_SETDELAYTIME, "Ptr", TTDT_INITIAL, "Ptr", Init)
      DllCall("SendMessage", "Ptr", This.HTIP, "Int", TTM_SETDELAYTIME, "Ptr", TTDT_AUTOPOP, "Ptr", PopUp)
      DllCall("SendMessage", "Ptr", This.HTIP, "Int", TTM_SETDELAYTIME, "Ptr", TTDT_RESHOW , "Ptr", ReShow)
   }
   ;added by SundayProgrammer who largely took it from AddTooltip v2.0
   SetTitle(theTitle := "", theIcon := 0) {
      Static TTM_SETTITLE := A_IsUnicode ? 0x421 : 0x420 ; TTM_SETTITLEW : TTM_SETTITLEA
      If StrLen(theTitle) > 99
         theTitle := SubStr(theTitle, 1, 99)
      If theIcon is not Integer
         theIcon := 0
      DllCall("SendMessage", "Ptr", This.HTIP, "Int", TTM_SETTITLE, "Ptr", theIcon, "Ptr", &theTitle)
   }
}

UpdateScrollBars(GuiNum, GuiWidth, GuiHeight) {	;written by lexikos
    static SIF_RANGE=0x1, SIF_PAGE=0x2, SIF_DISABLENOSCROLL=0x8, SB_HORZ=0, SB_VERT=1
   
    Gui, %GuiNum%:Default
    Gui, +LastFound
   
    ; Calculate scrolling area.
    Left := Top := 9999
    Right := Bottom := 0
    WinGet, ControlList, ControlList
    Loop, Parse, ControlList, `n
    {
        GuiControlGet, c, Pos, %A_LoopField%
        if (cX < Left)
            Left := cX
        if (cY < Top)
            Top := cY
        if (cX + cW > Right)
            Right := cX + cW
        if (cY + cH > Bottom)
            Bottom := cY + cH
    }
    Left -= 8
    Top -= 8
    Right += 8
    Bottom += 8
    ScrollWidth := Right-Left
    ScrollHeight := Bottom-Top
   
    ; Initialize SCROLLINFO.
    VarSetCapacity(si, 28, 0)
    NumPut(28, si) ; cbSize
    NumPut(SIF_RANGE | SIF_PAGE, si, 4) ; fMask
   
    ; Update horizontal scroll bar.
    NumPut(ScrollWidth, si, 12) ; nMax
    NumPut(GuiWidth, si, 16) ; nPage
    DllCall("SetScrollInfo", "uint", WinExist(), "uint", SB_HORZ, "uint", &si, "int", 1)
   
    ; Update vertical scroll bar.
;     NumPut(SIF_RANGE | SIF_PAGE | SIF_DISABLENOSCROLL, si, 4) ; fMask
    NumPut(ScrollHeight, si, 12) ; nMax
    NumPut(GuiHeight, si, 16) ; nPage
    DllCall("SetScrollInfo", "uint", WinExist(), "uint", SB_VERT, "uint", &si, "int", 1)
   
    if (Left < 0 && Right < GuiWidth)
        x := Abs(Left) > GuiWidth-Right ? GuiWidth-Right : Abs(Left)
    if (Top < 0 && Bottom < GuiHeight)
        y := Abs(Top) > GuiHeight-Bottom ? GuiHeight-Bottom : Abs(Top)
    if (x || y)
        DllCall("ScrollWindow", "uint", WinExist(), "int", x, "int", y, "uint", 0, "uint", 0)
}

OnScroll(wParam, lParam, msg, hwnd) {	;written by lexikos
    static SIF_ALL=0x17, SCROLL_STEP=85 ;changed by SundayProgrammer from 10 to 85 for a more practical outcome
	static xpos := 0, ypos := 0	;added by SundayProgrammer - for touch gesture scrolling
	global gFlag	;added by SundayProgrammer - for touch gesture scrolling
if DllCall("GetParent", "uint", hwnd)	;added by SundayProgrammer - a quick fix for the scenario when any scrollable control is involved
	return	;added by SundayProgrammer - a quick fix for the scenario when any scrollable control is involved
   
    bar := (msg=0x115) or (msg=0x20A) ; (SB_HORZ=0, SB_VERT=1) or (WM_MOUSEHWHEEL=0, WM_MOUSEWHEEL=1)	;changed by SundayProgrammer - for WM_MOUSEWHEEL and WM_MOUSEHWHEEL
   
	if gFlag	;added by SundayProgrammer - for touch gesture scrolling
	{	gAction(xpos, ypos, bar, hwnd)	;added by SundayProgrammer - for touch gesture scrolling
		return	;added by SundayProgrammer - for touch gesture scrolling
	}	;added by SundayProgrammer - for touch gesture scrolling
   
    VarSetCapacity(si, 28, 0)
    NumPut(28, si) ; cbSize
    NumPut(SIF_ALL, si, 4) ; fMask
    if !DllCall("GetScrollInfo", "uint", hwnd, "int", bar, "uint", &si)
        return
   
    VarSetCapacity(rect, 16)
    DllCall("GetClientRect", "uint", hwnd, "uint", &rect)
   
    new_pos := NumGet(si, 20) ; nPos (saw "25" in another version, which exhibited a bug in my testing, whereas "20" (this version) worked fine so far)
   
    if msg=0x20A	;added by SundayProgrammer - for WM_MOUSEWHEEL
        wParam := wParam>0x780000	;added by SundayProgrammer - for WM_MOUSEWHEEL
    else if msg=0x20E	;added by SundayProgrammer - for WM_MOUSEHWHEEL
        wParam := wParam=0x780000	;added by SundayProgrammer - for WM_MOUSEHWHEEL
    action := wParam & 0xFFFF
    if action = 0 ; SB_LINEUP
        new_pos -= SCROLL_STEP
    else if action = 1 ; SB_LINEDOWN
        new_pos += SCROLL_STEP
    else if action = 2 ; SB_PAGEUP
        new_pos -= NumGet(rect, 12, "int") - SCROLL_STEP
    else if action = 3 ; SB_PAGEDOWN
        new_pos += NumGet(rect, 12, "int") - SCROLL_STEP
    else if (action = 5 || action = 4) ; SB_THUMBTRACK || SB_THUMBPOSITION
        new_pos := wParam>>16
    else if action = 6 ; SB_TOP
        new_pos := NumGet(si, 8, "int") ; nMin
    else if action = 7 ; SB_BOTTOM
        new_pos := NumGet(si, 12, "int") ; nMax
    else
        return
   
    min := NumGet(si, 8, "int") ; nMin
    max := NumGet(si, 12, "int") - NumGet(si, 16) ; nMax-nPage
    new_pos := new_pos > max ? max : new_pos
    new_pos := new_pos < min ? min : new_pos
   
    old_pos := NumGet(si, 20, "int") ; nPos (saw "25" in another version, which exhibited a bug in my testing, whereas "20" (this version) worked fine so far)
   
    x := y := 0
    if bar = 0 ; SB_HORZ
        x := old_pos-new_pos
    else
        y := old_pos-new_pos
    ; Scroll contents of window and invalidate uncovered area.
    DllCall("ScrollWindow", "uint", hwnd, "int", x, "int", y, "uint", 0, "uint", 0)
   
    ; Update scroll bar.
    NumPut(new_pos, si, 20, "int") ; nPos (saw "25" in another version, which exhibited a bug in my testing, whereas "20" (this version) worked fine so far)
    DllCall("SetScrollInfo", "uint", hwnd, "int", bar, "uint", &si, "int", 1)

	z := bar ? "y" : "x", %z%pos := new_pos	;added by SundayProgrammer - for touch gesture scrolling
}
gAction(byref xpos, byref ypos, bar, hwnd) {	;written by SundayProgrammer - for touch gesture scrolling
	VarSetCapacity(si, 28, 0), NumPut(28, si), NumPut(0x17, si, 4), DllCall("GetScrollInfo", "uint", hwnd, "int", bar, "uint", &si), aPos := NumGet(si, 20, "int"), z := bar ? "y" : "x"
	if not (%z%pos = aPos)
		x := y := 0, %z% := %z%pos - aPos, DllCall("ScrollWindow", "uint", hwnd, "int", x, "int", y, "uint", 0, "uint", 0), %z%pos := aPos
}
gHandler(wParam, lParam, msg, hwnd) {	;written by SundayProgrammer - for touch gesture scrolling
	global gFlag
	gFlag := true
	settimer, Reset_gFlag, -100
	return
	Reset_gFlag:
		gFlag := false
		return
}

ObjRegisterActive(Object, CLSID, Flags:=0) {	;written by Lexikos
    static cookieJar := {}
    if (!CLSID) {
        if (cookie := cookieJar.Remove(Object)) != ""
            DllCall("oleaut32\RevokeActiveObject", "uint", cookie, "ptr", 0)
        return
    }
    if cookieJar[Object]
        throw Exception("Object is already registered", -1)
    VarSetCapacity(_clsid, 16, 0)
    if (hr := DllCall("ole32\CLSIDFromString", "wstr", CLSID, "ptr", &_clsid)) < 0
        throw Exception("Invalid CLSID", -1, CLSID)
    hr := DllCall("oleaut32\RegisterActiveObject"
        , "ptr", &Object, "ptr", &_clsid, "uint", Flags, "uint*", cookie
        , "uint")
    if hr < 0
        throw Exception(format("Error 0x{:x}", hr), -1)
    cookieJar[Object] := cookie
}

;Function Ripped out of CodeQuickTester written by GeekDude https://github.com/G33kDude/CodeQuickTester
ExecScript(Script, Params="", AhkPath="")	;copy from "Execute code stored in a variable (dynamic variable?)" https://www.reddit.com/r/AutoHotkey/comments/ebwora/comment/fbcwvuy/?utm_source=share&utm_medium=web2x&context=3
{
	static Shell := ComObjCreate("WScript.Shell")
	Name := "\\.\pipe\AHK_CQT_" A_TickCount
	Pipe := []
	Loop, 3
	{
		Pipe[A_Index] := DllCall("CreateNamedPipe"
		, "Str", Name
		, "UInt", 2, "UInt", 0
		, "UInt", 255, "UInt", 0
		, "UInt", 0, "UPtr", 0
		, "UPtr", 0, "UPtr")
	}
	if !FileExist(AhkPath)
		throw Exception("AutoHotkey runtime not found: " AhkPath)
	if (A_IsCompiled && AhkPath == A_ScriptFullPath)
		AhkPath .= " /E"
	if FileExist(Name)
	{
		Exec := Shell.Exec(AhkPath " /CP65001 " Name " " Params)
		DllCall("ConnectNamedPipe", "UPtr", Pipe[2], "UPtr", 0)
		DllCall("ConnectNamedPipe", "UPtr", Pipe[3], "UPtr", 0)
		FileOpen(Pipe[3], "h", "UTF-8").Write(Script)
	}
	else ; Running under WINE with improperly implemented pipes
	{
		FileOpen(Name := "AHK_CQT_TMP.ahk", "w").Write(Script)
		Exec := Shell.Exec(AhkPath " /CP65001 " Name " " Params)
	}
	Loop, 3
		DllCall("CloseHandle", "UPtr", Pipe[A_Index])
	return Exec
}
that's it for now.
SundayProgrammer
Posts: 143
Joined: 25 Dec 2020, 12:26

Re: An Alternative Menu (A Quick Access To Various Features)

27 Nov 2021, 22:01

what's new in this version:

(1) move spot to the left/right

move spot left/right (aka flip horizontally) can be triggered by "rightdrag". "rightdrag" means press and hold right mouse button, when mouse pointer is on top of the target spot, and move it. and as soon as mouse pointer started to move, the target spot is already leaped to the opposite side of the screen (it could be left or right, depending on the current position of the target).

if you "rightdrag" mspot, all spots (instead of just mspot itself) will be moved at once.

if "rightdrag" is not the way you like, add the line below to your menu text, and you will get a menu button to trigger the same thing, that is, to flip all at once.

Code: Select all

Flick Spots	{menu.Flick}	(Function)

(2) save/load spots

everytime you "save spots", it appends a full list of all the spots at the end of your menu text, so that you may load them back later on. you could also adjust them by "edit menu".

since every "save" appends a new copy of all, old stuff might be accumulated very quickly. so, it's better delete those (through "edit menu") when they are no longer needed.

you may use the menu buttons in the example (menu text) below to save/load spots.

Code: Select all

Save Spots	{Save All Spots}	(Script)
Load Spots	{menu.LoadSpots}	(Function)
;
{Script Save All Spots}
	dhw := A_DetectHiddenWindows
	DetectHiddenWindows, On
	tmm := A_TitleMatchMode
	SetTitleMatchMode, RegEx
	log := ""
	WinGet, wList, List, [sS]pot$
	Loop, % wList
	{	hwnd := wList%A_Index%
		WinGetTitle, wt, ahk_id %hwnd%
		If RegExMatch(wt, "^B\d+spot$") or wt = "mSpot"
		{	WinGetPos, x, y,,, ahk_id %hwnd%
			log .= "`nTitle`t= " wt "`n" "Pos`t= " x "," y
			If not (wt = "mSpot")
			Loop, 5
			{	ControlGetText, t, Static%A_Index%, ahk_id %hwnd%
				log .= "`nText" A_Index "`t= " StrReplace(t, "`n", "`t")
			}log .= "`n----------"
		}
	}If StrLen(log)
		a.Call("var", "Set:menu.Save", "{SpotsList@" A_Now "}`n----------" log "`n{/SpotsList}"), a.Call("menu.Append")
	SetTitleMatchMode, %tmm%
	DetectHiddenWindows, %dhw%
{/Script}

here is the updated script.

Code: Select all

pool()	;init variables
menu.Spot()	;show mSpot
Return
EditMenu:
	Run, Notepad MenuText.txt
	Return
demo1:
	msgbox, this is demo1
	return
demo2:
	msgbox, this is demo2 this is demo2
	return
demo3(){
	msgbox, this is demo3 this is demo3 this is demo3
}
demo4(p){
	msgbox, % p
}
demo5(p1, p2){
	msgbox, % p1 "`n`n=========================`n`n" p2
}

$RButton::keyRButton()
$RButton up::Return
#IfWinActive, Select A Shape Please
	Space::
	Enter::
	`::menu.Pick()
#IfWinActive, Select A Batch Please
	Space::
	Enter::
	`::menu.lsPick()
	~Down::menu.DownUp := A_TickCount
	~Up::menu.DownUp := A_TickCount
#If

pool(param = "") {	;written by SundayProgrammer
	static
	static clsid1 := "{A3C04B39-0465-4460-8CA0-7BFFF481FF98}", s1l := "a := ComObjActive(""" clsid1 """)`n"
	static dummy1 := "i am param the first"
	static dummy2 := "i am param the second"
If StrLen(param)
	If IsGetSet(param, action, varname, value)
	{	If (action = "Set:")
			%varname% := value
		v := %varname%
		Return v
	}Else{}
Else{
	ObjRegisterActive(agent, clsid1)
	IfExist, MenuText.txt
		FileRead, _menu_, MenuText.txt
	Else{
_menu_=
(
= = = Top = = =
^#{Left}	Switch Desktop	(Send)
^#{Right}	Switch Desktop	(Send)
#{Tab}	Windows Switcher	(Send)
[:\s- Microsoft.+?Edge:] m{Enter}	MyActivity.Google	(Send)
[:\s- Microsoft.+?Edge:] +^u	Rald Toggle	(Send)
^f	Find	(Send)
[:\s- Microsoft.+?Edge:] ^r	Reload Web Page	(Send)
^w	Close Tab	(Send)
[:\s- YouTube:] j	10s Backward	(Send)
[:\s- Microsoft.+?Edge:] !{Left}	Previous Web Page	(Send)
[:\s- Microsoft.+?Edge:] !{Right}	Next Web Page	(Send)
{Enter}	Enter	(Send)
[:\s- YouTube:] f	Fullscreen Toggle	(Send)
{Space}	Space	(Send)
{Del}	Delete	(Send)
{Esc}	Escape	(Send)
[:\s- Sublime Text:] ^g	Go To Line	(Send)
[:\s- Sublime Text:] !d	Duplicate	(Send)
^z	Undo	(Send)
^x	Cut	(Send)
^c	Copy	(Send)
^v	Paste	(Send)
^s	Save File	(Send)
^a	Select All	(Send)
- - - - - - - - - - - - - - - -
Close Menu	{menu.Gui_OnEscape}	Return To mSpot	(Function)
Reload Menu	{menu.Reload}	Reflect The Latest Content	(Function)
Edit Menu	{EditMenu}	(Gosub)
Demo One	{demo1}	hello world	(Gosub)
Demo Two	{demo2}	foobar	(Gosub)
Demo Three	{demo3}	blablabla	(Function)
Demo Four	{demo4}	bla bla	(Func+Param)	dummy1
Demo Five	{demo5}	bla bla bla	(Func+Param)	dummy1,dummy2
Script I	(Script)	msgbox `% "remote: " a.call("var","dummy1@pool")
Script II	(Script)	msgbox `% "remote: " a.call("var","dummy2@pool")
Script III	(Script)	a.call("var","Set:dummy3@pool",a_tickcount)|| ||msgbox `% "remote: " a.call("var","dummy3@pool")
Verify	{demo4}	(Func+Param)	dummy3
Script IV	(Script)	{Apart}
[:Text Filter:RICHEDIT50W1:] Script V	(Script)	{fghij}
Script VI	(Script)	{menuDemo}
= = = Bottom = = =
;
{Script Apart}
a.call("var","Set:dummy3@pool",a_tickcount "(a)")
msgbox `% "remote: " a.call("var","dummy3@pool")
{/Script}

{Script fghij}
a.call("demo5", "p1p", "p2p")
If InStr(a.call("var","dummy3@pool"), "(a)")
	msgbox found "(a)" in dummy3
{/Script}
)
FileAppend, %_menu_%, MenuText.txt, UTF-8
		}
	IfNotExist, menuDemo.ahk
FileAppend, msgbox im an island, menuDemo.ahk, UTF-8
	}
}

IsGetSet(Param, ByRef a, ByRef v, ByRef value) {	;written by SundayProgrammer
	a := SubStr(Param, 1, 4)
	If (a = "Set:") or (a = "Get:")
	{	v := Trim((p := InStr(Param, "=")) ? (SubStr(Param, 5, p - 5), value := SubStr(Param, p + 1)) : SubStr(Param, 5))
		Return True
	}Return False
}
var(name, value = "") {	;written by SundayProgrammer
	global
	local p, a, v, m1, m2, m3, segc
	If p := InStr(name, "@")
	{	a := SubStr(name, 1, 4), v := Trim(SubStr(name, 1, p - 1)), name := Trim(SubStr(name, p + 1))
		If (a = "Set:") or (a = "Get:")
			v := Trim(SubStr(v, 5))
		Else a := StrLen(value) ? "Set:" : "Get:"
		If StrLen(v) and StrLen(name)
		{	v := a v, v .= (a = "Set:") ? "=" value : ""
			If InStr(name, ".")
			{	Loop, Parse, name, .
					m%A_Index% := A_LoopField, segc := A_Index
				If segc = 2
					v := %m1%[m2](v)
				Else If segc = 3
					v := %m1%[m2][m3](v)
				Else v = Error: Segments More Than Allowed.
			}Else v := %name%(v)
		}Else v = Error: Invalid Parameter "%v%@%name%"
	}Else
	{	a := SubStr(name, 1, 4)
		If (a = "Set:") or (a = "Get:")
			name := Trim(SubStr(name, 5))
		If InStr(name, ".")
		{	Loop, Parse, name, .
				m%A_Index% := A_LoopField, segc := A_Index
			If segc = 2
			{	If (a = "Set:") or StrLen(value)
					%m1%[m2] := value
				v := %m1%[m2]
			}Else If segc = 3
			{	If (a = "Set:") or StrLen(value)
					%m1%[m2][m3] := value
				v := %m1%[m2][m3]
			}Else v = Error: Segments More Than Allowed.
		}Else
		{	If (a = "Set:") or StrLen(value)
				%name% := value
			v := %name%
		}
	}
	Return v
}

Class agent {	;written by SundayProgrammer who got the idea from this post https://www.autohotkey.com/boards/viewtopic.php?p=200597#p200597
	Call(name, p*) {	;allows you to call any function in this script
		If InStr(name, ".")
		{	Loop, Parse, name, .
				m%A_Index% := A_LoopField, segc := A_Index
			If segc = 2
				Return %m1%[m2](p*)
			Else If segc = 3
				Return %m1%[m2][m3](p*)
			Else Return "Error: Segments More Than Allowed."
		}Return %name%(p*)
	}
}

#IfWinActive ahk_group SpotMenu
	WheelUp::
	WheelDown::
	+WheelUp::
	+WheelDown::OnScroll(InStr(A_ThisHotkey, "Down") ? 1 : 0, 0, GetKeyState("Shift") ? 0x114 : 0x115, WinExist())
#If

WM_DISPLAYCHANGE() {
	WinGetPos, x, y,,, mSpot
	If (x > A_ScreenWidth - 50)
		x := A_ScreenWidth - 100
	If (y > A_ScreenHeight - 125)
		y := A_ScreenHeight - 175
	Gui, mSpot:Show, x%x% y%y% NoActivate
}

WM_ACTIVATE() {
	IfWinActive, mSpot
		WinSet, TransColor, White, mSpot
	Else WinSet, TransColor, White 64, mSpot
}

Class menu {	;written by SundayProgrammer
Spot() {
	If not StrLen(this.Spots)
		this.Spots := this.sgnHistory := "Spots"
	If not StrLen(this.Color)
		this.Color := this.fscHistory := "B2D6F3"
	Gui, mSpot:New
	Gui, +LastFound -Caption +AlwaysOnTop +ToolWindow
	Gui, Color, White
	Gui, Margin, 0, 0
	Gui, Font, s150, Consolas
	Gui, Add, Text, x0 y0 cFFB10F BackgroundTrans, ●
	OnSuch := this.HitEvent.Bind(this)
	GuiControl +g, Static1, % OnSuch
	WinSet, TransColor, White 64
	x := A_ScreenWidth - 100, y := A_ScreenHeight - 530
	Gui, Show, x%x% y%y% NoActivate, mSpot
	this.HitEvent() ; Init sPos
	OnMessage(0x7E, "WM_DISPLAYCHANGE")
	OnMessage(0x06, "WM_ACTIVATE")
}
HitEvent(hwnd := "") {
	static sPos
	If not StrLen(sPos) or (hwnd = "mSpot")
	{	WinGetPos, x, y,,, mSpot
		sPos := x "," y
	}If not StrLen(hwnd) or (hwnd = "mSpot")
		Return sPos
	SendMessage, 0xA1, 2,,, A ; WM_NCLBUTTONDOWN
	GuiControlGet, wHwnd, Hwnd, %hwnd%
	WinGetPos, x, y,,, ahk_id %wHwnd%
	If x "," y = sPos
	{	Gui, mSpot:Hide
		this.SpotMenu(var("_menu_@pool"))
	}Else
	{	sPos := x "," y
		SetTimer, Deactivate, -500
	}Return
	Deactivate:
		Gui, mSpot:Hide
		Gui, mSpot:Show, NoActivate
		WinSet, TransColor, White 64, mSpot
		Return
}
SpotMenu(t) {
	global TT
	WinWaitNotActive, mSpot
	WinGetActiveTitle, sWin
	ControlGetFocus, sCtl, A
	Loop, Parse, t, `n
		If SubStr(Trim(A_LoopField), 1, 1) not = ";"
		If RegExMatch(A_LoopField, "i)^\s*{\s*Script\s+[^}]+?\s*}")
			Break
		Else If RegExMatch(A_LoopField, "[^\t]+(?=\t)", m)
			w%A_Index% := this.GetButtonWidth(Trim(RegExReplace(m, "\s*\[([^\]]+)]")), "30 bold", "Consolas")
	OnMessage(0x115, "OnScroll") ; WM_VSCROLL
	OnMessage(0x114, "OnScroll") ; WM_HSCROLL
	OnMessage(0x20A, "OnScroll") ; WM_MOUSEWHEEL
	OnMessage(0x20E, "OnScroll") ; WM_MOUSEHWHEEL
	OnMessage(0x119, "gHandler") ; WM_GESTURE
	Gui, menu:New, hwndHmenu +Labelmenu.Gui_On
	Gui, +Resize +0x300000	; WS_VSCROLL | WS_HSCROLL
	TT := New GuiControlTips(Hmenu), TT.SetDelayTimes(1000, 15000, -1)
	Gui, Font, s30 bold, Consolas
	ci := nl := 1
	Loop, Parse, t, `n
		If SubStr(Trim(A_LoopField), 1, 1) not = ";"
		If RegExMatch(A_LoopField, "[^\t]+(?=\t)", m)
		{	Passed := False
			If RegExMatch(m, "\s*\[([^\]]+)]", mm)
			If RegExMatch(Trim(mm1), ":([^:]+(:[^:]+)?):", mm)
			If pos := InStr(mm1, ":")
				If not RegExMatch(sWin, Trim(SubStr(mm1, 1, pos - 1))) or not RegExMatch(sCtl, Trim(SubStr(mm1, pos + 1)))
					Continue
				Else Passed := True
			Else If not RegExMatch(sWin, Trim(mm1))
				Continue
			Else Passed := True
			If Passed
				m := Trim(RegExReplace(m, "\s*\[([^\]]+)]"))
			If not nl
				nl := (gx + gw + 25 + w%A_Index%) > A_ScreenWidth - 40
			p := nl ? "" : "+", y := nl ? "y+28" : ""
			Gui, Add, Button, HwndhBtn%ci% x%p%25 %y% h57, % (m, pi := ci, ci++)
			OnSuch := this.MenuClick.Bind(this)
			GuiControl +g, Button%pi%, % OnSuch
			GuiControlGet, g, Pos, Button%pi%
			TT.Attach(hBtn%pi%, RegExReplace(RegExReplace(SubStr(A_LoopField, 0) = "`r" ? SubStr(A_LoopField, 1, -1) : A_LoopField, "[^\t]+\t",,, 1), "\t", "`n"))
			nl := (gx + gw) > (A_ScreenWidth - 116)
		}Else If RegExMatch(A_LoopField, "i)^\s*{\s*Script\s+[^}]+?\s*}")
			Break
		Else
		{	Gui, Add, Text, x25 cWhite, % SubStr(A_LoopField, 0) = "`r" ? SubStr(A_LoopField, 1, -1) : A_LoopField
			nl := True
		}
	Gui, Color, Black
	TT.SetTitle("Descriptions", LoadPicture("shell32.dll", "Icon222", ImageType))
	SetCtrlFont(TT.HTIP, "s20", "Arial New")
	Gui, +LastFound
	WinSet, Transparent, 180
	Gui, Show,, Touch Friendly Menu
	GroupAdd, SpotMenu, % "ahk_id " . WinExist()
	this.t := t
}
Gui_OnSize() {
	UpdateScrollBars(A_Gui, A_GuiWidth, A_GuiHeight)
}
Gui_OnEscape() {
	menu.OnClose()
	Gui, mSpot:Show, NoActivate
	WinSet, TransColor, White 64, mSpot
}
Gui_OnClose() {
	ObjRegisterActive(agent, "")
	ExitApp
}
OnClose() {
	OnMessage(0x115, "") ; WM_VSCROLL
	OnMessage(0x114, "") ; WM_HSCROLL
	OnMessage(0x20A, "") ; WM_MOUSEWHEEL
	OnMessage(0x20E, "") ; WM_MOUSEHWHEEL
	OnMessage(0x119, "") ; WM_GESTURE
	Gui, menu:Destroy
}
GetButtonWidth(t, s, f) {
	Gui, New
	Gui, Font, s%s%, % f
	Gui, Add, Button,, % t
	GuiControlGet, g, Pos, Button1
	Gui, Destroy
	Return gW
}
MenuClick(h) {
	global TT
	theText := A_GuiControl
	this.OnClose()
	WinWaitNotActive, Touch Friendly Menu
	Sleep, 500
	this.do(theText, TT.GetText(h))
	Gui, mSpot:Show, NoActivate
	WinSet, TransColor, White 64, mSpot
}
do(theText, t) {
	If RegExMatch(t, "{\K[^}]+(?=})", m)
		If not InStr(t, "(Send)")
			theText := m
	If InStr(t, "(Send)")
		Send, %theText%
	Else If InStr(t, "(Gosub)")
		Gosub, %theText%
	Else If InStr(t, "(Function)")
		If pos := InStr(theText, ".")
			className := SubStr(theText, 1, pos - 1), method := SubStr(theText, pos + 1), %className%[method]()
		Else %theText%()
	Else If InStr(t, "(Func+Param)")
	{	RegExMatch(t, "[^\n]+$", m)
		If InStr(m, ",")
		{	Loop, Parse, m, `,
				p%A_Index% := var(A_LoopField "@pool")
			StrReplace(m, ",",, c)
			If c = 1
				%theText%(p1, p2)
			Else If c = 2
				%theText%(p1, p2, p3)
			Else If c = 3
				%theText%(p1, p2, p3, p4)
			Else If c = 4
				%theText%(p1, p2, p3, p4, p5)
			Else If c = 5
				%theText%(p1, p2, p3, p4, p5, p6)
			Else If c = 6
				%theText%(p1, p2, p3, p4, p5, p6, p7)
			Else If c = 7
				%theText%(p1, p2, p3, p4, p5, p6, p7, p8)
			Else If c = 8
				%theText%(p1, p2, p3, p4, p5, p6, p7, p8, p9)
			Else If c = 9
				%theText%(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10)
			Else{}	;more than 10 parameters is not supported
		}Else %theText%(var(m "@pool"))
	}Else If InStr(t, "(Script)")
		If RegExMatch(t, "{\K[^}]+(?=})", m)
			If RegExMatch(this.t, "is){\s*Script\s+" m "\s*}(.+?){\s*/\s*Script\s*}", mm)
				ExecScript(var("s1l@pool") this.HandleInclude(mm1),, A_AhkPath)
			Else IfExist, % m := (SubStr(Trim(m), -3) = ".ahk" ? m : Trim(m) ".ahk")
			{	FileRead, mm1, %m%
				ExecScript(var("s1l@pool") mm1,, A_AhkPath)
			}Else{}
		Else RegExMatch(t, "\(Script\)[\n\t]+\K.*$", m), m := StrReplace(m, "|| ||", "`n"), ExecScript(var("s1l@pool") this.HandleInclude(m),, A_AhkPath)
}
HandleInclude(s) {
	If RegExMatch(s, "is)#\s*Include\s+[^#]+#")
	{	RegExReplace(s, "is)#\s*Include\s+[^#]+#",, n), pos := 1, m := ""
		Loop, %n%
			pos := RegExMatch(s, "is)#\s*Include\s+\K[^#]+(?=#)", m, pos + StrLen(m)), RegExMatch(this.t, "is){\s*Script\s+" Trim(m) "\s*}(.+?){\s*/\s*Script\s*}", mm), s := RegExReplace(s, "is)#\s*Include\s+" Trim(m) "\s*#", RegExMatch(mm1, "is)#\s*Include\s+[^#]+#") ? this.HandleInclude(mm1) : mm1)
	}
	Return s
}
ExtraSpot() {
	btn := "B" A_TickCount
	MouseGetPos,,, mow, moc
	WinGetTitle, mow, ahk_id %mow%
	ControlGetText, m, %moc%, %mow%
	MouseGetPos,,,, h, 2
	t := agent.Call("TT.GetText", h)
	menu.Gui_OnEscape()
	Gui, shape:New, +ToolWindow
	Gui, Font, s30, Consolas
	Gui, Add, ListBox, r15, ●|◆|◼|◉|◈|▣|◙|▲|▼|◀|▶|◢|◥|◣|◤
	OnSuch := this.Pick.Bind(this)
	GuiControl +g, ListBox1, % OnSuch
	Gui, Show,, Select A Shape Please
	var("Set:Shape@pool", False), this.CurItem := "●"
	While, not var("Shape@pool") and WinExist("Select A Shape Please")
		Sleep, 100
	If not var("Shape@pool")
		var("Set:Shape@pool", this.CurItem)
	Gui, shape:Destroy
	Gui, %btn%:New, hwndHspot
	Gui, +LastFound -Caption +AlwaysOnTop +ToolWindow
	Gui, Color, White
	Gui, Margin, 0, 0
	Gui, Font, s120, Consolas
	Color := this.Color
	Gui, Add, Text, x0 y0 c%Color% BackgroundTrans, % var("Shape@pool")
	OnSuch := this.xHit.Bind(this)
	GuiControl +g, Static1, % OnSuch
	Gui, Add, Text, Hidden, %m%
	Gui, Add, Text, Hidden, %t%
	Gui, Add, Text, Hidden, % this.Spots
	Gui, Add, Text, Hidden, %Color%
	WinSet, TransColor, White 64
	x := A_ScreenWidth - 100
	Gui, Show, x%x% NoActivate, %btn%spot
	GroupAdd, % this.Spots, % "ahk_id " . WinExist()
	this.wt := btn "spot"
	ControlGet, sid, hwnd,, Static1, %btn%spot
	this.xHit(sid + 0) ; Init sPos
}
xHit(hwnd := "") {
	static sPos := []
	If SubStr(hwnd, -3) = "spot"
	{	wt := hwnd
		WinGetPos, x, y,,, %wt%
		ControlGet, hwnd, hwnd,, Static1, %wt%
		sPos[hwnd + 0] := x "," y
		Return
	}Else If not StrLen(sPos[hwnd])
	{	WinGetPos, x, y,,, % this.wt
		sPos[hwnd] := x "," y
		Return
	}SendMessage, 0xA1, 2,,, A ; WM_NCLBUTTONDOWN
	GuiControlGet, wHwnd, Hwnd, %hwnd%
	WinGetPos, x, y,,, ahk_id %wHwnd%
	WinGetActiveTitle, wt
	var("Set:wName@pool", wName := SubStr(wt, 1, -4))
	If x "," y = sPos[hwnd]
	{	GuiControlGet, theText,, Static2
		GuiControlGet, t,, Static3
		Gui, %wName%:Hide
		menu.do(theText, t)
	}Else sPos[hwnd] := x "," y
	SetTimer, DeactivateIt, -500
	Return
	DeactivateIt:
		wName := var("wName@pool")
		Gui, %wName%:Hide
		Gui, %wName%:Show, NoActivate
		Return
}
Pick(hwnd := "") {
	GuiControlGet, Choice,, ListBox1
	If StrLen(Choice)
	{	this.CurItem := Choice
		If not InStr(A_PriorKey, "LButton")
			Return
	}Else If A_ThisHotkey in Space,Enter,``
		Choice := this.CurItem
	var("Set:Shape@pool", Choice)
}
Flick() {
	dhw := A_DetectHiddenWindows
	DetectHiddenWindows, On
	tmm := A_TitleMatchMode
	SetTitleMatchMode, RegEx
	WinGet, wList, List, [sS]pot$
	Loop, % wList
	{	hwnd := wList%A_Index%
		WinGetTitle, wt, ahk_id %hwnd%
		If RegExMatch(wt, "^B\d+spot$") or wt = "mSpot"
		{	WinGetPos, x,,,, ahk_id %hwnd%
			WinMove, ahk_id %hwnd%,, (A_ScreenWidth - 110) - x
			If wt = mSpot
				this.HitEvent(wt)
			Else this.xHit(wt)
		}
	}SetTitleMatchMode, %tmm%
	DetectHiddenWindows, %dhw%
}
Append() {
	t := "`n`n" this.Save
	FileAppend, %t%, MenuText.txt, UTF-8
	MsgBox, Saved
	FileRead, _menu_, MenuText.txt
	var("Set:_menu_@pool", _menu_)
}
LoadSpots() {
	t := var("_menu_@pool")
	tt := RegExReplace(RegExReplace(t, "im`a)^[ \t]*(?!{SpotsList@\w+}).*\R?"), "{SpotsList@(\w+)}", "$1"), RegExReplace(tt, "im`a)^[ \t]*\w+[ \t]*$",, mCount)
	If mCount > 1
	{	ReverseByLine(tt)
		Gui, losp:New, +ToolWindow +Delimiter`n
		Gui, Font, s30, Consolas
		Gui, Add, ListBox, r15, %tt%
		OnSuch := this.lsPick.Bind(this)
		GuiControl +g, ListBox1, % OnSuch
		Gui, Show,, Select A Batch Please
		this.Batch := False, RegExMatch(tt, "\w+", m), this.CurItem := m
		While, not this.Batch and WinExist("Select A Batch Please")
			Sleep, 100
		If not this.Batch
			this.Batch := this.CurItem
		Gui, losp:Destroy
	}Else RegExMatch(tt, "\w+", m), this.Batch := m
	RegExMatch(this.Batch, "\w+", b), RegExMatch(t, "is){SpotsList@" b "}\K.+?(?={/SpotsList})", m)
	Loop, Parse, m, `n
	{	LoopField := SubStr(A_LoopField, 0) = "`r" ? SubStr(A_LoopField, 1, -1) : A_LoopField
		If not RegExMatch(LoopField, "[^\-\s]")
			If StrLen(wi)
			{	If (wt = "mSpot") {
Gui, mSpot:New
Gui, +LastFound -Caption +AlwaysOnTop +ToolWindow
Gui, Color, White
Gui, Margin, 0, 0
Gui, Font, s150, Consolas
Gui, Add, Text, x0 y0 cFFB10F BackgroundTrans, ●
OnSuch := this.HitEvent.Bind(this)
GuiControl +g, Static1, % OnSuch
WinSet, TransColor, White 64
Gui, Show, x%x% y%y% NoActivate, mSpot
this.HitEvent(wt) ; Init sPos
OnMessage(0x7E, "WM_DISPLAYCHANGE")
OnMessage(0x06, "WM_ACTIVATE")
wt := x := y := wi := ""
				}Else{
btn := SubStr(wt, 1, -4)
Gui, %btn%:New, hwndHspot
Gui, +LastFound -Caption +AlwaysOnTop +ToolWindow
Gui, Color, White
Gui, Margin, 0, 0
Gui, Font, s120, Consolas
Gui, Add, Text, x0 y0 c%Color% BackgroundTrans, %shape%
OnSuch := this.xHit.Bind(this)
GuiControl +g, Static1, % OnSuch
Gui, Add, Text, Hidden, %blbl%
Gui, Add, Text, Hidden, %ttip%
Gui, Add, Text, Hidden, %gname%
Gui, Add, Text, Hidden, %Color%
WinSet, TransColor, White 64
Gui, Show, x%x% y%y% NoActivate, %wt%
GroupAdd, %gname%, % "ahk_id " . WinExist()
this.wt := wt
ControlGet, sid, hwnd,, Static1, %wt%
this.xHit(sid) ; Init sPos
wt := x := y := shape := blbl := ttip := gname := color := wi := ""
				}
			}Else{}
		Else If RegExMatch(LoopField, "Title\t= \K.*$", mm)
			wt := mm, wi := mm
		Else If RegExMatch(LoopField, "Pos\t= \K.*$", mm)
			RegExMatch(mm, ".*(?=,)", x), RegExMatch(mm, ",\K.*$", y), wi .= "`n" mm
		Else If RegExMatch(LoopField, "Text1\t= \K.*$", mm)
			shape := mm, wi .= "`n" mm
		Else If RegExMatch(LoopField, "Text2\t= \K.*$", mm)
			blbl := mm, wi .= "`n" mm
		Else If RegExMatch(LoopField, "Text3\t= \K.*$", mm)
			ttip := StrReplace(mm, "`t", "`n"), wi .= "`n" mm
		Else If RegExMatch(LoopField, "Text4\t= \K.*$", mm)
			gname := mm, wi .= "`n" mm, glist .= "`n" mm
		Else If RegExMatch(LoopField, "Text5\t= \K.*$", mm)
			color := mm, wi .= "`n" mm, clist .= "`n" mm
	}v := this.sgnHistory glist
	Sort, v, U
	this.sgnHistory := v, v := this.fscHistory clist
	Sort, v, U
	this.fscHistory := v
}
lsPick(hwnd := "") {
	GuiControlGet, Choice,, ListBox1
	If StrLen(Choice)
	{	this.CurItem := Choice
		If A_TickCount - (StrLen(this.DownUp) ? this.DownUp : 0) < 100
			Return
	}Else If A_ThisHotkey in Space,$Enter,``
		Choice := this.CurItem
	this.Batch := Choice
}
Reload() {
	FileRead, _menu_, MenuText.txt
	var("Set:_menu_@pool", _menu_)
	SetTimer, ReloadMenu, -500
	Return
	ReloadMenu:
		Gui, mSpot:Hide
		menu.SpotMenu(var("_menu_@pool"))
		Return
}
}	;end of class

keyRButton() {
	MouseGetPos, xb4, yb4, mow, moc
	WinGetTitle, mow, ahk_id %mow%
	beg := A_TickCount
	While, GetKeyState("RButton", "P")
	{	MouseGetPos, x, y
		If SubStr(mow, -3) = "Spot" and moc = "Static1" and (Abs(x - xb4) > 5 or Abs(y - yb4) > 5)
		{	If mow = mSpot
				menu.Flick()
			Else
			{	WinGetPos, x,,,, %mow%
				WinMove, %mow%,, (A_ScreenWidth - 110) - x
				menu.xHit(mow)
			}beg := False
			Break
		}Else If A_TickCount - beg > 1000
		{	IfWinNotExist, Touch Friendly Menu
			IfWinExist, mSpot
				If (mow = "mSpot") and (moc = "Static1")
				{	Gui, mSpot:Destroy
					beg := False
				}Else{}
			Else menu.Spot(), menu.HitEvent("mSpot"), beg := False
			SoundBeep
			Break
		}
	}If not beg
		Return
	If SubStr(mow, -3) = "Spot" and moc = "Static1"
		If mow = mSpot
		{	Spots := menu.Spots
			WinGet, c, Count, ahk_group %Spots%
			dhw := A_DetectHiddenWindows
			DetectHiddenWindows, On
			WinGet, ca, Count, ahk_group %Spots%
			DetectHiddenWindows, %dhw%
			If (ca > c)
			{	aw := WinExist("A")
				WinShow, ahk_group %Spots%
				WinActivate, ahk_id %aw%
				WinWaitActive
			}Else If ca
				WinHide, ahk_group %Spots%
			Else Gui, mSpot:Destroy
		}Else ;WinClose, %mow%
		{	GuiName := SubStr(mow, 1, -4)
			Gui, %GuiName%:Destroy
		}
	Else If mow = Touch Friendly Menu
		If SubStr(moc, 1, 6) = "Button"
			menu.ExtraSpot()
		Else{}
	Else Send, {RButton}
}

ReverseByLine(ByRef text, delimiter := "`n") {	;written by SundayProgrammer
	array := StrSplit(SubStr(text, 0) = delimiter ? SubStr(text, 1, StrLen(text) - 1) : text, delimiter), mi := array.MaxIndex()
	Loop, % mi
		If A_Index > 1
			text .= array[mi - A_Index + 1] delimiter
		Else text := array[mi] delimiter
}

SetCtrlFont(CtrlHwnd, FontOptions := "", FontName := "") {	;written by iPhilip
   static WM_SETFONT := 0x0030, WM_GETFONT := 0x0031
   DefaultGui := A_DefaultGui
   Gui, New
   Gui, Font, % FontOptions, % FontName
   Gui, Add, Text, hwndhText, Text
   hFont := DllCall("SendMessage", "Ptr", hText, "UInt", WM_GETFONT, "Ptr", 0, "Ptr", 0, "Ptr")
   Gui, Destroy
   Gui, %DefaultGui%:Default
   DllCall("SendMessage", "Ptr", CtrlHwnd, "UInt", WM_SETFONT, "Ptr", hFont, "Ptr", true)
   Return hFont
}

; ======================================================================================================================
; Namespace:      GuiControlTips
; AHK version:    AHK 1.1.14.03
; Function:       Helper object to simply assign ToolTips for GUI controls
; Tested on:      Win 7 (x64)
; Change history:
;                 1.1.00.01/2020-06-03/just me - fixed missing Static WS_EX_TOPMOST
;                 1.1.00.00/2014-03-06/just me - Added SetDelayTimes()
;                 1.0.01.00/2012-07-29/just me
; ======================================================================================================================
; CLASS GuiControlTips
;
; The class provides four public methods to register (Attach), unregister (Detach), update (Update), and
; disable/enable (Suspend) common ToolTips for GUI controls.
;
; Usage:
; To assign ToolTips to GUI controls you have to create a new instance of GuiControlTips per GUI with
;     MyToolTipObject := New GuiControlTips(HGUI)
; passing the HWND of the GUI.
;
; After this you may assign ToolTips to your GUI controls by calling
;     MyToolTipObject.Attach(HCTRL, "ToolTip text")
; passing the HWND of the control and the ToolTip's text. Pass True/1 for the optional third parameter if you
; want the ToolTip to be shown centered below the control.
;
; To remove a ToolTip call
;     MyToolTipObject.Detach(HCTRL)
; passing the HWND of the control.
;
; To update the ToolTip's text call
;     MyToolTipObject.Update(HCTRL, "New text!")
; passing the HWND of the control and the new text.
;
; To deactivate the ToolTips call
;     MyToolTipObject.Suspend(True),
; to activate them again afterwards call
;     MyToolTipObject.Suspend(False).
;
; To adjust the ToolTips delay times call
;     MyToolTipObject.SetDelayTimesd(),
; specifying the delay times in milliseconds.
;
; That's all you can / have to do!
; ======================================================================================================================
Class GuiControlTips {	;written by @"just me"
   ; ===================================================================================================================
   ; INSTANCE variables
   ; ===================================================================================================================
   HTIP := 0
   HGUI := 0
   CTRL := {}
   ; ===================================================================================================================
   ; CONSTRUCTOR           __New()
   ; ===================================================================================================================
   __New(HGUI) {
      Static CLASS_TOOLTIP      := "tooltips_class32"
      Static CW_USEDEFAULT      := 0x80000000
      Static TTM_SETMAXTIPWIDTH := 0x0418
      Static TTM_SETMARGIN      := 0x041A
      Static WS_EX_TOPMOST      := 0x00000008
      Static WS_STYLES          := 0x80000002 ; WS_POPUP | TTS_NOPREFIX
      ; Create a Tooltip control ...
      HTIP := DllCall("User32.dll\CreateWindowEx", "UInt", WS_EX_TOPMOST, "Str", CLASS_TOOLTIP, "Ptr", 0
                    , "UInt", WS_STYLES
                    , "Int", CW_USEDEFAULT, "Int", CW_USEDEFAULT, "Int", CW_USEDEFAULT, "Int", CW_USEDEFAULT
                    , "Ptr", HGUI, "Ptr", 0, "Ptr", 0, "Ptr", 0, "Ptr")
      If ((ErrorLevel) || !(HTIP))
         Return False
      ; ... prepare it to display multiple lines if required
      DllCall("User32.dll\SendMessage", "Ptr", HTIP, "Int", TTM_SETMAXTIPWIDTH, "Ptr", 0, "Ptr", A_ScreenWidth*96//A_ScreenDPI)	;touched by SundayProgrammer who took it from iPhilip response for AddTooltip v2.0
      ; ... set the instance variables
      This.HTIP := HTIP
      This.HGUI := HGUI
      If (DllCall("Kernel32.dll\GetVersion", "UInt") & 0xFF) < 6 ; to avoid some XP issues ...
         This.Attach(HGUI, "") ; ... register the GUI with an empty tiptext
   }
   ; ===================================================================================================================
   ; DESTRUCTOR            __Delete()
   ; ===================================================================================================================
   __Delete() {
      If (This.HTIP) {
         DllCall("User32.dll\DestroyWindow", "Ptr", This.HTIP)
      }
   }
   ; ===================================================================================================================
   ; PRIVATE METHOD        SetToolInfo - Create and fill a TOOLINFO structure
   ; ===================================================================================================================
   SetToolInfo(ByRef TOOLINFO, HCTRL, TipTextAddr, CenterTip = 0) {
      Static TTF_IDISHWND  := 0x0001
      Static TTF_CENTERTIP := 0x0002
      Static TTF_SUBCLASS  := 0x0010
      Static OffsetSize  := 0
      Static OffsetFlags := 4
      Static OffsetHwnd  := 8
      Static OffsetID    := OffsetHwnd + A_PtrSize
      Static OffsetRect  := OffsetID + A_PtrSize
      Static OffsetInst  := OffsetRect + 16
      Static OffsetText  := OffsetInst + A_PtrSize
      Static StructSize  := (4 * 6) + (A_PtrSize * 6)
      Flags := TTF_IDISHWND | TTF_SUBCLASS
      If (CenterTip)
         Flags |= TTF_CENTERTIP
      VarSetCapacity(TOOLINFO, StructSize, 0)
      NumPut(StructSize, TOOLINFO, OffsetSize, "UInt")
      NumPut(Flags, TOOLINFO, OffsetFlags, "UInt")
      NumPut(This.HGUI, TOOLINFO, OffsetHwnd, "Ptr")
      NumPut(HCTRL, TOOLINFO, OffsetID, "Ptr")
      NumPut(TipTextAddr, TOOLINFO, OffsetText, "Ptr")
      Return True
   }
   ; ===================================================================================================================
   ; PUBLIC METHOD         Attach         -  Assign a ToolTip to a certain control
   ; Parameters:           HWND           -  Control's HWND
   ;                       TipText        -  ToolTip's text
   ;                       Optional:      ------------------------------------------------------------------------------
   ;                       CenterTip      -  Centers the tooltip window below the control
   ;                                         Values:  True/False
   ;                                         Default: False
   ; Return values:        On success: True
   ;                       On failure: False
   ; ===================================================================================================================
   Attach(HCTRL, TipText, CenterTip = False) {
      Static TTM_ADDTOOL  := A_IsUnicode ? 0x0432 : 0x0404 ; TTM_ADDTOOLW : TTM_ADDTOOLA
      If !(This.HTIP) {
         Return False
      }
      If This.CTRL.HasKey(HCTRL)
         Return False
      TOOLINFO := ""
      This.SetToolInfo(TOOLINFO, HCTRL, &TipText, CenterTip)
      If DllCall("User32.dll\SendMessage", "Ptr", This.HTIP, "Int", TTM_ADDTOOL, "Ptr", 0, "Ptr", &TOOLINFO) {
         This.CTRL[HCTRL] := 1
		 This.TTT[HCTRL] := TipText	;added by SundayProgrammer
         Return True
      } Else {
        Return False
      }
   }
   ; ===================================================================================================================
   ; PUBLIC METHOD         Detach         -  Remove the ToolTip for a certain control
   ; Parameters:           HWND           -  Control's HWND
   ; Return values:        On success: True
   ;                       On failure: False
   ; ===================================================================================================================
   Detach(HCTRL) {
      Static TTM_DELTOOL  := A_IsUnicode ? 0x0433 : 0x0405 ; TTM_DELTOOLW : TTM_DELTOOLA
      If !This.CTRL.HasKey(HCTRL)
         Return False
      TOOLINFO := ""
      This.SetToolInfo(TOOLINFO, HCTRL, 0)
      DllCall("User32.dll\SendMessage", "Ptr", This.HTIP, "Int", TTM_DELTOOL, "Ptr", 0, "Ptr", &TOOLINFO)
      This.CTRL.Remove(HCTRL, "")
	  This.TTT.Remove(HCTRL, "")	;added by SundayProgrammer
      Return True
   }
   ; ===================================================================================================================
   ; PUBLIC METHOD         Update         -  Update the ToolTip's text for a certain control
   ; Parameters:           HWND           -  Control's HWND
   ;                       TipText        -  New text                                                      
   ; Return values:        On success: True
   ;                       On failure: False
   ; ===================================================================================================================
   Update(HCTRL, TipText) {
      Static TTM_UPDATETIPTEXT  := A_IsUnicode ? 0x0439 : 0x040C ; TTM_UPDATETIPTEXTW : TTM_UPDATETIPTEXTA
      If !This.CTRL.HasKey(HCTRL)
         Return False
      TOOLINFO := ""
      This.SetToolInfo(TOOLINFO, HCTRL, &TipText)
      DllCall("SendMessage", "Ptr", This.HTIP, "Int", TTM_UPDATETIPTEXT, "Ptr", 0, "Ptr", &TOOLINFO)
	  This.TTT[HCTRL] := TipText	;added by SundayProgrammer
      Return True
   }
   ;added by SundayProgrammer
   GetText(HCTRL) {
      If !This.CTRL.HasKey(HCTRL)
         Return ""
      Return This.TTT[HCTRL]
   }
   ; ===================================================================================================================
   ; PUBLIC METHOD         Suspend        -  Disable/enable the ToolTip control (don't show / show ToolTips)
   ; Parameters:           Mode           -  True/False (1/0)
   ;                                         Default: True/1
   ; Return values:        On success: True
   ;                       On failure: False
   ; Remarks:              ToolTips are enabled automatically on creation.
   ; ===================================================================================================================
   Suspend(Mode = True) {
      Static TTM_ACTIVATE := 0x0401
      If !(This.HTIP)
         Return False
      DllCall("SendMessage", "Ptr", This.HTIP, "Int", TTM_ACTIVATE, "Ptr", !Mode, "Ptr", 0)
      Return True
   }
   ; ===================================================================================================================
   ; PUBLIC METHOD         SetDelayTimes  -  Set the initial, pop-up, and reshow durations for a tooltip control.
   ; Parameters:           Init           -  Amount of time, in milliseconds, a pointer must remain stationary within
   ;                                         a tool's bounding rectangle before the tooltip window appears.
   ;                                         Default: -1 (system default time)
   ;                       PopUp          -  Amount of time, in milliseconds, a tooltip window remains visible if the
   ;                                         pointer is stationary within a tool's bounding rectangle.
   ;                                         Default: -1 (system default time)
   ;                       ReShow         -  Amount of time, in milliseconds, it takes for subsequent tooltip windows
   ;                                         to appear as the pointer moves from one tool to another.
   ;                                         Default: -1 (system default time)
   ; Return values:        On success: True
   ;                       On failure: False
   ; Remarks:              Times are set per ToolTip control and applied to all added tools.
   ; ===================================================================================================================
   SetDelayTimes(Init = -1, PopUp = -1, ReShow = -1) {
      Static TTM_SETDELAYTIME   := 0x0403
      Static TTDT_RESHOW   := 1
      Static TTDT_AUTOPOP  := 2
      Static TTDT_INITIAL  := 3
      DllCall("SendMessage", "Ptr", This.HTIP, "Int", TTM_SETDELAYTIME, "Ptr", TTDT_INITIAL, "Ptr", Init)
      DllCall("SendMessage", "Ptr", This.HTIP, "Int", TTM_SETDELAYTIME, "Ptr", TTDT_AUTOPOP, "Ptr", PopUp)
      DllCall("SendMessage", "Ptr", This.HTIP, "Int", TTM_SETDELAYTIME, "Ptr", TTDT_RESHOW , "Ptr", ReShow)
   }
   ;added by SundayProgrammer who largely took it from AddTooltip v2.0
   SetTitle(theTitle := "", theIcon := 0) {
      Static TTM_SETTITLE := A_IsUnicode ? 0x421 : 0x420 ; TTM_SETTITLEW : TTM_SETTITLEA
      If StrLen(theTitle) > 99
         theTitle := SubStr(theTitle, 1, 99)
      If theIcon is not Integer
         theIcon := 0
      DllCall("SendMessage", "Ptr", This.HTIP, "Int", TTM_SETTITLE, "Ptr", theIcon, "Ptr", &theTitle)
   }
}

UpdateScrollBars(GuiNum, GuiWidth, GuiHeight) {	;written by lexikos
    static SIF_RANGE=0x1, SIF_PAGE=0x2, SIF_DISABLENOSCROLL=0x8, SB_HORZ=0, SB_VERT=1
   
    Gui, %GuiNum%:Default
    Gui, +LastFound
   
    ; Calculate scrolling area.
    Left := Top := 9999
    Right := Bottom := 0
    WinGet, ControlList, ControlList
    Loop, Parse, ControlList, `n
    {
        GuiControlGet, c, Pos, %A_LoopField%
        if (cX < Left)
            Left := cX
        if (cY < Top)
            Top := cY
        if (cX + cW > Right)
            Right := cX + cW
        if (cY + cH > Bottom)
            Bottom := cY + cH
    }
    Left -= 8
    Top -= 8
    Right += 8
    Bottom += 8
    ScrollWidth := Right-Left
    ScrollHeight := Bottom-Top
   
    ; Initialize SCROLLINFO.
    VarSetCapacity(si, 28, 0)
    NumPut(28, si) ; cbSize
    NumPut(SIF_RANGE | SIF_PAGE, si, 4) ; fMask
   
    ; Update horizontal scroll bar.
    NumPut(ScrollWidth, si, 12) ; nMax
    NumPut(GuiWidth, si, 16) ; nPage
    DllCall("SetScrollInfo", "uint", WinExist(), "uint", SB_HORZ, "uint", &si, "int", 1)
   
    ; Update vertical scroll bar.
;     NumPut(SIF_RANGE | SIF_PAGE | SIF_DISABLENOSCROLL, si, 4) ; fMask
    NumPut(ScrollHeight, si, 12) ; nMax
    NumPut(GuiHeight, si, 16) ; nPage
    DllCall("SetScrollInfo", "uint", WinExist(), "uint", SB_VERT, "uint", &si, "int", 1)
   
    if (Left < 0 && Right < GuiWidth)
        x := Abs(Left) > GuiWidth-Right ? GuiWidth-Right : Abs(Left)
    if (Top < 0 && Bottom < GuiHeight)
        y := Abs(Top) > GuiHeight-Bottom ? GuiHeight-Bottom : Abs(Top)
    if (x || y)
        DllCall("ScrollWindow", "uint", WinExist(), "int", x, "int", y, "uint", 0, "uint", 0)
}

OnScroll(wParam, lParam, msg, hwnd) {	;written by lexikos
    static SIF_ALL=0x17, SCROLL_STEP=85 ;changed by SundayProgrammer from 10 to 85 for a more practical outcome
	static xpos := 0, ypos := 0	;added by SundayProgrammer - for touch gesture scrolling
	global gFlag	;added by SundayProgrammer - for touch gesture scrolling
if DllCall("GetParent", "uint", hwnd)	;added by SundayProgrammer - a quick fix for the scenario when any scrollable control is involved
	return	;added by SundayProgrammer - a quick fix for the scenario when any scrollable control is involved
   
    bar := (msg=0x115) or (msg=0x20A) ; (SB_HORZ=0, SB_VERT=1) or (WM_MOUSEHWHEEL=0, WM_MOUSEWHEEL=1)	;changed by SundayProgrammer - for WM_MOUSEWHEEL and WM_MOUSEHWHEEL
   
	if gFlag	;added by SundayProgrammer - for touch gesture scrolling
	{	gAction(xpos, ypos, bar, hwnd)	;added by SundayProgrammer - for touch gesture scrolling
		return	;added by SundayProgrammer - for touch gesture scrolling
	}	;added by SundayProgrammer - for touch gesture scrolling
   
    VarSetCapacity(si, 28, 0)
    NumPut(28, si) ; cbSize
    NumPut(SIF_ALL, si, 4) ; fMask
    if !DllCall("GetScrollInfo", "uint", hwnd, "int", bar, "uint", &si)
        return
   
    VarSetCapacity(rect, 16)
    DllCall("GetClientRect", "uint", hwnd, "uint", &rect)
   
    new_pos := NumGet(si, 20) ; nPos (saw "25" in another version, which exhibited a bug in my testing, whereas "20" (this version) worked fine so far)
   
    if msg=0x20A	;added by SundayProgrammer - for WM_MOUSEWHEEL
        wParam := wParam>0x780000	;added by SundayProgrammer - for WM_MOUSEWHEEL
    else if msg=0x20E	;added by SundayProgrammer - for WM_MOUSEHWHEEL
        wParam := wParam=0x780000	;added by SundayProgrammer - for WM_MOUSEHWHEEL
    action := wParam & 0xFFFF
    if action = 0 ; SB_LINEUP
        new_pos -= SCROLL_STEP
    else if action = 1 ; SB_LINEDOWN
        new_pos += SCROLL_STEP
    else if action = 2 ; SB_PAGEUP
        new_pos -= NumGet(rect, 12, "int") - SCROLL_STEP
    else if action = 3 ; SB_PAGEDOWN
        new_pos += NumGet(rect, 12, "int") - SCROLL_STEP
    else if (action = 5 || action = 4) ; SB_THUMBTRACK || SB_THUMBPOSITION
        new_pos := wParam>>16
    else if action = 6 ; SB_TOP
        new_pos := NumGet(si, 8, "int") ; nMin
    else if action = 7 ; SB_BOTTOM
        new_pos := NumGet(si, 12, "int") ; nMax
    else
        return
   
    min := NumGet(si, 8, "int") ; nMin
    max := NumGet(si, 12, "int") - NumGet(si, 16) ; nMax-nPage
    new_pos := new_pos > max ? max : new_pos
    new_pos := new_pos < min ? min : new_pos
   
    old_pos := NumGet(si, 20, "int") ; nPos (saw "25" in another version, which exhibited a bug in my testing, whereas "20" (this version) worked fine so far)
   
    x := y := 0
    if bar = 0 ; SB_HORZ
        x := old_pos-new_pos
    else
        y := old_pos-new_pos
    ; Scroll contents of window and invalidate uncovered area.
    DllCall("ScrollWindow", "uint", hwnd, "int", x, "int", y, "uint", 0, "uint", 0)
   
    ; Update scroll bar.
    NumPut(new_pos, si, 20, "int") ; nPos (saw "25" in another version, which exhibited a bug in my testing, whereas "20" (this version) worked fine so far)
    DllCall("SetScrollInfo", "uint", hwnd, "int", bar, "uint", &si, "int", 1)

	z := bar ? "y" : "x", %z%pos := new_pos	;added by SundayProgrammer - for touch gesture scrolling
}
gAction(byref xpos, byref ypos, bar, hwnd) {	;written by SundayProgrammer - for touch gesture scrolling
	VarSetCapacity(si, 28, 0), NumPut(28, si), NumPut(0x17, si, 4), DllCall("GetScrollInfo", "uint", hwnd, "int", bar, "uint", &si), aPos := NumGet(si, 20, "int"), z := bar ? "y" : "x"
	if not (%z%pos = aPos)
		x := y := 0, %z% := %z%pos - aPos, DllCall("ScrollWindow", "uint", hwnd, "int", x, "int", y, "uint", 0, "uint", 0), %z%pos := aPos
}
gHandler(wParam, lParam, msg, hwnd) {	;written by SundayProgrammer - for touch gesture scrolling
	global gFlag
	gFlag := true
	settimer, Reset_gFlag, -100
	return
	Reset_gFlag:
		gFlag := false
		return
}

ObjRegisterActive(Object, CLSID, Flags:=0) {	;written by Lexikos
    static cookieJar := {}
    if (!CLSID) {
        if (cookie := cookieJar.Remove(Object)) != ""
            DllCall("oleaut32\RevokeActiveObject", "uint", cookie, "ptr", 0)
        return
    }
    if cookieJar[Object]
        throw Exception("Object is already registered", -1)
    VarSetCapacity(_clsid, 16, 0)
    if (hr := DllCall("ole32\CLSIDFromString", "wstr", CLSID, "ptr", &_clsid)) < 0
        throw Exception("Invalid CLSID", -1, CLSID)
    hr := DllCall("oleaut32\RegisterActiveObject"
        , "ptr", &Object, "ptr", &_clsid, "uint", Flags, "uint*", cookie
        , "uint")
    if hr < 0
        throw Exception(format("Error 0x{:x}", hr), -1)
    cookieJar[Object] := cookie
}

;Function Ripped out of CodeQuickTester written by GeekDude https://github.com/G33kDude/CodeQuickTester
ExecScript(Script, Params="", AhkPath="")	;copy from "Execute code stored in a variable (dynamic variable?)" https://www.reddit.com/r/AutoHotkey/comments/ebwora/comment/fbcwvuy/?utm_source=share&utm_medium=web2x&context=3
{
	static Shell := ComObjCreate("WScript.Shell")
	Name := "\\.\pipe\AHK_CQT_" A_TickCount
	Pipe := []
	Loop, 3
	{
		Pipe[A_Index] := DllCall("CreateNamedPipe"
		, "Str", Name
		, "UInt", 2, "UInt", 0
		, "UInt", 255, "UInt", 0
		, "UInt", 0, "UPtr", 0
		, "UPtr", 0, "UPtr")
	}
	if !FileExist(AhkPath)
		throw Exception("AutoHotkey runtime not found: " AhkPath)
	if (A_IsCompiled && AhkPath == A_ScriptFullPath)
		AhkPath .= " /E"
	if FileExist(Name)
	{
		Exec := Shell.Exec(AhkPath " /CP65001 " Name " " Params)
		DllCall("ConnectNamedPipe", "UPtr", Pipe[2], "UPtr", 0)
		DllCall("ConnectNamedPipe", "UPtr", Pipe[3], "UPtr", 0)
		FileOpen(Pipe[3], "h", "UTF-8").Write(Script)
	}
	else ; Running under WINE with improperly implemented pipes
	{
		FileOpen(Name := "AHK_CQT_TMP.ahk", "w").Write(Script)
		Exec := Shell.Exec(AhkPath " /CP65001 " Name " " Params)
	}
	Loop, 3
		DllCall("CloseHandle", "UPtr", Pipe[A_Index])
	return Exec
}

that's it for now.

Return to “Scripts and Functions (v1)”

Who is online

Users browsing this forum: No registered users and 120 guests