Class_ScrollGUI - updated on 2015-03-13

Post your working scripts, libraries and tools for AHK v1.1 and older
just me
Posts: 9406
Joined: 02 Oct 2013, 08:51
Location: Germany

Class_ScrollGUI - updated on 2015-03-13

06 Feb 2015, 10:10

This is a revised version of a script originally published in the old forum -> /board/topic/84333-class-class-scrollguiahk-simply-scroll-your-guis/

Change log:
Spoiler
Function:
Spoiler
How to use:
Spoiler
Class script:
Spoiler
Sample script:
Spoiler
Credits:
Spoiler
Maybe someone will find it useful.

:arrow: Class_ScrollGUI on GitHub
Last edited by just me on 13 Mar 2015, 09:12, edited 9 times in total.
User avatar
joedf
Posts: 8937
Joined: 29 Sep 2013, 17:08
Location: Canada
Contact:

Re: Class_ScrollGUI

06 Feb 2015, 16:59

Finally something that works! :D
Image Image Image Image Image
Windows 10 x64 Professional, Intel i5-8500, NVIDIA GTX 1060 6GB, 2x16GB Kingston FURY Beast - DDR4 3200 MHz | [About Me] | [About the AHK Foundation] | [Courses on AutoHotkey]
[ASPDM - StdLib Distribution] | [Qonsole - Quake-like console emulator] | [LibCon - Autohotkey Console Library]
User avatar
Soft
Posts: 174
Joined: 07 Jan 2015, 13:18
Location: Seoul
Contact:

Re: Class_ScrollGUI

07 Feb 2015, 01:44

awesome!
AutoHotkey & AutoHotkey_H v1.1.22.07
User avatar
Learning one
Posts: 173
Joined: 04 Oct 2013, 13:59
Location: Croatia
Contact:

Re: Class_ScrollGUI

07 Feb 2015, 04:05

Very nice! Thanks :)
User avatar
Soft
Posts: 174
Joined: 07 Jan 2015, 13:18
Location: Seoul
Contact:

Re: Class_ScrollGUI

07 Feb 2015, 05:27

@Learning one
I show your works on websites, and your works are incredible!!
Especially QPX()!
AutoHotkey & AutoHotkey_H v1.1.22.07
User avatar
evilC
Posts: 4822
Joined: 27 Feb 2014, 12:30

Re: Class_ScrollGUI

07 Feb 2015, 07:26

This is mind-blowingly awesome and exactly what I have been seeking!
I love the way it limits resizes too!
One small feature request...
If the mouse is over a horizontal scrollbar when you roll the wheel, could we have it scroll the horizontal bar pls?

Also, is it intentional that the mouse wheel does not work for the 2nd GUI in the demo, or a limitation / bug?
bobc119
Posts: 23
Joined: 10 Jan 2014, 17:02

Re: Class_ScrollGUI

07 Feb 2015, 09:11

Thank you for this! It works great and is simple to use
User avatar
evilC
Posts: 4822
Joined: 27 Feb 2014, 12:30

Re: Class_ScrollGUI

07 Feb 2015, 10:40

Here you go - this code ported to _Struct / WinStructs.

This library is now totally free of all BitWise Ops (NumGet / NumPut), so it is much easier to tell how it works (You can easily tell which properties from the structures it operates on, rather than having to interpret offsets)

https://github.com/evilC/CScrollGui/

If you want to add your version to GitHub, I will happily commit this code.
Or, I can maintain an alternate version - up to you.

[Edit] I *think* I got the correct logic in SetScrollInfo(), but am not 100% sure. If someone could verify, that would be great.
Last edited by evilC on 07 Feb 2015, 21:12, edited 1 time in total.
User avatar
evilC
Posts: 4822
Joined: 27 Feb 2014, 12:30

Re: Class_ScrollGUI

07 Feb 2015, 12:46

Whilst in some regards I love the max size limiting, it would be nice to be able to turn it off, ideally on a per-axis basis.

I was looking at combining this with AutoXYWH, but this feature kind of stops the two from working together.

