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)
And after several tries I'm pretty happy with the result!
The hardest part was to maximize speed and avoid flickering.
FIRST VERSION with images (handles)
Here are the button images I used
ButtonOn.png
and
ButtonOff.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.
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
}
}