Version 4.40 of MUSHclient provides support for something miniwindow scripters have been asking about: the ability to drag things around.
This is made possible by a new script function:
What this does is attach two drag handling functions to any existing hotspot. You need to have created the miniwindow first, and made a hotspot for it.
Then, what happens is, if:
... then the callback function is called every time the mouse moves (regardless of whether it is still over the current miniwindow).
Also, when the mouse is eventually released, the ReleaseCallback function is called. Unlike the existing mousedown and mouseup functions, these two functions are called no matter where the mouse is - it doesn't even have to be over the MUSHclient window.
This lets you do things like move a window around (eg. drag its title bar), or drag something from one window to another.
To make a visual effect (like, dragging an icon), the mouse-down handler could make a new miniwindow, with the drag icon in it, and draw it under the mouse. Then as the mouse moves the window position could be updated. Then when the mouse is released, the icon window is deleted, and steps taken, depending on where the mouse release is, to make something happen.
To make this more practical, there are new WindowInfo selectors (17 and 18) which return the current position of the mouse in "client" coordinates. That is, relative to the output window, not relative to the miniwindow.
You can always deduce which miniwindow the mouse is over by comparing those coordinates to WindowInfo selectors 10 to 13, which give the current location of each miniwindow. That is, the mouse is over a window if its x position is >= the window left position (selector 10), and <= the window right position (selector 12), and ditto for the y position.
My drag-and-drop test plugin is as follows:
This draws a box in the middle of the screen with a smaller "drag box" inside it. The drag box is a hotspot (hs1) which you can use to drag the window around. Various debugging messages appear in the output window. It is possible in this test to drag the window off the screen, in which case you can't drag it back as you can't see it. A more complete implementation would test that the window was in a reasonable position on the release handler, and if not, return it to its starting position.
An interesting complication is this line in the mousedown function:
This is needed to find the offset of the mouse from the edge of the miniwindow, and when dragging we offset the new location by this amount. Without this, the miniwindow "jumps" so that its edge is aligned to the mouse, which looks a bit silly.
To cancel a drag handler, just set the function names to empty strings, eg.
There is also a new function SetCursor, which lets you vary the mouse pointer shape as it is moved around. In the example above, the cursor turns into a "cannot do" shape (circle with line through it) if it is moved outside the main client window, otherwise it is a hand shape.
This is made possible by a new script function:
long WindowDragHandler(BSTR Name, BSTR HotspotId, BSTR MoveCallback, BSTR ReleaseCallback, long Flags);
What this does is attach two drag handling functions to any existing hotspot. You need to have created the miniwindow first, and made a hotspot for it.
Then, what happens is, if:
- The user clicks in the hotspot; and
- Moves the mouse; and
- There is a MoveCallback function defined
... then the callback function is called every time the mouse moves (regardless of whether it is still over the current miniwindow).
Also, when the mouse is eventually released, the ReleaseCallback function is called. Unlike the existing mousedown and mouseup functions, these two functions are called no matter where the mouse is - it doesn't even have to be over the MUSHclient window.
This lets you do things like move a window around (eg. drag its title bar), or drag something from one window to another.
To make a visual effect (like, dragging an icon), the mouse-down handler could make a new miniwindow, with the drag icon in it, and draw it under the mouse. Then as the mouse moves the window position could be updated. Then when the mouse is released, the icon window is deleted, and steps taken, depending on where the mouse release is, to make something happen.
To make this more practical, there are new WindowInfo selectors (17 and 18) which return the current position of the mouse in "client" coordinates. That is, relative to the output window, not relative to the miniwindow.
You can always deduce which miniwindow the mouse is over by comparing those coordinates to WindowInfo selectors 10 to 13, which give the current location of each miniwindow. That is, the mouse is over a window if its x position is >= the window left position (selector 10), and <= the window right position (selector 12), and ditto for the y position.
My drag-and-drop test plugin is as follows:
<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE muclient>
<!-- Saved on Friday, February 20, 2009, 8:17 AM -->
<!-- MuClient version 4.40 -->
<!-- Plugin "Drag_Drop_Test" generated by Plugin Wizard -->
<muclient>
<plugin
name="Drag_Drop_Test"
author="Nick Gammon"
id="90a050c0a37e500b45aa34c7"
language="Lua"
purpose="Testing drag and drop"
date_written="2009-02-20 08:16:21"
requires="4.40"
version="1.0"
>
</plugin>
<!-- Aliases -->
<aliases>
<alias
match="hs"
enabled="y"
send_to="12"
sequence="100"
>
<send>
function mousedown(flags, hotspot_id)
startx, starty = WindowInfo (win, 14), WindowInfo (win, 15)
print ("we moused down on hotspot " .. hotspot_id)
end -- mousedown
function cancelmousedown(flags, hotspot_id)
print ("we cancelled moused down for hotspot " .. hotspot_id)
end -- cancelmousedown
function mouseup(flags, hotspot_id)
print ("we moused up on hotspot " .. hotspot_id)
end -- mouseup
function dragmove(flags, hotspot_id)
local posx, posy = WindowInfo (win, 17),
WindowInfo (win, 18)
print ("moved to position", posx, posy)
-- move the window to the new location
WindowPosition(win, posx - startx, posy - starty, 0, 2);
-- change the mouse cursor shape appropriately
if posx < 0 or posx > GetInfo (281) or
posy < 0 or posy > GetInfo (280) then
check (SetCursor ( 11)) -- X cursor
else
check (SetCursor ( 1)) -- hand cursor
end -- if
end -- dragmove
function dragrelease(flags, hotspot_id)
print ("mouse drag release for " .. hotspot_id)
print ("released at position", WindowInfo (win, 17), WindowInfo (win, 18))
end -- dragrelease
-- script starts here
win = GetPluginID () -- get a unique name
WindowCreate (win, 0, 0, 200, 200, 12, 0, ColourNameToRGB("white")) -- create window
-- Grid
for i = 1, math.max (WindowInfo (win, 3), WindowInfo (win, 4)) / 20 do
WindowLine (win, i * 20, 0, i * 20, WindowInfo (win, 4), 0xC0C0C0, 0, 1)
WindowLine (win, 0, i * 20, WindowInfo (win, 3), i * 20, 0xC0C0C0, 0, 1)
end -- for
-- draw draggable rectangle
WindowRectOp (win, 1, 10, 10, 60, 20, 0, 0)
-- make a hotspot
WindowAddHotspot(win, "hs1",
10, 10, 60, 20, -- rectangle
"", -- MouseOver
"", -- CancelMouseOver
"mousedown",
"cancelmousedown",
"mouseup",
"Drag me!", -- tooltip text
1, 0) -- hand cursor
WindowDragHandler(win, "hs1", "dragmove", "dragrelease", 0)
WindowShow (win, true) -- show it
</send>
</alias>
</aliases>
</muclient>
This draws a box in the middle of the screen with a smaller "drag box" inside it. The drag box is a hotspot (hs1) which you can use to drag the window around. Various debugging messages appear in the output window. It is possible in this test to drag the window off the screen, in which case you can't drag it back as you can't see it. A more complete implementation would test that the window was in a reasonable position on the release handler, and if not, return it to its starting position.
An interesting complication is this line in the mousedown function:
startx, starty = WindowInfo (win, 14), WindowInfo (win, 15)
This is needed to find the offset of the mouse from the edge of the miniwindow, and when dragging we offset the new location by this amount. Without this, the miniwindow "jumps" so that its edge is aligned to the mouse, which looks a bit silly.
To cancel a drag handler, just set the function names to empty strings, eg.
WindowDragHandler(win, "hs1", "", "", 0)
There is also a new function SetCursor, which lets you vary the mouse pointer shape as it is moved around. In the example above, the cursor turns into a "cannot do" shape (circle with line through it) if it is moved outside the main client window, otherwise it is a hand shape.