Skip to content

Commit 15a5e5e

Browse files
committed
Enhance positioning features
- Added support for multiple user-defined "targets" (where to position the mouse cursor and for what number of clicks). The GUI, profile serialization and autoclicking logic have all been updated to incorporate this feature. - Changed the handling of window status bar updates from a loop to a timer function (asynchronous), for simplicity and to keep the `mouseX` and `mouseY` variables local.
1 parent 0a5387e commit 15a5e5e

File tree

4 files changed

+250
-78
lines changed

4 files changed

+250
-78
lines changed

src/AutoclickerGui.ahk

Lines changed: 165 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -172,45 +172,18 @@ AutoclickerGui.AddDropDownList("xp+62 yp-2 w140 vScheduling_PostStopAction_DropD
172172

173173
AutoclickerGui["Tab"].UseTab(SZ_TABLE.Tabs.Positioning)
174174

175-
AutoclickerGui.AddGroupBox("w226 h45 Section", "Boundary")
176-
makeRadioGroup(
177-
"Positioning_BoundaryMode_Radio"
178-
, [
179-
AutoclickerGui.AddRadio("xs+10 yp+20 v Checked", SZ_TABLE.Positioning_Boundary_Mode.None),
180-
AutoclickerGui.AddRadio("yp", SZ_TABLE.Positioning_Boundary_Mode.Point),
181-
AutoclickerGui.AddRadio("yp", SZ_TABLE.Positioning_Boundary_Mode.Box)
182-
]
183-
, Positioning_ChangedModeSelection
184-
)
185-
186-
PerBoundaryConfigControls := {
187-
%SZ_TABLE.Positioning_Boundary_Mode.None%: [],
188-
%SZ_TABLE.Positioning_Boundary_Mode.Point%: [
189-
AutoclickerGui.AddText("xs+10 ys+55 Hidden", "X:"),
190-
AutoclickerGui.AddEdit("xp+20 yp-2 w30 vPositioning_XPos_NumEdit Limit Number Hidden", "0"),
191-
AutoclickerGui.AddText("xp+45 yp+2 Hidden", "Y:"),
192-
AutoclickerGui.AddEdit("xp+20 yp-2 w30 vPositioning_YPos_NumEdit Limit Number Hidden", "0")
193-
],
194-
%SZ_TABLE.Positioning_Boundary_Mode.Box%: [
195-
AutoclickerGui.AddText("xs+10 ys+55 Hidden", "X min:"),
196-
AutoclickerGui.AddEdit("xp+35 yp-2 w30 vPositioning_XMinPos_NumEdit Limit Number Hidden", "0"),
197-
AutoclickerGui.AddText("xp+45 yp+2 Hidden", "X max:"),
198-
AutoclickerGui.AddEdit("xp+35 yp-2 w30 vPositioning_XMaxPos_NumEdit Limit Number Hidden", "0"),
199-
AutoclickerGui.AddText("xs+10 yp+30 Hidden", "Y min:"),
200-
AutoclickerGui.AddEdit("xp+35 yp-2 w30 vPositioning_YMinPos_NumEdit Limit Number Hidden", "0"),
201-
AutoclickerGui.AddText("xp+45 yp+2 Hidden", "Y max:"),
202-
AutoclickerGui.AddEdit("xp+35 yp-2 w30 vPositioning_YMaxPos_NumEdit Limit Number Hidden", "0")
203-
]
204-
}
205-
206-
AutoclickerGui.AddGroupBox("xs yp+44 w226 h45 Section", "Mouse position relative to")
207-
makeRadioGroup(
208-
"Positioning_RelativeTo_Radio"
209-
, [
210-
AutoclickerGui.AddRadio("xs+10 yp+20 vPositioning_RelativeTo_Radio Checked", "Entire &screen"),
211-
AutoclickerGui.AddRadio("yp", "Focused &window")
212-
]
213-
)
175+
AutoclickerGui.AddListView("w226 h140 vPositioning_TargetList_ListView -LV0x10 NoSortHdr", ["#", "Type", "Coordinates"])
176+
.OnEvent("ItemSelect", Positioning_ItemSelectionChanged)
177+
AutoclickerGui["Positioning_TargetList_ListView"].ModifyCol(1, 25)
178+
AutoclickerGui["Positioning_TargetList_ListView"].ModifyCol(2, 40)
179+
AutoclickerGui["Positioning_TargetList_ListView"].ModifyCol(3, 157)
180+
181+
AutoclickerGui.AddButton("xm+10 yp+147 w72 vPositioning_AddTarget_Button", "&Add")
182+
.OnEvent("Click", Positioning_AddTarget)
183+
AutoclickerGui.AddButton("yp wp vPositioning_RemoveTarget_Button Disabled", "&Remove")
184+
.OnEvent("Click", Positioning_RemoveTarget)
185+
AutoclickerGui.AddButton("yp wp vPositioning_ClearAllTargets_Button", "&Clear All")
186+
.OnEvent("Click", Positioning_ClearAllTargets)
214187

215188
AutoclickerGui["Tab"].UseTab(SZ_TABLE.Tabs.Hotkeys)
216189

@@ -282,11 +255,160 @@ Scheduling_StopAfterTimeToggled(*) {
282255
Scheduling_updateStopAfter()
283256
}
284257

285-
Positioning_ChangedModeSelection(radio, *) {
286-
for key, list in PerBoundaryConfigControls.OwnProps() {
287-
for ctrl in list
288-
ctrl.Visible := key = radio.Text
258+
Positioning_updateTargetsList(*) {
259+
AutoclickerGui["Positioning_TargetList_ListView"].Delete()
260+
261+
local cumulativeApplicableClickCount := 1
262+
for targetData in configured_targets {
263+
AutoclickerGui["Positioning_TargetList_ListView"].Add(
264+
, cumulativeApplicableClickCount
265+
, targetData.Type = 1 ? "Point" : "Box"
266+
, (targetData.Type = 1 ? targetData.X ", " targetData.Y
267+
: targetData.XMin ", " targetData.YMin " -- " targetData.XMax ", " targetData.YMax)
268+
. (targetData.RelativeTo = 1 ? " (ABS)" : " (REL)")
269+
)
270+
cumulativeApplicableClickCount += targetData.ApplicableClickCount
271+
}
272+
}
273+
274+
Positioning_ItemSelectionChanged(*) {
275+
AutoclickerGui["Positioning_RemoveTarget_Button"].Enabled := AutoclickerGui["Positioning_TargetList_ListView"].GetNext()
276+
}
277+
278+
Positioning_AddTarget(*) {
279+
static TargetAdderGui
280+
static PerTypeCoordControls
281+
if !IsSet(TargetAdderGui) {
282+
TargetAdderGui := Gui("-SysMenu +Owner" AutoclickerGui.Hwnd, "Add Target")
283+
284+
TargetAdderGui.OnEvent("Escape", hideOwnedGui)
285+
TargetAdderGui.OnEvent("Close", hideOwnedGui)
286+
287+
TargetAdderGui.AddText("ym+2", "Applies for:")
288+
TargetAdderGui.AddEdit("xp+56 yp-2 w45 vTargetApplicableClickCountEdit Limit Number", "1")
289+
TargetAdderGui.AddText("xp+48 yp+2", "contiguous click(s)")
290+
291+
TargetAdderGui.AddGroupBox("xm w226 h110 Section", "Coordinates")
292+
TargetAdderGui.AddRadio("xs+10 yp+20 vTargetTypePointRadio Checked", SZ_TABLE.Positioning_TargetType.Point)
293+
.OnEvent("Click", TargetTypeSelectionChanged)
294+
TargetAdderGui.AddRadio("yp vTargetTypeBoxRadio", SZ_TABLE.Positioning_TargetType.Box)
295+
.OnEvent("Click", TargetTypeSelectionChanged)
296+
297+
PerTypeCoordControls := {
298+
TargetTypePointRadio: [
299+
TargetAdderGui.AddText("xs+10 ys+50 Hidden", "X:"),
300+
TargetAdderGui.AddEdit("xp+20 yp-2 w30 vTargetXPosNumEdit Limit Number Hidden", "0"),
301+
TargetAdderGui.AddText("xp+45 yp+2 Hidden", "Y:"),
302+
TargetAdderGui.AddEdit("xp+20 yp-2 w30 vTargetYPosNumEdit Limit Number Hidden", "0")
303+
],
304+
TargetTypeBoxRadio: [
305+
TargetAdderGui.AddText("xs+10 ys+50 Hidden", "X min:"),
306+
TargetAdderGui.AddEdit("xp+34 yp-2 w30 vTargetXMinPosNumEdit Limit Number Hidden", "0"),
307+
TargetAdderGui.AddText("xp+45 yp+2 Hidden", "Y min:"),
308+
TargetAdderGui.AddEdit("xp+35 yp-2 w30 vTargetYMinPosNumEdit Limit Number Hidden", "0"),
309+
TargetAdderGui.AddText("xs+10 yp+30 Hidden", "X max:"),
310+
TargetAdderGui.AddEdit("xp+34 yp-2 w30 vTargetXMaxPosNumEdit Limit Number Hidden", "0"),
311+
TargetAdderGui.AddText("xp+45 yp+2 Hidden", "Y max:"),
312+
TargetAdderGui.AddEdit("xp+35 yp-2 w30 vTargetYMaxPosNumEdit Limit Number Hidden", "0")
313+
]
314+
}
315+
316+
TargetAdderGui.AddGroupBox("xs yp+40 w145 h59 Section", "Position relative to")
317+
TargetAdderGui.AddRadio("xs+10 yp+20 vTargetRelativeToScreenRadio Checked", "Entire &screen (ABS)")
318+
.OnEvent("Click", TargetRelativeToSelectionChanged)
319+
TargetAdderGui.AddRadio("xp vTargetRelativeToFocused", "Focused &window (REL)")
320+
.OnEvent("Click", TargetRelativeToSelectionChanged)
321+
322+
TargetAdderGui.AddButton("ys+6 w80 Default", "OK")
323+
.OnEvent("Click", Submit)
324+
TargetAdderGui.AddButton("xp wp", "Cancel")
325+
.OnEvent("Click", (*) => hideOwnedGui(TargetAdderGui))
326+
327+
TargetAdderGui.AddStatusBar("vStatusBar")
328+
TargetAdderGui["StatusBar"].SetParts(40)
329+
TargetAdderGui["StatusBar"].SetText(" (ABS)")
330+
TargetAdderGui["StatusBar"].SetText("X=? Y=?", 2, 2)
331+
332+
add_log("Created target adder GUI")
333+
334+
TargetTypeSelectionChanged(TargetAdderGui["TargetTypePointRadio"])
335+
}
336+
337+
TargetAdderGui["TargetApplicableClickCountEdit"].Value := 1
338+
TargetAdderGui["TargetXPosNumEdit"].Value := 0
339+
TargetAdderGui["TargetYPosNumEdit"].Value := 0
340+
TargetAdderGui["TargetXMinPosNumEdit"].Value := 0
341+
TargetAdderGui["TargetYMinPosNumEdit"].Value := 0
342+
TargetAdderGui["TargetXMaxPosNumEdit"].Value := 0
343+
TargetAdderGui["TargetYMaxPosNumEdit"].Value := 0
344+
showGuiAtAutoclickerGuiPos(TargetAdderGui)
345+
SetTimer updateTargetAdderGuiStatusBar, 100
346+
347+
updateTargetAdderGuiStatusBar() {
348+
CoordMode "Mouse", TargetAdderGui["TargetRelativeToScreenRadio"].Value = 1 ? "Screen" : "Client"
349+
local mouseX
350+
local mouseY
351+
MouseGetPos &mouseX, &mouseY
352+
TargetAdderGui["StatusBar"].SetText(Format("X={} Y={}", mouseX, mouseY), 2, 2)
353+
if !ControlGetVisible(TargetAdderGui.Hwnd, "ahk_id " TargetAdderGui.Hwnd)
354+
SetTimer , 0 ; mark timer for deletion
355+
}
356+
357+
TargetTypeSelectionChanged(radio, *) {
358+
for key, list in PerTypeCoordControls.OwnProps() {
359+
for ctrl in list
360+
ctrl.Visible := key = radio.Name
361+
}
362+
}
363+
364+
TargetRelativeToSelectionChanged(radio, *) {
365+
TargetAdderGui["StatusBar"].SetText(radio.Name = "TargetRelativeToScreenRadio" ? " (ABS)" : " (REL)")
366+
}
367+
368+
Submit(*) {
369+
hideOwnedGui(TargetAdderGui)
370+
371+
local targetData := {
372+
ApplicableClickCount: TargetAdderGui["TargetApplicableClickCountEdit"].Value,
373+
Type: TargetAdderGui["TargetTypePointRadio"].Value = 1 ? 1 : 2,
374+
RelativeTo: TargetAdderGui["TargetRelativeToScreenRadio"].Value = 1 ? 1 : 2
375+
}
376+
if TargetAdderGui["TargetTypePointRadio"].Value = 1 {
377+
targetData.X := Number(TargetAdderGui["TargetXPosNumEdit"].Value)
378+
targetData.Y := Number(TargetAdderGui["TargetYPosNumEdit"].Value)
379+
} else {
380+
targetData.XMin := Number(TargetAdderGui["TargetXMinPosNumEdit"].Value)
381+
targetData.YMin := Number(TargetAdderGui["TargetYMinPosNumEdit"].Value)
382+
targetData.XMax := Number(TargetAdderGui["TargetXMaxPosNumEdit"].Value)
383+
targetData.YMax := Number(TargetAdderGui["TargetYMaxPosNumEdit"].Value)
384+
}
385+
configured_targets.Push(targetData)
386+
387+
Positioning_updateTargetsList()
388+
Positioning_ItemSelectionChanged()
389+
}
390+
}
391+
392+
Positioning_RemoveTarget(*) {
393+
add_log "Starting targets removal"
394+
local rowNum := 0
395+
local nRemoved := 0
396+
Loop {
397+
rowNum := AutoclickerGui["Positioning_TargetList_ListView"].GetNext(rowNum)
398+
if !rowNum
399+
break
400+
add_log "Removing target #" rowNum
401+
configured_targets.RemoveAt(rowNum - nRemoved++)
289402
}
403+
404+
Positioning_updateTargetsList()
405+
Positioning_ItemSelectionChanged()
406+
}
407+
408+
Positioning_ClearAllTargets(*) {
409+
configured_targets.Length := 0
410+
Positioning_updateTargetsList()
411+
Positioning_ItemSelectionChanged()
290412
}
291413

292414
Hotkeys_updateHotkeyBindings() {

src/EC-Autoclicker.ahk

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ is_autoclicking := false
1919
is_simplified_view_on := false
2020
is_always_on_top_on := true
2121
are_hotkeys_active := true
22+
configured_targets := []
2223
configured_hotkeys := []
2324

2425
RadioGroups := {}
@@ -69,10 +70,9 @@ SZ_TABLE := {
6970
Hotkeys: "Hotkeys"
7071
},
7172
; Positioning
72-
Positioning_Boundary_Mode: {
73-
None: "&User-controlled",
73+
Positioning_TargetType: {
7474
Point: "Po&int",
75-
Box: "&Box"
75+
Box: "&Box (random distribution)"
7676
}
7777
}
7878

@@ -102,10 +102,13 @@ A_TrayMenu.Add(SZ_TABLE.TrayMenu_Exit, Close)
102102
#Include About.ahk
103103
#Include Updater.ahk
104104

105-
; Loop to update the status bar in the main window.
106-
Loop {
107-
CoordMode "Mouse", AutoclickerGui["Positioning_RelativeTo_Radio"].Value = 1 ? "Screen" : "Client"
105+
CoordMode "Mouse", "Screen"
106+
SetTimer updateAutoclickerGuiStatusBar, 100
107+
108+
updateAutoclickerGuiStatusBar() {
109+
local mouseX
110+
local mouseY
108111
MouseGetPos &mouseX, &mouseY
109112
AutoclickerGui["StatusBar"].SetText(Format("X={} Y={}", mouseX, mouseY), 3, 2)
110-
Sleep 100
111113
}
114+

src/Profiles.ahk

Lines changed: 56 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,42 @@ ProfileLoad(profileName, *) {
8282
Loop Reg REG_KEY_PATH "\Profiles\" profileName {
8383
local value := RegRead()
8484

85-
if A_LoopRegName = "Hotkeys" {
85+
switch A_LoopRegName {
86+
case "Targets":
87+
add_log("Update Targets")
88+
Positioning_ClearAllTargets()
89+
90+
Loop Parse value, "`n" {
91+
if !A_LoopField
92+
continue
93+
94+
local targetDataMatch
95+
RegExMatch A_LoopField, "^(?P<ApplicableClickCount>.+?)%(?P<Type>\d?)"
96+
. "%(?P<Coords>.+?)%(?P<RelativeTo>\d)$", &targetDataMatch
97+
98+
local targetData := {
99+
ApplicableClickCount: targetDataMatch["ApplicableClickCount"],
100+
Type: targetDataMatch["Type"],
101+
RelativeTo: targetDataMatch["RelativeTo"]
102+
}
103+
104+
local coords := StrSplit(targetDataMatch["Coords"], ",")
105+
switch targetDataMatch["Type"] {
106+
case 1:
107+
targetData.X := coords[1],
108+
targetData.Y := coords[2]
109+
case 2:
110+
targetData.XMin := coords[1],
111+
targetData.YMin := coords[2],
112+
targetData.XMax := coords[3],
113+
targetData.YMax := coords[4]
114+
}
115+
116+
configured_targets.Push(targetData)
117+
}
118+
119+
Positioning_updateTargetsList()
120+
case "Hotkeys":
86121
add_log("Update Hotkeys")
87122
Hotkeys_ClearAllHotkeys()
88123

@@ -102,7 +137,7 @@ ProfileLoad(profileName, *) {
102137
}
103138

104139
Hotkeys_updateHotkeyBindings()
105-
} else {
140+
default:
106141
add_log("Update: " A_LoopRegName " (value=" value ")")
107142
local ctrl := AutoclickerGui[A_LoopRegName]
108143

@@ -185,6 +220,18 @@ ProfileCreate(*) {
185220
, ctrlName
186221
}
187222

223+
local serializedTargets := ""
224+
for targetData in configured_targets
225+
serializedTargets .= targetData.ApplicableClickCount "%" targetData.Type
226+
. "%" (targetData.Type = 1 ? targetData.X "," targetData.Y
227+
: targetData.XMin "," targetData.YMin "," targetData.XMax "," targetData.YMax)
228+
. "%" targetData.RelativeTo "`n"
229+
230+
RegWrite serializedTargets
231+
, "REG_MULTI_SZ"
232+
, REG_KEY_PATH "\Profiles\" profileName
233+
, "Targets"
234+
188235
local serializedHotkeys := ""
189236
for hotkeyData in configured_hotkeys
190237
serializedHotkeys .= hotkeyData.Hotkey "%" hotkeyData.Scope "%" hotkeyData.Action "`n"
@@ -335,8 +382,8 @@ ProfileManage(*) {
335382

336383
local formatted := ""
337384
Loop Reg REG_KEY_PATH "\Profiles\" selectedProfileName {
338-
if A_LoopRegName = "Hotkeys"
339-
formatted .= "Hotkeys=" StrReplace(RegRead(), "`n", "`t") "`n"
385+
if A_LoopRegName = "Targets" || A_LoopRegName = "Hotkeys"
386+
formatted .= A_LoopRegName "=" StrReplace(RegRead(), "`n", "`t") "`n"
340387
else
341388
formatted .= A_LoopRegName "=" RegRead() "`n"
342389
}
@@ -382,16 +429,11 @@ ProfileManage(*) {
382429
RegExMatch A_LoopField, "^(?P<Name>\w+?)=(?P<Value>.+)$", &configMatch
383430
add_log("Read: " configMatch["Name"] " = " configMatch["Value"])
384431

385-
if configMatch["Name"] = "Hotkeys"
386-
RegWrite StrReplace(configMatch["Value"], "`t", "`n")
387-
, "REG_MULTI_SZ"
388-
, REG_KEY_PATH "\Profiles\" profileName
389-
, "Hotkeys"
390-
else
391-
RegWrite configMatch["Value"]
392-
, configMatch["Name"] ~= "DateTime" ? "REG_SZ" : "REG_DWORD"
393-
, REG_KEY_PATH "\Profiles\" profileName
394-
, configMatch["Name"]
432+
RegWrite (configMatch["Name"] = "Targets" || configMatch["Name"] = "Hotkeys"
433+
? StrReplace(configMatch["Value"], "`t", "`n") : configMatch["Value"])
434+
, configMatch["Name"] ~= "DateTime" ? "REG_SZ" : "REG_DWORD"
435+
, REG_KEY_PATH "\Profiles\" profileName
436+
, configMatch["Name"]
395437
}
396438
} catch as err {
397439
add_log("Import Profile error: " err.Message)

0 commit comments

Comments
 (0)