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 

Splitter bar window control with AHk

 
Post new topic   Reply to topic    AutoHotkey Community Forum Index -> Ask for Help
View previous topic :: View next topic  
Author Message
Serenity



Joined: 07 Nov 2004
Posts: 1275

PostPosted: Sun Mar 19, 2006 1:55 pm    Post subject: Splitter bar window control with AHk Reply with quote

I have a gui in which I would like to add a splitter window control so that I can resize two controls at once, like in the Windows File Manager. I was wondering if this was possible with AutoHotkey.
_________________
"Anything worth doing is worth doing slowly." - Mae West
Back to top
View user's profile Send private message Visit poster's website
Chris
Site Admin


Joined: 02 Mar 2004
Posts: 10480

PostPosted: Mon Mar 20, 2006 12:32 pm    Post subject: Reply with quote

Offhand, I'm not sure. Maybe someone else knows a way, perhaps via DllCall.
Back to top
View user's profile Send private message Send e-mail
PhiLho



Joined: 27 Dec 2005
Posts: 6721
Location: France (near Paris)

PostPosted: Mon Mar 20, 2006 2:11 pm    Post subject: Reply with quote

Should I have to do it, I would, for example, put a Picture control between two Edit (or List, etc.) controls. The picture would be a grip, and the user should be able to drag it. You can use Veovis/PhiLho drag control routine, constraining the move of course. And then, resize the two companion controls depending on the grip position.

[EDIT] OK, I did it... This is a quick bad looking hack, though.
Code:
/*
Splitter.ahk

Shows how to do a splitter control in a GUI window.

// by Philippe Lhoste <PhiLho(a)GMX.net> http://Phi.Lho.free.fr
// File/Project history:
 1.01.000 -- 2006/02/22 (PL) -- Added Serenity/shimanov's code to change cursor,
          renamed some names to be clearer.
 1.00.000 -- 2006/02/20 (PL) -- Creation.

http://www.autohotkey.com/forum/viewtopic.php?p=53600
I use the DragControl code based on Veovis' ideas,
and Serenity's code to change the cursor over the splitter,
based on shimanov's code (who else?...).
http://www.autohotkey.com/forum/viewtopic.php?t=6223
*/

#SingleInstance Force

; Globals... Not nice, but convenient for this quick hack.
; I would need some encapsulation (or structures!) to avoid passing lot of parameters...
#buddyCtrl1Pos = 10
#buddyCtrl1Size = 100
#buddyCtrl2Pos = 115
#buddyCtrl2Size = 100
#minSplitterPos := #buddyCtrl1Pos + 10
#maxSplitterPos := 185

Gui Add, Edit, w%#buddyCtrl1Size% h200 x%#buddyCtrl1Pos% y10 vLeft
Gui Add, Edit, w%#buddyCtrl2Size% h200 x%#buddyCtrl2Pos% y10 vRight
Gui Add, Picture, x110 y10 w5 h200 vsplitter gMoveSplitter, bluepixel.bmp
Gui Show, w300 h220

; Change the cursor when mouse is over splitter control
WM_SETCURSOR = 0x20
WM_MOUSEMOVE = 0x200
OnMessage(WM_SETCURSOR, "HandleMessage")
OnMessage(WM_MOUSEMOVE, "HandleMessage")

Return

MoveSplitter:
   DragSplitter(A_GuiControl, "Left", "Right")
Return

;----- Classical stock functions for DllCall structure handling

; @struct: hold the UInt to write
; _value: value to write
; _offset: offset of the UInt from the start of the struct, in UInt size units
SetUInt(ByRef @struct, _value, _offset=0)
{
   local addr
   addr := &@struct + _offset * 4
   DllCall("RtlFillMemory", "UInt", addr,     "UInt", 1, "UChar", (_value & 0x000000FF))
   DllCall("RtlFillMemory", "UInt", addr + 1, "UInt", 1, "UChar", (_value & 0x0000FF00) >> 8)
   DllCall("RtlFillMemory", "UInt", addr + 2, "UInt", 1, "UChar", (_value & 0x00FF0000) >> 16)
   DllCall("RtlFillMemory", "UInt", addr + 3, "UInt", 1, "UChar", (_value & 0xFF000000) >> 24)
}

