AutoHotkey Homepage AutoHotkey Community
Let's help each other out
 
 FAQFAQ   SearchSearch   MemberlistMemberlist   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

Scrollable Gui - Proof of Concept
Goto page 1, 2  Next
 
Post new topic   Reply to topic    AutoHotkey Community Forum Index -> Scripts & Functions
View previous topic :: View next topic  
Author Message
ahklerner



Joined: 26 Jun 2006
Posts: 1249
Location: USA

PostPosted: Sun Feb 10, 2008 8:24 pm    Post subject: Scrollable Gui - Proof of Concept Reply with quote

Many Thanks to the Guest Jamey in this post: http://www.autohotkey.com/forum/viewtopic.php?t=3730
If it originally came form someone else, let me know please.


This is just something I was playing with....maybe someone can make it better. Just posting for fun.

There are currently multiple bugs....tabbing through controls does not work....some issues with resizing....etc etc...

I do not care to really do anything more with it, so if some one makes enhancements, post them here, and I will refer to them from the first post.

Here is the code:
Code:
DetectHiddenWindows, On
Gui, 1:+LastFound +0x200000 +Resize +0x2000000
hGui := WinExist()
Gui, 1:Add, Edit, r2 w150 , Edit 1
Gui, 1:Add, Edit, r2 w150 , Edit 2
Gui, 1:Add, Edit, r2 w150 , Edit 3
Gui, 1:Add, Edit, r2 w150 , Edit 4
Gui, 1:Add, Edit, ym r2 w150 , Edit 5
Gui, 1:Add, Edit, r2 w150 , Edit 6
Gui, 1:Add, Edit, r2 w150 , Edit 7
Gui, 1:Add, Edit, r2 w150 , Edit 8
Gui, 1:Add, Button, gGo, Go
Gui, 1:Show, Hide
ScrollInit()
Gui, 1:Show, h100   
Return

ScrollInit() {
   Global
   VarSetCapacity(SCROLLBAR_INFO, 28, 0)   ;Allocate SCROLLBAR_INFO structure and zero it
   NumPut(28, &SCROLLBAR_INFO)         ;Initialize its count-bytes parameter
   NumPut(0x17, &SCROLLBAR_INFO + 4)      ;Initialize the mask for what properties to get or set, SIF_ALL = 0x17
   SetScrollBar(hGui, 0, 100, 10, 0)
   C_Create(1)
   GuiWinProc := RegisterCallback("GuiWindowProc", ""  ; "" to avoid fast-mode for subclassing.
       , 4, hGui)  ; Must specify exact ParamCount when EventInfo parameter is present.
   GuiWinProcOld := DllCall("SetWindowLong", UInt, hGui, Int, -4  ; -4 is GWL_WNDPROC
       , Int, GuiWinProc, UInt)  ; Return value must be set to UInt vs. Int.
   ConWinProc := RegisterCallback("ConWinProc", ""  ; "" to avoid fast-mode for subclassing.
       , 4, hGui)  ; Must specify exact ParamCount when EventInfo parameter is present.
   ConWinProcOld := DllCall("SetWindowLong", UInt, hContainer, Int, -4  ; -4 is GWL_WNDPROC
       , Int, ConWinProc, UInt)  ; Return value must be set to UInt vs. Int.
   }

