ResizeableGui - a framework for resieable windows

Post your working scripts, libraries and tools.
TheBigO
Posts: 26
Joined: 17 Dec 2020, 09:11

ResizeableGui - a framework for resieable windows

Post by TheBigO » 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.

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)
	}
}
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:

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))

Return to “Scripts and Functions (v2)”