; @struct: hold the UInt to extract
; _offset: offset of the UInt from the start of the struct, in UInt size units
GetUInt(ByRef @struct, _offset=0)
{
   local addr
   addr := &@struct + _offset * 4
   Return *addr + (*(addr + 1) << 8) +  (*(addr + 2) << 16) + (*(addr + 3) << 24)
}

;----- The real stuff...

DragSplitter(_controlName, _buddyCtrlName1, _buddyCtrlName2)
{
   local hWnd, point
   local initScrX, initScrY
   local initWinX, initWinY
   local initCliX, initCliY
   local offsetX, offsetY
   local mouseState, mouseX, mouseY
   ; I must declare all four variables to keep them local, not polluting global namespace...
   local controlPos, controlPosX, controlPosY, controlPosW, controlPosH
   local w1, a2, w2

   ; Init.: compute the offsets
   ; Screen coordinates of the mouse
   CoordMode Mouse, Screen
   MouseGetPos initScrX, initScrY, hWnd
   ; Window relative coordinates of the mouse
   CoordMode Mouse, Relative   ; Restore default
   MouseGetPos initWinX, initWinY
   ; Compute client area relative coordinates of the mouse
   VarSetCapacity(point, 8)
   SetUInt(point, initScrX)
   SetUInt(point, initScrY, 1)
   DllCall("ScreenToClient", "UInt", hWnd, "UInt", &point)
   initCliX := GetUInt(point)
   initCliY := GetUInt(point, 1)
   ; Coordinates of the control, relative to the client area
   GuiControlGet controlPos, Pos, %_controlName%
   mouseX := controlPosX
   mouseY := controlPosY
   ; Compute offset between click inside control and top-left corner of control
   offsetX :=  initCliX - controlPosX
   offsetY :=  initCliY - controlPosY
   ; Add offset between window and client area
   offsetX += initWinX - initCliX
   offsetY += initWinY - initCliY

   Loop
   {
      GetKeyState mouseState, LButton
      If (mouseState = "u")
         Break   ; Mouse button is released
      ; Window relative coordinates of the mouse
      MouseGetPos mouseX, mouseY
      ; Corrected to client relative coordinates
      mouseX -= offsetX
      ; Constrains move
      if (mouseX < #minSplitterPos)
      {
         mouseX := #minSplitterPos
      }
      if (mouseX > #maxSplitterPos)
      {
         mouseX := #maxSplitterPos
      }
;~       mouseY -= offsetY
      mouseY := controlPosY
      GuiControl MoveDraw, %_controlName%, x%mouseX% y%mouseY%
      w1 := mouseX - #buddyCtrl1Pos
      a2 := mouseX + controlPosW ; controlPosH for horizontal splitter
      w2 := #buddyCtrl2Size + #buddyCtrl1Size - w1
      GuiControl Move, %_buddyCtrlName1%, % "x" . #buddyCtrl1Pos . " w" . w1
      GuiControl Move, %_buddyCtrlName2%, % "x" . a2 . " w" . w2
      Tooltip x%mouseX% y%mouseY% %w1% %a2% %w2%
      Sleep 100
   }
   GuiControl Move, %_controlName%, x%mouseX% y%mouseY%
}

HandleMessage(_wParam, _lParam, _message, _hWnd)
{
   local hNewCursor
   static $bHover, $hOldCursor

   If (_message = WM_SETCURSOR)
   {
      If $bHover
         Return true   ; Eat the message...
   }
   Else If (_message = WM_MOUSEMOVE)
   {
      If (A_GuiControl = "splitter")
      {
         ; Cursor hovers splitter control
         If not $bHover
         {
            ; Entering the splitter, we change the cursor
            ; IDC_SIZEWE - Use IDC_SIZENS (32645) for horizontal splitter
            hNewCursor := DllCall("LoadCursor", "UInt", 0, "UInt", 32644)
            $hOldCursor := DllCall("SetCursor", "UInt", hNewCursor)
            $bHover := true
         }
      }
      Else If $bHover
      {
         ; Restore old cursor
         DllCall("SetCursor", "UInt", $hOldCursor)
         $bHover := false
      }
   }
}

GuiClose:
GuiEscape:
Tooltip
ExitApp

Horizonal splitter should be trivial to do.
[EDIT] Correct declaration of the controlPos* stuff... Some little improvements.
[EDIT] Added Serenity/shimanov's code to change cursor, renamed some names to be clearer. Corrected some typos (in variable names!).
_________________
vPhiLho := RegExReplace("Philippe Lhoste", "^(\w{3})\w*\s+\b(\w{3})\w*$", "$1$2")


Last edited by PhiLho on Wed Mar 29, 2006 1:51 pm; edited 3 times in total
Back to top
View user's profile Send private message Visit poster's website
toralf



Joined: 31 Jan 2005
Posts: 3841
Location: Bremen, Germany

PostPosted: Mon Mar 20, 2006 3:55 pm    Post subject: Reply with quote

You are a freak. :) Thanks for the demo.
_________________
Ciao
toralf
Back to top
View user's profile Send private message Send e-mail Visit poster's website
Thalon



Joined: 12 Jul 2005
Posts: 640

PostPosted: Mon Mar 20, 2006 4:40 pm    Post subject: Reply with quote

It's amazing Very Happy

I am shure I will have to spend more time to understand it than you to write it Wink

Thalon
_________________
AHK-Icon-Changer
AHK-IRC
deutsches Forum
SacredVault
Back to top
View user's profile Send private message
Serenity



Joined: 07 Nov 2004
Posts: 1275

PostPosted: Wed Mar 22, 2006 1:46 am    Post subject: Reply with quote

PhiLho, thank you!

I modified the code you posted to work in a resizable gui, and the cursor now changes too. :)

