Dynamic Gui sample - Vertical scrolling list of Guis

Post AHK_H specific scripts & libraries and discuss the usage and development of HotKeyIt's fork/branch
User avatar
evilC
Posts: 4342
Joined: 27 Feb 2014, 12:30

Dynamic Gui sample - Vertical scrolling list of Guis

26 Mar 2017, 17:12

This is basically the setup I use for UCR. AHK_H's scroll / resize system was implemented by myself and HotkeyIt, largely to facilitate UCR's GUI.

I am having some problems with my reorder / delete plugin code in UCR, so I thought I would write this to test whether this Gui setup worked properly, without having all of the rest of the project get in the way.

As it turns out, everything seems fine with this version, so I guess I must have made a mistake in UCR - but I thought it worth posting here.
If anyone can make it glitch out, lemme know.

Code: Select all

#SingleInstance force

Gui +Hwndhwnd
mc := new MainClass(hwnd)
return

^Esc::
GuiClose:
	ExitApp

class MainClass {
	PluginHeight := 35
	PluginWidth := 450
	FrameWidth := this.PluginWidth + 50
	Plugins := {}		; Holds plugin instances
	PluginOrder := []	; Holds order of plugins
	
	__New(hMainGui){
		this.MainGui := new WrappedGui(hMainGui)
		this.MainGui.GuiCmd("+Resize")

		this.AddButton := this.MainGui.AddControl("Button", this.AddPlugin.Bind(this), "Center w" this.FrameWidth " aw", "Add Plugin")
		this.PluginFrame := new WrappedGui()
		this.PluginFrame.GuiCmd("-Caption")
		this.PluginFrame.GuiCmd("+VScroll")
		this.PluginFrame.GuiCmd("Color", "Black")
		
		this.MainGui.GuiCmd("Add", "Gui", "xm y+5 w" this.FrameWidth " h200 aw ah", this.PluginFrame.hwnd)
		
		; You cannot remove a Gui if you add it with Gui, Add, Gui...
		; So we have to use +Parent to attach plugins to the parent Gui
		; We therefore use a Spacer GuiControl in the plugin area to force the scrollbar to the desired size
		this.Spacer := this.PluginFrame.AddControl("Edit", 0, "x5 y0 w5 h0")
		this.SetSpacerHeight(0)
		
		this.MainGui.GuiCmd("Show", "x0 y0")
	}
	
	AddPlugin(){
		scroll_pos := this.PluginFrame.GetScrollPos()
		
		plugin := new Plugin(this)
		
		this.Plugins[plugin.Guid] := plugin
		this.PluginOrder.Push(plugin.Guid)
		
		plugin_height := plugin.GetHeight()
		ypos := this.Spacer.Height + 5
		plugin.ypos := ypos
		adjusted_pos := ypos - scroll_pos
		plugin.GuiCmd("Show", "x15 y" adjusted_pos " w" this.PluginWidth " h" this.PluginHeight)
		this.SetSpacerHeight(this.Spacer.Height + plugin_height + 5)
	}
	
	RemovePlugin(id){
		scroll_pos := this.PluginFrame.GetScrollPos()
		
		plugin := this.Plugins[id]
		plugin_height := plugin.GetHeight()
		plugin.GuiCmd("Destroy")
		old_guid := plugin.Guid
		old_order := this.GetPluginOrder(old_guid)
		this.Plugins.Delete(old_guid)
		
		max := this.PluginOrder.Length()
		
		Loop % max - old_order {
			id := old_order + A_Index
			guid := this.PluginOrder[id]
			plugin := this.Plugins[guid]
			plugin.ypos -= plugin_height + 4
			adjusted_pos := plugin.ypos - scroll_pos
			plugin.GuiCmd("Show", "x15 y" adjusted_pos)
		}
		this.PluginOrder.RemoveAt(old_order)
		this.SetSpacerHeight(this.Spacer.Height - plugin_height - 4)
	}
	
	ReorderPlugin(guid, dir){
		scroll_pos := this.PluginFrame.GetScrollPos()
		
		; Find the order of the plugin that the user clicked on
		passed_order := this.GetPluginOrder(guid)
		if (passed_order == 0 || (dir == -1 && passed_order == 1) || (dir == 1 && passed_order == this.PluginOrder.Length()))
			return	; top tried to move up bottom tried to move down

		; Gui swapping is handled as if the user had always clicked Move Down
		; If user clicks Move Up, swap the inputs
		if (dir == - 1){
			top_guid := this.PluginOrder[passed_order - 1]
			bot_guid := guid
		} else {
			top_guid := guid
			bot_guid := this.PluginOrder[passed_order + 1]
		}
		top_plugin := this.Plugins[top_guid]
		bot_plugin := this.Plugins[bot_guid]
		
		; Move the "top" gui down and the "bot" gui up
		top_y := top_plugin.ypos
		bot_plugin.Move(top_y, scroll_pos)
		top_plugin.Move(bot_plugin.ypos + bot_plugin.GetHeight() + 4, scroll_pos)	; ToDo - Fix Bodge for GetHeight()

		; Swap the PluginOrder
		top_id := this.PluginOrder.RemoveAt(passed_order)
		this.PluginOrder.InsertAt(passed_order + dir, top_id)
	}
	
