diff --git a/Source/PaperWM.spoon/docs.json b/Source/PaperWM.spoon/docs.json index bf217ce8..80339905 100644 --- a/Source/PaperWM.spoon/docs.json +++ b/Source/PaperWM.spoon/docs.json @@ -13,7 +13,7 @@ "doc": "Adds a window to layout and tiles.\n\nParameters:\n * add_window - An hs.window\n\nReturns:\n * The hs.spaces space for added window or nil if window not added.", "examples": [], "file": "Source/PaperWM.spoon//init.lua", - "lineno": "594", + "lineno": "652", "name": "addWindow", "notes": [], "parameters": [ @@ -32,7 +32,7 @@ "doc": "Removes current window from column and places it to the right\n\nParameters:\n * None", "examples": [], "file": "Source/PaperWM.spoon//init.lua", - "lineno": "1026", + "lineno": "1135", "name": "barfWindow", "notes": [], "parameters": [ @@ -46,14 +46,14 @@ { "def": "PaperWM.bindHotkeys(mapping)", "desc": "Binds hotkeys for PaperWM", - "doc": "Binds hotkeys for PaperWM\n\nParameters:\n * mapping - A table containing hotkey modifer/key details for the following items:\n * stop_events - Stop automatic tiling\n * focus_left - Focus window to left of current window\n * focus_right - Focus window to right of current window\n * focus_up - Focus window to up of current window\n * focus_down - Focus window to down of current window\n * swap_left - Swap positions of window to the left and current window\n * swap_right - Swap positions of window to the right and current window\n * swap_up - Swap positions of window above and current window\n * swap_down - Swap positions of window below and current window\n * center_window - Move current window to center of screen\n * full_width - Resize width of current window to width of screen\n * cycle_width - Toggle through preset window widths\n * cycle_height - Toggle through preset window heights\n * slurp_in - Move current window into column to the left\n * barf_out - Remove current window from column and place to the right\n * switch_space_1 - Switch to Mission Control space 1\n * switch_space_2 - Switch to Mission Control space 2\n * switch_space_3 - Switch to Mission Control space 3\n * switch_space_4 - Switch to Mission Control space 4\n * switch_space_5 - Switch to Mission Control space 5\n * switch_space_6 - Switch to Mission Control space 6\n * switch_space_7 - Switch to Mission Control space 7\n * switch_space_8 - Switch to Mission Control space 8\n * switch_space_9 - Switch to Mission Control space 9\n * move_window_1 - Move current window to Mission Control space 1\n * move_window_2 - Move current window to Mission Control space 2\n * move_window_3 - Move current window to Mission Control space 3\n * move_window_4 - Move current window to Mission Control space 4\n * move_window_5 - Move current window to Mission Control space 5\n * move_window_6 - Move current window to Mission Control space 6\n * move_window_7 - Move current window to Mission Control space 7\n * move_window_8 - Move current window to Mission Control space 8\n * move_window_9 - Move current window to Mission Control space 9", + "doc": "Binds hotkeys for PaperWM\n\nParameters:\n * mapping - A table containing hotkey modifer/key details for the following items:\n * stop_events - Stop automatic tiling\n * refresh_windows - Refresh windows from window filter list\n * toggle_floating - Add or remove window from floating layer\n * focus_left - Focus window to left of current window\n * focus_right - Focus window to right of current window\n * focus_up - Focus window to up of current window\n * focus_down - Focus window to down of current window\n * swap_left - Swap positions of window to the left and current window\n * swap_right - Swap positions of window to the right and current window\n * swap_up - Swap positions of window above and current window\n * swap_down - Swap positions of window below and current window\n * center_window - Move current window to center of screen\n * full_width - Resize width of current window to width of screen\n * cycle_width - Toggle through preset window widths\n * cycle_height - Toggle through preset window heights\n * reverse_cycle_width - Toggle through preset window widths\n * reverse_cycle_height - Toggle through preset window heights\n * slurp_in - Move current window into column to the left\n * barf_out - Remove current window from column and place to the right\n * switch_space_l - Switch to Mission Control space to the left\n * switch_space_r - Switch to Mission Control space to the right\n * switch_space_1 - Switch to Mission Control space 1\n * switch_space_2 - Switch to Mission Control space 2\n * switch_space_3 - Switch to Mission Control space 3\n * switch_space_4 - Switch to Mission Control space 4\n * switch_space_5 - Switch to Mission Control space 5\n * switch_space_6 - Switch to Mission Control space 6\n * switch_space_7 - Switch to Mission Control space 7\n * switch_space_8 - Switch to Mission Control space 8\n * switch_space_9 - Switch to Mission Control space 9\n * move_window_1 - Move current window to Mission Control space 1\n * move_window_2 - Move current window to Mission Control space 2\n * move_window_3 - Move current window to Mission Control space 3\n * move_window_4 - Move current window to Mission Control space 4\n * move_window_5 - Move current window to Mission Control space 5\n * move_window_6 - Move current window to Mission Control space 6\n * move_window_7 - Move current window to Mission Control space 7\n * move_window_8 - Move current window to Mission Control space 8\n * move_window_9 - Move current window to Mission Control space 9", "examples": [], "file": "Source/PaperWM.spoon//init.lua", - "lineno": "275", + "lineno": "1455", "name": "bindHotkeys", "notes": [], "parameters": [ - " * mapping - A table containing hotkey modifer/key details for the following items:\n * stop_events - Stop automatic tiling\n * focus_left - Focus window to left of current window\n * focus_right - Focus window to right of current window\n * focus_up - Focus window to up of current window\n * focus_down - Focus window to down of current window\n * swap_left - Swap positions of window to the left and current window\n * swap_right - Swap positions of window to the right and current window\n * swap_up - Swap positions of window above and current window\n * swap_down - Swap positions of window below and current window\n * center_window - Move current window to center of screen\n * full_width - Resize width of current window to width of screen\n * cycle_width - Toggle through preset window widths\n * cycle_height - Toggle through preset window heights\n * slurp_in - Move current window into column to the left\n * barf_out - Remove current window from column and place to the right\n * switch_space_1 - Switch to Mission Control space 1\n * switch_space_2 - Switch to Mission Control space 2\n * switch_space_3 - Switch to Mission Control space 3\n * switch_space_4 - Switch to Mission Control space 4\n * switch_space_5 - Switch to Mission Control space 5\n * switch_space_6 - Switch to Mission Control space 6\n * switch_space_7 - Switch to Mission Control space 7\n * switch_space_8 - Switch to Mission Control space 8\n * switch_space_9 - Switch to Mission Control space 9\n * move_window_1 - Move current window to Mission Control space 1\n * move_window_2 - Move current window to Mission Control space 2\n * move_window_3 - Move current window to Mission Control space 3\n * move_window_4 - Move current window to Mission Control space 4\n * move_window_5 - Move current window to Mission Control space 5\n * move_window_6 - Move current window to Mission Control space 6\n * move_window_7 - Move current window to Mission Control space 7\n * move_window_8 - Move current window to Mission Control space 8\n * move_window_9 - Move current window to Mission Control space 9" + " * mapping - A table containing hotkey modifer/key details for the following items:\n * stop_events - Stop automatic tiling\n * refresh_windows - Refresh windows from window filter list\n * toggle_floating - Add or remove window from floating layer\n * focus_left - Focus window to left of current window\n * focus_right - Focus window to right of current window\n * focus_up - Focus window to up of current window\n * focus_down - Focus window to down of current window\n * swap_left - Swap positions of window to the left and current window\n * swap_right - Swap positions of window to the right and current window\n * swap_up - Swap positions of window above and current window\n * swap_down - Swap positions of window below and current window\n * center_window - Move current window to center of screen\n * full_width - Resize width of current window to width of screen\n * cycle_width - Toggle through preset window widths\n * cycle_height - Toggle through preset window heights\n * reverse_cycle_width - Toggle through preset window widths\n * reverse_cycle_height - Toggle through preset window heights\n * slurp_in - Move current window into column to the left\n * barf_out - Remove current window from column and place to the right\n * switch_space_l - Switch to Mission Control space to the left\n * switch_space_r - Switch to Mission Control space to the right\n * switch_space_1 - Switch to Mission Control space 1\n * switch_space_2 - Switch to Mission Control space 2\n * switch_space_3 - Switch to Mission Control space 3\n * switch_space_4 - Switch to Mission Control space 4\n * switch_space_5 - Switch to Mission Control space 5\n * switch_space_6 - Switch to Mission Control space 6\n * switch_space_7 - Switch to Mission Control space 7\n * switch_space_8 - Switch to Mission Control space 8\n * switch_space_9 - Switch to Mission Control space 9\n * move_window_1 - Move current window to Mission Control space 1\n * move_window_2 - Move current window to Mission Control space 2\n * move_window_3 - Move current window to Mission Control space 3\n * move_window_4 - Move current window to Mission Control space 4\n * move_window_5 - Move current window to Mission Control space 5\n * move_window_6 - Move current window to Mission Control space 6\n * move_window_7 - Move current window to Mission Control space 7\n * move_window_8 - Move current window to Mission Control space 8\n * move_window_9 - Move current window to Mission Control space 9" ], "returns": [], "signature": "PaperWM.bindHotkeys(mapping)", @@ -66,7 +66,7 @@ "doc": "Moves current window to center of screen, without resizing.\n\nParameters:\n * None", "examples": [], "file": "Source/PaperWM.spoon//init.lua", - "lineno": "860", + "lineno": "937", "name": "centerWindow", "notes": [], "parameters": [ @@ -78,19 +78,20 @@ "type": "Method" }, { - "def": "PaperWM:cycleWindowSize(direction)", + "def": "PaperWM:cycleWindowSize(direction, cycle_direction)", "desc": "Resizes current window by cycling through width or height ratios.", - "doc": "Resizes current window by cycling through width or height ratios.\n\nParameters:\n * direction - One of Direction { WIDTH, HEIGHT }", + "doc": "Resizes current window by cycling through width or height ratios.\n\nParameters:\n * direction - One of Direction { WIDTH, HEIGHT }\n * cycle_direction - One of Direction { ASCENDING, DESCENDING }", "examples": [], "file": "Source/PaperWM.spoon//init.lua", - "lineno": "907", + "lineno": "990", "name": "cycleWindowSize", "notes": [], "parameters": [ - " * direction - One of Direction { WIDTH, HEIGHT }" + " * direction - One of Direction { WIDTH, HEIGHT }", + " * cycle_direction - One of Direction { ASCENDING, DESCENDING }" ], "returns": [], - "signature": "PaperWM:cycleWindowSize(direction)", + "signature": "PaperWM:cycleWindowSize(direction, cycle_direction)", "stripped_doc": "", "type": "Method" }, @@ -100,7 +101,7 @@ "doc": "Change focus to a nearby window\n\nParameters:\n * direction - One of Direction { LEFT, RIGHT, DOWN, UP }\n * focused_index - The coordinates of the current window in the tiling layout\n\nReturns:\n * A boolean. True if a new window was focused. False if no nearby window\n was found in that direction.", "examples": [], "file": "Source/PaperWM.spoon//init.lua", - "lineno": "699", + "lineno": "771", "name": "focusWindow", "notes": [], "parameters": [ @@ -115,13 +116,30 @@ "stripped_doc": "", "type": "Method" }, + { + "def": "PaperWM:incrementSpace(direction)", + "desc": "Switch to a Mission Control space to the left or right of current space", + "doc": "Switch to a Mission Control space to the left or right of current space\n\nParameters:\n * direction - One of Direction { LEFT, RIGHT }", + "examples": [], + "file": "Source/PaperWM.spoon//init.lua", + "lineno": "1209", + "name": "incrementSpace", + "notes": [], + "parameters": [ + " * direction - One of Direction { LEFT, RIGHT }" + ], + "returns": [], + "signature": "PaperWM:incrementSpace(direction)", + "stripped_doc": "", + "type": "Method" + }, { "def": "PaperWM::moveWindow(window, frame)", "desc": "Resizes a window without triggering a windowMoved event", "doc": "Resizes a window without triggering a windowMoved event\n\nParameters:\n * window - An hs.window\n * frame - An hs.geometry.rect for the windows new frame size.", "examples": [], "file": "Source/PaperWM.spoon//init.lua", - "lineno": "1147", + "lineno": "1351", "name": "moveWindow", "notes": [], "parameters": [ @@ -134,19 +152,20 @@ "type": "Method" }, { - "def": "PaperWM:moveWindowToSpace(index)", + "def": "PaperWM:moveWindowToSpace(index, window)", "desc": "Moves the current window to a new Mission Control space", - "doc": "Moves the current window to a new Mission Control space\n\nParameters:\n * index - The space number", + "doc": "Moves the current window to a new Mission Control space\n\nParameters:\n * index - The space number\n * window - Optional window to move", "examples": [], "file": "Source/PaperWM.spoon//init.lua", - "lineno": "1096", + "lineno": "1243", "name": "moveWindowToSpace", "notes": [], "parameters": [ - " * index - The space number" + " * index - The space number", + " * window - Optional window to move" ], "returns": [], - "signature": "PaperWM:moveWindowToSpace(index)", + "signature": "PaperWM:moveWindowToSpace(index, window)", "stripped_doc": "", "type": "Method" }, @@ -156,7 +175,7 @@ "doc": "Searches for all windows that match window filter.\n\nParameters:\n * None\n\nReturns:\n * A boolean, true if the layout needs to be re-tiled, false if no change.", "examples": [], "file": "Source/PaperWM.spoon//init.lua", - "lineno": "563", + "lineno": "618", "name": "refreshWindows", "notes": [], "parameters": [ @@ -175,7 +194,7 @@ "doc": "Remove window from tiling layout\n\nParameters:\n * remove_window - A hs.window to remove from tiling layout\n * skip_new_window_focus - A boolean. True if a nearby window should not be\n focused after current window is removed.\n\nReturns:\n * The hs.spaces space for removed window.", "examples": [], "file": "Source/PaperWM.spoon//init.lua", - "lineno": "652", + "lineno": "720", "name": "remove_window", "notes": [], "parameters": [ @@ -195,7 +214,7 @@ "doc": "Resizes current window's width to width of screen, without adjusting height.\n\nParameters:\n * None", "examples": [], "file": "Source/PaperWM.spoon//init.lua", - "lineno": "885", + "lineno": "965", "name": "setWindowFullWidth", "notes": [], "parameters": [ @@ -212,7 +231,7 @@ "doc": "Moves current window into column of windows to the left\n\nParameters:\n * None", "examples": [], "file": "Source/PaperWM.spoon//init.lua", - "lineno": "961", + "lineno": "1067", "name": "slurpWindow", "notes": [], "parameters": [ @@ -229,7 +248,7 @@ "doc": "Start automatic tiling of windows\n\nParameters:\n * None\n\nReturns:\n * The PaperWM object", "examples": [], "file": "Source/PaperWM.spoon//init.lua", - "lineno": "354", + "lineno": "396", "name": "start", "notes": [], "parameters": [ @@ -248,7 +267,7 @@ "doc": "Stop automatic tiling of windows\n\nParameters:\n * None\n\nReturns:\n * The PaperWM object", "examples": [], "file": "Source/PaperWM.spoon//init.lua", - "lineno": "391", + "lineno": "447", "name": "stop", "notes": [], "parameters": [ @@ -267,7 +286,7 @@ "doc": "Swaps window postions between current window and window in specified direction.\n\nParameters:\n * direction - One of Direction { LEFT, RIGHT, DOWN, UP }", "examples": [], "file": "Source/PaperWM.spoon//init.lua", - "lineno": "749", + "lineno": "824", "name": "swapWindows", "notes": [], "parameters": [ @@ -284,7 +303,7 @@ "doc": "Switch to a Mission Control space\n\nParameters:\n * index - The space number", "examples": [], "file": "Source/PaperWM.spoon//init.lua", - "lineno": "1080", + "lineno": "1192", "name": "switchToSpace", "notes": [], "parameters": [ @@ -301,7 +320,7 @@ "doc": "Tile a column of windows\n\nParameters:\n * windows - A list of hs.windows.\n * bounds - An hs.geometry.rect. The area for this column to fill.\n * h - The height for each window in column.\n * w - The width for each window in column.\n * id - A hs.window.id() for a specific window in column.\n * h4id - The height for a window matching id in column.\n\nNotes:\n * The h, w, id, and h4id parameters are optional. The height and width of\n all windows will be calculated and set to fill column bounds.\n * If bounds width is not specified, all windows in column will be resized\n to width of first window.\n\nReturns:\n * The width of the column", "examples": [], "file": "Source/PaperWM.spoon//init.lua", - "lineno": "408", + "lineno": "470", "name": "tileColumn", "notes": [ " * The h, w, id, and h4id parameters are optional. The height and width of", @@ -330,7 +349,7 @@ "doc": "Tile all windows within a space\n\nParameters:\n * space - A hs.spaces space.", "examples": [], "file": "Source/PaperWM.spoon//init.lua", - "lineno": "460", + "lineno": "522", "name": "tileSpace", "notes": [], "parameters": [ @@ -340,6 +359,23 @@ "signature": "PaperWM:tileSpace(space)", "stripped_doc": "", "type": "Method" + }, + { + "def": "PaperWM:toggleFloating()", + "desc": "Add or remove focused window from the floating layer and retile the space", + "doc": "Add or remove focused window from the floating layer and retile the space\n\nParameters:\n * None", + "examples": [], + "file": "Source/PaperWM.spoon//init.lua", + "lineno": "1380", + "name": "toggleFloating", + "notes": [], + "parameters": [ + " * None" + ], + "returns": [], + "signature": "PaperWM:toggleFloating()", + "stripped_doc": "", + "type": "Method" } ], "Variable": [ @@ -348,7 +384,7 @@ "desc": "Default hotkeys for moving / resizing windows", "doc": "Default hotkeys for moving / resizing windows", "file": "Source/PaperWM.spoon//init.lua", - "lineno": "111", + "lineno": "132", "name": "default_hotkeys", "signature": "PaperWM.default_hotkeys", "stripped_doc": "", @@ -359,7 +395,7 @@ "desc": "Logger object. Can be accessed to set default log level.", "doc": "Logger object. Can be accessed to set default log level.", "file": "Source/PaperWM.spoon//init.lua", - "lineno": "165", + "lineno": "202", "name": "logger", "signature": "PaperWM.logger", "stripped_doc": "", @@ -370,7 +406,7 @@ "desc": "Windows captured by this filter are automatically tiled and managed", "doc": "Windows captured by this filter are automatically tiled and managed", "file": "Source/PaperWM.spoon//init.lua", - "lineno": "150", + "lineno": "177", "name": "window_filter", "signature": "PaperWM.window_filter", "stripped_doc": "", @@ -381,15 +417,26 @@ "desc": "Number of pixels between tiled windows", "doc": "Number of pixels between tiled windows", "file": "Source/PaperWM.spoon//init.lua", - "lineno": "160", + "lineno": "187", "name": "window_gap", "signature": "PaperWM.window_gap", "stripped_doc": "", "type": "Variable" + }, + { + "def": "PaperWM.window_ratios", + "desc": "Size of the on-screen margin to place off-screen windows", + "doc": "Size of the on-screen margin to place off-screen windows", + "file": "Source/PaperWM.spoon//init.lua", + "lineno": "197", + "name": "window_ratios", + "signature": "PaperWM.window_ratios", + "stripped_doc": "", + "type": "Variable" } ], "desc": "A scrolling window manager. Inspired by PaperWM Gnome extension.", - "doc": "A scrolling window manager. Inspired by PaperWM Gnome extension.\n\n# Usage\n\n`PaperWM:start()` will begin automatically tiling new and existing windows.\n`PaperWM:stop()` will release control over windows.\n`PaperWM::bindHotkeys()` will move / resize windows using keyboard shortcuts.\n\nHere is an example Hammerspoon config:\n\n```\nPaperWM = hs.loadSpoon(\"PaperWM\")\nPaperWM:bindHotkeys({\n -- switch to a new focused window in tiled grid\n focus_left = {{\"ctrl\", \"alt\", \"cmd\"}, \"left\"},\n focus_right = {{\"ctrl\", \"alt\", \"cmd\"}, \"right\"},\n focus_up = {{\"ctrl\", \"alt\", \"cmd\"}, \"up\"},\n focus_down = {{\"ctrl\", \"alt\", \"cmd\"}, \"down\"},\n\n -- move windows around in tiled grid\n swap_left = {{\"ctrl\", \"alt\", \"cmd\", \"shift\"}, \"left\"},\n swap_right = {{\"ctrl\", \"alt\", \"cmd\", \"shift\"}, \"right\"},\n swap_up = {{\"ctrl\", \"alt\", \"cmd\", \"shift\"}, \"up\"},\n swap_down = {{\"ctrl\", \"alt\", \"cmd\", \"shift\"}, \"down\"},\n\n -- position and resize focused window\n center_window = {{\"ctrl\", \"alt\", \"cmd\"}, \"c\"},\n full_width = {{\"ctrl\", \"alt\", \"cmd\"}, \"f\"},\n cycle_width = {{\"ctrl\", \"alt\", \"cmd\"}, \"r\"},\n cycle_height = {{\"ctrl\", \"alt\", \"cmd\", \"shift\"}, \"r\"},\n\n -- move focused window into / out of a column\n slurp_in = {{\"ctrl\", \"alt\", \"cmd\"}, \"i\"},\n barf_out = {{\"ctrl\", \"alt\", \"cmd\"}, \"o\"},\n\n -- switch to a new Mission Control space\n switch_space_1 = {{\"ctrl\", \"alt\", \"cmd\"}, \"1\"},\n switch_space_2 = {{\"ctrl\", \"alt\", \"cmd\"}, \"2\"},\n switch_space_3 = {{\"ctrl\", \"alt\", \"cmd\"}, \"3\"},\n switch_space_4 = {{\"ctrl\", \"alt\", \"cmd\"}, \"4\"},\n switch_space_5 = {{\"ctrl\", \"alt\", \"cmd\"}, \"5\"},\n switch_space_6 = {{\"ctrl\", \"alt\", \"cmd\"}, \"6\"},\n switch_space_7 = {{\"ctrl\", \"alt\", \"cmd\"}, \"7\"},\n switch_space_8 = {{\"ctrl\", \"alt\", \"cmd\"}, \"8\"},\n switch_space_9 = {{\"ctrl\", \"alt\", \"cmd\"}, \"9\"},\n\n -- move focused window to a new space and tile\n move_window_1 = {{\"ctrl\", \"alt\", \"cmd\", \"shift\"}, \"1\"},\n move_window_2 = {{\"ctrl\", \"alt\", \"cmd\", \"shift\"}, \"2\"},\n move_window_3 = {{\"ctrl\", \"alt\", \"cmd\", \"shift\"}, \"3\"},\n move_window_4 = {{\"ctrl\", \"alt\", \"cmd\", \"shift\"}, \"4\"},\n move_window_5 = {{\"ctrl\", \"alt\", \"cmd\", \"shift\"}, \"5\"},\n move_window_6 = {{\"ctrl\", \"alt\", \"cmd\", \"shift\"}, \"6\"},\n move_window_7 = {{\"ctrl\", \"alt\", \"cmd\", \"shift\"}, \"7\"},\n move_window_8 = {{\"ctrl\", \"alt\", \"cmd\", \"shift\"}, \"8\"},\n move_window_9 = {{\"ctrl\", \"alt\", \"cmd\", \"shift\"}, \"9\"}\n})\nPaperWM:start()\n```\n\nUse `PaperWM:bindHotkeys(PaperWM.default_hotkeys)` for defaults.\n\nSet `PaperWM.window_gap` to the number of pixels to space between windows and\nthe top and bottom screen edges.\n\nOverwrite `PaperWM.window_filter` to ignore specific applications. For example:\n\n```\nPaperWM.window_filter = PaperWM.window_filter:setAppFilter(\"Finder\", false)\nPaperWM:start() -- restart for new window filter to take effect\n```\n\n# Limitations\n\nUnder System Preferences -> Mission Control, unselect \"Automatically\nrearrange Spaces based on most recent use\" and select \"Displays have separate\nSpaces\".\n\nMacOS does not allow a window to be moved fully off-screen. Windows that would\nbe tiled off-screen are placed in a margin on the left and right edge of the\nscreen. They are still visible and clickable.\n\nIt's difficult to detect when a window is dragged from one space or screen to\nanother. Use the move_window_N commands to move windows between spaces and\nscreens.\n\nArrange screens vertically to prevent windows from bleeding into other screens.\n\n\nDownload: [https://github.com/mogenson/PaperWM.spoon](https://github.com/mogenson/PaperWM.spoon)", + "doc": "A scrolling window manager. Inspired by PaperWM Gnome extension.\n\n# Usage\n\n`PaperWM:start()` will begin automatically tiling new and existing windows.\n`PaperWM:stop()` will release control over windows.\n`PaperWM::bindHotkeys()` will move / resize windows using keyboard shortcuts.\n\nHere is an example Hammerspoon config:\n\n```\nPaperWM = hs.loadSpoon(\"PaperWM\")\nPaperWM:bindHotkeys({\n -- switch to a new focused window in tiled grid\n focus_left = {{\"alt\", \"cmd\"}, \"left\"},\n focus_right = {{\"alt\", \"cmd\"}, \"right\"},\n focus_up = {{\"alt\", \"cmd\"}, \"up\"},\n focus_down = {{\"alt\", \"cmd\"}, \"down\"},\n\n -- move windows around in tiled grid\n swap_left = {{\"alt\", \"cmd\", \"shift\"}, \"left\"},\n swap_right = {{\"alt\", \"cmd\", \"shift\"}, \"right\"},\n swap_up = {{\"alt\", \"cmd\", \"shift\"}, \"up\"},\n swap_down = {{\"alt\", \"cmd\", \"shift\"}, \"down\"},\n\n -- position and resize focused window\n center_window = {{\"alt\", \"cmd\"}, \"c\"},\n full_width = {{\"alt\", \"cmd\"}, \"f\"},\n cycle_width = {{\"alt\", \"cmd\"}, \"r\"},\n reverse_cycle_width = {{\"ctrl\", \"alt\", \"cmd\"}, \"r\"},\n cycle_height = {{\"alt\", \"cmd\", \"shift\"}, \"r\"},\n reverse_cycle_height = {{\"ctrl\", \"alt\", \"cmd\", \"shift\"}, \"r\"},\n\n -- move focused window into / out of a column\n slurp_in = {{\"alt\", \"cmd\"}, \"i\"},\n barf_out = {{\"alt\", \"cmd\"}, \"o\"},\n\n --- move the focused window into / out of the tiling layer\n toggle_floating = {{\"alt\", \"cmd\", \"shift\"}, \"escape\"},\n\n -- switch to a new Mission Control space\n switch_space_1 = {{\"alt\", \"cmd\"}, \"1\"},\n switch_space_2 = {{\"alt\", \"cmd\"}, \"2\"},\n switch_space_3 = {{\"alt\", \"cmd\"}, \"3\"},\n switch_space_4 = {{\"alt\", \"cmd\"}, \"4\"},\n switch_space_5 = {{\"alt\", \"cmd\"}, \"5\"},\n switch_space_6 = {{\"alt\", \"cmd\"}, \"6\"},\n switch_space_7 = {{\"alt\", \"cmd\"}, \"7\"},\n switch_space_8 = {{\"alt\", \"cmd\"}, \"8\"},\n switch_space_9 = {{\"alt\", \"cmd\"}, \"9\"},\n\n -- move focused window to a new space and tile\n move_window_1 = {{\"alt\", \"cmd\", \"shift\"}, \"1\"},\n move_window_2 = {{\"alt\", \"cmd\", \"shift\"}, \"2\"},\n move_window_3 = {{\"alt\", \"cmd\", \"shift\"}, \"3\"},\n move_window_4 = {{\"alt\", \"cmd\", \"shift\"}, \"4\"},\n move_window_5 = {{\"alt\", \"cmd\", \"shift\"}, \"5\"},\n move_window_6 = {{\"alt\", \"cmd\", \"shift\"}, \"6\"},\n move_window_7 = {{\"alt\", \"cmd\", \"shift\"}, \"7\"},\n move_window_8 = {{\"alt\", \"cmd\", \"shift\"}, \"8\"},\n move_window_9 = {{\"alt\", \"cmd\", \"shift\"}, \"9\"}\n})\nPaperWM:start()\n```\n\nUse `PaperWM:bindHotkeys(PaperWM.default_hotkeys)` for defaults.\n\nSet `PaperWM.window_gap` to the number of pixels to space between windows and\nthe top and bottom screen edges.\n\nOverwrite `PaperWM.window_filter` to ignore specific applications. For example:\n\n```\nPaperWM.window_filter = PaperWM.window_filter:setAppFilter(\"Finder\", false)\nPaperWM:start() -- restart for new window filter to take effect\n```\n\nSet `PaperWM.window_ratios` to the ratios to cycle window widths and heights\nthrough. For example:\n\n```\nPaperWM.window_ratios = { 1/3, 1/2, 2/3 }\n```\n\n# Limitations\n\nUnder System Preferences -> Mission Control, unselect \"Automatically\nrearrange Spaces based on most recent use\" and select \"Displays have separate\nSpaces\".\n\nMacOS does not allow a window to be moved fully off-screen. Windows that would\nbe tiled off-screen are placed in a margin on the left and right edge of the\nscreen. They are still visible and clickable.\n\nIt's difficult to detect when a window is dragged from one space or screen to\nanother. Use the move_window_N commands to move windows between spaces and\nscreens.\n\nArrange screens vertically to prevent windows from bleeding into other screens.\n\n\nDownload: [https://github.com/mogenson/PaperWM.spoon](https://github.com/mogenson/PaperWM.spoon)", "items": [ { "def": "PaperWM:addWindow(add_window)", @@ -397,7 +444,7 @@ "doc": "Adds a window to layout and tiles.\n\nParameters:\n * add_window - An hs.window\n\nReturns:\n * The hs.spaces space for added window or nil if window not added.", "examples": [], "file": "Source/PaperWM.spoon//init.lua", - "lineno": "594", + "lineno": "652", "name": "addWindow", "notes": [], "parameters": [ @@ -416,7 +463,7 @@ "doc": "Removes current window from column and places it to the right\n\nParameters:\n * None", "examples": [], "file": "Source/PaperWM.spoon//init.lua", - "lineno": "1026", + "lineno": "1135", "name": "barfWindow", "notes": [], "parameters": [ @@ -430,14 +477,14 @@ { "def": "PaperWM.bindHotkeys(mapping)", "desc": "Binds hotkeys for PaperWM", - "doc": "Binds hotkeys for PaperWM\n\nParameters:\n * mapping - A table containing hotkey modifer/key details for the following items:\n * stop_events - Stop automatic tiling\n * focus_left - Focus window to left of current window\n * focus_right - Focus window to right of current window\n * focus_up - Focus window to up of current window\n * focus_down - Focus window to down of current window\n * swap_left - Swap positions of window to the left and current window\n * swap_right - Swap positions of window to the right and current window\n * swap_up - Swap positions of window above and current window\n * swap_down - Swap positions of window below and current window\n * center_window - Move current window to center of screen\n * full_width - Resize width of current window to width of screen\n * cycle_width - Toggle through preset window widths\n * cycle_height - Toggle through preset window heights\n * slurp_in - Move current window into column to the left\n * barf_out - Remove current window from column and place to the right\n * switch_space_1 - Switch to Mission Control space 1\n * switch_space_2 - Switch to Mission Control space 2\n * switch_space_3 - Switch to Mission Control space 3\n * switch_space_4 - Switch to Mission Control space 4\n * switch_space_5 - Switch to Mission Control space 5\n * switch_space_6 - Switch to Mission Control space 6\n * switch_space_7 - Switch to Mission Control space 7\n * switch_space_8 - Switch to Mission Control space 8\n * switch_space_9 - Switch to Mission Control space 9\n * move_window_1 - Move current window to Mission Control space 1\n * move_window_2 - Move current window to Mission Control space 2\n * move_window_3 - Move current window to Mission Control space 3\n * move_window_4 - Move current window to Mission Control space 4\n * move_window_5 - Move current window to Mission Control space 5\n * move_window_6 - Move current window to Mission Control space 6\n * move_window_7 - Move current window to Mission Control space 7\n * move_window_8 - Move current window to Mission Control space 8\n * move_window_9 - Move current window to Mission Control space 9", + "doc": "Binds hotkeys for PaperWM\n\nParameters:\n * mapping - A table containing hotkey modifer/key details for the following items:\n * stop_events - Stop automatic tiling\n * refresh_windows - Refresh windows from window filter list\n * toggle_floating - Add or remove window from floating layer\n * focus_left - Focus window to left of current window\n * focus_right - Focus window to right of current window\n * focus_up - Focus window to up of current window\n * focus_down - Focus window to down of current window\n * swap_left - Swap positions of window to the left and current window\n * swap_right - Swap positions of window to the right and current window\n * swap_up - Swap positions of window above and current window\n * swap_down - Swap positions of window below and current window\n * center_window - Move current window to center of screen\n * full_width - Resize width of current window to width of screen\n * cycle_width - Toggle through preset window widths\n * cycle_height - Toggle through preset window heights\n * reverse_cycle_width - Toggle through preset window widths\n * reverse_cycle_height - Toggle through preset window heights\n * slurp_in - Move current window into column to the left\n * barf_out - Remove current window from column and place to the right\n * switch_space_l - Switch to Mission Control space to the left\n * switch_space_r - Switch to Mission Control space to the right\n * switch_space_1 - Switch to Mission Control space 1\n * switch_space_2 - Switch to Mission Control space 2\n * switch_space_3 - Switch to Mission Control space 3\n * switch_space_4 - Switch to Mission Control space 4\n * switch_space_5 - Switch to Mission Control space 5\n * switch_space_6 - Switch to Mission Control space 6\n * switch_space_7 - Switch to Mission Control space 7\n * switch_space_8 - Switch to Mission Control space 8\n * switch_space_9 - Switch to Mission Control space 9\n * move_window_1 - Move current window to Mission Control space 1\n * move_window_2 - Move current window to Mission Control space 2\n * move_window_3 - Move current window to Mission Control space 3\n * move_window_4 - Move current window to Mission Control space 4\n * move_window_5 - Move current window to Mission Control space 5\n * move_window_6 - Move current window to Mission Control space 6\n * move_window_7 - Move current window to Mission Control space 7\n * move_window_8 - Move current window to Mission Control space 8\n * move_window_9 - Move current window to Mission Control space 9", "examples": [], "file": "Source/PaperWM.spoon//init.lua", - "lineno": "275", + "lineno": "1455", "name": "bindHotkeys", "notes": [], "parameters": [ - " * mapping - A table containing hotkey modifer/key details for the following items:\n * stop_events - Stop automatic tiling\n * focus_left - Focus window to left of current window\n * focus_right - Focus window to right of current window\n * focus_up - Focus window to up of current window\n * focus_down - Focus window to down of current window\n * swap_left - Swap positions of window to the left and current window\n * swap_right - Swap positions of window to the right and current window\n * swap_up - Swap positions of window above and current window\n * swap_down - Swap positions of window below and current window\n * center_window - Move current window to center of screen\n * full_width - Resize width of current window to width of screen\n * cycle_width - Toggle through preset window widths\n * cycle_height - Toggle through preset window heights\n * slurp_in - Move current window into column to the left\n * barf_out - Remove current window from column and place to the right\n * switch_space_1 - Switch to Mission Control space 1\n * switch_space_2 - Switch to Mission Control space 2\n * switch_space_3 - Switch to Mission Control space 3\n * switch_space_4 - Switch to Mission Control space 4\n * switch_space_5 - Switch to Mission Control space 5\n * switch_space_6 - Switch to Mission Control space 6\n * switch_space_7 - Switch to Mission Control space 7\n * switch_space_8 - Switch to Mission Control space 8\n * switch_space_9 - Switch to Mission Control space 9\n * move_window_1 - Move current window to Mission Control space 1\n * move_window_2 - Move current window to Mission Control space 2\n * move_window_3 - Move current window to Mission Control space 3\n * move_window_4 - Move current window to Mission Control space 4\n * move_window_5 - Move current window to Mission Control space 5\n * move_window_6 - Move current window to Mission Control space 6\n * move_window_7 - Move current window to Mission Control space 7\n * move_window_8 - Move current window to Mission Control space 8\n * move_window_9 - Move current window to Mission Control space 9" + " * mapping - A table containing hotkey modifer/key details for the following items:\n * stop_events - Stop automatic tiling\n * refresh_windows - Refresh windows from window filter list\n * toggle_floating - Add or remove window from floating layer\n * focus_left - Focus window to left of current window\n * focus_right - Focus window to right of current window\n * focus_up - Focus window to up of current window\n * focus_down - Focus window to down of current window\n * swap_left - Swap positions of window to the left and current window\n * swap_right - Swap positions of window to the right and current window\n * swap_up - Swap positions of window above and current window\n * swap_down - Swap positions of window below and current window\n * center_window - Move current window to center of screen\n * full_width - Resize width of current window to width of screen\n * cycle_width - Toggle through preset window widths\n * cycle_height - Toggle through preset window heights\n * reverse_cycle_width - Toggle through preset window widths\n * reverse_cycle_height - Toggle through preset window heights\n * slurp_in - Move current window into column to the left\n * barf_out - Remove current window from column and place to the right\n * switch_space_l - Switch to Mission Control space to the left\n * switch_space_r - Switch to Mission Control space to the right\n * switch_space_1 - Switch to Mission Control space 1\n * switch_space_2 - Switch to Mission Control space 2\n * switch_space_3 - Switch to Mission Control space 3\n * switch_space_4 - Switch to Mission Control space 4\n * switch_space_5 - Switch to Mission Control space 5\n * switch_space_6 - Switch to Mission Control space 6\n * switch_space_7 - Switch to Mission Control space 7\n * switch_space_8 - Switch to Mission Control space 8\n * switch_space_9 - Switch to Mission Control space 9\n * move_window_1 - Move current window to Mission Control space 1\n * move_window_2 - Move current window to Mission Control space 2\n * move_window_3 - Move current window to Mission Control space 3\n * move_window_4 - Move current window to Mission Control space 4\n * move_window_5 - Move current window to Mission Control space 5\n * move_window_6 - Move current window to Mission Control space 6\n * move_window_7 - Move current window to Mission Control space 7\n * move_window_8 - Move current window to Mission Control space 8\n * move_window_9 - Move current window to Mission Control space 9" ], "returns": [], "signature": "PaperWM.bindHotkeys(mapping)", @@ -450,7 +497,7 @@ "doc": "Moves current window to center of screen, without resizing.\n\nParameters:\n * None", "examples": [], "file": "Source/PaperWM.spoon//init.lua", - "lineno": "860", + "lineno": "937", "name": "centerWindow", "notes": [], "parameters": [ @@ -462,19 +509,20 @@ "type": "Method" }, { - "def": "PaperWM:cycleWindowSize(direction)", + "def": "PaperWM:cycleWindowSize(direction, cycle_direction)", "desc": "Resizes current window by cycling through width or height ratios.", - "doc": "Resizes current window by cycling through width or height ratios.\n\nParameters:\n * direction - One of Direction { WIDTH, HEIGHT }", + "doc": "Resizes current window by cycling through width or height ratios.\n\nParameters:\n * direction - One of Direction { WIDTH, HEIGHT }\n * cycle_direction - One of Direction { ASCENDING, DESCENDING }", "examples": [], "file": "Source/PaperWM.spoon//init.lua", - "lineno": "907", + "lineno": "990", "name": "cycleWindowSize", "notes": [], "parameters": [ - " * direction - One of Direction { WIDTH, HEIGHT }" + " * direction - One of Direction { WIDTH, HEIGHT }", + " * cycle_direction - One of Direction { ASCENDING, DESCENDING }" ], "returns": [], - "signature": "PaperWM:cycleWindowSize(direction)", + "signature": "PaperWM:cycleWindowSize(direction, cycle_direction)", "stripped_doc": "", "type": "Method" }, @@ -483,7 +531,7 @@ "desc": "Default hotkeys for moving / resizing windows", "doc": "Default hotkeys for moving / resizing windows", "file": "Source/PaperWM.spoon//init.lua", - "lineno": "111", + "lineno": "132", "name": "default_hotkeys", "signature": "PaperWM.default_hotkeys", "stripped_doc": "", @@ -495,7 +543,7 @@ "doc": "Change focus to a nearby window\n\nParameters:\n * direction - One of Direction { LEFT, RIGHT, DOWN, UP }\n * focused_index - The coordinates of the current window in the tiling layout\n\nReturns:\n * A boolean. True if a new window was focused. False if no nearby window\n was found in that direction.", "examples": [], "file": "Source/PaperWM.spoon//init.lua", - "lineno": "699", + "lineno": "771", "name": "focusWindow", "notes": [], "parameters": [ @@ -510,12 +558,29 @@ "stripped_doc": "", "type": "Method" }, + { + "def": "PaperWM:incrementSpace(direction)", + "desc": "Switch to a Mission Control space to the left or right of current space", + "doc": "Switch to a Mission Control space to the left or right of current space\n\nParameters:\n * direction - One of Direction { LEFT, RIGHT }", + "examples": [], + "file": "Source/PaperWM.spoon//init.lua", + "lineno": "1209", + "name": "incrementSpace", + "notes": [], + "parameters": [ + " * direction - One of Direction { LEFT, RIGHT }" + ], + "returns": [], + "signature": "PaperWM:incrementSpace(direction)", + "stripped_doc": "", + "type": "Method" + }, { "def": "PaperWM.logger", "desc": "Logger object. Can be accessed to set default log level.", "doc": "Logger object. Can be accessed to set default log level.", "file": "Source/PaperWM.spoon//init.lua", - "lineno": "165", + "lineno": "202", "name": "logger", "signature": "PaperWM.logger", "stripped_doc": "", @@ -527,7 +592,7 @@ "doc": "Resizes a window without triggering a windowMoved event\n\nParameters:\n * window - An hs.window\n * frame - An hs.geometry.rect for the windows new frame size.", "examples": [], "file": "Source/PaperWM.spoon//init.lua", - "lineno": "1147", + "lineno": "1351", "name": "moveWindow", "notes": [], "parameters": [ @@ -540,19 +605,20 @@ "type": "Method" }, { - "def": "PaperWM:moveWindowToSpace(index)", + "def": "PaperWM:moveWindowToSpace(index, window)", "desc": "Moves the current window to a new Mission Control space", - "doc": "Moves the current window to a new Mission Control space\n\nParameters:\n * index - The space number", + "doc": "Moves the current window to a new Mission Control space\n\nParameters:\n * index - The space number\n * window - Optional window to move", "examples": [], "file": "Source/PaperWM.spoon//init.lua", - "lineno": "1096", + "lineno": "1243", "name": "moveWindowToSpace", "notes": [], "parameters": [ - " * index - The space number" + " * index - The space number", + " * window - Optional window to move" ], "returns": [], - "signature": "PaperWM:moveWindowToSpace(index)", + "signature": "PaperWM:moveWindowToSpace(index, window)", "stripped_doc": "", "type": "Method" }, @@ -562,7 +628,7 @@ "doc": "Searches for all windows that match window filter.\n\nParameters:\n * None\n\nReturns:\n * A boolean, true if the layout needs to be re-tiled, false if no change.", "examples": [], "file": "Source/PaperWM.spoon//init.lua", - "lineno": "563", + "lineno": "618", "name": "refreshWindows", "notes": [], "parameters": [ @@ -581,7 +647,7 @@ "doc": "Remove window from tiling layout\n\nParameters:\n * remove_window - A hs.window to remove from tiling layout\n * skip_new_window_focus - A boolean. True if a nearby window should not be\n focused after current window is removed.\n\nReturns:\n * The hs.spaces space for removed window.", "examples": [], "file": "Source/PaperWM.spoon//init.lua", - "lineno": "652", + "lineno": "720", "name": "remove_window", "notes": [], "parameters": [ @@ -601,7 +667,7 @@ "doc": "Resizes current window's width to width of screen, without adjusting height.\n\nParameters:\n * None", "examples": [], "file": "Source/PaperWM.spoon//init.lua", - "lineno": "885", + "lineno": "965", "name": "setWindowFullWidth", "notes": [], "parameters": [ @@ -618,7 +684,7 @@ "doc": "Moves current window into column of windows to the left\n\nParameters:\n * None", "examples": [], "file": "Source/PaperWM.spoon//init.lua", - "lineno": "961", + "lineno": "1067", "name": "slurpWindow", "notes": [], "parameters": [ @@ -635,7 +701,7 @@ "doc": "Start automatic tiling of windows\n\nParameters:\n * None\n\nReturns:\n * The PaperWM object", "examples": [], "file": "Source/PaperWM.spoon//init.lua", - "lineno": "354", + "lineno": "396", "name": "start", "notes": [], "parameters": [ @@ -654,7 +720,7 @@ "doc": "Stop automatic tiling of windows\n\nParameters:\n * None\n\nReturns:\n * The PaperWM object", "examples": [], "file": "Source/PaperWM.spoon//init.lua", - "lineno": "391", + "lineno": "447", "name": "stop", "notes": [], "parameters": [ @@ -673,7 +739,7 @@ "doc": "Swaps window postions between current window and window in specified direction.\n\nParameters:\n * direction - One of Direction { LEFT, RIGHT, DOWN, UP }", "examples": [], "file": "Source/PaperWM.spoon//init.lua", - "lineno": "749", + "lineno": "824", "name": "swapWindows", "notes": [], "parameters": [ @@ -690,7 +756,7 @@ "doc": "Switch to a Mission Control space\n\nParameters:\n * index - The space number", "examples": [], "file": "Source/PaperWM.spoon//init.lua", - "lineno": "1080", + "lineno": "1192", "name": "switchToSpace", "notes": [], "parameters": [ @@ -707,7 +773,7 @@ "doc": "Tile a column of windows\n\nParameters:\n * windows - A list of hs.windows.\n * bounds - An hs.geometry.rect. The area for this column to fill.\n * h - The height for each window in column.\n * w - The width for each window in column.\n * id - A hs.window.id() for a specific window in column.\n * h4id - The height for a window matching id in column.\n\nNotes:\n * The h, w, id, and h4id parameters are optional. The height and width of\n all windows will be calculated and set to fill column bounds.\n * If bounds width is not specified, all windows in column will be resized\n to width of first window.\n\nReturns:\n * The width of the column", "examples": [], "file": "Source/PaperWM.spoon//init.lua", - "lineno": "408", + "lineno": "470", "name": "tileColumn", "notes": [ " * The h, w, id, and h4id parameters are optional. The height and width of", @@ -736,7 +802,7 @@ "doc": "Tile all windows within a space\n\nParameters:\n * space - A hs.spaces space.", "examples": [], "file": "Source/PaperWM.spoon//init.lua", - "lineno": "460", + "lineno": "522", "name": "tileSpace", "notes": [], "parameters": [ @@ -747,12 +813,29 @@ "stripped_doc": "", "type": "Method" }, + { + "def": "PaperWM:toggleFloating()", + "desc": "Add or remove focused window from the floating layer and retile the space", + "doc": "Add or remove focused window from the floating layer and retile the space\n\nParameters:\n * None", + "examples": [], + "file": "Source/PaperWM.spoon//init.lua", + "lineno": "1380", + "name": "toggleFloating", + "notes": [], + "parameters": [ + " * None" + ], + "returns": [], + "signature": "PaperWM:toggleFloating()", + "stripped_doc": "", + "type": "Method" + }, { "def": "PaperWM.window_filter", "desc": "Windows captured by this filter are automatically tiled and managed", "doc": "Windows captured by this filter are automatically tiled and managed", "file": "Source/PaperWM.spoon//init.lua", - "lineno": "150", + "lineno": "177", "name": "window_filter", "signature": "PaperWM.window_filter", "stripped_doc": "", @@ -763,15 +846,26 @@ "desc": "Number of pixels between tiled windows", "doc": "Number of pixels between tiled windows", "file": "Source/PaperWM.spoon//init.lua", - "lineno": "160", + "lineno": "187", "name": "window_gap", "signature": "PaperWM.window_gap", "stripped_doc": "", "type": "Variable" + }, + { + "def": "PaperWM.window_ratios", + "desc": "Size of the on-screen margin to place off-screen windows", + "doc": "Size of the on-screen margin to place off-screen windows", + "file": "Source/PaperWM.spoon//init.lua", + "lineno": "197", + "name": "window_ratios", + "signature": "PaperWM.window_ratios", + "stripped_doc": "", + "type": "Variable" } ], "name": "PaperWM", - "stripped_doc": "\n# Usage\n\n`PaperWM:start()` will begin automatically tiling new and existing windows.\n`PaperWM:stop()` will release control over windows.\n`PaperWM::bindHotkeys()` will move / resize windows using keyboard shortcuts.\n\nHere is an example Hammerspoon config:\n\n```\nPaperWM = hs.loadSpoon(\"PaperWM\")\nPaperWM:bindHotkeys({\n -- switch to a new focused window in tiled grid\n focus_left = {{\"ctrl\", \"alt\", \"cmd\"}, \"left\"},\n focus_right = {{\"ctrl\", \"alt\", \"cmd\"}, \"right\"},\n focus_up = {{\"ctrl\", \"alt\", \"cmd\"}, \"up\"},\n focus_down = {{\"ctrl\", \"alt\", \"cmd\"}, \"down\"},\n\n -- move windows around in tiled grid\n swap_left = {{\"ctrl\", \"alt\", \"cmd\", \"shift\"}, \"left\"},\n swap_right = {{\"ctrl\", \"alt\", \"cmd\", \"shift\"}, \"right\"},\n swap_up = {{\"ctrl\", \"alt\", \"cmd\", \"shift\"}, \"up\"},\n swap_down = {{\"ctrl\", \"alt\", \"cmd\", \"shift\"}, \"down\"},\n\n -- position and resize focused window\n center_window = {{\"ctrl\", \"alt\", \"cmd\"}, \"c\"},\n full_width = {{\"ctrl\", \"alt\", \"cmd\"}, \"f\"},\n cycle_width = {{\"ctrl\", \"alt\", \"cmd\"}, \"r\"},\n cycle_height = {{\"ctrl\", \"alt\", \"cmd\", \"shift\"}, \"r\"},\n\n -- move focused window into / out of a column\n slurp_in = {{\"ctrl\", \"alt\", \"cmd\"}, \"i\"},\n barf_out = {{\"ctrl\", \"alt\", \"cmd\"}, \"o\"},\n\n -- switch to a new Mission Control space\n switch_space_1 = {{\"ctrl\", \"alt\", \"cmd\"}, \"1\"},\n switch_space_2 = {{\"ctrl\", \"alt\", \"cmd\"}, \"2\"},\n switch_space_3 = {{\"ctrl\", \"alt\", \"cmd\"}, \"3\"},\n switch_space_4 = {{\"ctrl\", \"alt\", \"cmd\"}, \"4\"},\n switch_space_5 = {{\"ctrl\", \"alt\", \"cmd\"}, \"5\"},\n switch_space_6 = {{\"ctrl\", \"alt\", \"cmd\"}, \"6\"},\n switch_space_7 = {{\"ctrl\", \"alt\", \"cmd\"}, \"7\"},\n switch_space_8 = {{\"ctrl\", \"alt\", \"cmd\"}, \"8\"},\n switch_space_9 = {{\"ctrl\", \"alt\", \"cmd\"}, \"9\"},\n\n -- move focused window to a new space and tile\n move_window_1 = {{\"ctrl\", \"alt\", \"cmd\", \"shift\"}, \"1\"},\n move_window_2 = {{\"ctrl\", \"alt\", \"cmd\", \"shift\"}, \"2\"},\n move_window_3 = {{\"ctrl\", \"alt\", \"cmd\", \"shift\"}, \"3\"},\n move_window_4 = {{\"ctrl\", \"alt\", \"cmd\", \"shift\"}, \"4\"},\n move_window_5 = {{\"ctrl\", \"alt\", \"cmd\", \"shift\"}, \"5\"},\n move_window_6 = {{\"ctrl\", \"alt\", \"cmd\", \"shift\"}, \"6\"},\n move_window_7 = {{\"ctrl\", \"alt\", \"cmd\", \"shift\"}, \"7\"},\n move_window_8 = {{\"ctrl\", \"alt\", \"cmd\", \"shift\"}, \"8\"},\n move_window_9 = {{\"ctrl\", \"alt\", \"cmd\", \"shift\"}, \"9\"}\n})\nPaperWM:start()\n```\n\nUse `PaperWM:bindHotkeys(PaperWM.default_hotkeys)` for defaults.\n\nSet `PaperWM.window_gap` to the number of pixels to space between windows and\nthe top and bottom screen edges.\n\nOverwrite `PaperWM.window_filter` to ignore specific applications. For example:\n\n```\nPaperWM.window_filter = PaperWM.window_filter:setAppFilter(\"Finder\", false)\nPaperWM:start() -- restart for new window filter to take effect\n```\n\n# Limitations\n\nUnder System Preferences -> Mission Control, unselect \"Automatically\nrearrange Spaces based on most recent use\" and select \"Displays have separate\nSpaces\".\n\nMacOS does not allow a window to be moved fully off-screen. Windows that would\nbe tiled off-screen are placed in a margin on the left and right edge of the\nscreen. They are still visible and clickable.\n\nIt's difficult to detect when a window is dragged from one space or screen to\nanother. Use the move_window_N commands to move windows between spaces and\nscreens.\n\nArrange screens vertically to prevent windows from bleeding into other screens.\n\n\nDownload: [https://github.com/mogenson/PaperWM.spoon](https://github.com/mogenson/PaperWM.spoon)", + "stripped_doc": "\n# Usage\n\n`PaperWM:start()` will begin automatically tiling new and existing windows.\n`PaperWM:stop()` will release control over windows.\n`PaperWM::bindHotkeys()` will move / resize windows using keyboard shortcuts.\n\nHere is an example Hammerspoon config:\n\n```\nPaperWM = hs.loadSpoon(\"PaperWM\")\nPaperWM:bindHotkeys({\n -- switch to a new focused window in tiled grid\n focus_left = {{\"alt\", \"cmd\"}, \"left\"},\n focus_right = {{\"alt\", \"cmd\"}, \"right\"},\n focus_up = {{\"alt\", \"cmd\"}, \"up\"},\n focus_down = {{\"alt\", \"cmd\"}, \"down\"},\n\n -- move windows around in tiled grid\n swap_left = {{\"alt\", \"cmd\", \"shift\"}, \"left\"},\n swap_right = {{\"alt\", \"cmd\", \"shift\"}, \"right\"},\n swap_up = {{\"alt\", \"cmd\", \"shift\"}, \"up\"},\n swap_down = {{\"alt\", \"cmd\", \"shift\"}, \"down\"},\n\n -- position and resize focused window\n center_window = {{\"alt\", \"cmd\"}, \"c\"},\n full_width = {{\"alt\", \"cmd\"}, \"f\"},\n cycle_width = {{\"alt\", \"cmd\"}, \"r\"},\n reverse_cycle_width = {{\"ctrl\", \"alt\", \"cmd\"}, \"r\"},\n cycle_height = {{\"alt\", \"cmd\", \"shift\"}, \"r\"},\n reverse_cycle_height = {{\"ctrl\", \"alt\", \"cmd\", \"shift\"}, \"r\"},\n\n -- move focused window into / out of a column\n slurp_in = {{\"alt\", \"cmd\"}, \"i\"},\n barf_out = {{\"alt\", \"cmd\"}, \"o\"},\n\n --- move the focused window into / out of the tiling layer\n toggle_floating = {{\"alt\", \"cmd\", \"shift\"}, \"escape\"},\n\n -- switch to a new Mission Control space\n switch_space_1 = {{\"alt\", \"cmd\"}, \"1\"},\n switch_space_2 = {{\"alt\", \"cmd\"}, \"2\"},\n switch_space_3 = {{\"alt\", \"cmd\"}, \"3\"},\n switch_space_4 = {{\"alt\", \"cmd\"}, \"4\"},\n switch_space_5 = {{\"alt\", \"cmd\"}, \"5\"},\n switch_space_6 = {{\"alt\", \"cmd\"}, \"6\"},\n switch_space_7 = {{\"alt\", \"cmd\"}, \"7\"},\n switch_space_8 = {{\"alt\", \"cmd\"}, \"8\"},\n switch_space_9 = {{\"alt\", \"cmd\"}, \"9\"},\n\n -- move focused window to a new space and tile\n move_window_1 = {{\"alt\", \"cmd\", \"shift\"}, \"1\"},\n move_window_2 = {{\"alt\", \"cmd\", \"shift\"}, \"2\"},\n move_window_3 = {{\"alt\", \"cmd\", \"shift\"}, \"3\"},\n move_window_4 = {{\"alt\", \"cmd\", \"shift\"}, \"4\"},\n move_window_5 = {{\"alt\", \"cmd\", \"shift\"}, \"5\"},\n move_window_6 = {{\"alt\", \"cmd\", \"shift\"}, \"6\"},\n move_window_7 = {{\"alt\", \"cmd\", \"shift\"}, \"7\"},\n move_window_8 = {{\"alt\", \"cmd\", \"shift\"}, \"8\"},\n move_window_9 = {{\"alt\", \"cmd\", \"shift\"}, \"9\"}\n})\nPaperWM:start()\n```\n\nUse `PaperWM:bindHotkeys(PaperWM.default_hotkeys)` for defaults.\n\nSet `PaperWM.window_gap` to the number of pixels to space between windows and\nthe top and bottom screen edges.\n\nOverwrite `PaperWM.window_filter` to ignore specific applications. For example:\n\n```\nPaperWM.window_filter = PaperWM.window_filter:setAppFilter(\"Finder\", false)\nPaperWM:start() -- restart for new window filter to take effect\n```\n\nSet `PaperWM.window_ratios` to the ratios to cycle window widths and heights\nthrough. For example:\n\n```\nPaperWM.window_ratios = { 1/3, 1/2, 2/3 }\n```\n\n# Limitations\n\nUnder System Preferences -> Mission Control, unselect \"Automatically\nrearrange Spaces based on most recent use\" and select \"Displays have separate\nSpaces\".\n\nMacOS does not allow a window to be moved fully off-screen. Windows that would\nbe tiled off-screen are placed in a margin on the left and right edge of the\nscreen. They are still visible and clickable.\n\nIt's difficult to detect when a window is dragged from one space or screen to\nanother. Use the move_window_N commands to move windows between spaces and\nscreens.\n\nArrange screens vertically to prevent windows from bleeding into other screens.\n\n\nDownload: [https://github.com/mogenson/PaperWM.spoon](https://github.com/mogenson/PaperWM.spoon)", "submodules": [], "type": "Module" } diff --git a/Source/PaperWM.spoon/init.lua b/Source/PaperWM.spoon/init.lua index 819b9be2..dc93499f 100644 --- a/Source/PaperWM.spoon/init.lua +++ b/Source/PaperWM.spoon/init.lua @@ -14,48 +14,53 @@ --- PaperWM = hs.loadSpoon("PaperWM") --- PaperWM:bindHotkeys({ --- -- switch to a new focused window in tiled grid ---- focus_left = {{"ctrl", "alt", "cmd"}, "left"}, ---- focus_right = {{"ctrl", "alt", "cmd"}, "right"}, ---- focus_up = {{"ctrl", "alt", "cmd"}, "up"}, ---- focus_down = {{"ctrl", "alt", "cmd"}, "down"}, +--- focus_left = {{"alt", "cmd"}, "left"}, +--- focus_right = {{"alt", "cmd"}, "right"}, +--- focus_up = {{"alt", "cmd"}, "up"}, +--- focus_down = {{"alt", "cmd"}, "down"}, --- --- -- move windows around in tiled grid ---- swap_left = {{"ctrl", "alt", "cmd", "shift"}, "left"}, ---- swap_right = {{"ctrl", "alt", "cmd", "shift"}, "right"}, ---- swap_up = {{"ctrl", "alt", "cmd", "shift"}, "up"}, ---- swap_down = {{"ctrl", "alt", "cmd", "shift"}, "down"}, +--- swap_left = {{"alt", "cmd", "shift"}, "left"}, +--- swap_right = {{"alt", "cmd", "shift"}, "right"}, +--- swap_up = {{"alt", "cmd", "shift"}, "up"}, +--- swap_down = {{"alt", "cmd", "shift"}, "down"}, --- --- -- position and resize focused window ---- center_window = {{"ctrl", "alt", "cmd"}, "c"}, ---- full_width = {{"ctrl", "alt", "cmd"}, "f"}, ---- cycle_width = {{"ctrl", "alt", "cmd"}, "r"}, ---- cycle_height = {{"ctrl", "alt", "cmd", "shift"}, "r"}, +--- center_window = {{"alt", "cmd"}, "c"}, +--- full_width = {{"alt", "cmd"}, "f"}, +--- cycle_width = {{"alt", "cmd"}, "r"}, +--- reverse_cycle_width = {{"ctrl", "alt", "cmd"}, "r"}, +--- cycle_height = {{"alt", "cmd", "shift"}, "r"}, +--- reverse_cycle_height = {{"ctrl", "alt", "cmd", "shift"}, "r"}, --- --- -- move focused window into / out of a column ---- slurp_in = {{"ctrl", "alt", "cmd"}, "i"}, ---- barf_out = {{"ctrl", "alt", "cmd"}, "o"}, +--- slurp_in = {{"alt", "cmd"}, "i"}, +--- barf_out = {{"alt", "cmd"}, "o"}, +--- +--- --- move the focused window into / out of the tiling layer +--- toggle_floating = {{"alt", "cmd", "shift"}, "escape"}, --- --- -- switch to a new Mission Control space ---- switch_space_1 = {{"ctrl", "alt", "cmd"}, "1"}, ---- switch_space_2 = {{"ctrl", "alt", "cmd"}, "2"}, ---- switch_space_3 = {{"ctrl", "alt", "cmd"}, "3"}, ---- switch_space_4 = {{"ctrl", "alt", "cmd"}, "4"}, ---- switch_space_5 = {{"ctrl", "alt", "cmd"}, "5"}, ---- switch_space_6 = {{"ctrl", "alt", "cmd"}, "6"}, ---- switch_space_7 = {{"ctrl", "alt", "cmd"}, "7"}, ---- switch_space_8 = {{"ctrl", "alt", "cmd"}, "8"}, ---- switch_space_9 = {{"ctrl", "alt", "cmd"}, "9"}, +--- switch_space_1 = {{"alt", "cmd"}, "1"}, +--- switch_space_2 = {{"alt", "cmd"}, "2"}, +--- switch_space_3 = {{"alt", "cmd"}, "3"}, +--- switch_space_4 = {{"alt", "cmd"}, "4"}, +--- switch_space_5 = {{"alt", "cmd"}, "5"}, +--- switch_space_6 = {{"alt", "cmd"}, "6"}, +--- switch_space_7 = {{"alt", "cmd"}, "7"}, +--- switch_space_8 = {{"alt", "cmd"}, "8"}, +--- switch_space_9 = {{"alt", "cmd"}, "9"}, --- --- -- move focused window to a new space and tile ---- move_window_1 = {{"ctrl", "alt", "cmd", "shift"}, "1"}, ---- move_window_2 = {{"ctrl", "alt", "cmd", "shift"}, "2"}, ---- move_window_3 = {{"ctrl", "alt", "cmd", "shift"}, "3"}, ---- move_window_4 = {{"ctrl", "alt", "cmd", "shift"}, "4"}, ---- move_window_5 = {{"ctrl", "alt", "cmd", "shift"}, "5"}, ---- move_window_6 = {{"ctrl", "alt", "cmd", "shift"}, "6"}, ---- move_window_7 = {{"ctrl", "alt", "cmd", "shift"}, "7"}, ---- move_window_8 = {{"ctrl", "alt", "cmd", "shift"}, "8"}, ---- move_window_9 = {{"ctrl", "alt", "cmd", "shift"}, "9"} +--- move_window_1 = {{"alt", "cmd", "shift"}, "1"}, +--- move_window_2 = {{"alt", "cmd", "shift"}, "2"}, +--- move_window_3 = {{"alt", "cmd", "shift"}, "3"}, +--- move_window_4 = {{"alt", "cmd", "shift"}, "4"}, +--- move_window_5 = {{"alt", "cmd", "shift"}, "5"}, +--- move_window_6 = {{"alt", "cmd", "shift"}, "6"}, +--- move_window_7 = {{"alt", "cmd", "shift"}, "7"}, +--- move_window_8 = {{"alt", "cmd", "shift"}, "8"}, +--- move_window_9 = {{"alt", "cmd", "shift"}, "9"} --- }) --- PaperWM:start() --- ``` @@ -72,13 +77,20 @@ --- PaperWM:start() -- restart for new window filter to take effect --- ``` --- +--- Set `PaperWM.window_ratios` to the ratios to cycle window widths and heights +--- through. For example: +--- +--- ``` +--- PaperWM.window_ratios = { 1/3, 1/2, 2/3 } +--- ``` +--- --- # Limitations --- --- Under System Preferences -> Mission Control, unselect "Automatically --- rearrange Spaces based on most recent use" and select "Displays have separate --- Spaces". --- ---- MacOS does not allow a window to be moved fully off-screen. Windows that would-- +--- MacOS does not allow a window to be moved fully off-screen. Windows that would --- be tiled off-screen are placed in a margin on the left and right edge of the --- screen. They are still visible and clickable. --- @@ -90,20 +102,29 @@ --- --- --- Download: [https://github.com/mogenson/PaperWM.spoon](https://github.com/mogenson/PaperWM.spoon) -local WindowFilter = hs.window.filter -local Window = hs.window -local Spaces = hs.spaces -local Screen = hs.screen -local DoAfter = hs.timer.doAfter +local Mouse = hs.mouse local Rect = hs.geometry.rect +local Screen = hs.screen +local Spaces = hs.spaces +local Timer = hs.timer local Watcher = hs.uielement.watcher +local Window = hs.window +local WindowFilter = hs.window.filter +local leftClick = hs.eventtap.leftClick +local leftMouseDown = hs.eventtap.event.types.leftMouseDown +local leftMouseDragged = hs.eventtap.event.types.leftMouseDragged +local leftMouseUp = hs.eventtap.event.types.leftMouseUp +local newMouseEvent = hs.eventtap.event.newMouseEvent +local operatingSystemVersion = hs.host.operatingSystemVersion +local partial = hs.fnutils.partial +local rectMidPoint = hs.geometry.rectMidPoint local PaperWM = {} PaperWM.__index = PaperWM -- Metadata PaperWM.name = "PaperWM" -PaperWM.version = "0.4" +PaperWM.version = "0.5" PaperWM.author = "Michael Mogenson" PaperWM.homepage = "https://github.com/mogenson/PaperWM.spoon" PaperWM.license = "MIT - https://opensource.org/licenses/MIT" @@ -112,39 +133,45 @@ PaperWM.license = "MIT - https://opensource.org/licenses/MIT" --- Variable --- Default hotkeys for moving / resizing windows PaperWM.default_hotkeys = { - stop_events = { { "ctrl", "alt", "cmd", "shift" }, "q" }, - focus_left = { { "ctrl", "alt", "cmd" }, "left" }, - focus_right = { { "ctrl", "alt", "cmd" }, "right" }, - focus_up = { { "ctrl", "alt", "cmd" }, "up" }, - focus_down = { { "ctrl", "alt", "cmd" }, "down" }, - swap_left = { { "ctrl", "alt", "cmd", "shift" }, "left" }, - swap_right = { { "ctrl", "alt", "cmd", "shift" }, "right" }, - swap_up = { { "ctrl", "alt", "cmd", "shift" }, "up" }, - swap_down = { { "ctrl", "alt", "cmd", "shift" }, "down" }, - center_window = { { "ctrl", "alt", "cmd" }, "c" }, - full_width = { { "ctrl", "alt", "cmd" }, "f" }, - cycle_width = { { "ctrl", "alt", "cmd" }, "r" }, - cycle_height = { { "ctrl", "alt", "cmd", "shift" }, "r" }, - slurp_in = { { "ctrl", "alt", "cmd" }, "i" }, - barf_out = { { "ctrl", "alt", "cmd" }, "o" }, - switch_space_1 = { { "ctrl", "alt", "cmd" }, "1" }, - switch_space_2 = { { "ctrl", "alt", "cmd" }, "2" }, - switch_space_3 = { { "ctrl", "alt", "cmd" }, "3" }, - switch_space_4 = { { "ctrl", "alt", "cmd" }, "4" }, - switch_space_5 = { { "ctrl", "alt", "cmd" }, "5" }, - switch_space_6 = { { "ctrl", "alt", "cmd" }, "6" }, - switch_space_7 = { { "ctrl", "alt", "cmd" }, "7" }, - switch_space_8 = { { "ctrl", "alt", "cmd" }, "8" }, - switch_space_9 = { { "ctrl", "alt", "cmd" }, "9" }, - move_window_1 = { { "ctrl", "alt", "cmd", "shift" }, "1" }, - move_window_2 = { { "ctrl", "alt", "cmd", "shift" }, "2" }, - move_window_3 = { { "ctrl", "alt", "cmd", "shift" }, "3" }, - move_window_4 = { { "ctrl", "alt", "cmd", "shift" }, "4" }, - move_window_5 = { { "ctrl", "alt", "cmd", "shift" }, "5" }, - move_window_6 = { { "ctrl", "alt", "cmd", "shift" }, "6" }, - move_window_7 = { { "ctrl", "alt", "cmd", "shift" }, "7" }, - move_window_8 = { { "ctrl", "alt", "cmd", "shift" }, "8" }, - move_window_9 = { { "ctrl", "alt", "cmd", "shift" }, "9" } + stop_events = { { "alt", "cmd", "shift" }, "q" }, + refresh_windows = { { "alt", "cmd", "shift" }, "r" }, + toggle_floating = { { "alt", "cmd", "shift" }, "escape" }, + focus_left = { { "alt", "cmd" }, "left" }, + focus_right = { { "alt", "cmd" }, "right" }, + focus_up = { { "alt", "cmd" }, "up" }, + focus_down = { { "alt", "cmd" }, "down" }, + swap_left = { { "alt", "cmd", "shift" }, "left" }, + swap_right = { { "alt", "cmd", "shift" }, "right" }, + swap_up = { { "alt", "cmd", "shift" }, "up" }, + swap_down = { { "alt", "cmd", "shift" }, "down" }, + center_window = { { "alt", "cmd" }, "c" }, + full_width = { { "alt", "cmd" }, "f" }, + cycle_width = { { "alt", "cmd" }, "r" }, + cycle_height = { { "alt", "cmd", "shift" }, "r" }, + reverse_cycle_width = { { "ctrl", "alt", "cmd" }, "r" }, + reverse_cycle_height = { { "ctrl", "alt", "cmd", "shift" }, "r" }, + slurp_in = { { "alt", "cmd" }, "i" }, + barf_out = { { "alt", "cmd" }, "o" }, + switch_space_l = { { "alt", "cmd" }, "," }, + switch_space_r = { { "alt", "cmd" }, "." }, + switch_space_1 = { { "alt", "cmd" }, "1" }, + switch_space_2 = { { "alt", "cmd" }, "2" }, + switch_space_3 = { { "alt", "cmd" }, "3" }, + switch_space_4 = { { "alt", "cmd" }, "4" }, + switch_space_5 = { { "alt", "cmd" }, "5" }, + switch_space_6 = { { "alt", "cmd" }, "6" }, + switch_space_7 = { { "alt", "cmd" }, "7" }, + switch_space_8 = { { "alt", "cmd" }, "8" }, + switch_space_9 = { { "alt", "cmd" }, "9" }, + move_window_1 = { { "alt", "cmd", "shift" }, "1" }, + move_window_2 = { { "alt", "cmd", "shift" }, "2" }, + move_window_3 = { { "alt", "cmd", "shift" }, "3" }, + move_window_4 = { { "alt", "cmd", "shift" }, "4" }, + move_window_5 = { { "alt", "cmd", "shift" }, "5" }, + move_window_6 = { { "alt", "cmd", "shift" }, "6" }, + move_window_7 = { { "alt", "cmd", "shift" }, "7" }, + move_window_8 = { { "alt", "cmd", "shift" }, "8" }, + move_window_9 = { { "alt", "cmd", "shift" }, "9" } } --- PaperWM.window_filter @@ -162,6 +189,16 @@ PaperWM.window_filter = WindowFilter.new():setOverrideFilter({ --- Number of pixels between tiled windows PaperWM.window_gap = 8 +--- PaperWM.window_ratios +--- Variable +--- Ratios to use when cycling widths and heights, golden ratio by default +PaperWM.window_ratios = { 0.23607, 0.38195, 0.61804 } + +--- PaperWM.window_ratios +--- Variable +--- Size of the on-screen margin to place off-screen windows +PaperWM.screen_margin = 1 + --- PaperWM.logger --- Variable --- Logger object. Can be accessed to set default log level. @@ -174,17 +211,24 @@ local Direction = { UP = -2, DOWN = 2, WIDTH = 3, - HEIGHT = 4 + HEIGHT = 4, + ASCENDING = 5, + DESCENDING = 6 } +-- hs.settings key for persisting is_floating, stored as an array of window id +local IsFloatingKey = 'PaperWM_is_floating' + -- array of windows sorted from left to right local window_list = {} -- 3D array of tiles in order of [space][x][y] local index_table = {} -- dictionary of {space, x, y} with window id for keys local ui_watchers = {} -- dictionary of uielement watchers with window id for keys +local is_floating = {} -- dictionary of boolean with window id for keys --- current focused window -local focused_window = nil +-- refresh window layout on screen change +local screen_watcher = Screen.watcher.new(function() PaperWM:refreshWindows() end) +-- get the Mission Control space for the provided index local function getSpace(index) local layout = Spaces.allSpaces() for _, screen in ipairs(Screen.allScreens()) do @@ -195,6 +239,7 @@ local function getSpace(index) end end +-- return the leftmost window that's completely on the screen local function getFirstVisibleWindow(columns, screen) local x = screen:frame().x for _, windows in ipairs(columns or {}) do @@ -203,12 +248,15 @@ local function getFirstVisibleWindow(columns, screen) end end +-- get a column of windows for a space from the window_list local function getColumn(space, col) return (window_list[space] or {})[col] end +-- get a window in a row, in a column, in a space from the window_list local function getWindow(space, col, row) return (getColumn(space, col) or {})[row] end +-- get the tileable bounds for a screen local function getCanvas(screen) local screen_frame = screen:frame() return Rect(screen_frame.x + PaperWM.window_gap, @@ -217,6 +265,7 @@ local function getCanvas(screen) screen_frame.h - (2 * PaperWM.window_gap)) end +-- update the column number in window_list to be ascending from provided column up local function updateIndexTable(space, column) local columns = window_list[space] or {} for col = column, #columns do @@ -226,9 +275,21 @@ local function updateIndexTable(space, column) end end +-- save the is_floating list to settings +local function persistFloatingList() + local persisted = {} + for k, _ in pairs(is_floating) do + table.insert(persisted, k) + end + hs.settings.set(IsFloatingKey, persisted) +end + +local focused_window = nil local pending_window = nil + +-- callback for window events local function windowEventHandler(window, event, self) - self.logger.df("%s for %s", event, window) + self.logger.df("%s for [%s] id: %d", event, window, window and window:id() or -1) local space = nil --[[ When a new window is created, We first get a windowVisible event but @@ -239,10 +300,21 @@ local function windowEventHandler(window, event, self) after the window was added ]] -- + if is_floating[window:id()] then + -- this event is only meaningful for floating windows + if event == "windowDestroyed" then + is_floating[window:id()] = nil + persistFloatingList() + end + -- no other events are meaningful for floating windows + return + end + if event == "windowFocused" then if pending_window and window == pending_window then - DoAfter(Window.animationDuration, + Timer.doAfter(Window.animationDuration, function() + self.logger.vf("pending window timer for %s", window) windowEventHandler(window, event, self) end) return @@ -255,14 +327,14 @@ local function windowEventHandler(window, event, self) pending_window = nil -- tried to add window for the second time elseif not space then pending_window = window - DoAfter(Window.animationDuration, + Timer.doAfter(Window.animationDuration, function() windowEventHandler(window, event, self) end) return end elseif event == "windowNotVisible" then - self:removeWindow(window) -- destroyed windows don't have a space + space = self:removeWindow(window) elseif event == "windowFullscreened" then space = self:removeWindow(window, true) -- don't focus new window if fullscreened elseif event == "AXWindowMoved" or event == "AXWindowResized" then @@ -272,83 +344,53 @@ local function windowEventHandler(window, event, self) if space then self:tileSpace(space) end end ---- PaperWM.bindHotkeys(mapping) ---- Method ---- Binds hotkeys for PaperWM ---- ---- Parameters: ---- * mapping - A table containing hotkey modifer/key details for the following items: ---- * stop_events - Stop automatic tiling ---- * focus_left - Focus window to left of current window ---- * focus_right - Focus window to right of current window ---- * focus_up - Focus window to up of current window ---- * focus_down - Focus window to down of current window ---- * swap_left - Swap positions of window to the left and current window ---- * swap_right - Swap positions of window to the right and current window ---- * swap_up - Swap positions of window above and current window ---- * swap_down - Swap positions of window below and current window ---- * center_window - Move current window to center of screen ---- * full_width - Resize width of current window to width of screen ---- * cycle_width - Toggle through preset window widths ---- * cycle_height - Toggle through preset window heights ---- * slurp_in - Move current window into column to the left ---- * barf_out - Remove current window from column and place to the right ---- * switch_space_1 - Switch to Mission Control space 1 ---- * switch_space_2 - Switch to Mission Control space 2 ---- * switch_space_3 - Switch to Mission Control space 3 ---- * switch_space_4 - Switch to Mission Control space 4 ---- * switch_space_5 - Switch to Mission Control space 5 ---- * switch_space_6 - Switch to Mission Control space 6 ---- * switch_space_7 - Switch to Mission Control space 7 ---- * switch_space_8 - Switch to Mission Control space 8 ---- * switch_space_9 - Switch to Mission Control space 9 ---- * move_window_1 - Move current window to Mission Control space 1 ---- * move_window_2 - Move current window to Mission Control space 2 ---- * move_window_3 - Move current window to Mission Control space 3 ---- * move_window_4 - Move current window to Mission Control space 4 ---- * move_window_5 - Move current window to Mission Control space 5 ---- * move_window_6 - Move current window to Mission Control space 6 ---- * move_window_7 - Move current window to Mission Control space 7 ---- * move_window_8 - Move current window to Mission Control space 8 ---- * move_window_9 - Move current window to Mission Control space 9 -function PaperWM:bindHotkeys(mapping) - local partial = hs.fnutils.partial - local spec = { - stop_events = partial(self.stop, self), - focus_left = partial(self.focusWindow, self, Direction.LEFT), - focus_right = partial(self.focusWindow, self, Direction.RIGHT), - focus_up = partial(self.focusWindow, self, Direction.UP), - focus_down = partial(self.focusWindow, self, Direction.DOWN), - swap_left = partial(self.swapWindows, self, Direction.LEFT), - swap_right = partial(self.swapWindows, self, Direction.RIGHT), - swap_up = partial(self.swapWindows, self, Direction.UP), - swap_down = partial(self.swapWindows, self, Direction.DOWN), - center_window = partial(self.centerWindow, self), - full_width = partial(self.setWindowFullWidth, self), - cycle_width = partial(self.cycleWindowSize, self, Direction.WIDTH), - cycle_height = partial(self.cycleWindowSize, self, Direction.HEIGHT), - slurp_in = partial(self.slurpWindow, self), - barf_out = partial(self.barfWindow, self), - switch_space_1 = partial(self.switchToSpace, self, 1), - switch_space_2 = partial(self.switchToSpace, self, 2), - switch_space_3 = partial(self.switchToSpace, self, 3), - switch_space_4 = partial(self.switchToSpace, self, 4), - switch_space_5 = partial(self.switchToSpace, self, 5), - switch_space_6 = partial(self.switchToSpace, self, 6), - switch_space_7 = partial(self.switchToSpace, self, 7), - switch_space_8 = partial(self.switchToSpace, self, 8), - switch_space_9 = partial(self.switchToSpace, self, 9), - move_window_1 = partial(self.moveWindowToSpace, self, 1), - move_window_2 = partial(self.moveWindowToSpace, self, 2), - move_window_3 = partial(self.moveWindowToSpace, self, 3), - move_window_4 = partial(self.moveWindowToSpace, self, 4), - move_window_5 = partial(self.moveWindowToSpace, self, 5), - move_window_6 = partial(self.moveWindowToSpace, self, 6), - move_window_7 = partial(self.moveWindowToSpace, self, 7), - move_window_8 = partial(self.moveWindowToSpace, self, 8), - move_window_9 = partial(self.moveWindowToSpace, self, 9) - } - hs.spoons.bindHotkeysToSpec(spec, mapping) +-- make the specified space the active space +local function focusSpace(space, window) + local screen = Screen(Spaces.spaceDisplay(space)) + if not screen then + return + end + + -- focus provided window or first window on new space + window = window or getFirstVisibleWindow(window_list[space], screen) + + local do_space_focus = coroutine.wrap(function() + if window then + local function check_focus(win, n) + local focused = true + for i = 1, n do -- ensure that window focus does not change + focused = focused and (Window.focusedWindow() == win) + if not focused then return false end + coroutine.yield(false) -- not done + end + return focused + end + repeat + window:focus() + coroutine.yield(false) -- not done + until (Spaces.focusedSpace() == space) and check_focus(window, 3) + else + local point = screen:frame() + point.x = point.x + (point.w // 2) + point.y = point.y - 4 + repeat + leftClick(point) -- click on menubar + coroutine.yield(false) -- not done + until Spaces.focusedSpace() == space + end + + -- move cursor to center of screen + Mouse.absolutePosition(rectMidPoint(screen:frame())) + return true -- done + end) + + local start_time = Timer.secondsSinceEpoch() + Timer.doUntil(do_space_focus, function(timer) + if Timer.secondsSinceEpoch() - start_time > 4 then + PaperWM.logger.ef("focusSpace() timeout! space %d focused space %d", space, Spaces.focusedSpace()) + timer:stop() + end + end, Window.animationDuration) end --- PaperWM:start() @@ -371,6 +413,17 @@ function PaperWM:start() window_list = {} index_table = {} ui_watchers = {} + is_floating = {} + + -- restore saved is_floating state, filtering for valid windows + local persisted = hs.settings.get(IsFloatingKey) or {} + for _, id in ipairs(persisted) do + local window = Window.get(id) + if window and self.window_filter:isWindowAllowed(window) then + is_floating[id] = true + end + end + persistFloatingList() -- populate window list, index table, and ui_watchers self:refreshWindows() @@ -382,9 +435,12 @@ function PaperWM:start() self.window_filter:subscribe({ WindowFilter.windowFocused, WindowFilter.windowVisible, WindowFilter.windowNotVisible, WindowFilter.windowFullscreened, - WindowFilter.windowUnfullscreened + WindowFilter.windowUnfullscreened, WindowFilter.windowDestroyed }, function(window, _, event) windowEventHandler(window, event, self) end) + -- watch for external monitor plug / unplug + screen_watcher:start() + return self end @@ -401,6 +457,12 @@ function PaperWM:stop() -- stop events self.window_filter:unsubscribeAll() for _, watcher in pairs(ui_watchers) do watcher:stop() end + screen_watcher:stop() + + -- fit all windows within the bounds of the screen + for _, window in ipairs(self.window_filter:getWindows()) do + window:setFrameInScreenBounds() + end return self end @@ -464,10 +526,6 @@ end --- Parameters: --- * space - A hs.spaces space. function PaperWM:tileSpace(space) - -- MacOS doesn't allow windows to be moved off screen - -- stack windows in a visible margin on either side - local screen_margin = 40 - if not space or Spaces.spaceType(space) ~= "user" then self.logger.e("current space invalid") return @@ -481,11 +539,13 @@ function PaperWM:tileSpace(space) end -- if focused window is in space, tile from that - focused_window = focused_window or Window.focusedWindow() - local anchor_window = (focused_window and - (Spaces.windowSpaces(focused_window)[1] == space)) and - focused_window or - getFirstVisibleWindow(window_list[space], screen) + local focused_window = Window.focusedWindow() + local anchor_window = nil + if focused_window and not is_floating[focused_window:id()] and Spaces.windowSpaces(focused_window)[1] == space then + anchor_window = focused_window + else + anchor_window = getFirstVisibleWindow(window_list[space], screen) + end if not anchor_window then self.logger.e("no anchor window in space") @@ -495,18 +555,13 @@ function PaperWM:tileSpace(space) local anchor_index = index_table[anchor_window:id()] if not anchor_index then self.logger.e("anchor index not found") - if self:addWindow(anchor_window) == space then - self.logger.d("added missing window") - anchor_index = index_table[anchor_window:id()] - else - return -- bail - end + return -- bail end -- get some global coordinates local screen_frame = screen:frame() - local left_margin = screen_frame.x + screen_margin - local right_margin = screen_frame.x2 - screen_margin + local left_margin = screen_frame.x + self.screen_margin + local right_margin = screen_frame.x2 - self.screen_margin local canvas = getCanvas(screen) -- make sure anchor window is on screen @@ -573,22 +628,25 @@ function PaperWM:refreshWindows() -- get all windows across spaces local all_windows = self.window_filter:getWindows() - local refresh_needed = false + local retile_spaces = {} -- spaces that need to be retiled for _, window in ipairs(all_windows) do local index = index_table[window:id()] - if not index then + if is_floating[window:id()] then + -- ignore floating windows + elseif not index then -- add window - self:addWindow(window) - refresh_needed = true + local space = self:addWindow(window) + if space then retile_spaces[space] = true end elseif index.space ~= Spaces.windowSpaces(window)[1] then -- move to window list in new space self:removeWindow(window) - self:addWindow(window) - refresh_needed = true + local space = self:addWindow(window) + if space then retile_spaces[space] = true end end end - return refresh_needed + -- retile spaces + for space, _ in pairs(retile_spaces) do self:tileSpace(space) end end --- PaperWM:addWindow(add_window) @@ -601,6 +659,16 @@ end --- Returns: --- * The hs.spaces space for added window or nil if window not added. function PaperWM:addWindow(add_window) + -- A window with no tabs will have a tabCount of 0 + -- A new tab for a window will have tabCount equal to the total number of tabs + -- All existing tabs in a window will have their tabCount reset to 0 + -- We can't query whether an exiting hs.window is a tab or not after creation + if add_window:tabCount() > 0 then + hs.notify.show("PaperWM", "Windows with tabs are not supported!", + "See https://github.com/mogenson/PaperWM.spoon/issues/39") + return + end + -- check if window is already in window list if index_table[add_window:id()] then return end @@ -669,9 +737,12 @@ function PaperWM:removeWindow(remove_window, skip_new_window_focus) end if not skip_new_window_focus then -- find nearby window to focus - for _, direction in ipairs({ - Direction.DOWN, Direction.UP, Direction.LEFT, Direction.RIGHT - }) do if self:focusWindow(direction, remove_index) then break end end + local focused_window = Window.focusedWindow() + if focused_window and remove_window:id() == focused_window:id() then + for _, direction in ipairs({ + Direction.DOWN, Direction.UP, Direction.LEFT, Direction.RIGHT + }) do if self:focusWindow(direction, remove_index) then break end end + end end -- remove window @@ -682,6 +753,7 @@ function PaperWM:removeWindow(remove_window, skip_new_window_focus) end -- remove watcher + ui_watchers[remove_window:id()]:stop() ui_watchers[remove_window:id()] = nil -- update index table @@ -710,8 +782,11 @@ end function PaperWM:focusWindow(direction, focused_index) if not focused_index then -- get current focused window - focused_window = focused_window or Window.focusedWindow() - if not focused_window then return false end + local focused_window = Window.focusedWindow() + if not focused_window then + self.logger.d("focused window not found") + return + end -- get focused window index focused_index = index_table[focused_window:id()] @@ -719,7 +794,7 @@ function PaperWM:focusWindow(direction, focused_index) if not focused_index then self.logger.e("focused index not found") - return false + return end -- get new focused window @@ -738,12 +813,12 @@ function PaperWM:focusWindow(direction, focused_index) if not new_focused_window then self.logger.d("new focused window not found") - return false + return end -- focus new window, windowFocused event will be emited immediately new_focused_window:focus() - return true + return new_focused_window end --- PaperWM:swapWindows(direction) @@ -754,8 +829,11 @@ end --- * direction - One of Direction { LEFT, RIGHT, DOWN, UP } function PaperWM:swapWindows(direction) -- use focused window as source window - focused_window = focused_window or Window.focusedWindow() - if not focused_window then return end + local focused_window = Window.focusedWindow() + if not focused_window then + self.logger.d("focused window not found") + return + end -- get focused window index local focused_index = index_table[focused_window:id()] @@ -767,15 +845,14 @@ function PaperWM:swapWindows(direction) if direction == Direction.LEFT or direction == Direction.RIGHT then -- get target windows local target_index = { col = focused_index.col + direction } - local target_column = window_list[focused_index.space][target_index.col] + local target_column = getColumn(focused_index.space, target_index.col) if not target_column then self.logger.d("target column not found") return end -- swap place in window list - local focused_column = - window_list[focused_index.space][focused_index.col] + local focused_column = getColumn(focused_index.space, focused_index.col) window_list[focused_index.space][target_index.col] = focused_column window_list[focused_index.space][focused_index.col] = target_column @@ -865,8 +942,11 @@ end --- * None function PaperWM:centerWindow() -- get current focused window - focused_window = focused_window or Window.focusedWindow() - if not focused_window then return end + local focused_window = Window.focusedWindow() + if not focused_window then + self.logger.d("focused window not found") + return + end -- get global coordinates local focused_frame = focused_window:frame() @@ -890,8 +970,11 @@ end --- * None function PaperWM:setWindowFullWidth() -- get current focused window - focused_window = focused_window or Window.focusedWindow() - if not focused_window then return end + local focused_window = Window.focusedWindow() + if not focused_window then + self.logger.d("focused window not found") + return + end -- fullscreen window width local canvas = getCanvas(focused_window:screen()) @@ -904,31 +987,53 @@ function PaperWM:setWindowFullWidth() self:tileSpace(space) end ---- PaperWM:cycleWindowSize(direction) +--- PaperWM:cycleWindowSize(direction, cycle_direction) --- Method --- Resizes current window by cycling through width or height ratios. --- --- Parameters: --- * direction - One of Direction { WIDTH, HEIGHT } -function PaperWM:cycleWindowSize(direction) +--- * cycle_direction - One of Direction { ASCENDING, DESCENDING } +function PaperWM:cycleWindowSize(direction, cycle_direction) -- get current focused window - focused_window = focused_window or Window.focusedWindow() - if not focused_window then return end - - local function findNewSize(area_size, frame_size) - -- calculate pixel widths from ratios - local sizes = { 0.38195, 0.5, 0.61804 } - for index, size in ipairs(sizes) do - sizes[index] = size * area_size - end + local focused_window = Window.focusedWindow() + if not focused_window then + self.logger.d("focused window not found") + return + end - -- find new size - local new_size = sizes[1] - for _, size in ipairs(sizes) do - if size > frame_size + 10 then - new_size = size - break + local function findNewSize(area_size, frame_size, cycle_direction) + local sizes = {} + local new_size + if cycle_direction == Direction.ASCENDING then + for index, ratio in ipairs(self.window_ratios) do + sizes[index] = ratio * (area_size + self.window_gap) - self.window_gap + end + + -- find new size + new_size = sizes[1] + for _, size in ipairs(sizes) do + if size > frame_size + 10 then + new_size = size + break + end + end + elseif cycle_direction == Direction.DESCENDING then + for index, ratio in ipairs(self.window_ratios) do + sizes[index] = ratio * (area_size + self.window_gap) - self.window_gap + end + + -- find new size, starting from the end + new_size = sizes[#sizes] -- Start with the largest size + for i = #sizes, 1, -1 do + if sizes[i] < frame_size - 10 then + new_size = sizes[i] + break + end end + else + self.logger.e("cycle_direction must be either Direction.ASCENDING or Direction.DESCENDING") + return end return new_size @@ -938,16 +1043,17 @@ function PaperWM:cycleWindowSize(direction) local focused_frame = focused_window:frame() if direction == Direction.WIDTH then - local new_width = findNewSize(canvas.w, focused_frame.w) + local new_width = findNewSize(canvas.w, focused_frame.w, cycle_direction) focused_frame.x = focused_frame.x + ((focused_frame.w - new_width) // 2) focused_frame.w = new_width elseif direction == Direction.HEIGHT then - local new_height = findNewSize(canvas.h, focused_frame.h) - focused_frame.y = math.max(canvas.y, focused_frame.y + - ((focused_frame.h - new_height) // 2)) + local new_height = findNewSize(canvas.h, focused_frame.h, cycle_direction) + focused_frame.y = math.max(canvas.y, focused_frame.y + ((focused_frame.h - new_height) // 2)) focused_frame.h = new_height - focused_frame.y = focused_frame.y - - math.max(0, focused_frame.y2 - canvas.y2) + focused_frame.y = focused_frame.y - math.max(0, focused_frame.y2 - canvas.y2) + else + self.logger.e("direction must be either Direction.WIDTH or Direction.HEIGHT") + return end -- apply new size @@ -971,8 +1077,11 @@ function PaperWM:slurpWindow() -- add current window to bottom of column to the left -- get current focused window - focused_window = focused_window or Window.focusedWindow() - if not focused_window then return end + local focused_window = Window.focusedWindow() + if not focused_window then + self.logger.d("focused window not found") + return + end -- get window index local focused_index = index_table[focused_window:id()] @@ -982,7 +1091,7 @@ function PaperWM:slurpWindow() end -- get column to left - local column = window_list[focused_index.space][focused_index.col - 1] + local column = getColumn(focused_index.space, focused_index.col - 1) if not column then self.logger.d("column not found") return @@ -1035,8 +1144,11 @@ function PaperWM:barfWindow() -- place window into a new column to the right-- -- get current focused window - focused_window = focused_window or Window.focusedWindow() - if not focused_window then return end + local focused_window = Window.focusedWindow() + if not focused_window then + self.logger.d("focused window not found") + return + end -- get window index local focused_index = index_table[focused_window:id()] @@ -1046,7 +1158,7 @@ function PaperWM:barfWindow() end -- get column - local column = window_list[focused_index.space][focused_index.col] + local column = getColumn(focused_index.space, focused_index.col) if #column == 1 then self.logger.d("only window in column") return @@ -1091,16 +1203,52 @@ function PaperWM:switchToSpace(index) end Spaces.gotoSpace(space) + focusSpace(space) end ---- PaperWM:moveWindowToSpace(index) +--- PaperWM:incrementSpace(direction) +--- Method +--- Switch to a Mission Control space to the left or right of current space +--- +--- Parameters: +--- * direction - One of Direction { LEFT, RIGHT } +function PaperWM:incrementSpace(direction) + if (direction ~= Direction.LEFT and direction ~= Direction.RIGHT) then + self.logger.d("move is invalid, left and right only") + return + end + local curr_space_id = Spaces.focusedSpace() + local layout = Spaces.allSpaces() + local curr_space_idx = -1 + local num_spaces = 0 + for _, screen in ipairs(Screen.allScreens()) do + local screen_uuid = screen:getUUID() + if curr_space_idx < 0 then + for idx, space_id in ipairs(layout[screen_uuid]) do + if curr_space_id == space_id then + curr_space_idx = idx + num_spaces + break + end + end + end + num_spaces = num_spaces + #layout[screen_uuid] + end + + if curr_space_idx >= 0 then + local new_space_idx = ((curr_space_idx - 1 + direction) % num_spaces) + 1 + self:switchToSpace(new_space_idx) + end +end + +--- PaperWM:moveWindowToSpace(index, window) --- Method --- Moves the current window to a new Mission Control space --- --- Parameters: --- * index - The space number -function PaperWM:moveWindowToSpace(index) - focused_window = focused_window or Window.focusedWindow() +--- * window - Optional window to move +function PaperWM:moveWindowToSpace(index, window) + local focused_window = window or Window.focusedWindow() if not focused_window then self.logger.d("focused window not found") return @@ -1118,30 +1266,86 @@ function PaperWM:moveWindowToSpace(index) return end + if new_space == Spaces.windowSpaces(focused_window)[1] then + self.logger.d("window already on space") + return + end + if Spaces.spaceType(new_space) ~= "user" then self.logger.d("space is invalid") return end + local screen = Screen(Spaces.spaceDisplay(new_space)) if not screen then - self.logger.d("screen not found") + self.logger.d("no screen for space") return end - -- cache a local copy, removeWindow() will clear global focused_window - local focused_window = focused_window - local old_space = self:removeWindow(focused_window) + -- cache a copy of focused_window, don't switch focus when removing window + local old_space = self:removeWindow(focused_window, true) if not old_space then self.logger.e("can't remove focused window") return end - Spaces.moveWindowToSpace(focused_window, new_space) - self:addWindow(focused_window) - self:tileSpace(old_space) - self:tileSpace(new_space) - Spaces.gotoSpace(new_space) + -- Hopefully this ugly hack isn't around for long + -- https://github.com/Hammerspoon/hammerspoon/issues/3636 + local version = operatingSystemVersion() + if version.major * 100 + version.minor >= 1405 then + local start_point = focused_window:frame() + start_point.x = start_point.x + start_point.w // 2 + start_point.y = start_point.y + 4 + + local end_point = screen:frame() + end_point.x = end_point.x + end_point.w // 2 + end_point.y = end_point.y + self.window_gap + 4 + + local do_window_drag = coroutine.wrap(function() + -- drag window half way there + start_point.x = start_point.x + ((end_point.x - start_point.x) // 2) + start_point.y = start_point.y + ((end_point.y - start_point.y) // 2) + newMouseEvent(leftMouseDragged, start_point):post() + coroutine.yield(false) -- not done + + -- finish drag and release + newMouseEvent(leftMouseUp, end_point):post() + + -- wait until window registers as on the new space + repeat + coroutine.yield(false) -- not done + until Spaces.windowSpaces(focused_window)[1] == new_space + + -- add window and tile + self:addWindow(focused_window) + self:tileSpace(old_space) + self:tileSpace(new_space) + focusSpace(new_space, focused_window) + return true -- done + end) + + -- pick up window, switch spaces, wait for space to be ready, drag and drop window, wait for window to be ready + newMouseEvent(leftMouseDown, start_point):post() + Spaces.gotoSpace(new_space) + local start_time = Timer.secondsSinceEpoch() + Timer.doUntil(do_window_drag, function(timer) + if Timer.secondsSinceEpoch() - start_time > 4 then + self.logger.ef("moveWindowToSpace() timeout! new space %d curr space %d window space %d", new_space, + Spaces.activeSpaceOnScreen(screen:id()), Spaces.windowSpaces(focused_window)[1]) + timer:stop() + end + end, + Window.animationDuration) + else -- MacOS < 14.5 + Spaces.moveWindowToSpace(focused_window, new_space) + self:addWindow(focused_window) + self:tileSpace(old_space) + self:tileSpace(new_space) + Spaces.gotoSpace(new_space) + + focusSpace(new_space, focused_window) + end end --- PaperWM::moveWindow(window, frame) @@ -1168,9 +1372,134 @@ function PaperWM:moveWindow(window, frame) watcher:stop() window:setFrame(frame) - DoAfter(Window.animationDuration + padding, function() + Timer.doAfter(Window.animationDuration + padding, function() watcher:start({ Watcher.windowMoved, Watcher.windowResized }) end) end +--- PaperWM:toggleFloating() +--- Method +--- Add or remove focused window from the floating layer and retile the space +--- +--- Parameters: +--- * None +function PaperWM:toggleFloating() + local window = Window.focusedWindow() + if not window then + self.logger.d("focused window not found") + return + end + + local id = window:id() + if is_floating[id] then + is_floating[id] = nil + else + is_floating[id] = true + end + persistFloatingList() + + local space = nil + if is_floating[id] then + space = self:removeWindow(window, true) + else + space = self:addWindow(window) + end + if space then + self:tileSpace(space) + end +end + +-- supported window movement actions +PaperWM.actions = { + stop_events = partial(PaperWM.stop, PaperWM), + refresh_windows = partial(PaperWM.refreshWindows, PaperWM), + toggle_floating = partial(PaperWM.toggleFloating, PaperWM), + focus_left = partial(PaperWM.focusWindow, PaperWM, Direction.LEFT), + focus_right = partial(PaperWM.focusWindow, PaperWM, Direction.RIGHT), + focus_up = partial(PaperWM.focusWindow, PaperWM, Direction.UP), + focus_down = partial(PaperWM.focusWindow, PaperWM, Direction.DOWN), + swap_left = partial(PaperWM.swapWindows, PaperWM, Direction.LEFT), + swap_right = partial(PaperWM.swapWindows, PaperWM, Direction.RIGHT), + swap_up = partial(PaperWM.swapWindows, PaperWM, Direction.UP), + swap_down = partial(PaperWM.swapWindows, PaperWM, Direction.DOWN), + center_window = partial(PaperWM.centerWindow, PaperWM), + full_width = partial(PaperWM.setWindowFullWidth, PaperWM), + cycle_width = partial(PaperWM.cycleWindowSize, PaperWM, Direction.WIDTH, Direction.ASCENDING), + cycle_height = partial(PaperWM.cycleWindowSize, PaperWM, Direction.HEIGHT, Direction.ASCENDING), + reverse_cycle_width = partial(PaperWM.cycleWindowSize, PaperWM, Direction.WIDTH, Direction.DESCENDING), + reverse_cycle_height = partial(PaperWM.cycleWindowSize, PaperWM, Direction.HEIGHT, Direction.DESCENDING), + slurp_in = partial(PaperWM.slurpWindow, PaperWM), + barf_out = partial(PaperWM.barfWindow, PaperWM), + switch_space_l = partial(PaperWM.incrementSpace, PaperWM, Direction.LEFT), + switch_space_r = partial(PaperWM.incrementSpace, PaperWM, Direction.RIGHT), + switch_space_1 = partial(PaperWM.switchToSpace, PaperWM, 1), + switch_space_2 = partial(PaperWM.switchToSpace, PaperWM, 2), + switch_space_3 = partial(PaperWM.switchToSpace, PaperWM, 3), + switch_space_4 = partial(PaperWM.switchToSpace, PaperWM, 4), + switch_space_5 = partial(PaperWM.switchToSpace, PaperWM, 5), + switch_space_6 = partial(PaperWM.switchToSpace, PaperWM, 6), + switch_space_7 = partial(PaperWM.switchToSpace, PaperWM, 7), + switch_space_8 = partial(PaperWM.switchToSpace, PaperWM, 8), + switch_space_9 = partial(PaperWM.switchToSpace, PaperWM, 9), + move_window_1 = partial(PaperWM.moveWindowToSpace, PaperWM, 1), + move_window_2 = partial(PaperWM.moveWindowToSpace, PaperWM, 2), + move_window_3 = partial(PaperWM.moveWindowToSpace, PaperWM, 3), + move_window_4 = partial(PaperWM.moveWindowToSpace, PaperWM, 4), + move_window_5 = partial(PaperWM.moveWindowToSpace, PaperWM, 5), + move_window_6 = partial(PaperWM.moveWindowToSpace, PaperWM, 6), + move_window_7 = partial(PaperWM.moveWindowToSpace, PaperWM, 7), + move_window_8 = partial(PaperWM.moveWindowToSpace, PaperWM, 8), + move_window_9 = partial(PaperWM.moveWindowToSpace, PaperWM, 9) +} + +--- PaperWM.bindHotkeys(mapping) +--- Method +--- Binds hotkeys for PaperWM +--- +--- Parameters: +--- * mapping - A table containing hotkey modifer/key details for the following items: +--- * stop_events - Stop automatic tiling +--- * refresh_windows - Refresh windows from window filter list +--- * toggle_floating - Add or remove window from floating layer +--- * focus_left - Focus window to left of current window +--- * focus_right - Focus window to right of current window +--- * focus_up - Focus window to up of current window +--- * focus_down - Focus window to down of current window +--- * swap_left - Swap positions of window to the left and current window +--- * swap_right - Swap positions of window to the right and current window +--- * swap_up - Swap positions of window above and current window +--- * swap_down - Swap positions of window below and current window +--- * center_window - Move current window to center of screen +--- * full_width - Resize width of current window to width of screen +--- * cycle_width - Toggle through preset window widths +--- * cycle_height - Toggle through preset window heights +--- * reverse_cycle_width - Toggle through preset window widths +--- * reverse_cycle_height - Toggle through preset window heights +--- * slurp_in - Move current window into column to the left +--- * barf_out - Remove current window from column and place to the right +--- * switch_space_l - Switch to Mission Control space to the left +--- * switch_space_r - Switch to Mission Control space to the right +--- * switch_space_1 - Switch to Mission Control space 1 +--- * switch_space_2 - Switch to Mission Control space 2 +--- * switch_space_3 - Switch to Mission Control space 3 +--- * switch_space_4 - Switch to Mission Control space 4 +--- * switch_space_5 - Switch to Mission Control space 5 +--- * switch_space_6 - Switch to Mission Control space 6 +--- * switch_space_7 - Switch to Mission Control space 7 +--- * switch_space_8 - Switch to Mission Control space 8 +--- * switch_space_9 - Switch to Mission Control space 9 +--- * move_window_1 - Move current window to Mission Control space 1 +--- * move_window_2 - Move current window to Mission Control space 2 +--- * move_window_3 - Move current window to Mission Control space 3 +--- * move_window_4 - Move current window to Mission Control space 4 +--- * move_window_5 - Move current window to Mission Control space 5 +--- * move_window_6 - Move current window to Mission Control space 6 +--- * move_window_7 - Move current window to Mission Control space 7 +--- * move_window_8 - Move current window to Mission Control space 8 +--- * move_window_9 - Move current window to Mission Control space 9 +function PaperWM:bindHotkeys(mapping) + local spec = self.actions + hs.spoons.bindHotkeysToSpec(spec, mapping) +end + return PaperWM