What I was looking to do was create a GUI where a text box grew as you made the GUI taller, but had scrollbars also.

Code: Select all

#SingleInstance force
#Include <CScrollGUI>
#Include <AutoXYWH>

Gui, +Resize hwndHGUI
Gui, Add, Edit  ,    w200 h200    hwndhEdit
Gui, Add, Button, ys w40 hp vBtn1
Gui, Add, Button, ys wp  hp vBtn2
Gui, Add, Text, ,Hello
Gui, Add, Text, ,Hello
Gui, Add, Text, ,Hello
Gui, Add, Text, ,Hello
Gui, Add, Text, ,Hello
Gui, Add, Text, ,Hello

; Create ScrollGUI1 with both horizontal and vertical scrollbars and mouse wheel capturing
SG1 := New ScrollGUI(HGUI, 0, 0, "+Resize +LabelGui1", 3, 3)
; Show ScrollGUI1
SG1.Show("ScrollGUI1 Title", "")
;Gui, Show
Return

GuiSize:
    AutoXYWH(hEdit, "w")
    AutoXYWH("Btn1|Btn2", "x")
Return

GuiClose:
ExitApp
Also, there seems to be a problem with the height calculation?
In this pic, there is nothing outside the scrollbars - they are not needed, so they should not be showing...
Image
User avatar
evilC
Posts: 4822
Joined: 27 Feb 2014, 12:30

Re: Class_ScrollGUI

07 Feb 2015, 13:26

; If you register mouse wheel messages, the messages will be captured solely to scroll the ScrollGUI.
; You won't be able to use the wheel to scroll child GUI controls.
IIRC, I managed to work around this in a previous attempt I made at scrolling GUIs.
I would have to hunt back thru my source though to work out exactly what I did, but if memory serves correctly, I added the HWND of scrollable GuiControls to an exception list, and when the scroll message came in, if the mouse was over one of these GuiControls, I passed the message to the HWND of the GuiControl or something.
User avatar
evilC
Posts: 4822
Joined: 27 Feb 2014, 12:30

Re: Class_ScrollGUI

07 Feb 2015, 14:00

Proof of concept for being able to scroll GUI items with the mouse wheel if the cursor is over the GUI item

Code: Select all

; an attempt at allowing controls to scroll using the mouse wheel
#SingleInstance force
#NoEnv

#Include <CScrollGUI>

SetBatchLines, -1
; -------------------------------------------------------------------------------------------------------------------
; ChildGUI 1
Gui, New, +hwndHGUI
Gui, Margin, 20, 20
Gui, Add, ListView, w380 h200 hwndLVTest, Col
Loop 20 {
	LV_ADD("", "Test")
}
; Create ScrollGUI1 with both horizontal and vertical scrollbars and mouse wheel capturing
SG1 := New NewScrollGui(HGUI, 400, 400, "+Resize +MinSize +LabelGui1", 3, 3)
; Show ScrollGUI1
SG1.Show("ScrollGUI1 Title", "y0 xcenter")

; ----------------------------------------------------------------------------------------------------------------------
ShowHide:
   GuiControlGet, V, %HGUI2%:Visible, TX2
   GuiControl, %HGUI2%:Hide%V%, TX2
   GuiControlGet, V, %HGUI2%:Visible, TX3
   GuiControl, %HGUI2%:Hide%V%, TX3
   SG2.AdjustToChild()
Return
; ----------------------------------------------------------------------------------------------------------------------
Esc::
Gui1Close:
Gui1Escape:
ExitApp
; ----------------------------------------------------------------------------------------------------------------------
Gui1Size:
   If (A_EventInfo <> 1)
      SG1.AdjustToParent()
Return
; ----------------------------------------------------------------------------------------------------------------------
Gui2Close:
Gui2Escape:
   SG2 := ""
Return

