smart_borders for awesomewm

January 1, 2023 ยท View on GitHub

Features:

  • Full titlebar functionality without sacrificing space
  • Hot corners
  • Improved mouse controls
  • Custom client menu
  • Anti-aliased rounded corners
  • Various configuration options
  • Easy installation

How does it work:

smart_borders are essentially 4 individual titlebars that offer full titlebar functionality and controls while being minimalistic and aesthetically pleasing like borders. The regular X11 borders are additionally available and can be used to achieve a double-border-look (like 2bwm).

Installation:

Clone the repo and import the module:

  1. git clone https://github.com/intrntbrn/smart_borders ~/.config/awesome/smart_borders
  2. echo "require('smart_borders'){ show_button_tooltips = true }" >> ~/.config/awesome/rc.lua

To disable the regular X11 borders, set theme.border_width = 0 in theme.lua.

If you end up using this module, consider removing default titlebar initialization code (e.g. request::titlebars signal handlers) from rc.lua to improve startup times and performance.

Controls:

buttondefault actionhandlersignal
left clickmove clientbutton_left_clicksmart_borders::left_click
right clickopen client menubutton_right_clicksmart_borders::right_click
double left clicktoggle maximizebutton_double_clicksmart_borders::left_click
middle clickresize clientbutton_middle_clicksmart_borders::middle_click
mousewheel upincrease client heightbutton_wheel_upsmart_borders::wheel_up
mousewheel downdecrease client heightbutton_wheel_downsmart_borders::wheel_down
mouse forwardswap client with next client by indexbutton_forwardsmart_borders::forward_click
mouse backswap client with previous client by indexbutton_backsmart_borders::back_click

Customization:

namedefaultdescription
positions{ "left", "right", "top", "bottom" }border positions
button_positions{ "top" }button positions
buttons{ "floating", "minimize", "maximize", "close" }possible values: { "floating", "minimize", "maximize", "close", "sticky", "top" }
border_widthdpi(6)width of border
rounded_cornerdpi(0)border radius of rounded corners
show_button_tooltipsfalseshow a tooltip on button mouse over
show_title_tooltipsfalseshow a tooltip of client title on border mouse over (might cause issues when sloppy focus mode (focus follows mouse) is enabled)
align_horizontal"right"alignment of buttons on top and bottom positions. possible values: { "left", "center", "right" }
align_vertical"center"alignment of buttons on left and right positions. possible values: { "bottom", "center", "top" }
layout"fixed"possible values: { "fixed", "ratio" }. "fixed": every button size can be configured individually (see buttion_size). "ratio": each button is assigned a ratio (percentage) equally of the total space (see button_ratio).
button_sizedpi(40)default button size when layout is set to "fixed". each button size can be configured individually.
button_ratio0.2relative size of buttons in percent when layout is set to "ratio"
spacing_widgetnilspacing widget (between buttons)
button_maximize_sizebutton_sizesize of maximize button
button_minimize_sizebutton_sizesize of minimize button
button_floating_sizebutton_sizesize of floating button
button_sticky_sizebutton_sizesize of sticky button
button_close_sizebutton_sizesize of close button
button_top_sizebutton_sizesize of top button
color_normal"#56666f"border color
color_focus"#a1bfcf"border color when client is focused
color_hovernilborder color on border hover
color_maximizednilborder color when client is maximized
color_floatingnilborder color when client is floating
color_maximize_normal"#a9dd9d"color of maximize button
color_maximize_focus"#a9dd9d"color of maximize button when client is focused
color_maximize_hover"#c3f7b7"color of maximize button on mouse hover
color_minimize_normal"#f0eaaa"color of minimize button
color_minimize_focus"#f0eaaa"color of minimize button when client is focused
color_minimize_hover"#f6ffea"color of minimize button on mouse hover
color_floating_normal"#ddace7"color of floating button
color_floating_focus"#ddace7"color of floating button when client is focused
color_floating_hover"#f7c6ff"color of floating button on mouse hover
color_sticky_normal"#fb8965"color of sticky button
color_sticky_focus"#fb8965"color of sticky button when client is focused
color_sticky_hover"#ffa37f"color of sticky button on mouse hover
color_close_normal"#fd8489"color of close button
color_close_focus"#fd8489"color of close button when client is focused
color_close_hover"#ff9ea3"color of close button on mouse hover
color_top_normal"#7fc1ca"color of top button
color_top_focus"#7fc1ca"color of top button when client is focused
color_top_hover"#99dbe4"color of top button on mouse hover
menu_selection_symbol"โœ”"default menu selection indicator
resize_factor0.01default client resize factor
stealthfalseshow only button colors on hover (set button colors to border colors)
snappingfalseenable snapping mode (see snapping section)
snapping_max_distancenilmaximum snapping distance (mouse to client border)
snapping_center_mousefalsecenter mouse on client when snapping
custom_menu_entries{}list of custom menu entries (see custom menues section)
hot_corners{}hot_corners definitions (see hot corners section)
hot_corners_color"#00000000"color of hot_corners
hot_corners_widthdpi(1)width of hot_corners
hot_corners_heightdpi(1)height of hot_corners

Snapping:

When useless_gaps are disabled it is very easy and fast to control clients by using the mouse since you only have to move your mouse to the edge of the screen to hit the client border. However, when useless_gaps are enabled it can be frustrating to hit the border. Therefore snapping will snap to the closest client if you click on the desktop/wallpaper.

Please note that snapping currently requires awesomewm git version.

Unfortunately it is not possible to remove default mouse bindings, therefore you have to remove these on your own.

Remove the following from rc.lua:

