 |
AutoHotkey Community Let's help each other out
|
| View previous topic :: View next topic |
| Author |
Message |
Serenity
Joined: 07 Nov 2004 Posts: 1275
|
Posted: Sun Mar 19, 2006 1:55 pm Post subject: Splitter bar window control with AHk |
|
|
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 |
|
 |
Chris Site Admin
Joined: 02 Mar 2004 Posts: 10480
|
Posted: Mon Mar 20, 2006 12:32 pm Post subject: |
|
|
| Offhand, I'm not sure. Maybe someone else knows a way, perhaps via DllCall. |
|
| Back to top |
|
 |
PhiLho
Joined: 27 Dec 2005 Posts: 6721 Location: France (near Paris)
|
Posted: Mon Mar 20, 2006 2:11 pm Post subject: |
|
|
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 |
|
 |
toralf
Joined: 31 Jan 2005 Posts: 3841 Location: Bremen, Germany
|
Posted: Mon Mar 20, 2006 3:55 pm Post subject: |
|
|
You are a freak. :) Thanks for the demo. _________________ Ciao
toralf  |
|
| Back to top |
|
 |
Thalon
Joined: 12 Jul 2005 Posts: 640
|
|
| Back to top |
|
 |
Serenity
Joined: 07 Nov 2004 Posts: 1275
|
Posted: Wed Mar 22, 2006 1:46 am Post subject: |
|
|
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 |
|
 |
Veovis
Joined: 13 Feb 2006 Posts: 390 Location: Utah
|
Posted: Wed Mar 22, 2006 2:08 am Post subject: |
|
|
Eureka!!!
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)"  _________________
"Power can be given overnight, but responsibility must be taught. Long years go into its making." |
|
| Back to top |
|
 |
PhiLho
Joined: 27 Dec 2005 Posts: 6721 Location: France (near Paris)
|
Posted: Wed Mar 22, 2006 11:07 am Post subject: |
|
|
| Serenity wrote: | the cursor now changes too.  |
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 |
|
 |
moorpipe
Joined: 04 Oct 2006 Posts: 19
|
Posted: Mon Oct 16, 2006 12:06 am Post subject: |
|
|
| 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 |
|
 |
soggos
Joined: 27 Mar 2008 Posts: 37 Location: France
|
Posted: Fri Sep 12, 2008 11:36 am Post subject: for change cursor ? |
|
|
hello i like it. splitter is so good
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 |
|
 |
soggos
Joined: 27 Mar 2008 Posts: 37 Location: France
|
Posted: Sun Sep 14, 2008 12:20 pm Post subject: Serenity, i am very sorry! |
|
|
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)
Serenity still thank you for splitter it's very super brilliant! |
|
| Back to top |
|
 |
|
|
You can post new topics in this forum You can reply to topics in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|