class NewScrollGui extends ScrollGUI {
   __New(HGUI, Width, Height, GuiOptions := "", ScrollBars := 3, Wheel := 0) {
      Static SB_HORZ := 0, SB_VERT = 1
      Static WM_HSCROLL := 0x0114, WM_VSCROLL := 0x0115
      Static WM_MOUSEWHEEL := 0x020A, WM_MOUSEHWHEEL := 0x020E
      Static WS_HSCROLL := "0x100000", WS_VSCROLL := "0x200000"
      If ((ScrollBars <> 1) && (ScrollBars <> 2) && (ScrollBars <> 3))
      || ((Wheel <> 0) && (Wheel <> 1) && (Wheel <> 2) && (Wheel <> 3))
         Return False
      If !DllCall("User32.dll\IsWindow", "Ptr", HGUI, "UInt")
         Return False
      ; Child GUI
      Gui, %HGUI%:-Caption
      Gui, %HGUI%:Show, AutoSize Hide
      Rect := new _Struct(WinStructs.RECT)
      RECT := new _Struct(WinStructs.RECT)
      DllCall("User32.dll\GetWindowRect", "Ptr", HGUI, "Ptr", RECT[])
      MaxH := RECT.Right - RECT.Left
      MaxV := RECT.Bottom - RECT.Top
      
      LineH := Ceil(MaxH / 20)
      LineV := Ceil(MaxV / 20)
      ; ScrollGUI
      If (Width = 0)
         Width := MaxH
      If (Height = 0)
         Height := MaxV
      MX := MY := Styles := ""
      If (ScrollBars & 1) {
         MX := MaxH + 1
         Styles .= " +" . WS_HSCROLL
      }
      If (ScrollBars & 2) {
         Styles .= " +" . WS_VSCROLL
         MY := MaxV + 1
      }
      Gui, New, %GuiOptions% %Styles% +hwndHWND
      If (MX <> "") || (MY <> "")
         Gui, %HWND%:+MaxSize%MX%x%MY%
      Gui, %HWND%:Show, w%Width% h%Height% Hide
      DllCall("User32.dll\GetClientRect", "Ptr", HWND, "Ptr", RECT[])
      PageH := RECT.Right
      PageV := RECT.Bottom
      ; Instance variables
      This.HWND := HWND
      This.HGUI := HGUI
      This.Width := Width
      This.Height := Height
      If (ScrollBars & 1) {
         ;This.SetScrollInfo(SB_HORZ, {Max: MaxH, Page: PageH, Pos: 0})
         ;SI := new 
         SI := new _Struct(WinStructs.SCROLLINFO)
         SI.nMax := MaxH
         SI.nPage := PageH
         SI.nPos := 0
         This.SetScrollInfo(SB_HORZ, SI)
         OnMessage(WM_HSCROLL, "NewScrollGUI.On_WM_Scroll")
         If (Wheel & 1)
            OnMessage(WM_MOUSEHWHEEL, "NewScrollGUI.On_WM_Wheel")
         This.MaxH := MaxH
         This.LineH := LineH
         This.PageH := PageH
         This.PosH := 0
         This.ScrollH := True
         If (Wheel)
            This.WheelH := True
      }
      If (ScrollBars & 2) {
         SI := new _Struct(WinStructs.SCROLLINFO)
         SI.nMax := MaxV
         SI.nPage := PageV
         SI.nPos := 0
         ;This.SetScrollInfo(SB_VERT, {Max: MaxV, Page: PageV, Pos: 0})
         This.SetScrollInfo(SB_VERT, SI)
         OnMessage(WM_VSCROLL, "NewScrollGUI.On_WM_Scroll")
         If (Wheel & 2)
            OnMessage(WM_MOUSEWHEEL, "NewScrollGUI.On_WM_Wheel")
         This.MaxV := MaxV
         This.LineV := LineV
         This.PageV := PageV
         This.PosV := 0
         This.ScrollV := True
         If (Wheel)
            This.WheelV := True
      }
      ; Set the position of the child GUI
      Gui, %HGUI%:+parent%HWND%
      Gui, %HGUI%:Show, x0 y0
      This.Instances[HWND] := &This
   }

