Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 49 additions & 13 deletions Source/RoundedCorners.spoon/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@ obj.__index = obj

-- Metadata
obj.name = "RoundedCorners"
obj.version = "1.0"
obj.version = "1.1"
obj.author = "Chris Jones <cmsj@tenshu.net>"
obj.homepage = "https://github.com/Hammerspoon/Spoons"
obj.license = "MIT - https://opensource.org/licenses/MIT"

obj.corners = {}
obj.topCorners = {}
obj.screenWatcher = nil
obj.spacesWatcher = nil

--- RoundedCorners.allScreens
--- Variable
Expand All @@ -23,8 +25,15 @@ obj.allScreens = true

--- RoundedCorners.radius
--- Variable
--- Controls the radius of the rounded corners, in points. Defaults to 6
obj.radius = 6
--- Controls the radius of the rounded corners, in points. Defaults to 12
obj.radius = 12


--- RoundedCorners.fullscreenOffset
--- Variable
--- Controls the offset of the top corners when in fullscreen, in points. Defaults to 0
--- Place just under the MenuBar in Tahoe: 33
obj.fullscreenOffset = 0

--- RoundedCorners.level
--- Variable
Expand All @@ -40,6 +49,7 @@ obj.spoonPath = script_path()

function obj:init()
self.screenWatcher = hs.screen.watcher.new(function() self:screensChanged() end)
self.spacesWatcher = hs.spaces.watcher.new(function() self:spacesChanged() end)
end

--- RoundedCorners:start()
Expand All @@ -56,6 +66,7 @@ end
--- * This will draw the rounded screen corners and start watching for changes in screen sizes/layouts, reacting accordingly
function obj:start()
self.screenWatcher:start()
self.spacesWatcher:start()
self:render()
return self
end
Expand All @@ -74,12 +85,26 @@ end
--- * This will remove all rounded screen corners and stop watching for changes in screen sizes/layouts
function obj:stop()
self.screenWatcher:stop()
self.spacesWatcher:stop()
self:deleteAllCorners()
return self
end

-- Delete only the dynamic top corners
function obj:deleteTopCorners()
hs.fnutils.each(self.topCorners, function(corner) corner:delete() end)
self.topCorners = {}
end

-- React to the spaces having changed
function obj:spacesChanged()
self:deleteTopCorners()
self:render(true)
end

-- Delete all the corners
function obj:deleteAllCorners()
self:deleteTopCorners()
hs.fnutils.each(self.corners, function(corner) corner:delete() end)
self.corners = {}
end
Expand All @@ -99,25 +124,36 @@ function obj:getScreens()
end
end

-- Draw a single corner
function obj:draw(data, radius, offset, behavior)
return hs.canvas.new({x=data.frame.x,y=data.frame.y+offset,w=radius,h=radius}):appendElements(
{ action="build", type="rectangle", },
{ action="clip", type="circle", center=data.center, radius=radius, reversePath=true, },
{ action="fill", type="rectangle", frame={x=0, y=0, w=radius, h=radius, }, fillColor={ alpha=1, }},
{ type="resetClip", }
):behavior({hs.canvas.windowBehaviors.stationary, behavior}):level(self.level):show()
end

-- Draw the corners
function obj:render()
local screens = self:getScreens()
function obj:render(topOnly)
local radius = self.radius
hs.fnutils.each(screens, function(screen)
local mission = hs.spaces.focusedSpace() == 1
hs.fnutils.each(self:getScreens(), function(screen)
local windows = hs.window.filter.new():setScreens(screen:id()):getWindows()
local offset = not mission and screen:id() == 1 and #windows > 0 and windows[1]:isFullscreen() and self.fullscreenOffset or 0
local screenFrame = screen:fullFrame()
local cornerData = {
{ frame={x=screenFrame.x, y=screenFrame.y}, center={x=radius,y=radius} },
{ frame={x=screenFrame.x + screenFrame.w - radius, y=screenFrame.y}, center={x=0,y=radius} },
{ frame={x=screenFrame.x, y=screenFrame.y + screenFrame.h - radius}, center={x=radius,y=0} },
{ frame={x=screenFrame.x + screenFrame.w - radius, y=screenFrame.y + screenFrame.h - radius}, center={x=0,y=0} },
}
for _,data in pairs(cornerData) do
self.corners[#self.corners+1] = hs.canvas.new({x=data.frame.x,y=data.frame.y,w=radius,h=radius}):appendElements(
{ action="build", type="rectangle", },
{ action="clip", type="circle", center=data.center, radius=radius, reversePath=true, },
{ action="fill", type="rectangle", frame={x=0, y=0, w=radius, h=radius, }, fillColor={ alpha=1, }},
{ type="resetClip", }
):behavior(hs.canvas.windowBehaviors.canJoinAllSpaces):level(self.level):show()
for i, data in pairs(cornerData) do
if (offset > 0 and i < 3) then
self.topCorners[#self.topCorners+1] = obj:draw(data, radius, offset)
elseif (not topOnly) then
self.corners[#self.corners+1] = obj:draw(data, radius, 0, hs.canvas.windowBehaviors.canJoinAllSpaces)
end
end
end)
end
Expand Down