C_Create(GuiNum,Height=0,Width=0) {
   global hContainer, Container
   Gui %GuiNum%:+LastFound
   hGui := WinExist()
   If !Height && !Width
      WinGetPos,,, Width, Height, ahk_id %hGui%
   Gui %GuiNum%:Add, Text,x0 y0 h%Height% w%Width% +0x4000000 +0x2000000 hwndhContainer vContainer
   WinGet, CList, ControlListhWnd, ahk_id %hGui%
   ;MsgBox % CList
   Loop, Parse, CList, `n
      DllCall("SetParent", "uint", A_LoopField, "uint", hContainer)
   }

GuiSize:

VScrollPixelsPerLine := A_GuiHeight / 100
Return



Esc::ExitApp

ConWinProc(hwnd, uMsg, wParam, lParam) {
   global ConWinProcOld, GuiWinProcOld, GuiWinProc
   ;Critical
   OldFormat := A_FormatInteger
   SetFormat, Integer, Hex
   MsgLst := WM_COMMAND := 0x111
   MsgLst .= "," . WM_SYSCOMMAND := 0x112
   uMsg += 0
   SetFormat, Integer, %OldFormat%
   if uMsg in %MsgLst%
      {   
      ReturnVal := DllCall("CallWindowProcA", UInt, GuiWinProcOld, UInt, A_EventInfo, UInt, uMsg, UInt, wParam, UInt, lParam)
      return ReturnVal
      }
   return DllCall("CallWindowProcA", UInt, ConWinProcOld, UInt, hwnd, UInt, uMsg, UInt, wParam, UInt, lParam)
   }

Go:
MsgBox
Return

GuiWindowProc(hwnd, uMsg, wParam, lParam) {
   ;Critical
   OldFormat := A_FormatInteger
   SetFormat, Integer, Hex
   global GuiWinProcOld, GuiWinProc, VScrollPixelsPerLine, Container, hContainer
   MsgLst := WM_VSCROLL := 0x115
   uMsg += 0
   SetFormat, Integer, %OldFormat%
   if uMsg in %MsgLst%
      {   
      global hGui      ;Only handle messages for the window we want to scroll
      if (hwnd != hGui)
         return DllCall("CallWindowProcA", UInt, GuiWinProcOld, UInt, hwnd, UInt, uMsg, UInt, wParam, UInt, lParam)

      wParamWordLow := Mod(wParam, 0x10000)
      wParamWordHigh := (wParam - wParamWordLow) / 0x10000

      if (wParamWordLow = 5 or wParamWordLow = 8)      ;SB_THUMBTRACK or SB_ENDSCROLL
         return DllCall("CallWindowProcA", UInt, GuiWinProcOld, UInt, hwnd, UInt, uMsg, UInt, wParam, UInt, lParam)

      QueryScrollBar(hwnd, nMin, nMax, nPage, nPos, nTrackPos)

      if (wParamWordLow = 7)            ;SB_BOTTOM
         a:= "" ; MsgBox, SB_BOTTOM
      else if (wParamWordLow = 6)            ;SB_TOP
         a:= "" ; MsgBox, SB_TOP
      else if (wParamWordLow = 1) {            ;SB_LINEDOWN
         SetScrollBar(hwnd, nMin, nMax, nPage, NewPos := nPos+1)
         GuiControl,1:Move, Container, % "y" . -NewPos * VScrollPixelsPerLine
         }else if (wParamWordLow = 0) {            ;SB_LINEUP
         SetScrollBar(hwnd, nMin, nMax, nPage, NewPos := nPos-1)
         GuiControl,1:Move, Container, % "y" . -NewPos * VScrollPixelsPerLine
            }else if (wParamWordLow = 3) {            ;SB_PAGEDOWN
         SetScrollBar(hwnd, nMin, nMax, nPage, NewPos := nPos+nPage)
         GuiControl,1:Move, Container, % "y" . -NewPos * VScrollPixelsPerLine
      }else if (wParamWordLow = 2) {            ;SB_PAGEUP
         SetScrollBar(hwnd, nMin, nMax, nPage, NewPos := nPos-nPage)
         GuiControl,1:Move, Container, % "y" . -NewPos * VScrollPixelsPerLine
      }else if (wParamWordLow = 4) {            ;SB_THUMBPOSITION
         SetScrollBar(hwnd, nMin, nMax, nPage, NewPos := wParamWordHigh)
         GuiControl,1:Move, Container, % "y" . -NewPos * VScrollPixelsPerLine
         }
;      ToolTip wParamWordLow = %wParamWordLow%`nContainerY = %ContainerY%`nVScrollPixelsPerLine = %VScrollPixelsPerLine%`nhwnd = %hwnd%`nnMin = %nMin%`nnMax = %nMax%`nnPage = %nPage%`nnPos = %nPos%`nnTrackPos = %nTrackPos% ;`nMsgLst= |%MsgLst%|
      return DllCall("CallWindowProcA", UInt, GuiWinProcOld, UInt, hwnd, UInt, uMsg, UInt, wParam, UInt, lParam)
      }