   On_WM_Wheel(LP, Msg, H) {
		global LVTest
		MouseGetPos,tmp,tmp,tmp,hwnd,2
		if (hwnd = LVTest){
			HWND := LVTest
		} else {
			HWND := WinExist("A")	
		}
		Static WM_HSCROLL := 0x0114, WM_VSCROLL := 0x0115
		Static WM_MOUSEWHEEL := 0x020A, WM_MOUSEHWHEEL := 0x020E
		If ScrollGUI.Instances.HasKey(HWND) {
			Instance := Object(NewScrollGUI.Instances[HWND])
			If ((Msg = WM_MOUSEHWHEEL) && Instance.WheelH) || ((Msg = WM_MOUSEWHEEL)  && Instance.WheelV){
				Return Instance.Wheel(This, LP, Msg, HWND)
			}
		}
   }
}
just me
Posts: 9406
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: Class_ScrollGUI

08 Feb 2015, 03:59

Thanks!

@evilC:
Also, is it intentional that the mouse wheel does not work for the 2nd GUI in the demo, or a limitation / bug?
It's intentional.
If the mouse is over a horizontal scrollbar when you roll the wheel, could we have it scroll the horizontal bar pls?
Lexikos used Shift+MouseWheel in his script on autohotkey dot com -> /board/topic/26033-scrollable-gui-proof-of-concept/?p=168174. I'll think about it.
Here you go - this code ported to _Struct / WinStructs.
I don't see any need to use _Struct() here, because all used structures are just simple ones so _Struct() will needlessly blow up the code size.
Whilst in some regards I love the max size limiting, it would be nice to be able to turn it off, ideally on a per-axis basis.
Why should the client area of the ScrollGUI grow larger than the contained child GUI?
I was looking at combining this with AutoXYWH, but this feature kind of stops the two from working together.
I didn't test it, but at least you have to call AdjustToChild() after resizing the child GUI.
Also, there seems to be a problem with the height calculation?
You're right, it is caused by the fact that MaxSize was set before the first Gui, Show, .... I'll fix it.
User avatar
evilC
Posts: 4822
Joined: 27 Feb 2014, 12:30

Re: Class_ScrollGUI - updated on 2015-02-08

08 Feb 2015, 09:59

I don't see any need to use _Struct() here, because all used structures are just simple ones so _Struct() will needlessly blow up the code size.
Well it took me a while to decipher what was going on, so without Struct, it is certainly not as readable.
If memory is that big a concern, just use _Struct and not WinStructs.
I compiled some scripts to find out how big a difference it is...
Original: 807KB
WinStructs / _Struct: 845KB
Just _Struct: 841KB
Meh. ~40K is nothing.
Why should the client area of the ScrollGUI grow larger than the contained child GUI?
Because the child GUI can expand.
Lets forget about X for the moment and just consider a situation with a fixed-width, vertically scrolling GUI.
Imagine you have a text box (200px high) and a ListView (dynamic height).
The TextBox is always 200px high, with the LV taking up all available remaining space. If you size down to <200px, scrollbars should appear, because the 200px high text box and the 20px min height LV cannot all be shown in 200px.
However, if you take the width up to 300px, the Textbox would take 200px of that and the LV would take the remaining 100px.
No scrollbars should appear.

Furthermore, it should always be possible for a GUI to be larger than the size of it's contents.
What if you have a space in your GUI that could contain any number of elements? Sure, at any given moment it may be blank, but when the user clicks "Add" and adds an element, something would appear in that blank space.
If the user keeps adding elements until it overflows the available space, scrollbars should appear.
See here for an example of the kind of thing I am talking about:
http://screencast-o-matic.com/watch/c2jeYjnKgC