Code:
#singleinstance force
#notrayicon
setbatchlines, -1

SplitterW = 5

Gui, +resize
Gui Add, Edit, x0 y0 w150 h200 vLeft,
Gui, add, text, x+0 y0 w%SplitterW% h200 vsplitter gMoveSplitter
Gui Add, Edit, x+0 y0 h200 vRight,

GuiControlGet, edit1, Pos, Edit1
GuiControlGet, edit2, Pos, Edit2
Gui Show, w300 h100

; change the cursor when mouse is over splitter control
WM_SETCURSOR = 0x20
WM_MOUSEMOVE = 0x200
OnMessage(WM_SETCURSOR, "HandleMessage")
OnMessage(WM_MOUSEMOVE, "HandleMessage")

return


MoveSplitter:
   DragSplitter(A_GuiControl, "Left", "Right")
Return

;----- Classical stock functions for DllCall structure handling

; @struct: hold the UInt to write
; _value: value to write
; _offset: offset of the UInt from the start of the struct, in UInt size units
SetUInt(ByRef @struct, _value, _offset=0)
{
   local addr
   addr := &@struct + _offset * 4
   DllCall("RtlFillMemory", "UInt", addr,     "UInt", 1, "UChar", (_value & 0x000000FF))
   DllCall("RtlFillMemory", "UInt", addr + 1, "UInt", 1, "UChar", (_value & 0x0000FF00) >> 8)
   DllCall("RtlFillMemory", "UInt", addr + 2, "UInt", 1, "UChar", (_value & 0x00FF0000) >> 16)
   DllCall("RtlFillMemory", "UInt", addr + 3, "UInt", 1, "UChar", (_value & 0xFF000000) >> 24)
}

; @struct: hold the UInt to extract

; _offset: offset of the UInt from the start of the struct, in UInt size units
GetUInt(ByRef @struct, _offset=0)
{
   local addr
   addr := &@struct + _offset * 4
   Return *addr + *(addr + 1) << 8 +  *(addr + 2) << 16 + *(addr + 3) << 24
}

;----- The real stuff...