; Otherwise (since above didn't return), pass all unhandled events to the original WindowProc.
;   SetFormat, Integer, %OldFormat%
   return DllCall("CallWindowProcA", UInt, GuiWinProcOld, UInt, hwnd, UInt, uMsg, UInt, wParam, UInt, lParam)
   }

QueryScrollBar(hwnd, ByRef nMin, ByRef nMax, ByRef nPage, ByRef nPos, ByRef nTrackPos)
{
   ;Win32 API:   BOOL GetScrollInfo( HWND hwnd, int fnBar, LPSCROLLINFO lpsi )

   global SCROLLBAR_INFO

   bSuccess := DllCall("GetScrollInfo", UInt, hwnd, Int, 1, UInt, &SCROLLBAR_INFO)   ;SB_VERT = 1
   if (!bSuccess)
      return false

   nMin := NumGet(&SCROLLBAR_INFO, 8)
   nMax := NumGet(&SCROLLBAR_INFO, 12)
   nPage := NumGet(&SCROLLBAR_INFO, 16)
   nPos := NumGet(&SCROLLBAR_INFO, 20)
   nTrackPos := NumGet(&SCROLLBAR_INFO, 24)

   return true
}
;---------------------------------------------------------------------------------------------------------------
SetScrollBar(hwnd, nMin, nMax, nPage, nPos)
{
   ;Win32 API:   int SetScrollInfo( HWND hwnd, int fnBar, LPCSCROLLINFO lpsi, BOOL fRedraw )

   global SCROLLBAR_INFO
   NumPut(nMin, &SCROLLBAR_INFO + 8)      ;Min
   NumPut(nMax, &SCROLLBAR_INFO + 12)      ;Max
   NumPut(nPage, &SCROLLBAR_INFO + 16)      ;Page
   NumPut(nPos, &SCROLLBAR_INFO + 20)      ;Pos
   iReturnPos := DllCall("SetScrollInfo", UInt, hwnd, Int, 1, UInt, &SCROLLBAR_INFO, Int, true)   ;SB_VERT = 1
   return (iReturnPos == nPos)
}
;---------------------------------------------------------------------------------------------------------------
GuiClose:
   ExitApp
return
;---------------------------------------------------------------------------------------------------------------

_________________

ʞɔпɟ əɥʇ ʇɐɥʍ
Back to top
View user's profile Send private message
Lexikos



Joined: 17 Oct 2006
Posts: 2737
Location: Australia, Qld

PostPosted: Mon Feb 11, 2008 2:50 am    Post subject: Reply with quote

Here is my attempt (based on some C++ code I wrote long ago.)
Code:
#NoEnv

OnMessage(0x115, "OnScroll") ; WM_VSCROLL
OnMessage(0x114, "OnScroll") ; WM_HSCROLL

Gui, +Resize +0x300000 ; WS_VSCROLL | WS_HSCROLL

Loop 8
    Gui, Add, Edit, R5 W400, Edit %A_Index%
Gui, Add, Button,, Do absolutely nothing
Gui, Show, W200 H200

Gui, +LastFound
GroupAdd, MyGui, % "ahk_id " . WinExist()

return

GuiSize:
    UpdateScrollBars(A_Gui, A_GuiWidth, A_GuiHeight)
return

GuiClose:
ExitApp

#IfWinActive ahk_group MyGui
WheelUp::
WheelDown::
+WheelUp::
+WheelDown::
    ; SB_LINEDOWN=1, SB_LINEUP=0, WM_HSCROLL=0x114, WM_VSCROLL=0x115
    OnScroll(InStr(A_ThisHotkey,"Down") ? 1 : 0, 0, GetKeyState("Shift") ? 0x114 : 0x115, WinExist())
