ResizeableGui - a framework for resieable windows
Posted: 28 Mar 2023, 12:28
Hi,
while porting my > 150.000 lines of code from AHK V1 to V2, I created a little framework for resizeable windows. Maybe useful for some of you.
Usage:
myWIndow := ResizeableGui()
...
myControl := myWindow.Add(....)
myWindow.DefineResizeRule(myControl, rule)
rule is like "X:Move;Y:Move" or "W:Grow;H:Grow" or "X:Center" or "Y:Move/2", and so on.
Here is a real life example from my software with a couple of controls growing and moving according to the window size:
while porting my > 150.000 lines of code from AHK V1 to V2, I created a little framework for resizeable windows. Maybe useful for some of you.
Code: Select all
class ResizeableGui extends Gui {
iMinWidth := 0
iMinHeight := 0
iWidth := 0
iHeight := 0
iResizers := []
iDescriptor := false
class Resizer {
iGui := false
Gui {
Get {
return this.iGui
}
}
__New(resizeableGui) {
this.iGui := resizeableGui
}
Initialize() {
}
CanResize(deltaWidth, deltaHeight) {
return true
}
Resize(deltaWidth, deltaHeight) {
}
}
class ControlResizer extends ResizeableGui.Resizer {
iRule := false
iControl := false
iOriginalX := 0
iOriginalY := 0
iOriginalWidth := 0
iOriginalHeight := 0
Control {
Get {
return this.iControl
}
}
Rule {
Get {
return this.iRule
}
}
OriginalX {
Get {
return this.iOriginalX
}
}
OriginalY {
Get {
return this.iOriginalY
}
}
OriginalWidth {
Get {
return this.iOriginalWidth
}
}
OriginalHeight {
Get {
return this.iOriginalHeight
}
}
__New(resizeableGui, control, rule) {
this.iControl := control
this.iRule := rule
super.__New(resizeableGui)
}
Initialize() {
local x, y, w, h
ControlGetPos(&x, &y, &w, &h, this.Control)
this.iOriginalX := x
this.iOriginalY := y
this.iOriginalWidth := w
this.iOriginalHeight := h
}
CanResize(deltaWidth, deltaHeight) {
return !!this.Rule
}
Resize(deltaWidth, deltaHeight) {
local x := this.OriginalX
local y := this.OriginalY
local w := this.OriginalWidth
local h := this.OriginalHeight
local ignore, part, variable, horizontal
for ignore, part in string2Values(";", this.Rule) {
part := string2Values(":", part)
variable := part[1]
if (variable = "Width")
variable := "w"
else if (variable = "Height")
variable := "h"
horizontal := ((variable = "x") || (variable = "w"))
switch part[2], false {
case "Move":
%variable% += (horizontal ? deltaWidth : deltaHeight)
case "Move/2":
%variable% += Round((horizontal ? deltaWidth : deltaHeight) / 2)
case "Grow":
%variable% += (horizontal ? deltaWidth : deltaHeight)
case "Grow/2":
%variable% += Round((horizontal ? deltaWidth : deltaHeight) / 2)
case "Center":
if horizontal
x := Round((this.Gui.Width / 2) - (w / 2))
else
y := Round((this.Gui.Height / 2) - (h / 2))
}
}
ControlMove(x, y, w, h, this.Control)
this.Control.Redraw()
}
}
Descriptor {
Get {
return this.iDescriptor
}
Set {
return (this.iDescriptor := value)
}
}
MinWidth {
Get {
return this.iMinWidth
}
}
MinHeight {
Get {
return this.iMinHeight
}
}
Width {
Get {
return this.iWidth
}
}
Height {
Get {
return this.iHeight
}
}
Resizers {
Get {
return this.iResizers
}
}
__New(arguments*) {
super.__New(arguments*)
this.Opt("+Resize -MaximizeBox")
this.OnEvent("Size", this.Resize)
}
Show(arguments*) {
local x, y, width, height
super.Show(arguments*)
WinGetPos(&x, &y, &width, &height, this)
this.iMinWidth := width
this.iMinHeight := height
this.iWidth := width
this.iHeight := height
for ignore, resizer in this.Resizers
resizer.Initialize()
}
AddResizer(resizer) {
this.Resizers.Push(resizer)
}
DefineResizeRule(control, rule) {
this.AddResizer(ResizeableGui.ControlResizer(this, control, rule))
}
Resize(minMax, width, height) {
local descriptor := this.Descriptor
local x, y, w, h, settings
if (minMax = "Initialize") {
WinGetPos(&x, &y, &w, &h, this)
this.iWidth := width
this.iHeight := height
WinMove(x, y, width, height, this)
}
else {
if !this.Width
return
WinGetPos(&x, &y, &w, &h, this)
width := w
height := h
if ((width < this.iMinWidth) || (height < this.iMinHeight)) {
this.iWidth := this.MinWidth
this.iHeight := this.MinHeight
WinMove(x, y, this.MinWidth, this.MinHeight, this)
this.ControlsResize(this.MinWidth, this.MinHeight)
}
else if ((this.Resizers.Length = 0) || !this.ControlsCanResize(width, height)) {
if (this.Width && this.Height)
WinMove(x, y, this.Width, this.Height, this)
}
else {
this.iWidth := width
this.iHeight := height
this.ControlsResize(width, height)
WinRedraw(this)
}
}
}
ControlsCanResize(width, height) {
local ignore, resizer
for ignore, resizer in this.Resizers
if !resizer.CanResize(width - this.MinWidth, height - this.MinHeight)
return false
return true
}
ControlsResize(width, height) {
local ignore, resizer
for ignore, resizer in this.Resizers
resizer.Resize(width - this.MinWidth, height - this.MinHeight)
}
}
myWIndow := ResizeableGui()
...
myControl := myWindow.Add(....)
myWindow.DefineResizeRule(myControl, rule)
rule is like "X:Move;Y:Move" or "W:Grow;H:Grow" or "X:Center" or "Y:Move/2", and so on.
Here is a real life example from my software with a couple of controls growing and moving according to the window size:
Code: Select all
raceReportsGui := ResizeableGui()
this.iWindow := raceReportsGui
raceReportsGui.Opt("-Border -Caption +0x800000")
raceReportsGui.BackColor := "D0D0D0"
raceReportsGui.SetFont("s10 Bold", "Arial")
control := raceReportsGui.Add("Text", "w1184 Center", translate("Modular Simulator Controller System"))
control.OnEvent("Click", moveByMouse.Bind(raceReportsGui, "Race Reports"))
raceReportsGui.DefineResizeRule(control, "X:Center")
raceReportsGui.SetFont("s9 Norm", "Arial")
raceReportsGui.SetFont("Italic Underline", "Arial")
control := raceReportsGui.Add("Text", "x508 YP+20 w184 cBlue Center", translate("Race Reports"))
control.OnEvent("Click", openDocumentation.Bind(raceReportsGui, "https://github.com/SeriousOldMan/Simulator-Controller/wiki/Virtual-Race-Strategist#race-reports"))
raceReportsGui.DefineResizeRule(control, "X:Center")
raceReportsGui.DefineResizeRule(raceReportsGui.Add("Text", "x8 yp+30 w1200 0x10"), "W:Grow")
raceReportsGui.SetFont("s8 Norm", "Arial")
raceReportsGui.Add("Text", "x16 yp+10 w70 h23 +0x200 Section", translate("Simulator"))
simulators := this.getSimulators()
simulator := ((simulators.Length > 0) ? 1 : 0)
raceReportsGui.Add("DropDownList", "x90 yp w180 Choose" . simulator . " vsimulatorDropDown", simulators).OnEvent("Change", chooseSimulator)
if (simulator > 0)
simulator := simulators[simulator]
else
simulator := false
raceReportsGui.Add("Text", "x16 yp+24 w70 h23 +0x200", translate("Car"))
raceReportsGui.Add("DropDownList", "x90 yp w180 vcarDropDown").OnEvent("Change", chooseCar)
raceReportsGui.Add("Text", "x16 yp24 w70 h23 +0x200", translate("Track"))
raceReportsGui.Add("DropDownList", "x90 yp w180 vtrackDropDown").OnEvent("Change", chooseTrack)
raceReportsGui.Add("Text", "x16 yp+26 w70 h23 +0x200", translate("Races"))
this.iRacesListView := raceReportsGui.Add("ListView", "x90 yp-2 w180 h252 BackgroundD8D8D8 -Multi -LV0x10 AltSubmit NoSort NoSortHdr", collect(["Date", "Time", "Duration", "Starting Grid"], translate))
this.iRacesListView.OnEvent("Click", chooseRace)
raceReportsGui.DefineResizeRule(this.iRacesListView, "H:Grow")
raceReportsGui.Add("Button", "x62 yp+205 w23 h23 vreloadReportsButton").OnEvent("Click", reloadRaceReports)
setButtonIcon(raceReportsGui["reloadReportsButton"], kIconsDirectory . "Renew.ico", 1)
raceReportsGui.DefineResizeRule(raceReportsGui["reloadReportsButton"], "Y:Move")
raceReportsGui.Add("Button", "x62 yp+24 w23 h23 vdeleteReportButton").OnEvent("Click", deleteRaceReport)
setButtonIcon(raceReportsGui["deleteReportButton"], kIconsDirectory . "Minus.ico", 1)
raceReportsGui.DefineResizeRule(raceReportsGui["deleteReportButton"], "Y:Move")
raceReportsGui.DefineResizeRule(raceReportsGui.Add("Text", "x16 yp+30 w70 h23 +0x200", translate("Info")), "Y:Move")
raceReportsGui.Add("ActiveX", "x90 yp-2 w180 h170 Border vinfoViewer", "shell.explorer").Value.Navigate("about:blank")
raceReportsGui.DefineResizeRule(raceReportsGui["infoViewer"], "Y:Move")
raceReportsGui.Add("Text", "x290 ys w40 h23 +0x200", translate("Report"))
raceReportsGui.Add("DropDownList", "x334 yp w120 AltSubmit Disabled Choose0 vreportsDropDown", collect(kRaceReports, translate)).OnEvent("Change", chooseReport)
raceReportsGui.Add("Button", "x1177 yp w23 h23 vreportSettingsButton").OnEvent("Click", reportSettings)
setButtonIcon(raceReportsGui["reportSettingsButton"], kIconsDirectory . "Report Settings.ico", 1)
raceReportsGui.DefineResizeRule(raceReportsGui["reportSettingsButton"], "X:Move")
raceReportsGui.Add("ActiveX", "x290 yp+24 w910 h475 Border vchartViewer", "shell.explorer").Value.Navigate("about:blank")
raceReportsGui.DefineResizeRule(raceReportsGui["chartViewer"], "W:Grow;H:Grow")
this.iReportViewer := RaceReportViewer(raceReportsGui, raceReportsGui["chartViewer"].Value, raceReportsGui["infoViewer"].Value)
this.loadSimulator(simulator, true)
raceReportsGui.DefineResizeRule(raceReportsGui.Add("Text", "x8 y574 w1200 0x10"), "Y:Move;W:Grow")
control := raceReportsGui.Add("Button", "x574 y580 w80 h23", translate("Close"))
control.OnEvent("Click", closeReports)
raceReportsGui.DefineResizeRule(control, "X:Center;Y:Move")
raceReportsGui.AddResizer(RaceReports.ReportResizer(raceReportsGui))