my personal dotfiles managed by dotbot, zinit
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

279 lines
9.7 KiB

--- === WinWin ===
---
--- Windows manipulation
---
--- Download: [https://github.com/Hammerspoon/Spoons/raw/master/Spoons/WinWin.spoon.zip](https://github.com/Hammerspoon/Spoons/raw/master/Spoons/WinWin.spoon.zip)
local obj={}
obj.__index = obj
-- Metadata
obj.name = "WinWin"
obj.version = "1.0"
obj.author = "ashfinal <[email protected]>"
obj.homepage = "https://github.com/Hammerspoon/Spoons"
obj.license = "MIT - https://opensource.org/licenses/MIT"
-- Windows manipulation history. Only the last operation is stored.
obj.history = {}
--- WinWin.gridparts
--- Variable
--- An integer specifying how many gridparts the screen should be divided into. Defaults to 30.
obj.gridparts = 30
--- WinWin:stepMove(direction)
--- Method
--- Move the focused window in the `direction` by on step. The step scale equals to the width/height of one gridpart.
---
--- Parameters:
--- * direction - A string specifying the direction, valid strings are: `left`, `right`, `up`, `down`.
function obj:stepMove(direction)
local cwin = hs.window.focusedWindow()
if cwin then
local cscreen = cwin:screen()
local cres = cscreen:fullFrame()
local stepw = cres.w/obj.gridparts
local steph = cres.h/obj.gridparts
local wtopleft = cwin:topLeft()
if direction == "left" then
cwin:setTopLeft({x=wtopleft.x-stepw, y=wtopleft.y})
elseif direction == "right" then
cwin:setTopLeft({x=wtopleft.x+stepw, y=wtopleft.y})
elseif direction == "up" then
cwin:setTopLeft({x=wtopleft.x, y=wtopleft.y-steph})
elseif direction == "down" then
cwin:setTopLeft({x=wtopleft.x, y=wtopleft.y+steph})
end
else
hs.alert.show("No focused window!")
end
end
--- WinWin:stepResize(direction)
--- Method
--- Resize the focused window in the `direction` by on step.
---
--- Parameters:
--- * direction - A string specifying the direction, valid strings are: `left`, `right`, `up`, `down`.
function obj:stepResize(direction)
local cwin = hs.window.focusedWindow()
if cwin then
local cscreen = cwin:screen()
local cres = cscreen:fullFrame()
local stepw = cres.w/obj.gridparts
local steph = cres.h/obj.gridparts
local wsize = cwin:size()
if direction == "left" then
cwin:setSize({w=wsize.w-stepw, h=wsize.h})
elseif direction == "right" then
cwin:setSize({w=wsize.w+stepw, h=wsize.h})
elseif direction == "up" then
cwin:setSize({w=wsize.w, h=wsize.h-steph})
elseif direction == "down" then
cwin:setSize({w=wsize.w, h=wsize.h+steph})
end
else
hs.alert.show("No focused window!")
end
end
--- WinWin:stash()
--- Method
--- Stash current windows's position and size.
---
local function isInHistory(windowid)
for idx,val in ipairs(obj.history) do
if val[1] == windowid then
return idx
end
end
return false
end
function obj:stash()
local cwin = hs.window.focusedWindow()
local winid = cwin:id()
local winf = cwin:frame()
local id_idx = isInHistory(winid)
if id_idx then
-- Bring recently used window id up, so they wouldn't get removed because of exceeding capacity
if id_idx == 100 then
local tmptable = obj.history[id_idx]
table.remove(obj.history, id_idx)
table.insert(obj.history, 1, tmptable)
-- Make sure the history for each application doesn't reach the maximum (100 items)
local id_history = obj.history[1][2]
if #id_history > 100 then table.remove(id_history) end
table.insert(id_history, 1, winf)
else
local id_history = obj.history[id_idx][2]
if #id_history > 100 then table.remove(id_history) end
table.insert(id_history, 1, winf)
end
else
-- Make sure the history of window id doesn't reach the maximum (100 items).
if #obj.history > 100 then table.remove(obj.history) end
-- Stash new window id and its first history
local newtable = {winid, {winf}}
table.insert(obj.history, 1, newtable)
end
end
--- WinWin:moveAndResize(option)
--- Method
--- Move and resize the focused window.
---
--- Parameters:
--- * option - A string specifying the option, valid strings are: `halfleft`, `halfright`, `halfup`, `halfdown`, `cornerNW`, `cornerSW`, `cornerNE`, `cornerSE`, `center`, `fullscreen`, `expand`, `shrink`.
function obj:moveAndResize(option)
local cwin = hs.window.focusedWindow()
if cwin then
local cscreen = cwin:screen()
local cres = cscreen:fullFrame()
local stepw = cres.w/obj.gridparts
local steph = cres.h/obj.gridparts
local wf = cwin:frame()
if option == "halfleft" then
cwin:setFrame({x=cres.x, y=cres.y, w=cres.w/2, h=cres.h})
elseif option == "halfright" then
cwin:setFrame({x=cres.x+cres.w/2, y=cres.y, w=cres.w/2, h=cres.h})
elseif option == "halfup" then
cwin:setFrame({x=cres.x, y=cres.y, w=cres.w, h=cres.h/2})
elseif option == "halfdown" then
cwin:setFrame({x=cres.x, y=cres.y+cres.h/2, w=cres.w, h=cres.h/2})
elseif option == "cornerNW" then
cwin:setFrame({x=cres.x, y=cres.y, w=cres.w/2, h=cres.h/2})
elseif option == "cornerNE" then
cwin:setFrame({x=cres.x+cres.w/2, y=cres.y, w=cres.w/2, h=cres.h/2})
elseif option == "cornerSW" then
cwin:setFrame({x=cres.x, y=cres.y+cres.h/2, w=cres.w/2, h=cres.h/2})
elseif option == "cornerSE" then
cwin:setFrame({x=cres.x+cres.w/2, y=cres.y+cres.h/2, w=cres.w/2, h=cres.h/2})
elseif option == "fullscreen" then
cwin:setFrame({x=cres.x, y=cres.y, w=cres.w, h=cres.h})
elseif option == "center" then
cwin:centerOnScreen()
elseif option == "expand" then
cwin:setFrame({x=wf.x-stepw, y=wf.y-steph, w=wf.w+(stepw*2), h=wf.h+(steph*2)})
elseif option == "shrink" then
cwin:setFrame({x=wf.x+stepw, y=wf.y+steph, w=wf.w-(stepw*2), h=wf.h-(steph*2)})
end
else
hs.alert.show("No focused window!")
end
end
--- WinWin:moveToScreen(direction)
--- Method
--- Move the focused window between all of the screens in the `direction`.
---
--- Parameters:
--- * direction - A string specifying the direction, valid strings are: `left`, `right`, `up`, `down`, `next`.
function obj:moveToScreen(direction)
local cwin = hs.window.focusedWindow()
if cwin then
local cscreen = cwin:screen()
if direction == "up" then
cwin:moveOneScreenNorth()
elseif direction == "down" then
cwin:moveOneScreenSouth()
elseif direction == "left" then
cwin:moveOneScreenWest()
elseif direction == "right" then
cwin:moveOneScreenEast()
elseif direction == "next" then
cwin:moveToScreen(cscreen:next())
end
else
hs.alert.show("No focused window!")
end
end
--- WinWin:undo()
--- Method
--- Undo the last window manipulation. Only those "moveAndResize" manipulations can be undone.
---
function obj:undo()
local cwin = hs.window.focusedWindow()
local winid = cwin:id()
-- Has this window been stored previously?
local id_idx = isInHistory(winid)
if id_idx then
-- Bring recently used window id up, so they wouldn't get removed because of exceeding capacity
if id_idx == 100 then
local tmptable = obj.history[id_idx]
table.remove(obj.history, id_idx)
table.insert(obj.history, 1, tmptable)
local id_history = obj.history[1][2]
cwin:setFrame(id_history[1])
-- Rewind the history
local tmpframe = id_history[1]
table.remove(id_history, 1)
table.insert(id_history, tmpframe)
else
local id_history = obj.history[id_idx][2]
cwin:setFrame(id_history[1])
local tmpframe = id_history[1]
table.remove(id_history, 1)
table.insert(id_history, tmpframe)
end
end
end
--- WinWin:redo()
--- Method
--- Redo the window manipulation. Only those "moveAndResize" manipulations can be undone.
---
function obj:redo()
local cwin = hs.window.focusedWindow()
local winid = cwin:id()
-- Has this window been stored previously?
local id_idx = isInHistory(winid)
if id_idx then
-- Bring recently used window id up, so they wouldn't get removed because of exceeding capacity
if id_idx == 100 then
local tmptable = obj.history[id_idx]
table.remove(obj.history, id_idx)
table.insert(obj.history, 1, tmptable)
local id_history = obj.history[1][2]
cwin:setFrame(id_history[#id_history])
-- Play the history
local tmpframe = id_history[#id_history]
table.remove(id_history)
table.insert(id_history, 1, tmpframe)
else
local id_history = obj.history[id_idx][2]
cwin:setFrame(id_history[#id_history])
local tmpframe = id_history[#id_history]
table.remove(id_history)
table.insert(id_history, 1, tmpframe)
end
end
end
--- WinWin:centerCursor()
--- Method
--- Center the cursor on the focused window.
---
function obj:centerCursor()
local cwin = hs.window.focusedWindow()
local wf = cwin:frame()
local cscreen = cwin:screen()
local cres = cscreen:fullFrame()
if cwin then
-- Center the cursor one the focused window
hs.mouse.setAbsolutePosition({x=wf.x+wf.w/2, y=wf.y+wf.h/2})
else
-- Center the cursor on the screen
hs.mouse.setAbsolutePosition({x=cres.x+cres.w/2, y=cres.y+cres.h/2})
end
end
return obj