This issue can be demonstrated using your own demo:
Take GUI 2 and make it resizable, run the script (SG2 := New ScrollGUI(HGUI2, 600, 200, "+Resize +LabelGui2"))
Click "Show / Hide additional controls".
Resize the GUI as big as it will go, so you can see the extra controls.
Click "Show / Hide additional controls" again to hide the controls - the GUI remains sized up (Not a problem).
Try to resize or move the GUI - what happens?
As soon as you click the mouse down, the GUI suddenly jumps to fit the *current* size of the GUI.
This is especially jarring if the user grabbed the bottom right resize handle - as soon as they press LMB down and move the mouse, the thing they had "grabbed" with the mouse suddenly moves without them asking it to.
This is not in line with what the user would reasonably expect to happen.
Lexikos used Shift+MouseWheel in his script on autohotkey dot com -> /board/topic/26033-scrollable-gui-proof-of-concept/?p=168174. I'll think about it.
It is possible to detect if the cursor is over a scrollbar (Using a similar technique to how I did the fix to make controls scrollable with the mouse wheel). Shift would be superfluous in this instance.

Well it sounds like we maybe have some differences of opinion here, I take it that it is OK with you for me to maintain my own fork?
just me
Posts: 9406
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: Class_ScrollGUI - updated on 2015-02-08

09 Feb 2015, 05:56

Hi evilC,

seemingly we are talking about different concepts.

Class_ScrollGUI is designed to scroll a non-resizable client GUI, though controls may be added or hidden. Whenever changes affect the size of the client GUI needed to display all visible controls, you must call AdjustToChild(). The dimensions of the client GUI will be recalculated using the AutoSize option and the scroll bars will be adjusted, then. The required scroll bars are always visible in the ScrollGUI, but will be disabled if there is nothing to scroll.
Well it took me a while to decipher what was going on, so without Struct, it is certainly not as readable.
As said, Class_ScrollGUI is using just two structures: RECT and SCROLLINFO. Both are simple and it shouldn't be difficult to 'decipher' the used NumGet()/NumPut() functions. Also, even if you want to use _Struct() for convenience, you always should know the 'physical layout' of a struture. IMO, it's still not save in either case to pass a structure definition found in the MSDN to _Struct() and rely blind on the result.
As soon as you click the mouse down, the GUI suddenly jumps to fit the *current* size of the GUI.
This will be fixed.
It is possible to detect if the cursor is over a scrollbar (Using a similar technique to how I did the fix to make controls scrollable with the mouse wheel). Shift would be superfluous in this instance.
This is not common the way used for scrolling. As long as a scrollable control has the focus, it will scroll on mouse wheel messages even if the cursor is outside of the control; it doesn't have to be over a scroll bar.
... I take it that it is OK with you for me to maintain my own fork?
Sure.
User avatar
evilC
Posts: 4822
Joined: 27 Feb 2014, 12:30

Re: Class_ScrollGUI - updated on 2015-02-08

09 Feb 2015, 10:57

Hmm, gonna have starting to keep a copy of your scripts so I can see what you changed...

Do you have a copy of the previous version, or was it only the one line that changed? I only noticed the line Gui, %HWND%:Show, w%Width% h%Height% Hide move up 2 lines so far.
just me
Posts: 9406
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: Class_ScrollGUI - updated on 2015-02-08

09 Feb 2015, 11:47

Well, here's the complete first release:
Spoiler
User avatar
evilC
Posts: 4822
Joined: 27 Feb 2014, 12:30

Re: Class_ScrollGUI - updated on 2015-02-08

09 Feb 2015, 11:51

Ah, that makes it a lot easier, thanks!
BTW, did you notice my solution for scrolling of GUI controls with the mouse wheel?

I think it could maybe even be improved even further by checking if the control has scrollbars visible, and if not, ignoring the exception. Hell, maybe we could even use this to avoid having to declare exceptions at all - if you roll the mouse wheel over any control with a scrollbar visible, route the scroll message to that control instead of scrolling the main window.
just me
Posts: 9406
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: Class_ScrollGUI - updated on 2015-02-08

09 Feb 2015, 12:05

I'm already working on another approach, but I think you won't like it.

Nevertheless I've transferred the class script source to GitHub. You'll find all coming changes there.

Return to “Scripts and Functions (v1)”

Who is online

Users browsing this forum: furqan and 84 guests