Cascade all open app windows

4

1

I'm looking for a way to arrange all open app windows on a given screen. I would like to "cascade" them so that part of each window is visible, and when any of them are clicked I can easily click back to another window, meaning that no one window is ever lost behind another.

Here's the idea:

enter image description here

This involves both repositioning each window on the X and Y axis of the screen and resizing each window. to be the same size.

  • Is there any way to do this natively within the Mac OS?
  • Are there any apps that can achieve this functionality?

Update:

Tried the Hammerspoon solution and got the error below. It did move all my windows from the external monitor to the MacBook, and it didn't really cascade.

enter image description here

ThomasReggi

Posted 2016-02-19T14:17:28.493

Reputation: 443

Answers

4

I don't know of any way to do this natively, aside from Mission Control.

Hammerspoon is a low-level automation tool which is great for this type of thing, if a little clumsy. It exposes many OS-level operations to a Lua scripting engine, including positioning of windows, among very many other things.

To implement this functionality in Hammerspoon, you could do something like:

function cascadeWindows()
    local windows = hs.window.allWindows()
    local screen = windows[1]:screen():frame()

    local xMargin, yMargin = screen.w/5, screen.h/5  -- This is equal to the gap between the edge of the topleft window and the edge of the screen.
    local layout = {}
    for i, win in ipairs(windows) do
        local winPos = {
            win:application(),
            win:title(),
            win:screen(),
            nil, hs.geometry.rect(
            (i-1)*(xMargin/(#windows-1)), -- x
            (i-1)*(yMargin/(#windows-1)), -- y, you might end up having to add some number here
            screen.w - xMargin,           -- w
            screen.h - yMargin            -- h
            ), nil
        }
        layout[#layout+1] = winPos
    end
    hs.layout.apply(layout)
end
hs.hotkey.bind({'cmd','alt','ctrl'}, 'space', cascadeWindows)

This code has been loosely tested, but should work as a starting point. To install, first install Hammerspoon, then put this code into your ~/.hammerspoon/init.lua file. You can find more information on exactly what is happening up there in the documentation for hs.layout.apply, and more generally in the Hammerspoon documentary.

If you don't know Lua and don't want to learn Lua (It's quick and easy!), or don't want to get involved in learning a new tool as deep as Hammerspoon, you can just follow the above instructions changing the key binding to whatever you want.

Jesse Paroz

Posted 2016-02-19T14:17:28.493

Reputation: 41

Thanks for this, it's a great start. Just tried this out, put error message above. – ThomasReggi – 2016-03-04T19:04:35.407

2

From the Window menu, hold down Option and click Arrange in Front. This will do exactly what you want, cascading all the windows as your diagram shows.

It sorts them in alphabetical order.

Michael McQuade

Posted 2016-02-19T14:17:28.493

Reputation: 121

Your answer could use improvement by explaining why/how your solution fixes/addresses the OPs question.

– I say Reinstate Monica – 2017-04-11T00:31:19.983

1

I was just looking for the same thing myself and came up with my own Hammerspoon solution:

hs.hotkey.bind({'cmd','alt','ctrl'}, ',', function()
    local windows = hs.window.orderedWindows()
    local screen = windows[1]:screen():frame()
    local nOfSpaces = #windows > 1 and #windows - 1 or 1

    local xMargin = screen.w / 10 -- unused horizontal margin
    local yMargin = 20            -- unused vertical margin
    local spacing = 40            -- the visible margin for each window

    for i, win in ipairs(windows) do
        local offset = (i - 1) * spacing
        local rect = {
            x = xMargin + offset,
            y = screen.y + yMargin + offset,
            w = screen.w - (2 * xMargin) - (nOfSpaces * spacing),
            h = screen.h - (2 * yMargin) - (nOfSpaces * spacing),
        }
        win:setFrame(rect)
    end
end)

I'm quite satisfied with it so far. You may also be interested in the rest of my Hammerspoon config: https://github.com/dbmrq/dotfiles/tree/master/home/.hammerspoon

dbmrq

Posted 2016-02-19T14:17:28.493

Reputation: 111