411 lines
14 KiB
Lua
411 lines
14 KiB
Lua
-- movewindow.lua
|
|
|
|
--[[
|
|
|
|
Miniwindow drag-to-move functions.
|
|
|
|
Author: Nick Gammon
|
|
Date: 15th July 2009
|
|
Modified: 16th November 2010 to add preprocessing
|
|
Modified: 29th November 2010 by Fiendish to improve dragging offscreen
|
|
Modified: 8th February 2018 by Nick to remember the flags setting (eg. absolute position)
|
|
|
|
This module is intended to make it easier to add drag handlers for miniwindows.
|
|
|
|
It implements the following:
|
|
|
|
-- find previous location
|
|
nocheck: if true, don't check if offscreen (boolean)
|
|
friends: other windows to move with this one (table)
|
|
preprocess: preprocessing for mousedown, mouseup etc. (table)
|
|
|
|
Handler names for preprocess table:
|
|
|
|
mousedown
|
|
mouseup
|
|
mouseover
|
|
cancelmouseover
|
|
cancelmousedown
|
|
dragmove
|
|
dragrelease
|
|
|
|
If any preprocess handler returns true (that is, neither nil nor false) then the default handler
|
|
in this module is not used. (The miniwindow name is the third argument)
|
|
|
|
function mousedown (flags, id, win)
|
|
if bit.band (flags, miniwin.hotspot_got_rh_mouse) then
|
|
-- do something different here
|
|
return true
|
|
end -- if
|
|
|
|
return false -- take normal movewindow behaviour
|
|
end -- mousedown
|
|
|
|
windowinfo = movewindow.install (win, default_position, default_flags, nocheck, friends, preprocess)
|
|
|
|
movewindow.add_drag_handler (win, left, top, right, bottom, cursor) -- add a drag handler for the nominated rectangle
|
|
|
|
movewindow.save_state (win) -- saves the miniwindow location to the appropriate variables
|
|
|
|
It also installs a position-checker that moves the miniwindow into view after 5 seconds, in case
|
|
you resize the main world window, and the window is no longer visible. The 5 seconds are to give
|
|
the main world window's position and size time to stabilize. (Unless nocheck is true)
|
|
|
|
|
|
Example of use:
|
|
|
|
require "movewindow" -- pull in this module
|
|
|
|
|
|
-- CREATE WINDOW in OnPluginInstall
|
|
|
|
win = GetPluginID () -- miniwindow ID
|
|
|
|
windowinfo = movewindow.install (win, miniwin.pos_center_right, 0) -- default position / flags
|
|
|
|
-- make miniwindow (use locations returned from last time we saved the state)
|
|
-- note that the width and height are not part of the window position info, and can thus change as required
|
|
|
|
WindowCreate (win,
|
|
windowinfo.window_left,
|
|
windowinfo.window_top,
|
|
WINDOW_WIDTH,
|
|
WINDOW_HEIGHT,
|
|
windowinfo.window_mode,
|
|
windowinfo.window_flags,
|
|
ColourNameToRGB "slategray")
|
|
|
|
|
|
|
|
-- INSTALL DRAG HANDLER when required (eg. when drawing stuff to window)
|
|
-- in this case we use 0,0,0,0 as the rectangle (ie. the whole window)
|
|
-- typically the height would be the size of the title bar
|
|
|
|
movewindow.add_drag_handler (win, 0, 0, 0, 0, miniwin.cursor_both_arrow)
|
|
|
|
-- SAVE STATE in OnPluginSaveState
|
|
|
|
movewindow.save_state (win)
|
|
|
|
|
|
The module makes one global variable (table) when installed. This is named:
|
|
|
|
mw_<window_id>_movewindow_info
|
|
|
|
|
|
This contains handler functions (the table is an upvalue to the functions)
|
|
|
|
"check_map_position"=function: 023D9368 -- the position checker
|
|
"dragmove"=function: 01AD1158 -- the dragmove handler
|
|
"dragrelease"=function: 023E4238 -- the dragrelease handler
|
|
"margin"=20 -- margin for dragging offscreen
|
|
"mousedown"=function: 01AD1108 -- the mousedown handler
|
|
"origx"=648 -- used during dragging
|
|
"origy"=39
|
|
"startx"=88
|
|
"starty"=8
|
|
"win"="23c3c91af0a26790c625f5d1" -- the supplied window ID
|
|
"window_flags"=2 -- flags (eg. 2, absolute position)
|
|
"window_left"=652 -- current left location
|
|
"window_mode"=0 -- window mode
|
|
"window_top"=31 -- current top location
|
|
|
|
|
|
This table is returned from movewindow.install so you can find where to put the
|
|
window the first time it is created.
|
|
|
|
--]]
|
|
|
|
movewindow = {} -- table to hold functions like movewindow.install
|
|
|
|
-- make a mouse-down handler with the movement information as an upvalue
|
|
|
|
local function make_mousedown_handler (mwi)
|
|
|
|
return function (flags, hotspot_id)
|
|
|
|
local win = mwi.win
|
|
|
|
-- see if other action wanted
|
|
if mwi.preprocess.mousedown then
|
|
if mwi.preprocess.mousedown (flags, hotspot_id, win) then
|
|
return
|
|
end -- if handled already
|
|
end -- if handler
|
|
|
|
-- find where mouse is so we can adjust window relative to mouse
|
|
mwi.startx = WindowInfo (win, 14)
|
|
mwi.starty = WindowInfo (win, 15)
|
|
|
|
-- find where window is in case we drag it offscreen
|
|
mwi.origx = WindowInfo (win, 10)
|
|
mwi.origy = WindowInfo (win, 11)
|
|
|
|
-- find where the friends are relative to the window
|
|
for i, v in ipairs (mwi.window_friends) do
|
|
if v then
|
|
mwi.window_friend_deltas [i] =
|
|
{
|
|
WindowInfo (v, 10) - mwi.origx,
|
|
WindowInfo (v, 11) - mwi.origy
|
|
}
|
|
end -- if
|
|
end -- for
|
|
|
|
end -- mousedown
|
|
|
|
end -- make_mousedown_handler
|
|
|
|
-- make a mouse drag-move handler with the movement information as an upvalue
|
|
|
|
local function make_dragmove_handler (mwi)
|
|
|
|
return function (flags, hotspot_id)
|
|
|
|
local win = mwi.win
|
|
|
|
-- see if other action wanted
|
|
if mwi.preprocess.dragmove then
|
|
if mwi.preprocess.dragmove (flags, hotspot_id, win) then
|
|
return
|
|
end -- if handled already
|
|
end -- if handler
|
|
|
|
-- find where it is now
|
|
local posx, posy = WindowInfo (win, 17) - mwi.startx,
|
|
WindowInfo (win, 18) - mwi.starty
|
|
|
|
-- change the mouse cursor shape appropriately
|
|
if posx < 0 or
|
|
posx > GetInfo (281) - mwi.margin or
|
|
posy < 0 or -- don't drag title out of view
|
|
posy > GetInfo (280) - mwi.margin then
|
|
SetCursor (miniwin.cursor_x) -- X cursor
|
|
else
|
|
SetCursor (miniwin.cursor_hand) -- hand cursor
|
|
end -- if
|
|
|
|
if posx < 0 then
|
|
posx = 0
|
|
elseif posx > GetInfo (281) - mwi.margin then
|
|
posx = GetInfo(281) - mwi.margin
|
|
end
|
|
if posy < 0 then
|
|
posy = 0
|
|
elseif posy > GetInfo(280) - mwi.margin then
|
|
posy = GetInfo(280) - mwi.margin
|
|
end
|
|
|
|
-- move the window to the new location - offset by how far mouse was into window
|
|
WindowPosition(win, posx, posy, 0, miniwin.create_absolute_location);
|
|
|
|
-- move the friends if they still exist
|
|
for i, v in ipairs(mwi.window_friends) do
|
|
if v then
|
|
WindowPosition (v, posx + mwi.window_friend_deltas [i] [1],
|
|
posy + mwi.window_friend_deltas [i] [2],
|
|
0,
|
|
WindowInfo (v, 8))
|
|
end -- if
|
|
end -- for
|
|
|
|
mwi.window_left = posx -- remember for saving state
|
|
mwi.window_top = posy
|
|
mwi.window_mode = 0
|
|
mwi.window_flags = miniwin.create_absolute_location -- absolute position
|
|
|
|
end -- dragmove
|
|
|
|
end -- make_dragmove_handler
|
|
|
|
-- make a mouse drag-release handler with the movement information as an upvalue
|
|
|
|
local function make_dragrelease_handler (mwi)
|
|
|
|
return function (flags, hotspot_id)
|
|
|
|
local win = mwi.win
|
|
|
|
-- see if other action wanted
|
|
if mwi.preprocess.dragrelease then
|
|
if mwi.preprocess.dragrelease (flags, hotspot_id, win) then
|
|
return
|
|
end -- if handled already
|
|
end -- if handler
|
|
|
|
Repaint () -- update window location
|
|
|
|
end -- dragrelease
|
|
|
|
end -- make_dragrelease_handler
|
|
|
|
-- make other handler with the movement information as an upvalue
|
|
|
|
local function make_other_handler (mwi, name)
|
|
|
|
return function (flags, hotspot_id)
|
|
|
|
-- send to supplied handler
|
|
if mwi.preprocess [name] then
|
|
mwi.preprocess [name] (flags, hotspot_id, mwi.win)
|
|
end -- if handler
|
|
|
|
end -- other
|
|
|
|
end -- make_other_handler
|
|
|
|
-- make a mouse position-checking function with the movement information as an upvalue
|
|
|
|
local function make_check_map_position_handler (mwi)
|
|
|
|
return function ()
|
|
|
|
local win = mwi.win
|
|
|
|
if not WindowInfo (win, 1) then
|
|
ColourNote ("white", "red", "Error in make_check_map_position_handler: no window named: " .. win)
|
|
return
|
|
end -- no such window
|
|
|
|
-- check miniwindow visible
|
|
if mwi.window_left < 0 or
|
|
mwi.window_left > GetInfo (281) - mwi.margin or
|
|
mwi.window_top < 0 or -- don't drag title out of view
|
|
mwi.window_top > GetInfo (280) - mwi.margin then
|
|
mwi.window_mode = miniwin.pos_center_right
|
|
mwi.window_flags = 0
|
|
end -- if not visible
|
|
|
|
WindowPosition (win,
|
|
mwi.window_left,
|
|
mwi.window_top,
|
|
mwi.window_mode,
|
|
mwi.window_flags)
|
|
|
|
end -- check_map_position
|
|
|
|
end -- make_check_map_position_handler
|
|
|
|
-- call movewindow.install in OnPluginInstall to find the position of the window, before creating it
|
|
-- - it also creates the handler functions ready for use later
|
|
|
|
function movewindow.install (win, default_position, default_flags, nocheck, friends, preprocess, start_position)
|
|
|
|
win = win or GetPluginID () -- default to current plugin ID
|
|
|
|
assert (not string.match (win, "[^A-Za-z0-9_]"), "Invalid window name in movewindow.install: " .. win)
|
|
|
|
default_position = default_position or miniwin.pos_center_right -- on right, center top/bottom
|
|
default_flags = default_flags or 0
|
|
|
|
-- set up handlers and where window should be shown (from saved state, if any)
|
|
local movewindow_info = {
|
|
win = win, -- save window ID
|
|
|
|
-- save current position in table (obtained from state file)
|
|
window_left = tonumber (GetVariable ("mw_" .. win .. "_windowx")) or (start_position and start_position.x) or 0,
|
|
window_top = tonumber (GetVariable ("mw_" .. win .. "_windowy")) or (start_position and start_position.y) or 0,
|
|
window_mode = default_position,
|
|
window_flags = tonumber (GetVariable ("mw_" .. win .. "_windowflags")) or default_flags,
|
|
window_friends = friends or {},
|
|
window_friend_deltas = {},
|
|
margin = 20, -- how close we can put to the edge of the window
|
|
preprocess = preprocess or {},
|
|
}
|
|
|
|
-- check valid
|
|
for k, v in pairs (movewindow_info.preprocess) do
|
|
assert (type (v) == "function", "Handler '" .. k .. "' is not a function")
|
|
end -- for
|
|
|
|
-- handler to reposition window
|
|
movewindow_info.check_map_position = make_check_map_position_handler (movewindow_info) -- for startup
|
|
|
|
-- mouse handlers
|
|
movewindow_info.mousedown = make_mousedown_handler (movewindow_info)
|
|
movewindow_info.mouseup = make_other_handler (movewindow_info, "mouseup")
|
|
movewindow_info.mouseover = make_other_handler (movewindow_info, "mouseover")
|
|
movewindow_info.cancelmouseover = make_other_handler (movewindow_info, "cancelmouseover")
|
|
movewindow_info.cancelmousedown = make_other_handler (movewindow_info, "cancelmousedown")
|
|
movewindow_info.dragmove = make_dragmove_handler (movewindow_info)
|
|
movewindow_info.dragrelease = make_dragrelease_handler (movewindow_info)
|
|
|
|
-- save table in global namespace
|
|
_G ["mw_" .. win .. "_movewindow_info"] = movewindow_info
|
|
|
|
|
|
-- give main world window time to stabilize its size and position
|
|
-- eg. this might be: mw_23c3c91af0a26790c625f5d1_movewindow_info.check_map_position ()
|
|
|
|
if not nocheck then -- if wanted
|
|
DoAfterSpecial (5, "mw_" .. win .. "_movewindow_info.check_map_position ()" , sendto.script)
|
|
end -- if
|
|
|
|
return movewindow_info -- the caller might appreciate access to this table
|
|
end -- movewindow.install
|
|
|
|
-- call movewindow.add_drag_handler after creating the window, and after deleting hotspots where applicable
|
|
-- to add a drag hotspot
|
|
|
|
function movewindow.add_drag_handler (win, left, top, right, bottom, cursor)
|
|
|
|
win = win or GetPluginID () -- default to current plugin ID
|
|
|
|
-- the zz puts it under other hotspots on the drag area
|
|
local hotspot_id = "zz_mw_" .. win .. "_movewindow_hotspot"
|
|
|
|
if not WindowInfo (win, 1) then
|
|
ColourNote ("white", "red", "Error in movewindow.add_drag_handler: no window named: " .. win)
|
|
return
|
|
end -- no such window
|
|
|
|
-- make a hotspot
|
|
WindowAddHotspot(win, hotspot_id,
|
|
left or 0, top or 0, right or 0, bottom or 0, -- rectangle
|
|
"mw_" .. win .. "_movewindow_info.mouseover", -- MouseOver
|
|
"mw_" .. win .. "_movewindow_info.cancelmouseover", -- CancelMouseOver
|
|
"mw_" .. win .. "_movewindow_info.mousedown", -- MouseDown
|
|
"mw_" .. win .. "_movewindow_info.cancelmousedown", -- CancelMouseDown
|
|
"mw_" .. win .. "_movewindow_info.mouseup", -- MouseUp
|
|
"Drag to move window", -- tooltip text
|
|
cursor or miniwin.cursor_hand, -- cursor
|
|
0) -- flags
|
|
|
|
WindowDragHandler (win, hotspot_id,
|
|
"mw_" .. win .. "_movewindow_info.dragmove",
|
|
"mw_" .. win .. "_movewindow_info.dragrelease",
|
|
0) -- flags
|
|
|
|
end -- movewindow.add_drag_handler
|
|
|
|
-- call movewindow.save_state in OnPluginSaveState
|
|
|
|
function movewindow.save_state (win)
|
|
|
|
win = win or GetPluginID () -- default to current plugin ID
|
|
|
|
-- get movewindow variable from global namespace
|
|
local mwi = _G ["mw_" .. win .. "_movewindow_info"]
|
|
|
|
if not mwi then
|
|
ColourNote ("white", "red", "Error in movewindow.save_state: no window movement info for: " .. win)
|
|
return
|
|
end -- no such window
|
|
|
|
-- remember where the window was
|
|
|
|
-- use actual last specified position, not where we happen to think it is, in case another plugin moves it
|
|
-- suggested by Fiendish, 27 August 2012.
|
|
if WindowInfo (win, 1) then
|
|
mwi.window_left = WindowInfo(win, 1)
|
|
end
|
|
if WindowInfo (win, 2) then
|
|
mwi.window_top = WindowInfo(win, 2)
|
|
end
|
|
|
|
SetVariable ("mw_" .. win .. "_windowx", mwi.window_left)
|
|
SetVariable ("mw_" .. win .. "_windowy", mwi.window_top)
|
|
SetVariable ("mw_" .. win .. "_windowflags", mwi.window_flags)
|
|
|
|
end -- movewindow.save_state
|