-- {{{ Mouse bindings
-- @DOC_ROOT_BUTTONS@
awful.mouse.append_global_mousebindings({
    awful.button({ }, 3, function () mymainmenu:toggle() end),
    awful.button({ }, 4, awful.tag.viewprev),
    awful.button({ }, 5, awful.tag.viewnext),
})
-- }}}

Hot Corners:

Execute custom functions at the corners of your screen (e.g. appmenu, volume control, etc.).

hot_corners = {
    ["top_right"] = {
        left_click = function()
            require("naughty").notify({text = "left_click"})
        end,
        right_click = function()
            require("naughty").notify({text = "right_click"})
        end,
        middle_click = function()
            require("naughty").notify({text = "middle_click"})
        end,
        forward_click = function()
            require("naughty").notify({text = "forward_click"})
        end,
        back_click = function()
            require("naughty").notify({text = "back_click"})
        end,
        wheel_up = function()
            require("naughty").notify({text = "wheel_up"})
        end,
        wheel_down = function()
            require("naughty").notify({text = "wheel_down"})
        end,
        enter = function()
            require("naughty").notify({text = "enter"})
        end,
        leave = function()
            require("naughty").notify({text = "leave"})
        end
    }
}

Possible positions are "top_left", "top_right", "bottom_left", "bottom_right" or any awful.placement defintion (e.g. "centered").

Execution can also be scriped:

echo "awesome.emit_signal('hot_corners::top_right::left_click')" | awesome-client

Custom Menues:

It is possible to add your own menu entries. Entries can be added globally or only for certain classes based on regex matching.

custom_menu_entries = {
    ["Chromium"] = {
        {
            text = "toggle tabbar",
            func = function(c)
                keygrabber.stop()
                root.fake_input("key_press", "F11")
                awful.spawn.easy_async_with_shell("sleep 0.15", function()
                    c.fullscreen = false
                end)
                root.fake_input("key_release", "F11")
            end
        }
    },
    -- for every client:
    [".*"] = {
        {
            text = "toggle top overlay",
            func = function(c)
                if c.floating and c.ontop and c.sticky then
                    c.floating = false
                    c.ontop = false
                    c.sticky = false
                else
                    c.floating = true
                    c.width = 640
                    c.height = 360
                    c.ontop = true
                    c.sticky = true
                    awful.placement.top_right(c)
                end
            end
        }
    }
}

Custom Buttons:

Add a custom button to buttons (marked in this example):

buttons = { "minimize", "maximize", "close", "marked" }

Define the button:

button_marked_name = "my fancy custom button",
button_marked_size = "25",
button_marked_function = function(c) c.marked = not c.marked end,
color_marked_focus = "#ff00ff",
color_marked_normal = "#ffff00",
color_marked_hover = "#ff0000",

Disable:

To disable smart_borders for certain clients you can set the disable_smart_borders property:

ruled.client.append_rule({
    id = "dont_show_smart_borders",
    rule = { class = "myclass" },
    properties = {
        disable_smart_borders = true,
    },
})

Integration / Signals:

It is possible to use smart_borders features in other modules by emitting signals.

E.g. you might want add the rightclick menu to your tasklist:

-- @TASKLIST_BUTTON@
-- Create a tasklist widget
s.mytasklist = awful.widget.tasklist {
    screen = s,
    filter = awful.widget.tasklist.filter.currenttags,
    buttons = {
        awful.button({}, 1, function(c)
            c:activate{context = "tasklist", action = "toggle_minimization"}
        end), awful.button({}, 3, function(c)
            c:emit_signal("smart_borders::right_click")
        end)
    }
}

Example Configuration (as shown on top gif):

require("smart_borders") {
    show_button_tooltips = true,

    button_positions = {"top"},
    buttons = {"floating", "minimize", "maximize", "close"},

    layout = "fixed",
    align_horizontal = "center",
    button_size = 40,
    button_floating_size = 60,
    button_close_size = 60,
    border_width = 6,

    color_close_normal = {
        type = "linear",
        from = {0, 0},
        to = {60, 0},
        stops = {{0, "#fd8489"}, {1, "#56666f"}}
    },
    color_close_focus = {
        type = "linear",
        from = {0, 0},
        to = {60, 0},
        stops = {{0, "#fd8489"}, {1, "#a1bfcf"}}
    },
    color_close_hover = {
        type = "linear",
        from = {0, 0},
        to = {60, 0},
        stops = {{0, "#FF9EA3"}, {1, "#a1bfcf"}}
    },
    color_floating_normal = {
        type = "linear",
        from = {0, 0},
        to = {40, 0},
        stops = {{0, "#56666f"}, {1, "#ddace7"}}
    },
    color_floating_focus = {
        type = "linear",
        from = {0, 0},
        to = {40, 0},
        stops = {{0, "#a1bfcf"}, {1, "#ddace7"}}
    },
    color_floating_hover = {
        type = "linear",
        from = {0, 0},
        to = {40, 0},
        stops = {{0, "#a1bfcf"}, {1, "#F7C6FF"}}
    },

    snapping = true,
    snapping_center_mouse = true,

    -- custom control example:
    button_back = function(c)
        -- set client as master
        c:swap(awful.client.getmaster())
    end,

    -- hot_corners
    hot_corners_color = "#FD8489",
    hot_corners_width = 10,
    hot_corners_height = 10,
    hot_corners = {
        ["top_right"] = {
            left_click = function()
                -- unfullscreen the focused client with left click
                local c = client.focus
                if c and c.fullscreen then
                    c.fullscreen = false
                end
            end,
            middle_click = function()
                awesome.restart()
            end,
        }
    },
}