I asked in Help if it was done before but didn't get any reply Grid GUI to select table size (row & column number) so I've worked on it from scratch.
Here is what I wanted to achieve (taken from Word insert table feature)
![Image](https://i.imgur.com/k5GlY8d.png)
And after several tries I'm pretty happy with the result!
![Smile :)](./images/smilies/icon_e_smile.gif)
The hardest part was to maximize speed and avoid flickering.
FIRST VERSION with images (handles)
Here are the button images I used
ButtonOn.png
![Image](https://i.imgur.com/3lymKkU.png)
ButtonOff.png
![Image](https://i.imgur.com/ldkZEvT.png)
Code: Select all
#NoEnv ; Recommended for performance and compatibility with future AutoHotkey releases.
; #Warn ; Enable warnings to assist with detecting common errors.
SendMode Input ; Recommended for new scripts due to its superior speed and reliability.
SetWorkingDir %A_ScriptDir% ; Ensures a consistent starting directory.
Gui, GridGui:New
Gui +hwndGridGUIHwnd
NumbCol:=10
NumbRow:=10
StartYPos:=10
StartXPos:=10
ButtonW:=20
ButtonH:=20
hButtonOn:=LoadPicture("ButtonOn.png")
hButtonOff:=LoadPicture("ButtonOff.png")
loop %NumbRow% {
AA_Index:=A_Index
if A_Index=1
CurrYPos:=StartYPos
CurrYPos+=ButtonH-1
loop %NumbCol%
{
if A_Index=1
{
Gui,Add, Picture, % "Hidden x" StartXPos " y" CurrYPos " h" ButtonH " w" ButtonW " gGridClick vBTN" AA_Index "X" A_Index " HwndhBTN" AA_Index "X" A_Index
Gui,Add, Picture, % "xp yp h" ButtonH " w" ButtonW " gGridClick vBTF" AA_Index "X" A_Index " HwndhBTF" AA_Index "X" A_Index
Bitmap_SetImage(hBTN%AA_Index%X%A_Index%, hButtonOn)
Bitmap_SetImage(hBTF%AA_Index%X%A_Index%, hButtonOff)
}
else
{
Gui,Add, Picture, % "Hidden xp+" ButtonW-1 " h" ButtonH " w" ButtonW " y" CurrYPos " gGridClick vBTN" AA_Index "X" A_Index " HwndhBTN" AA_Index "X" A_Index
Gui,Add, Picture, % "xp yp h" ButtonH " w" ButtonW " gGridClick vBTF" AA_Index "X" A_Index " HwndhBTF" AA_Index "X" A_Index
Bitmap_SetImage(hBTN%AA_Index%X%A_Index%, hButtonOn)
Bitmap_SetImage(hBTF%AA_Index%X%A_Index%, hButtonOff)
}
}
}
Gui, Show
OnMessage(0x200, "WM_MOUSEMOVE")
return
Esc::ExitApp
Bitmap_SetImage(hCtrl, hBitmapBTN) {
; STM_SETIMAGE = 0x172, IMAGE_BITMAP = 0x00, SS_BITMAP = 0x0E
WinSet, Style, +0x0E, ahk_id %hCtrl%
SendMessage, 0x172, 0x00, %hBitmapBTN%, , ahk_id %hCtrl%
Return ErrorLevel
}
GridClick:
CurrMouseControl2:=A_GuiControl
CurrMouseControl2:=StrReplace(CurrMouseControl2,"BTF")
CurrMouseControl2:=StrReplace(CurrMouseControl2,"BTN")
PosY:= RegExReplace(CurrMouseControl2, "X\d*")
PosX:=RegExReplace(CurrMouseControl2, "\d*X")
msgbox % "columns " PosX " rows " PosY
return
WM_MOUSEMOVE() {
global
critical
SetBatchLines -1
ListLines, Off
static CurrControl, PrevControl, _TT, Current := 0
CurrControl := A_GuiControl
if (CurrControl=PrevControl)
return
PrevControl := CurrControl
if !CurrControl
{
loop %NumbRow%
{
AA_Index:=A_Index
loop %NumbCol%
{
if !(sBT%AA_Index%X%A_Index%)
continue
GuiControl,Show,BTF%AA_Index%X%A_Index%
GuiControl,Hide,BTN%AA_Index%X%A_Index%
sBT%AA_Index%X%A_Index%:=0
}
}
return
}
if !(A_Gui)
return
CurrControl:=StrReplace(CurrControl,"BTF")
CurrControl:=StrReplace(CurrControl,"BTN")
PosY:= RegExReplace(CurrControl, "X\d*")
PosX:=RegExReplace(CurrControl, "\d*X")
Gui %A_Gui%:Default
;Could be used to desactive/then force redraw
; SendMessage, 0xB, 0, 0,, ahk_id %GridGUIHwnd%
; DetectHiddenWindows, ON
loop %NumbRow%
{
AA_Index:=A_Index
loop %NumbCol%
{
if (AA_Index<=PosY and A_Index<=PosX) {
if (sBT%AA_Index%X%A_Index%)
continue
GuiControl,Show,BTN%AA_Index%X%A_Index%
GuiControl,Hide,BTF%AA_Index%X%A_Index%
sBT%AA_Index%X%A_Index%:=1
}
else
{
if !(sBT%AA_Index%X%A_Index%)
continue
GuiControl,Show,BTF%AA_Index%X%A_Index%
GuiControl,Hide,BTN%AA_Index%X%A_Index%
sBT%AA_Index%X%A_Index%:=0
}
}
}
;Could be used to desactive/then force redraw
; SendMessage, 0xB, 1, 0,, ahk_id %GridGUIHwnd%
; WinSet, Redraw,,ahk_id %GridGUIHwnd%
}
Code: Select all
ChooseGridGUI(NumbCol=10,NumbRow=10,Spacing=1,ButtonW=20,ButtonH=20,bCol=0xC9C9C9,sCol=0x17B773) {
global
CoordMode, Mouse , Screen
MouseGetPos, OutputVarX, OutputVarY
Gui, GridGui:New
Gui +hwndGridGUIHwnd
StartYPos:=10
StartXPos:=10
loop %NumbRow% {
AA_Index:=A_Index
if A_Index=1
CurrYPos:=StartYPos
else
CurrYPos+=ButtonH+Spacing
loop %NumbCol%
{
if A_Index=1
{
Gui, Add, Progress, % "vertical x" StartXPos " y" CurrYPos " h" ButtonH " w" ButtonW " vBTN" AA_Index "X" A_Index " HwndhBTN" AA_Index "X" A_Index " background" bCol " c" sCol,0
}
else
{
Gui, Add, Progress, % "vertical xp+" ButtonW+Spacing " y" CurrYPos " h" ButtonH " w" ButtonW " vBTN" AA_Index "X" A_Index " HwndhBTN" AA_Index "X" A_Index " background" bCol " c" sCol,0
}
}
}
Gui,Add,Picture, % "AltSubmit BackGroundTrans y" StartYPos " x" StartXPos " w" (ButtonW + Spacing) * NumbCol - Spacing " h" (ButtonH + Spacing) * NumbRow - Spacing " vFakePic gboguslabel"
Gui, Show, x%OutputVarX% y%OutputVarY%
OnMessage(0x200, "WM_MOUSEMOVE")
OnMessage(0x201, "WM_LBUTTONDOWN")
}
Esc::ExitApp
boguslabel:
return
WM_LBUTTONDOWN() {
global
if (A_GuiControl="FakePic")
return
CurrMouseControl2:=A_GuiControl
CurrMouseControl2:=StrReplace(CurrMouseControl2,"BTF")
CurrMouseControl2:=StrReplace(CurrMouseControl2,"BTN")
PosY:= RegExReplace(CurrMouseControl2, "X\d*")
PosX:=RegExReplace(CurrMouseControl2, "\d*X")
msgbox % "columns " PosX " rows " PosY
}
WM_MOUSEMOVE() {
global
critical
SetBatchLines -1
ListLines, Off
static CurrControl, PrevControl, _TT, Current := 0
CurrControl := A_GuiControl
if (CurrControl=PrevControl)
return
if (CurrControl="FakePic")
return
PrevControl := CurrControl
if !CurrControl
{
loop %NumbRow%
{
AA_Index:=A_Index
loop %NumbCol%
{
if !(sBT%AA_Index%X%A_Index%)
continue
GuiControl,, BTN%AA_Index%X%A_Index%, 0
sBT%AA_Index%X%A_Index%:=0
}
}
return
}
if !(A_Gui)
return
CurrControl:=StrReplace(CurrControl,"BTF")
CurrControl:=StrReplace(CurrControl,"BTN")
PosY:= RegExReplace(CurrControl, "X\d*")
PosX:=RegExReplace(CurrControl, "\d*X")
Gui %A_Gui%:Default
loop %NumbRow%
{
AA_Index:=A_Index
loop %NumbCol%
{
if (AA_Index<=PosY and A_Index<=PosX) {
if (sBT%AA_Index%X%A_Index%)
continue
GuiControl,, BTN%AA_Index%X%A_Index%, 100
sBT%AA_Index%X%A_Index%:=1
}
else
{
if !(sBT%AA_Index%X%A_Index%)
continue
GuiControl,, BTN%AA_Index%X%A_Index%, 0
sBT%AA_Index%X%A_Index%:=0
}
}
}
}
Allosw multiple anchor points etc.
![Image](https://i.imgur.com/LhDNF0z.gif)
Code: Select all
/*
Name: grid Selector
Version: 1.2 (Tuesday, October 18, 2016)
Created: Tuesday, September 27, 2016
Autohor: tidbit
Notes:
This is based on "ROWS, COLUMNS" not "X, Y". "Rows, Columns" is the same as "Y, X".
row: ---------------
column:|
|
|
Usage:
myControl:=new gridSelector("uniqueGuiName")
myControl.show()
myControl.destroy()
myControl.hide()
myControl.select()
myControl.submit()
gridSelector(name, row=5, col=5, w=20, h=20, gap-2, bCol="0xC9C9C9", sCol="0x17B773")
name = the name of the grid control, for multiple grids
rows = how many rows should there be?
cols = how many columns should there be?
w = how wide should each tile be?
h = how tall should each tile be?
gap = the space between tiles
bCol = the base color of an unselected tile
sCol = the color of a select tile
.show(woptions="", wstyles="-border -caption +owner +alwaysOnTop")
* Shows the tile gui
woptions = all the availble options for: Gui, Show
wstyles = all the styles for: "Options and styles for a window"
Returns:
an object is wiith its info as {"hwnd":hwnd, "x":x, "y":y, "w":w, "h":h}
.destroy()
* Destroy the grid freeing up its name.
.hide()
* Hides the grid so but doesn't destroy it.
.select(mode=2, lockR=1, lockC=1, coords=1)
* The stuff to select a region, it's locked coordinates, how to return values and selection behaviour.
Mode:
1 = raw array of all the tile cells. a tile value of 1 means selected, 0 unselected
2 = w/h count. Returns an object such as: {"x":1, "y":1, "rows":3, "columns":5} if you have a 3x5 selection
3 = w/h percent. Returns an object such as: {"x"0.0:, "y":0.0, "rows":1.0, "columns":0.8} if you have a 4x5 selection in a 5x5 grid
lockR = The row to lock the selection to.
lockC = The column to lock the selection to.
coords: (What tile range from the locked coords to this range)
1 = If 1, it'll be based off of the tile under the cursor.
[] = otherwise specify an array in the form of [row,column] to select to, such as: [3,5]
Returns:
returns 0 if you are over a tile
otherwise it returns -1 if you are of a non-tile element, such as between 2 tiles.
.submit()
Returns an array value based on .select()'s mode.
*/
#NoEnv ; Recommended for performance and compatibility with future AutoHotkey releases.
SetWorkingDir %A_ScriptDir% ; Ensures a consistent starting directory.
; #singleInstance, off
#singleInstance, force
; coordMode, mouse, window
setBatchLines, -1
grid:=new gridSelector("bbb", 8, 8, 15, 15, 1)
canShow:=1
; rrr:=5
; ccc:=5
; zzz:=grid.select(2,,, [rrr, ccc])
gui, +hwndMYHWND
gui, margin, 1, 1
gui, add, button, xm ym w40 h40 vbtn1 gdoit,1
gui, add, button, x+m yp w40 h40 vbtn2 gdoit,2
gui, add, button, xm y+m w40 h40 vbtn3 gdoit,3
gui, add, button, x+m yp w40 h40 vbtn4 gdoit,4
gui, show, w400
Return
doit:
; mouseGetPos, x, y,, cont, 2
GuiControlGet, btn, Hwnd, %A_GuiControl%
winGetPos, wx, wy,,, ahk_id %MYHWND%
controlGetPos, x, y, w, h,, ahk_id %btn%
; show it if we can
if (canShow=1)
{
pos:=grid.show("x" x+wx " y" y+wy)
canShow:=0
}
; otherwise hide it if we click outside the window
keyWait, lbutton, d
if (winActive("A")!=pos["hwnd"])
{
grid.hide()
canShow:=1
return
}
if (A_GuiControl="btn1")
lr:=1, lc:=1
if (A_GuiControl="btn2")
lr:=1, lc:=8
if (A_GuiControl="btn3")
lr:=8, lc:=1
if (A_GuiControl="btn4")
lr:=8, lc:=8
while ((getKeyState("lbutton", "p")))
{
zzz1:=grid.select(2, lr, lc)
sleep, 50
}
zzz2:=grid.submit()
grid.hide()
canShow:=1
msgbox % canShow "/" zzz1 "`n---`n" st_printArr(zzz2)
return
left::lc-=1
right::lc+=1
down::lr+=1
up::lr-=1
; left::ccc-=1
; right::ccc+=1
; down::rrr+=1
; up::rrr-=1
; out.destroy()
~esc::exitapp
st_printArr(array, depth=5, indentLevel="")
{
for k,v in Array
{
list.= indentLevel "[" k "]"
if (IsObject(v) && depth>1)
list.="`n" st_printArr(v, depth-1, indentLevel . " ")
Else
list.=" => " v
list.="`n"
}
return rtrim(list)
}
class gridSelector
{
__New(name, rows=5, cols=5, w=20, h=20, gap=2, bCol="0xC9C9C9", sCol="0x17B773")
{
global ; this is required for the unknown amount of generated controls
static xpos, ypos, xxx
; gap:=((w+h)/2)/5
if (trim(name, "`r`n `t")="")
return -1
xpos:=ypos:=gap ; starting gui coords
this.name:=name ; guis unique name, couldn't think of a way for Gui, New to be useful
this.rows:=rows ; how many tiles tall
this.cols:=cols ; how many tiles wide
this.grid:=[] ; the output/return array
this.w:=w ; width size of each tile
this.h:=h ; height size of each tile
this.bCol:=bCol ; base color, non-selected
this.sCol:=sCol ; selected color
this.gap:=gap ; spacing between tiles
loop, %rows%
{
rrr:=A_Index
loop, %cols%
{
ccc:=A_Index
; gui, %name%: add, edit ; for testing the "effect" down below
; , x%xpos% y%ypos% w%w% h%h% vr%rrr%c%ccc%_%name%
; , 0
gui, %name%: add, progress
, x%xpos% y%ypos% w%w% h%h% vertical c%sCol% background%bCol% vr%rrr%c%ccc%_%name%
, 0
xpos+=w+gap
}
xpos:=gap
ypos+=h+gap
}
gui, %name%: add, edit, x%gap% y+%gap% w100 h20 readonly disabled vinfo%name%
}
submit()
{
return this.grid
}
destroy()
{
name:=this.name
gui, %name%: destroy
}
hide()
{
name:=this.name
gui, %name%: cancel
}
show(woptions="", wstyles="-border -caption +owner +alwaysOnTop")
{
name:=this.name
gap:=this.gap
w:=((this.w+gap)*this.cols)+gap
h:=((this.h+gap)*this.rows)+gap*2+20 ; +20 is the edit control
; msgbox %w% %h%
gui, %name%: %wstyles% -theme +hwndTHWND%name% ; TEMP HWND
GuiControl, %name%: move, info%name%, % "w" w-gap*2
gui, %name%: show, %woptions% w%w% h%h%
winGetPos, x, y, w, h, % "ahk_id " THWND%name%
return {"hwnd": THWND%name%, "x":x, "y":y, "w":w, "h":h}
}
select(mode=2, lockR=1, lockC=1, coords=1) ; returns: 1 = raw, 2 = w/h count, 3 = w/h percent
{
; critical
static cUnderMouse, cUnderMouseP
name:=this.name
rowRowRowYourBoat:=this.rows
cols:=this.cols
bCol:=this.bCol ; color base
sCol:=this.sCol ; color sel
if (!isObject(coords))
{
mouseGetPos,,,,cUnderMouse, 2 ; control under mouse
GuiControlGet, cUnderMouse, %name%: name, %cUnderMouse% ; get its vVar
if (cUnderMouse=cUnderMouseP) ; do less work
return 0
cUnderMouseP:=cUnderMouse
}
else
cUnderMouse:="r" coords[1] "c" coords[2] "_" name
; over a non-cell area
if (!(inStr(cUnderMouse, "r") && inStr(cUnderMouse, "c")))
return -1
; get the grid cell coords
regExMatch(cUnderMouse, "^r(\d+)c(\d+)", o)
hoverR:=o1
hoverC:=o2
; figure out the corners
tc:=((lockC>hoverC) ? hoverC : lockC)-1
tr:=((lockR>hoverR) ? hoverR : lockR)-1
bc:=((lockC<hoverC) ? hoverC : lockC)
br:=((lockR<hoverR) ? hoverR : lockR)
; can these loops be avoided?
loop, %rowRowRowYourBoat%
{
ttr:=A_Index
loop, %cols%
{
GuiControl, %name%:, r%ttr%c%A_Index%_%name%,
this.grid[ttr, A_Index]:=0
}
}
distR:=abs(hoverR-lockR)+1
distC:=abs(lockC-hoverC)+1
totR:=totC:=0
loop, %distR%
{ ; lr/lc = loop row/loop column
lr:=(hoverR>lockR) ? hoverR-A_Index+1 : hoverR+A_Index-1
totC:=0
totR+=1
loop, %distC%
{
totC+=1
lc:=(hoverC>lockC) ? hoverC-A_Index+1 : hoverC+A_Index-1
this.grid[lr, lc]:=1
; GuiControl, %name%: +c%sCol%, r%lr%c%lc%_%name%
GuiControl, %name%:, r%lr%c%lc%_%name%, 100
; ------ THIS EFFECT STUFF IS FAIRLY BROKEN ------
; what formula name is this? no idea. just made it up.
; further away from the cursor = less filled
; but a minimum fill of 1/5 (20%) 20+40+40=100
; effect:=20 + (tr/(a_index))*40 + (tc/(a_index))*40
; effect:=20 + ((lr-tr)/br)*40 + ((lc-tc)/bc)*40
; effect:=floor(effect)
; GuiControl, %name%:, r%lr%c%lc%_%name%, %effect%
}
}
; GuiControl, %name%:, info%name%, %tr% | %tc% %br% | %bc%
if (mode=2)
{
bc:=bc-tc
br:=br-tr
GuiControl, %name%:, info%name%, %tr%`, %tc% | %br%`, %bc%
this.grid:={"x":tc, "y":tr, "rows":bc, "columns":br}
}
else if (mode=3)
{
tc:=tc/cols
tr:=tr/rowRowRowYourBoat
bc:=totC/Cols
br:=totR/rowRowRowYourBoat
; bc:=bc/cols
; br:=br/rowRowRowYourBoat
GuiControl, %name%:, info%name%, % round(tr*100) "%"
. "`, " round(tc*100) "%"
. "`, " round(br*100) "%"
. "`, " round(bc*100) "%"
this.grid:={"x":tc
, "y": tr
, "rows":br
, "columns":bc}
}
else
{
bc:=bc-tc
br:=br-tr
GuiControl, %name%:, info%name%, %tr%`, %tc% | %br%`, %bc%
}
return 0
}
}
![Wink ;)](./images/smilies/icon_e_wink.gif)