the stack layout in action in my AwesomeWM environment
Guide ToAwesomeWM Stack Layout
September 1st, 2021

The bling library is great, but I find a lot of it is unnecessary for my uses and while I love the mstab layout, I don't love the tab bar it provides. Instead let's make our own layout without the tab bar or bloat!

So we will be creating the same thing, a layout that divides the screen in half and stacks extra windows on the right or left of the master, which we can access easily with our alt-tab solution(future post) or taskbar.

Guide

As always, start by importing the necessary functionality.

local math = math
local screen = screen

local stack = {}

Layouts in AwesomeWM are specific to the tag they are being called from, so we will start by getting variables assigned to our present tag

local function arrange(p, dir)
    local t = p.tag or screen[p.screen].selected_tag
    local wa = p.workarea
    local cls = p.clients

    if #cls == 0 then
        return
    end

Unfortunately, awesomewm uses the master-slave naming convention I personally find distasteful, but layouts are extremely fickle and so we should use the naming conventions the other layouts use so we don't have to see that hideous default wallpaper.

We must now assign the variables relating to the two sides of the screen, one for the master and one for the slave window that the stacking will occur on top of.

local mstrWidthFact = t.master_width_factor

    local mstrWidth = math.floor(wa.width * mstrWidthFact)
    local mstrHeight = math.floor(wa.height)

    local slavesNumber = #cls - 1
    local slavesWidth = math.floor(wa.width - mstrWidth)
    local slavesHeight = math.floor(wa.height)

if slavesNumber == 0 then
        mstrWidth = wa.width
    end

Now we have to assign the positions of the two, which can change depending on if we use stack for stacking on the right or stackLeft for the converse.

-- Places master
    local c, g = cls[1], {}

    g.height = math.max(mstrHeight, 1)
    g.width = math.max(mstrWidth, 1)

    g.y = wa.y
    g.x = (dir == "right") and (wa.x) or (wa.x + slavesWidth)


    if slavesNumber == 0 then
        g.x = wa.x
    end

    p.geometries[c] = g

Now, we have to limit the screen to showing the two windows and insuring the stacking is occuring on the right side.


    if #cls == 1 then
        return
    end

    -- Places slaves
    for i = 2, #cls do
        local c, g = cls[i], {}

        g.height = math.max(slavesHeight, 1)
        g.width = math.max(slavesWidth, 1)

        g.y = wa.y
        g.x = (dir == "right") and (wa.x + mstrWidth) or (wa.x)

        p.geometries[c] = g
    end
end

And the final bit we will have to configure is the name of each variant of the layout, with the return statement at the end so we can require it in whatever file we append layouts to the default list. Notice in the arrange method we assing the actual side for the stacking to occur!

stack.name = "stack"
function stack.arrange(p)
    return arrange(p, "right")
end

stack.left = {}
stack.left.name = "stackLeft"
function stack.left.arrange(p)
    return arrange(p, "left")
end

stack.name = "stack"
function stack.arrange(p)
    return arrange(p, "right")
end
-- ########################################################################
-- ########################################################################
-- ########################################################################
stack.left = {}
stack.left.name = "stackLeft"
function stack.left.arrange(p)
    return arrange(p, "left")
end

Conclusion

With the comment bars added back in, your final source code should look something like this. You will need to add this file to where ever you append to the default layouts in your configuration. I keep custom layouts in their own directory, but you are free to do as you like of course.

local math = math
local screen = screen

local stack = {}
-- ########################################################################
-- ########################################################################
-- ########################################################################
local function arrange(p, dir)
    local t = p.tag or screen[p.screen].selected_tag
    local wa = p.workarea
    local cls = p.clients

    if #cls == 0 then
        return
    end
    -- ########################################################################
    -- ########################################################################
    -- ########################################################################
    local mstrWidthFact = t.master_width_factor

    local mstrWidth = math.floor(wa.width * mstrWidthFact)
    local mstrHeight = math.floor(wa.height)

    local slavesNumber = #cls - 1
    local slavesWidth = math.floor(wa.width - mstrWidth)
    local slavesHeight = math.floor(wa.height)
    -- ########################################################################
    -- ########################################################################
    -- ########################################################################
    if slavesNumber == 0 then
        mstrWidth = wa.width
    end
    -- ########################################################################
    -- ########################################################################
    -- ########################################################################
    -- Places master
    local c, g = cls[1], {}

    g.height = math.max(mstrHeight, 1)
    g.width = math.max(mstrWidth, 1)

    g.y = wa.y
    g.x = (dir == "right") and (wa.x) or (wa.x + slavesWidth)
    -- ########################################################################
    -- ########################################################################
    -- ########################################################################
    if slavesNumber == 0 then
        g.x = wa.x
    end

    p.geometries[c] = g

    if #cls == 1 then
        return
    end

    -- Places slaves
    for i = 2, #cls do
        local c, g = cls[i], {}

        g.height = math.max(slavesHeight, 1)
        g.width = math.max(slavesWidth, 1)

        g.y = wa.y
        g.x = (dir == "right") and (wa.x + mstrWidth) or (wa.x)

        p.geometries[c] = g
    end
end
-- ########################################################################
-- ########################################################################
-- ########################################################################
stack.name = "stack"
function stack.arrange(p)
    return arrange(p, "right")
end
-- ########################################################################
-- ########################################################################
-- ########################################################################
stack.left = {}
stack.left.name = "stackLeft"
function stack.left.arrange(p)
    return arrange(p, "left")
end
-- ########################################################################
-- ########################################################################
-- ########################################################################
return stack