-- 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__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