Recently I was playing around with the window resize of a GUI I was working on. There is no limit to how small you can make the window so some of the objects become hidden/inaccessible when the window is sized too small. I thought to myself, "It sure would be nice if I could figure out a way to limit the window to fixed "minimum" size!"
I did a quick search on forum and found this topic:
http://www.autohotkey.com/forum/viewtopic.php?t=2441. Apparently, I wasn't the only one with this requirement. If you read through the post you'll see that
Chris came up a with a way to limit sizing for a single GUI using
OnMessage(0x24, "WM_GETMINMAXINFO") . A few days ago,
toralf wrote a solution (with some
Titan influenced code) that is able to work with multiple GUIs and uses the GUI's current width/height as the minimum.
Long story still long, the code written by
Chris/toralf/Titan inspired me to create/assemble a couple of functions with the following capabilities/features:
- Set minimum and maximum width and/or height limits for multiple GUIs.
- Set or reset GUI size limits on demand.
- Options to set minimum and maximum limits to the GUI's current W/H.
- Include room in the design to support minimum/maximum X/Y positions in the future.
Here are the functions
(v1.0):
Code:
;***************************************
;* *
;* LimitGUISize_WM_GETMINMAXINFO *
;* *
;***************************************
;
;
; Description
; ===========
; This function restricts the sizing of a GUI window to developer-defined
; limits. It is automatically triggered by an OnMessage(0x24,...) statement.
; See the "Processing Notes" section (below) for more information.
;
;
;
; Parameters
; ==========
;
; Name Description
; ---- -----------
; wParam Not used.
;
; lParam From msdn - Pointer to a MINMAXINFO structure
; that contains the default maximized position and
; dimensions, and the default minimum and maximum tracking
; sizes. An application can override the defaults by
; setting the members of this structure.
;
; See the "Processing Notes" section (below) for more
; information.
;
; msg The message number. Contains 0x24 in this case. Only
; used for debugging purposes.
;
; hWnd The hWnd (unique ID) of the window or control to which
; the message was sent. Only used for debugging purposes.
;
;
;
;
; Global/System variables
; =======================
; * A_GUI. This AHK system variable contains the GUI window number that
; triggered this message.
;
; * LimitGUISize_Table. This global variable contains the developer-defined
; sizing limits for one or more GUI windows.
;
;
;
; Processing Notes
; ================
; * Monitoring of the WM_GETMINMAXINFO message is automatically enabled and
; disabled by the LimitGUISize function. There is no need to specify a
; separate "OnMessage" statement outside of these functions.
;
; * This function updates the GUI's ptMinTrackSize and ptMaxTrackSize values
; (as necessary) in the MINMAXINFO structure so that the GUI cannot be
; resized to a width or height that is outside of the developer-defined
; width and/or height limits. For a complete description of the
; MINMAXINFO structure, go to http://msdn.microsoft.com/ and do a search
; for WM_GETMINMAXINFO.
;
;
;
; Credit
; ======
; The original idea/code by Chris and significantly enhanced by toralf with
; influences and code by Titan. Original forum topic:
;
; http://www.autohotkey.com/forum/viewtopic.php?t=2441
;
;
;
; Return code
; ===========
; 0 (per msdn)
;
;
;
; Calls To Other Functions
; ========================
; (None)
;
;-------------------------------------------------------------------------------
LimitGUISize_WM_GETMINMAXINFO(wParam,lParam,msg,hWnd)
{
global LimitGUISize_Table
;-- Define search pattern for the current GUI
CurrentGUI:= "|" . A_Gui . ","
;-- Limits defined for this GUI?
If not instr(LimitGUISize_Table,CurrentGUI)
return 0
;-- Extract GUI's limits from the table (skip the GUI window number field)
StringTrimLeft GUILimitRecord
,LimitGUISize_Table
,instr(LimitGUISize_Table,CurrentGUI)+strlen(CurrentGUI)-1
;-- Split record into fields
StringSplit GUILimit,GUILimitRecord,`,|
;[===========================]
;[ Update ptMinTrackSize ]
;[ and/or ptMaxTrackSize ]
;[===========================]
loop 4
if GUILimit%A_Index%
{
SizeLimit:=GUILimit%A_Index%
OffSet:=20+(A_Index*4)
loop 4
DLLCall("RtlFillMemory"
,"UInt"
,lParam+Offset+A_Index-1
,"UInt"
,1
,"UChar"
,SizeLimit>>8*(A_Index-1) & 0xFF)
}
;-- Return to sender
return 0
}
;**********************
;* *
;* LimitGUISize *
;* *
;**********************
;
;
; Description
; ===========
; This function maintains user-defined sizing limits for one or more GUI
; windows.
;
;
;
; Parameters
; ==========
;
; Name Description
; ---- -----------
; p_Command Command. [Required] The following commands are
; currently supported:
;
; Name Action
; ---- ------
; Set Creates or updates a record in the
; LimitGUISize_Table variable.
;
; CurrentMin Creates or updates a record in the
; LimitGUISize_Table variable. The
; p_MinW and p_MinH parameters are
; automatically set to the GUI's current
; width and height.
;
; CurrentMin Creates or update a record in the
; LimitGUISize_Table variable. The p_MaxW
; and p_MaxH parameters are automatically
; set to the GUI's current width and
; height.
;
; Delete Deletes the GUI window record from the
; LimitGUISize_Table variable. This will
; remove all sizing limits for the GUI
; window.
;
; p_GUI GUI window number. [Required]
;
; p_MinW Minimum width (in pixels). [Optional]
;
; p_MinH Minimum height (in pixels). [Optional]
;
; p_MaxW Maximum width (in pixels). [Optional]
;
; p_MaxH Maximum height (in pixels). [Optional]
;
;
;
; Parameter Notes
; ===============
; * p_Command. At this writing, any p_Command value that is not exactly
; defined as "Delete", "CurrentMin", or "CurrentMax" (not case sensitive),
; will perform the same actions as the "Set" command. So, if you want to
; use command names such as "Add", "Update", etc., go for it!
;
; * p_GUI. The GUI window number is used to identify individual GUI windows
; instead of the GUI's handle (unique ID) for many reasons. The primary
; reason is to give the developer the ability to create/delete sizing
; limits on demand, regardless of the existence of the associated GUI
; window. The one consideration in this design is that if the developer
; does not delete (or update) sizing limits for a GUI window after the
; window has been destroyed, any new window using the same GUI window
; number will inherit the previous sizing limits.
;
; * The p_MinW, p_MinH, p_MaxW, and p_MaxH parameters can be defined/used as
; follows:
;
; Definition/Value Description/Operation
; ---------------- ---------------------
; Not defined All of these parameters are optional. If a min/max
; parameter is not defined, the related value on the
; GUI record will be set to null if for a new record.
; If for an existing record, no change will be made
; to the related field.
;
; Blank or null Same action as "Not defined". (See above)
;
; "C" | "Current" If the GUI window exists, the window's current
; width or height (whichever is appropriate) is used
; to populate this parameter. If the GUI window does
; not exist, the parameter value is set to 0 (zero).
;
; 0 (zero) A value of 0 (zero) effectively turns the associated
; limit off. If all min/max parameter values are set
; to 0 (zero), the GUI record is deleted.
;
; {Anything else} Should be the appropriate window width or height in
; pixels. Very little integrity checking is performed
; on these parameters so use care when assigning
; values.
;
;
;
; Processing Notes
; ================
; Monitoring of the WM_GETMINMAXINFO message is automatically enabled when the
; first GUI record is added, and is disabled when all GUI records are deleted.
; There is no need to specify a separate OnMessage statement outside of these
; functions.
;
;
;
; Table structure
; ===============
; The data that is used to determine sizing limits for GUI windows is stored
; in a global variable named LimitGUISize_Table. The variable is treated like
; a data table in that it has records and the records have fields.
;
; Each "record" is delimited by the "|" character and each "field" is
; delimited by a comma "," character. There are currently 5 fields per record
; and they are as follows (and in the following order):
;
; Field Description/Comment
; ----- -------------------
; GUI window number Only 1 record per GUI window number.
; Minimum width May be 0 or null.
; Minimum height May be 0 or null.
; Maximum width May be 0 or null.
; Maximum height May be 0 or null.
;
;
;
; Return codes
; ============
; Returns TRUE if there are invalid parameters. Returns FALSE if there are no
; errors.
;
;
;
; Calls To Other Functions
; ========================
; (None)
;
;-------------------------------------------------------------------------------
LimitGUISize(p_Command,p_GUI,p_MinW="",p_MinH="",p_MaxW="",p_MaxH="")
{
global LimitGUISize_Table
;[==============]
;[ Initialize ]
;[==============]
;-- AutoTrim all parameters
p_Command=%p_Command%
p_GUI=%p_GUI%
p_MinW=%p_MinW%
p_MinH=%p_MinH%
p_MaxW=%p_MaxW%
p_MaxH=%p_MaxH%
;-- Check p_GUI
if p_GUI is not Integer
return false
else
if p_GUI not between 1 and 99
return false
;[=================]
;[ Process table ]
;[=================]
;-- Extract GUI record if it exists
if LimitGUISize_Table is not Space
{
;-- Trim leading delimiter
StringTrimLeft LimitGUISize_Table,LimitGUISize_Table,1
;-- Split table into an array of records
StringSplit t_Record,LimitGUISize_Table,|
;-- Re-build table excluding target record
LimitGUISize_Table:=""
loop %t_Record0%
{
;-- Target record?
if instr(t_Record%A_Index%,p_GUI . ",")=1
{
;-- Break record into an array of fields
StringSplit t_Field,t_Record%A_Index%,`,
continue
}
;-- Add record
LimitGUISize_Table:=LimitGUISize_Table . "|" . t_Record%A_Index%
}
}
;[======================]
;[ Process parameters ]
;[======================]
;-- If any parm is not defined, set to previous table value (if anything)
if p_MinW is Space
p_MinW:=t_Field2
if p_MinH is Space
p_MinH:=t_Field3
if p_MaxW is Space
p_MaxW:=t_Field4
if p_MaxH is Space
p_MaxH:=t_Field5
;-- Delete command?
if p_Command=Delete
{
p_MinW=0
p_MinH=0
p_MaxW=0
p_MaxH=0
}
;---------------
;-- Current W/H
;---------------
;-- Initialize
CurrentW=0
CurrentH=0
;-- GUI exist?
gui %p_GUI%:+LastFoundExist
ifWinExist
WinGetPos,,,CurrentW,CUrrentH
;-- CurrentMin command?
if p_Command=CurrentMin
{
p_MinW:=CurrentW
p_MinH:=CurrentH
}
;-- CurrentMax command
if p_Command=CurrentMax
{
p_MaxW:=CurrentW
p_MaxH:=CurrentH
}
;------------------------
;-- Embedded "current" ?
;------------------------
if instr(p_MinW,"C")=1
p_MinW:=CurrentW
if instr(p_MinH,"C")=1
p_MinH:=CurrentH
if instr(p_MaxW,"C")=1
p_MaxW:=CurrentW
if instr(p_MaxH,"C")=1
p_MaxH:=CurrentH
;-------------------------
;-- Final integrity check
;-------------------------
if p_MinW
if p_MinW is not Integer
p_MinW=0
if p_MinH
if p_MinH is not Integer
p_MinH=0
if p_MaxW
if p_MaxW is not Integer
p_MaxW=0
if p_MaxH
if p_MaxH is not Integer
p_MaxH=0
;--------------------------------
;-- If any non-zero/null values,
;-- add record to the table
;--------------------------------
if (p_MinW or p_MinH or p_MaxW or p_MaxH)
LimitGUISize_Table=
(ltrim join
%LimitGUISize_Table%
|
%p_GUI%,
%p_MinW%,
%p_MinH%,
%p_MaxW%,
%p_MaxH%
)
;[======================]
;[ Message monitoring ]
;[======================]
;-- Empty table?
if not LimitGUISize_Table
{
;-- Disable monitoring?
if OnMessage(0x24)
OnMessage(0x24,"")
}
else
;-- Enable monitoring if not already enabled
if not OnMessage(0x24)
if not OnMessage(0x24,"LimitGUISize_WM_GETMINMAXINFO") ;-- Name must be exact
msgbox 262160,,Unable to set OnMessage for WM_GETMINMAXINFO
;-- Return to sender
return true
}
Here is an example that will demonstrate all of the options. After clicking on a button, resize (or try to resize) the window to see the changes.
Code:
gui Margin,,2
gui 1:Default
gui +Resize
gui Add,Button,w220 h25 gRemoveAll,Remove all sizing limits
gui Add,Button,w220 h25 gRemoveMin,Remove all minimum limits
gui Add,Button,w220 h25 gRemoveMax,Remove all maximum limits
gui Add,Button,w220 h25 gSetCurrentMin,Set current window size as minimum W/H
gui Add,Button,w220 h25 gSetCurrentMax,Set current window size as maximum W/H
gui Add,Text,x10 w110,Minimum width:
gui Add,Edit,x+05 w50 vMinW,500
gui Add,Button,x+05 w50 h25 gSetMinW,Set
gui Add,Text,x10 w110,Minimum height:
gui Add,Edit,x+05 w50 vMinH,400
gui Add,Button,x+05 w50 h25 gSetMinH,Set
gui Add,Text,x10 w110,Maximum width:
gui Add,Edit,x+05 w50 vMaxW,650
gui Add,Button,x+05 w50 h25 gSetMaxW,Set
gui Add,Text,x10 w110,Maximum height:
gui Add,Edit,x+05 w50 vMaxH,450
gui Add,Button,x+05 w50 h25 gSetMaxH,Set
gui Add,Text,x10 w10,----------
gui Add,Text,x10 w110,(Try to) change width:
gui Add,Edit,x+05 w50 vSetW,700
gui Add,Button,x+05 w50 h25 gSetW,Change
gui Add,Text,x10 w110,(Try to) change height:
gui Add,Edit,x+05 w50 vSetH,500
gui Add,Button,x+05 w50 h25 gSetH,Change
gui Add,Text,x10 w10,----------
gui Add,Button,w220 h25 g23Window,Build/Rebuild 2nd window
gui Show,w600 h400
return
RemoveAll:
LimitGUISize("Delete",1)
return
RemoveMin:
LimitGUISize("Set",1,0,0)
return
RemoveMax:
LimitGUISize("Set",1,"","",0,0)
return
SetCurrentMin:
LimitGUISize("CurrentMin",1)
return
SetCurrentMax:
LimitGUISize("CurrentMax",1)
return
SetMinW:
gui Submit,NoHide
LimitGUISize("Set",1,MinW)
return
SetMinH:
gui Submit,NoHide
LimitGUISize("Set",1,"",MinH)
return
SetMaxW:
gui Submit,NoHide
LimitGUISize("Set",1,"","",MaxW)
return
SetMaxH:
gui Submit,NoHide
LimitGUISize("Set",1,"","","",MaxH)
return
SetW:
gui Submit,NoHide
gui show,% "w" . SetW
return
SetH:
gui Submit,NoHide
gui show,% "h" . SetH
return
23Window:
LimitGUISize("Delete",23)
gui 23:Default
gui Destroy
Gui +Resize
Gui Add, Text,,23: This window cannot be drag-resized to smaller than 400x100.
Gui Show,w400 h100
LimitGUISize("CurrentMin",23)
return
guiescape:
guiclose:
exitapp
23guiescape:
23guiclose:
LimitGUISize("Delete",23)
gui Destroy
return
I hope that someone finds this useful.
---------------
20060903: v1.0
Minor improvements for release. Updated documentation.