DragSplitter(_controlName, _companionName1, _companionName2)
{
   local hWnd, point
   local initScrX, initScrY
   local initWinX, initWinY
   local initCliX, initCliY
   local offsetX, offsetY
   local mouseState, mouseX, mouseY
   ; I must declare all four variables to keep them local, not polluting global namespace...
   local controlPos, controlPosX, controlPosY, controlPosW, controlPosH
   local w1, a2, w2

   ; Init.: compute the offsets
   ; Screen coordinates of the mouse
   CoordMode Mouse, Screen
   MouseGetPos initScrX, initScrY, hWnd
   ; Window relative coordinates of the mouse
   CoordMode Mouse, Relative   ; Restore default
   MouseGetPos initWinX, initWinY
   ; Compute client area relative coordinates of the mouse
   VarSetCapacity(point, 8)
   SetUInt(point, initScrX)
   SetUInt(point, initScrY, 1)
   DllCall("ScreenToClient", "UInt", hWnd, "UInt", &point)
   initCliX := GetUInt(point)
   initCliY := GetUInt(point, 1)
   ; Coordinates of the control, relative to the client area
   GuiControlGet controlPos, Pos, %_controlName%
   mouseX := controlPosX
   mouseY := controlPosY
   ; Compute offset between click inside control and top-left corner of control
   offsetX :=  initCliX - controlPosX
   offsetY :=  initCliY - controlPosY
   ; Add offset between window and client area
   offsetX += initWinX - initCliX
   offsetY += initWinY - initCliY

   Loop
   {
      GetKeyState mouseState, LButton
      If (mouseState = "u")
         Break   ; Mouse button is released
      ; Window relative coordinates of the mouse
      MouseGetPos mouseX, mouseY
      ; Corrected to client relative coordinates
      mouseX -= offsetX
         
         
      ; Constrains move
      if (mouseX < #minSplitterPos)
      {
         mouseX := #minSplitterPos
      }
      if (mouseX > #maxSplitterPos)
      {
         mouseX := #maxSplitterPos
      }
         
      mouseY := controlPosY
      GuiControl MoveDraw, %_controlName%, x%mouseX% y%mouseY%

      w1 := mouseX ; new width of edit1
      a2 := mouseX + controlPosW ; a2 is x coords of edit2
      w2 := ((A_GuiWidth-w1)-SplitterW) ; width of edit2 now changes according to size of gui
         
      GuiControl Move, %_companionName1%, % "x" . 0 . " w" . w1
      GuiControl Move, %_companionName2%, % "x" . a2 . " w" . w2
      Sleep 100
   }
   GuiControl Move, %_controlName%, x%mouseX% y%mouseY%
}



HandleMessage(p_w, p_l, p_m, p_hw)
{
   global   WM_SETCURSOR, WM_MOUSEMOVE
   static   hover, IDC_SIZEWE, h_old_cursor
 
   if (p_m = WM_SETCURSOR)
   {
      if hover
         return, true
   }
   else if (p_m = WM_MOUSEMOVE)
   {
         ; cursor hovers splitter control
         if (A_GuiControl = "splitter")
         {
            if hover =
            {
               IDC_SIZEWE := DllCall("LoadCursor", "uint", 0, "uint", 32644) ; IDC_SIZEWE = 32644
               hover = true
            }                 
            h_old_cursor := DllCall("SetCursor", "uint", IDC_SIZEWE)
         }            
         else if hover
         {
            DllCall("SetCursor", "uint", h_old_cursor)
            hover=
         }
   }
}



GuiSize:
#minSplitterPos = 0
#maxSplitterPos := (A_GuiWidth - SplitterW)

GuiControlGet, edit1, Pos, Edit1 ; get the width

GuiControl, Move, Edit1, % "h" (A_GuiHeight)
GuiControl, Move, Edit2, % "h" (A_GuiHeight) "w" (A_GuiWidth-(edit1W+SplitterW))
GuiControl, Move, Static1, % "h" (A_GuiHeight)
return


GuiClose:
GuiEscape:
ExitApp

_________________
"Anything worth doing is worth doing slowly." - Mae West
Back to top
View user's profile Send private message Visit poster's website
Veovis



Joined: 13 Feb 2006
Posts: 390
Location: Utah

PostPosted: Wed Mar 22, 2006 2:08 am    Post subject: Reply with quote

Shocked Eureka!!! Shocked
PhiLho you amaze me!
Just another example that AHK can do *EVERYTHING!!!!!

*Rhetorical statement.


Also when you said " Veovis/PhiLho drag control" I think you stated incorrectly, you should have said "PhiLho drag control (that was for Veovis)" Very Happy
_________________

"Power can be given overnight, but responsibility must be taught. Long years go into its making."
Back to top
View user's profile Send private message Send e-mail Visit poster's website
PhiLho



Joined: 27 Dec 2005
Posts: 6721
Location: France (near Paris)

PostPosted: Wed Mar 22, 2006 11:07 am    Post subject: Reply with quote

Serenity wrote:
the cursor now changes too. Smile

Thanks, I wanted to do that, but didn't had time (plus I wasn't too sure how to do it)..
I integrated that in the first post.

Veovis wrote:
Also when you said " Veovis/PhiLho drag control" I think you stated incorrectly, you should have said "PhiLho drag control (that was for Veovis)"

Naah, if you didn't posted the original code, I wouldn't have made it myself, so I wouldn't be able to reuse it here.
The base tricks are your, I just bugfixed it.
_________________
vPhiLho := RegExReplace("Philippe Lhoste", "^(\w{3})\w*\s+\b(\w{3})\w*$", "$1$2")
Back to top
View user's profile Send private message Visit poster's website
moorpipe



Joined: 04 Oct 2006
Posts: 19

PostPosted: Mon Oct 16, 2006 12:06 am    Post subject: Reply with quote

Serenity wrote:
I modified the code you posted to work in a resizable gui, and the cursor now changes too.
Code:
DragSplitter(_controlName, _companionName1, _companionName2)
{
   (...)
   w2 := ((A_GuiWidth-w1)-SplitterW) ; width of edit2 now changes according to size of gui
   (...)
         
}

GuiSize:
#minSplitterPos = 0
#maxSplitterPos := (A_GuiWidth - SplitterW)

GuiControlGet, edit1, Pos, Edit1 ; get the width

GuiControl, Move, Edit1, % "h" (A_GuiHeight)
GuiControl, Move, Edit2, % "h" (A_GuiHeight) "w" (A_GuiWidth-(edit1W+SplitterW))
GuiControl, Move, Static1, % "h" (A_GuiHeight)
return


The right panel turns grey if you move the splitter. You need to define a global variable and assign A_GuiWidth from within the GuiSize routine. I just found out that the scope of A_GuiWidth (and A_GuiHeight) is limited to the GuiSize routine.

Regards.
Back to top
View user's profile Send private message
soggos



Joined: 27 Mar 2008
Posts: 37
Location: France

PostPosted: Fri Sep 12, 2008 11:36 am    Post subject: for change cursor ? Reply with quote

hello i like it. splitter is so good Smile

but How i can change the cursor, cause i have a splitter vertical?

moorpipe you say:
Quote:
I just found out that the scope of A_GuiWidth (and A_GuiHeight) is limited to the GuiSize routine.
me too
and thanks for guisize
Back to top
View user's profile Send private message
soggos



Joined: 27 Mar 2008
Posts: 37
Location: France

PostPosted: Sun Sep 14, 2008 12:20 pm    Post subject: Serenity, i am very sorry! Reply with quote

I Serenity.
I am poor a rabitt. very very sorry.
The solution was already under my eyes, cause in your comment:
Code:
; IDC_SIZEWE - Use IDC_SIZENS (32645) for horizontal splitter

arf escusez me again .
but like now; i don't see how to put it into practice?
( i am a little beginner. ans speak english is not easy for me)

Smile Serenity still thank you for splitter it's very super brilliant!
Back to top
View user's profile Send private message
Display posts from previous:   
Post new topic   Reply to topic    AutoHotkey Community Forum Index -> Ask for Help All times are GMT
Page 1 of 1

 
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