return
#IfWinActive

UpdateScrollBars(GuiNum, GuiWidth, GuiHeight)
{
    static SIF_RANGE=0x1, SIF_PAGE=0x2, SIF_DISABLENOSCROLL=0x8, SB_HORZ=0, SB_VERT=1
   
    Gui, %GuiNum%:Default
    Gui, +LastFound
   
    ; Calculate scrolling area.
    Left := Top := 9999
    Right := Bottom := 0
    WinGet, ControlList, ControlList
    Loop, Parse, ControlList, `n
    {
        GuiControlGet, c, Pos, %A_LoopField%
        if (cX < Left)
            Left := cX
        if (cY < Top)
            Top := cY
        if (cX + cW > Right)
            Right := cX + cW
        if (cY + cH > Bottom)
            Bottom := cY + cH
    }
    Left -= 8
    Top -= 8
    Right += 8
    Bottom += 8
    ScrollWidth := Right-Left
    ScrollHeight := Bottom-Top
   
    ; Initialize SCROLLINFO.
    VarSetCapacity(si, 28, 0)
    NumPut(28, si) ; cbSize
    NumPut(SIF_RANGE | SIF_PAGE, si, 4) ; fMask
   
    ; Update horizontal scroll bar.
    NumPut(ScrollWidth, si, 12) ; nMax
    NumPut(GuiWidth, si, 16) ; nPage
    DllCall("SetScrollInfo", "uint", WinExist(), "uint", SB_HORZ, "uint", &si, "int", 1)
   
    ; Update vertical scroll bar.
;     NumPut(SIF_RANGE | SIF_PAGE | SIF_DISABLENOSCROLL, si, 4) ; fMask
    NumPut(ScrollHeight, si, 12) ; nMax
    NumPut(GuiHeight, si, 16) ; nPage
    DllCall("SetScrollInfo", "uint", WinExist(), "uint", SB_VERT, "uint", &si, "int", 1)
   
    if (Left < 0 && Right < GuiWidth)
        x := Abs(Left) > GuiWidth-Right ? GuiWidth-Right : Abs(Left)
    if (Top < 0 && Bottom < GuiHeight)
        y := Abs(Top) > GuiHeight-Bottom ? GuiHeight-Bottom : Abs(Top)
    if (x || y)
        DllCall("ScrollWindow", "uint", WinExist(), "int", x, "int", y, "uint", 0, "uint", 0)
}

OnScroll(wParam, lParam, msg, hwnd)
{
    static SIF_ALL=0x17, SCROLL_STEP=10
   
    bar := msg=0x115 ; SB_HORZ=0, SB_VERT=1
   
    VarSetCapacity(si, 28, 0)
    NumPut(28, si) ; cbSize
    NumPut(SIF_ALL, si, 4) ; fMask
    if !DllCall("GetScrollInfo", "uint", hwnd, "int", bar, "uint", &si)
        return
   
    VarSetCapacity(rect, 16)
    DllCall("GetClientRect", "uint", hwnd, "uint", &rect)
   
    new_pos := NumGet(si, 20) ; nPos
   
    action := wParam & 0xFFFF
    if action = 0 ; SB_LINEUP
        new_pos -= SCROLL_STEP
    else if action = 1 ; SB_LINEDOWN
        new_pos += SCROLL_STEP
    else if action = 2 ; SB_PAGEUP
        new_pos -= NumGet(rect, 12, "int") - SCROLL_STEP
    else if action = 3 ; SB_PAGEDOWN
        new_pos += NumGet(rect, 12, "int") - SCROLL_STEP
    else if action = 5 ; SB_THUMBTRACK
        new_pos := NumGet(si, 24, "int") ; nTrackPos
    else if action = 6 ; SB_TOP
        new_pos := NumGet(si, 8, "int") ; nMin
    else if action = 7 ; SB_BOTTOM
        new_pos := NumGet(si, 12, "int") ; nMax
    else
        return
   
    min := NumGet(si, 8, "int") ; nMin
    max := NumGet(si, 12, "int") - NumGet(si, 16) ; nMax-nPage
    new_pos := new_pos > max ? max : new_pos
    new_pos := new_pos < min ? min : new_pos
   
    old_pos := NumGet(si, 20, "int") ; nPos
   
    x := y := 0
    if bar = 0 ; SB_HORZ
        x := old_pos-new_pos
    else
        y := old_pos-new_pos
    ; Scroll contents of window and invalidate uncovered area.
    DllCall("ScrollWindow", "uint", hwnd, "int", x, "int", y, "uint", 0, "uint", 0)
   
    ; Update scroll bar.
    NumPut(new_pos, si, 20, "int") ; nPos
    DllCall("SetScrollInfo", "uint", hwnd, "int", bar, "uint", &si, "int", 1)
}

The main differences are:
  • OnMessage is used rather than subclassing the window.
  • SB_THUMBTRACK is handled instead of SB_THUMBPOSITION, so the controls scroll in real-time (before you release the button.)
  • Resizing is handled better.
  • Tabbing works, though it doesn't scroll automatically (so the control tabbed to may not be visible.)
  • ScrollWindow() is used to move all of the controls.

Bug: The horizontal scroll bar isn't accounted for until the next resize after it is shown. (This is usually only noticeable when restoring the window after maximizing.)

Updated to support mouse wheel.


Last edited by Lexikos on Wed Feb 20, 2008 3:15 am; edited 1 time in total
Back to top
View user's profile Send private message
ahklerner



Joined: 26 Jun 2006
Posts: 1249
Location: USA

PostPosted: Mon Feb 11, 2008 2:58 am    Post subject: Reply with quote

Well now, That is just freaking awesome!!!!!

Thanks for sharing.
_________________

ʞɔпɟ əɥʇ ʇɐɥʍ
Back to top
View user's profile Send private message
AdamPash



Joined: 27 Sep 2007
Posts: 25
Location: http://lifehacker.com

PostPosted: Wed Feb 20, 2008 1:20 am    Post subject: Reply with quote

This really is incredible, thanks a lot. It's perfect for a script I'm putting together right now. One question, though: Is it possible to get scrolling to work with the mouse scroll wheel? Or even from the keyboard with the up/down arrows?
Back to top
View user's profile Send private message
engunneer



Joined: 30 Aug 2005
Posts: 6847
Location: Pacific Northwest, US

PostPosted: Wed Feb 20, 2008 1:35 am    Post subject: Reply with quote

Adam, Is your code based on lexiKos' or ahklerner's code?
_________________
Unless otherwise noted, all code is untested.
Common Answers: 1.(Loops, Viruses, etc.) 2. Search 3.RTFM
Back to top
View user's profile Send private message Visit poster's website
AdamPash



Joined: 27 Sep 2007
Posts: 25
Location: http://lifehacker.com

PostPosted: Wed Feb 20, 2008 1:37 am    Post subject: Reply with quote

Ah, sorry about that. It's based on lexiKos'.
Back to top
View user's profile Send private message
Lexikos



Joined: 17 Oct 2006
Posts: 2737
Location: Australia, Qld

PostPosted: Wed Feb 20, 2008 3:16 am    Post subject: Reply with quote

I originally tried catching WM_MOUSEWHEEL, but apparently that is only sent to the focused control. I've updated the script to support the mouse wheel, via hotkeys.
Back to top
View user's profile Send private message
Wdb



Joined: 27 Feb 2006
Posts: 15
Location: Italy

PostPosted: Thu Feb 21, 2008 3:36 pm    Post subject: Reply with quote

lexiKos wrote:
Here is my attempt (based on some C++ code I wrote long ago.)

This is a very good job lexiKos, thank you!

And is it possible to retrieve the absolute coordinates of the displayed page, specially when I scroll down the wheel mouse ??? (i.e: if the page dimension exceed the screen widht?)
Back to top
View user's profile Send private message
AdamPash



Joined: 27 Sep 2007
Posts: 25
Location: http://lifehacker.com

PostPosted: Fri Feb 22, 2008 12:10 am    Post subject: Reply with quote

That mouse scroll update worked like a charm, thanks a lot lexiKos!

I also included the up and down keys with the rest of the hotkeys to scroll the window using those (there's user input where that'll be a problem with my GUI window, so there's no concern for conflict). So now it just looks like this:

Code:
WheelUp::
WheelDown::
+WheelUp::
+WheelDown::
Up::
Down::
    ; SB_LINEDOWN=1, SB_LINEUP=0, WM_HSCROLL=0x114, WM_VSCROLL=0x115
    OnScroll(InStr(A_ThisHotkey,"Down") ? 1 : 0, 0, GetKeyState("Shift") ? 0x114 : 0x115, WinExist())
return
Back to top
View user's profile Send private message
Lexikos



Joined: 17 Oct 2006
Posts: 2737
Location: Australia, Qld

PostPosted: Fri Feb 22, 2008 12:22 am    Post subject: Reply with quote

Wdb wrote:
And is it possible to retrieve the absolute coordinates of the displayed page, specially when I scroll down the wheel mouse ??? (i.e: if the page dimension exceed the screen widht?)
You could get the position of the scroll bar using GetScrollPos or similar. There is no "page" - ScrollWindow just offsets each control by the difference in scroll bar position (when it moves.)
Back to top
View user's profile Send private message
Wdb



Joined: 27 Feb 2006
Posts: 15
Location: Italy

PostPosted: Fri Feb 22, 2008 7:38 am    Post subject: Reply with quote

lexiKos wrote:
Wdb wrote:
And is it possible to retrieve the absolute coordinates of the displayed page, specially when I scroll down the wheel mouse ??? (i.e: if the page dimension exceed the screen widht?)
You could get the position of the scroll bar using GetScrollPos or similar. There is no "page" - ScrollWindow just offsets each control by the difference in scroll bar position (when it moves.)


Ok, thank you.

Another question:
what happens if I de-comment your statements NumPut:
Code:

    ; Update vertical scroll bar.
;     NumPut(SIF_RANGE | SIF_PAGE | SIF_DISABLENOSCROLL, si, 4) ; fMask
Back to top
View user's profile Send private message
Lexikos



Joined: 17 Oct 2006
Posts: 2737
Location: Australia, Qld

PostPosted: Fri Feb 22, 2008 9:45 am    Post subject: Reply with quote

That adds the SIF_DISABLENOSCROLL flag, which causes the scroll bar to be disabled rather than hidden when it is not needed.
Back to top
View user's profile Send private message
automaticman



Joined: 27 Oct 2006
Posts: 372

PostPosted: Fri Feb 22, 2008 2:20 pm    Post subject: Reply with quote

A zoomable GUI (=ZUI), proof of concept would be also interesting.
Back to top
View user's profile Send private message
YokoiL



Joined: 18 Jun 2008
Posts: 3

PostPosted: Thu Jun 19, 2008 10:30 am    Post subject: Reply with quote

Quote:
Tabbing works, though it doesn't scroll automatically (so the control tabbed to may not be visible.)


Is there anyway to make it so that you can use tab and it scrolls to where the cursor is?
Back to top
View user's profile Send private message
Dra_Gon



Joined: 25 May 2007
Posts: 313

PostPosted: Tue Dec 02, 2008 2:01 am    Post subject: Reply with quote

Only saw this when SKAN pointed it out, but it looks great!

Ciao,
Dra'Gon
_________________

For a good laugh {hopefully} >> megamatts.50megs.com

My WritersCafe profile>>
http://www.writerscafe.org/writers/BlueDragonFire/
Back to top
View user's profile Send private message Send e-mail
Display posts from previous:   
Post new topic   Reply to topic    AutoHotkey Community Forum Index -> Scripts & Functions All times are GMT
Goto page 1, 2  Next
Page 1 of 2

 
Jump to:  
You can post new topics in this forum
You can reply to topics in this forum


Powered by phpBB © 2001, 2005 phpBB Group