	SetSpacerHeight(height){
		this.Spacer.Height := height
		this.Spacer.CtrlCmd("Move", "h" this.Spacer.Height)
	}
	
	GetPluginOrder(guid){
		for i, candidate in this.PluginOrder {
			if (candidate == guid){
				return i
			}
		}
		return 0
	}
}

class Plugin extends WrappedGui {
	ypos := 0
	__New(parent){
		static short_id := 1
		this.Guid := CreateGuid()
		this.Parent := parent
		base.__New(0)
		this.AddControl("Text", 0, "y10", "Plugin #" short_id " - " this.Guid)
		this.AddControl("Button", parent.RemovePlugin.Bind(parent, this.Guid), "x+5 yp-5", "Remove")
		this.AddControl("Button", parent.ReorderPlugin.Bind(parent, this.Guid, -1), "x+5 yp", "Up")
		this.AddControl("Button", parent.ReorderPlugin.Bind(parent, this.Guid, 1), "x+5 yp", "Down")
		this.GuiCmd("-Caption")
		this.GuiCmd("+Parent" parent.hwnd)
		this.GuiCmd("Show", "Hide")	; Force layout, so we can get size
		short_id++
	}
	
	Move(ypos, scroll_pos){
		this.GuiCmd("Show", "x15 y" ypos - scroll_pos)
		this.ypos := ypos
	}
}

class WrappedGui {
	__New(hwnd := 0, options := ""){
		if (hwnd == 0){
			Gui, New, hwndhwnd
		}
		this.hwnd := hwnd
	}
	
	GuiCmd(cmd, aParams*){
		Gui, % this.hwnd ":" cmd, % aParams[1], % aParams[2], % aParams[3]
	}
	
	AddControl(type, callback := 0, aParams*){
		return new WrappedControl(this, type, callback, aParams*)
	}
	
	GetScrollPos(){
		static SCROLLINFO:="UINT cbSize;UINT fMask;int  nMin;int  nMax;UINT nPage;int  nPos;int  nTrackPos"
				,scroll:=Struct(SCROLLINFO,{cbSize:sizeof(SCROLLINFO),fMask:4})
		GetScrollInfo(this.hwnd,true,scroll[])
		return scroll.nPos
	}
	
	GetHeight(){
		ControlGetPos,,,,h,,% "ahk_id " this.hwnd
		return h
	}
}

class WrappedControl {
	__New(parent, type, callback := 0, aParams*){
		this.Parent := parent
		Gui, % this.Parent.hwnd ":Add", % type, % "hwndhwnd " aParams[1], % aParams[2]
		this.hwnd := hwnd
		if (callback != 0){
			this.CtrlCmd("+g", callback)
		}
	}
	
	CtrlCmd(cmd, param3){
		GuiControl, % cmd, % this.hwnd, % param3
	}
}

CreateGUID(){
	VarSetCapacity(foo_guid, 16, 0)
	if !(DllCall("ole32.dll\CoCreateGuid", "Ptr", &foo_guid))
	{
		VarSetCapacity(tmp_guid, 38 * 2 + 1)
		DllCall("ole32.dll\StringFromGUID2", "Ptr", &foo_guid, "Ptr", &tmp_guid, "Int", 38 + 1)
		fin_guid := StrGet(&tmp_guid, "UTF-16")
	}
	return SubStr(fin_guid, 2, 36)
}
Last edited by evilC on 02 May 2017, 14:40, edited 1 time in total.
HotKeyIt
Posts: 1697
Joined: 29 Sep 2013, 18:35
Contact:

Re: Dynamic Gui sample - Vertical scrolling list of Guis

01 May 2017, 10:36

GetHeight is correct, the size is 35!
Following seems to work fine, or did I miss something!?

Code: Select all

	RemovePlugin(id){
		scroll_pos := this.PluginFrame.GetScrollPos()
		
		plugin := this.Plugins[id]
		plugin_height := plugin.GetHeight()
		plugin.GuiCmd("Destroy")
		old_guid := plugin.Guid
		old_order := this.GetPluginOrder(old_guid)
		this.Plugins.Delete(old_guid)
		
		max := this.PluginOrder.Length()
		
		Loop % max - old_order {
			id := old_order + A_Index
			guid := this.PluginOrder[id]
			plugin := this.Plugins[guid]
			plugin.ypos -= plugin_height + 4
			adjusted_pos := plugin.ypos - scroll_pos
			plugin.GuiCmd("Show", "x15 y" adjusted_pos)
		}
		this.PluginOrder.RemoveAt(old_order)
		this.SetSpacerHeight(this.Spacer.Height - plugin_height - 4)
	}

Return to “AutoHotkey_H”

Who is online

Users browsing this forum: No registered users and 2 guests