Prevent content shrinking in awesomewm

1.3k views Asked by At

I'm customizing my awesome-wm taskbar and what I'm trying to achieve is :

  • have a tasklist with fixed items width, which can shrink if there is not enough space
  • have a button right after the tasklist to open a program launcher on click (rofi), this button should never shrink

For debugging purpose, the button was replace by the red textbox

This is how it looks when there is only few items, exactly what I want : enter image description here enter image description here

When there is a lot of clients, the tasklist items shrink as expected, but the textfield too : enter image description here enter image description here

here is my complete rc.lua, which is mainly the same as the default one :

-- If LuaRocks is installed, make sure that packages installed through it are
-- found (e.g. lgi). If LuaRocks is not installed, do nothing.
pcall(require, "luarocks.loader")

-- Standard awesome library
local gears = require("gears")
local awful = require("awful")
require("awful.autofocus")
-- Widget and layout library
local wibox = require("wibox")
-- Theme handling library
local beautiful = require("beautiful")
-- Notification library
local naughty = require("naughty")
local menubar = require("menubar")
local hotkeys_popup = require("awful.hotkeys_popup")
-- Enable hotkeys help widget for VIM and other apps
-- when client with a matching name is opened:
require("awful.hotkeys_popup.keys")

-- {{{ Error handling
-- Check if awesome encountered an error during startup and fell back to
-- another config (This code will only ever execute for the fallback config)
if awesome.startup_errors then
    naughty.notify({ preset = naughty.config.presets.critical,
                     title = "Oops, there were errors during startup!",
                     text = awesome.startup_errors })
end

-- Handle runtime errors after startup
do
    local in_error = false
    awesome.connect_signal("debug::error", function (err)
        -- Make sure we don't go into an endless error loop
        if in_error then return end
        in_error = true

        naughty.notify({ preset = naughty.config.presets.critical,
                         title = "Oops, an error happened!",
                         text = tostring(err) })
        in_error = false
    end)
end
-- }}}

-- {{{ Variable definitions
-- Themes define colours, icons, font and wallpapers.
beautiful.init(gears.filesystem.get_themes_dir() .. "default/theme.lua")

-- This is used later as the default terminal and editor to run.
terminal = "xterm"
editor = os.getenv("EDITOR") or "nano"
editor_cmd = terminal .. " -e " .. editor

-- Default modkey.
-- Usually, Mod4 is the key with a logo between Control and Alt.
-- If you do not like this or do not have such a key,
-- I suggest you to remap Mod4 to another key using xmodmap or other tools.
-- However, you can use another modifier like Mod1, but it may interact with others.
modkey = "Mod4"

-- Table of layouts to cover with awful.layout.inc, order matters.
awful.layout.layouts = {
    awful.layout.suit.floating,
    awful.layout.suit.tile,
    awful.layout.suit.tile.left,
    awful.layout.suit.tile.bottom,
    awful.layout.suit.tile.top,
    awful.layout.suit.fair,
    awful.layout.suit.fair.horizontal,
    awful.layout.suit.spiral,
    awful.layout.suit.spiral.dwindle,
    awful.layout.suit.max,
    awful.layout.suit.max.fullscreen,
    awful.layout.suit.magnifier,
    awful.layout.suit.corner.nw,
    -- awful.layout.suit.corner.ne,
    -- awful.layout.suit.corner.sw,
    -- awful.layout.suit.corner.se,
}
-- }}}

-- {{{ Menu
-- Create a launcher widget and a main menu
myawesomemenu = {
   { "hotkeys", function() hotkeys_popup.show_help(nil, awful.screen.focused()) end },
   { "manual", terminal .. " -e man awesome" },
   { "edit config", editor_cmd .. " " .. awesome.conffile },
   { "restart", awesome.restart },
   { "quit", function() awesome.quit() end },
}

mymainmenu = awful.menu({ items = { { "awesome", myawesomemenu, beautiful.awesome_icon },
                                    { "open terminal", terminal }
                                  }
                        })

mylauncher = awful.widget.launcher({ image = beautiful.awesome_icon,
                                     menu = mymainmenu })

-- Menubar configuration
menubar.utils.terminal = terminal -- Set the terminal for applications that require it
-- }}}

-- Keyboard map indicator and switcher
mykeyboardlayout = awful.widget.keyboardlayout()

-- {{{ Wibar
-- Create a textclock widget
mytextclock = wibox.widget.textclock()

-- Create a wibox for each screen and add it
local taglist_buttons = gears.table.join(
                    awful.button({ }, 1, function(t) t:view_only() end),
                    awful.button({ modkey }, 1, function(t)
                                              if client.focus then
                                                  client.focus:move_to_tag(t)
                                              end
                                          end),
                    awful.button({ }, 3, awful.tag.viewtoggle),
                    awful.button({ modkey }, 3, function(t)
                                              if client.focus then
                                                  client.focus:toggle_tag(t)
                                              end
                                          end),
                    awful.button({ }, 4, function(t) awful.tag.viewnext(t.screen) end),
                    awful.button({ }, 5, function(t) awful.tag.viewprev(t.screen) end)
                )

local tasklist_buttons = gears.table.join(
                     awful.button({ }, 1, function (c)
                                              if c == client.focus then
                                                  c.minimized = true
                                              else
                                                  c:emit_signal(
                                                      "request::activate",
                                                      "tasklist",
                                                      {raise = true}
                                                  )
                                              end
                                          end),
                     awful.button({ }, 3, function()
                                              awful.menu.client_list({ theme = { width = 250 } })
                                          end),
                     awful.button({ }, 4, function ()
                                              awful.client.focus.byidx(1)
                                          end),
                     awful.button({ }, 5, function ()
                                              awful.client.focus.byidx(-1)
                                          end))


local tasklist_layout = {
  layout = wibox.layout.flex.horizontal,
  max_widget_size = 300
}

local function tasklist_create_function(self, c, index, objects) --luacheck: no unused
  -- adding close action on button
  local cbm = self:get_children_by_id('close_button_role')[1]
  cbm:buttons(
    awful.button({}, 1, nil,
      function()
        c:kill()
      end
    )
  )
end

local tasklist_template = {
  -- bgb
  id = "background_role",
  widget = wibox.container.background,
  create_callback = tasklist_create_function,
  {
    -- l
    layout = wibox.layout.align.horizontal,
    {
      -- ibm
      id = "icon_margin_role",
      widget = wibox.container.margin,
      left = 12,
      right = 12,
      {
        -- ib
        id = 'icon_role',
        widget = wibox.widget.imagebox,
      }
    },
    {
      -- tbm
      id = "text_margin_role",
      widget = wibox.container.margin,
      left = 4,
      right = 4,
      {
        -- tb
        id = "text_role",
        widget = wibox.widget.textbox,
        ellipsize = 'end'
      }
    },
    {
      -- cbm
      id = "close_button_role",
      widget = wibox.container.margin,
      left = 4,
      right = 8,
      {
        widget = wibox.widget.textbox,
        text = "X"
      }
    }
  }
}

local function set_wallpaper(s)
    -- Wallpaper
    if beautiful.wallpaper then
        local wallpaper = beautiful.wallpaper
        -- If wallpaper is a function, call it with the screen
        if type(wallpaper) == "function" then
            wallpaper = wallpaper(s)
        end
        gears.wallpaper.maximized(wallpaper, s, true)
    end
end

-- Re-set wallpaper when a screen's geometry changes (e.g. different resolution)
screen.connect_signal("property::geometry", set_wallpaper)

awful.screen.connect_for_each_screen(function(s)
    -- Wallpaper
    set_wallpaper(s)

    -- Each screen has its own tag table.
    awful.tag({ "1", "2", "3", "4", "5", "6", "7", "8", "9" }, s, awful.layout.layouts[1])

    -- Create a promptbox for each screen
    s.mypromptbox = awful.widget.prompt()
    -- Create an imagebox widget which will contain an icon indicating which layout we're using.
    -- We need one layoutbox per screen.
    s.mylayoutbox = awful.widget.layoutbox(s)
    s.mylayoutbox:buttons(gears.table.join(
                           awful.button({ }, 1, function () awful.layout.inc( 1) end),
                           awful.button({ }, 3, function () awful.layout.inc(-1) end),
                           awful.button({ }, 4, function () awful.layout.inc( 1) end),
                           awful.button({ }, 5, function () awful.layout.inc(-1) end)))
    -- Create a taglist widget
    s.mytaglist = awful.widget.taglist {
        screen  = s,
        filter  = awful.widget.taglist.filter.all,
        buttons = taglist_buttons
    }

    -- Create a tasklist widget
    s.mytasklist = awful.widget.tasklist {
        screen  = s,
        filter  = awful.widget.tasklist.filter.currenttags,
        buttons = tasklist_buttons,
        layout = tasklist_layout,
        widget_template = tasklist_template
    }

    s.add_button = {
      widget = wibox.container.background,
      bg = "#FF0000",
      fg = "#FFFFFF",
      {
        widget = wibox.widget.textbox,
        text = "I don't want to be wrapped !"
      }
    }

    -- Create the wibox
    s.mywibox = awful.wibar({ position = "top", screen = s })

    -- Add widgets to the wibox
    s.mywibox:setup {
        layout = wibox.layout.align.horizontal,
        { -- Left widgets
            layout = wibox.layout.fixed.horizontal,
            mylauncher,
            s.mytaglist,
            s.mypromptbox,
        },
        { -- Middle widget
          layout = wibox.layout.fixed.horizontal,
          s.mytasklist,
          s.add_button,
        },
        { -- Right widgets
            layout = wibox.layout.fixed.horizontal,
            mykeyboardlayout,
            wibox.widget.systray(),
            mytextclock,
            s.mylayoutbox,
        },
    }
end)
-- }}}

-- {{{ Mouse bindings
root.buttons(gears.table.join(
    awful.button({ }, 3, function () mymainmenu:toggle() end),
    awful.button({ }, 4, awful.tag.viewnext),
    awful.button({ }, 5, awful.tag.viewprev)
))
-- }}}

-- {{{ Key bindings
globalkeys = gears.table.join(
    awful.key({ modkey,           }, "s",      hotkeys_popup.show_help,
              {description="show help", group="awesome"}),
    awful.key({ modkey,           }, "Left",   awful.tag.viewprev,
              {description = "view previous", group = "tag"}),
    awful.key({ modkey,           }, "Right",  awful.tag.viewnext,
              {description = "view next", group = "tag"}),
    awful.key({ modkey,           }, "Escape", awful.tag.history.restore,
              {description = "go back", group = "tag"}),

    awful.key({ modkey,           }, "j",
        function ()
            awful.client.focus.byidx( 1)
        end,
        {description = "focus next by index", group = "client"}
    ),
    awful.key({ modkey,           }, "k",
        function ()
            awful.client.focus.byidx(-1)
        end,
        {description = "focus previous by index", group = "client"}
    ),
    awful.key({ modkey,           }, "w", function () mymainmenu:show() end,
              {description = "show main menu", group = "awesome"}),

    -- Layout manipulation
    awful.key({ modkey, "Shift"   }, "j", function () awful.client.swap.byidx(  1)    end,
              {description = "swap with next client by index", group = "client"}),
    awful.key({ modkey, "Shift"   }, "k", function () awful.client.swap.byidx( -1)    end,
              {description = "swap with previous client by index", group = "client"}),
    awful.key({ modkey, "Control" }, "j", function () awful.screen.focus_relative( 1) end,
              {description = "focus the next screen", group = "screen"}),
    awful.key({ modkey, "Control" }, "k", function () awful.screen.focus_relative(-1) end,
              {description = "focus the previous screen", group = "screen"}),
    awful.key({ modkey,           }, "u", awful.client.urgent.jumpto,
              {description = "jump to urgent client", group = "client"}),
    awful.key({ modkey,           }, "Tab",
        function ()
            awful.client.focus.history.previous()
            if client.focus then
                client.focus:raise()
            end
        end,
        {description = "go back", group = "client"}),

    -- Standard program
    awful.key({ modkey,           }, "Return", function () awful.spawn(terminal) end,
              {description = "open a terminal", group = "launcher"}),
    awful.key({ modkey, "Control" }, "r", awesome.restart,
              {description = "reload awesome", group = "awesome"}),
    awful.key({ modkey, "Shift"   }, "q", awesome.quit,
              {description = "quit awesome", group = "awesome"}),

    awful.key({ modkey,           }, "l",     function () awful.tag.incmwfact( 0.05)          end,
              {description = "increase master width factor", group = "layout"}),
    awful.key({ modkey,           }, "h",     function () awful.tag.incmwfact(-0.05)          end,
              {description = "decrease master width factor", group = "layout"}),
    awful.key({ modkey, "Shift"   }, "h",     function () awful.tag.incnmaster( 1, nil, true) end,
              {description = "increase the number of master clients", group = "layout"}),
    awful.key({ modkey, "Shift"   }, "l",     function () awful.tag.incnmaster(-1, nil, true) end,
              {description = "decrease the number of master clients", group = "layout"}),
    awful.key({ modkey, "Control" }, "h",     function () awful.tag.incncol( 1, nil, true)    end,
              {description = "increase the number of columns", group = "layout"}),
    awful.key({ modkey, "Control" }, "l",     function () awful.tag.incncol(-1, nil, true)    end,
              {description = "decrease the number of columns", group = "layout"}),
    awful.key({ modkey,           }, "space", function () awful.layout.inc( 1)                end,
              {description = "select next", group = "layout"}),
    awful.key({ modkey, "Shift"   }, "space", function () awful.layout.inc(-1)                end,
              {description = "select previous", group = "layout"}),

    awful.key({ modkey, "Control" }, "n",
              function ()
                  local c = awful.client.restore()
                  -- Focus restored client
                  if c then
                    c:emit_signal(
                        "request::activate", "key.unminimize", {raise = true}
                    )
                  end
              end,
              {description = "restore minimized", group = "client"}),

    -- Prompt
    awful.key({ modkey },            "r",     function () awful.screen.focused().mypromptbox:run() end,
              {description = "run prompt", group = "launcher"}),

    awful.key({ modkey }, "x",
              function ()
                  awful.prompt.run {
                    prompt       = "Run Lua code: ",
                    textbox      = awful.screen.focused().mypromptbox.widget,
                    exe_callback = awful.util.eval,
                    history_path = awful.util.get_cache_dir() .. "/history_eval"
                  }
              end,
              {description = "lua execute prompt", group = "awesome"}),
    -- Menubar
    awful.key({ modkey }, "p", function() menubar.show() end,
              {description = "show the menubar", group = "launcher"})
)

clientkeys = gears.table.join(
    awful.key({ modkey,           }, "f",
        function (c)
            c.fullscreen = not c.fullscreen
            c:raise()
        end,
        {description = "toggle fullscreen", group = "client"}),
    awful.key({ modkey, "Shift"   }, "c",      function (c) c:kill()                         end,
              {description = "close", group = "client"}),
    awful.key({ modkey, "Control" }, "space",  awful.client.floating.toggle                     ,
              {description = "toggle floating", group = "client"}),
    awful.key({ modkey, "Control" }, "Return", function (c) c:swap(awful.client.getmaster()) end,
              {description = "move to master", group = "client"}),
    awful.key({ modkey,           }, "o",      function (c) c:move_to_screen()               end,
              {description = "move to screen", group = "client"}),
    awful.key({ modkey,           }, "t",      function (c) c.ontop = not c.ontop            end,
              {description = "toggle keep on top", group = "client"}),
    awful.key({ modkey,           }, "n",
        function (c)
            -- The client currently has the input focus, so it cannot be
            -- minimized, since minimized clients can't have the focus.
            c.minimized = true
        end ,
        {description = "minimize", group = "client"}),
    awful.key({ modkey,           }, "m",
        function (c)
            c.maximized = not c.maximized
            c:raise()
        end ,
        {description = "(un)maximize", group = "client"}),
    awful.key({ modkey, "Control" }, "m",
        function (c)
            c.maximized_vertical = not c.maximized_vertical
            c:raise()
        end ,
        {description = "(un)maximize vertically", group = "client"}),
    awful.key({ modkey, "Shift"   }, "m",
        function (c)
            c.maximized_horizontal = not c.maximized_horizontal
            c:raise()
        end ,
        {description = "(un)maximize horizontally", group = "client"})
)

-- Bind all key numbers to tags.
-- Be careful: we use keycodes to make it work on any keyboard layout.
-- This should map on the top row of your keyboard, usually 1 to 9.
for i = 1, 9 do
    globalkeys = gears.table.join(globalkeys,
        -- View tag only.
        awful.key({ modkey }, "#" .. i + 9,
                  function ()
                        local screen = awful.screen.focused()
                        local tag = screen.tags[i]
                        if tag then
                           tag:view_only()
                        end
                  end,
                  {description = "view tag #"..i, group = "tag"}),
        -- Toggle tag display.
        awful.key({ modkey, "Control" }, "#" .. i + 9,
                  function ()
                      local screen = awful.screen.focused()
                      local tag = screen.tags[i]
                      if tag then
                         awful.tag.viewtoggle(tag)
                      end
                  end,
                  {description = "toggle tag #" .. i, group = "tag"}),
        -- Move client to tag.
        awful.key({ modkey, "Shift" }, "#" .. i + 9,
                  function ()
                      if client.focus then
                          local tag = client.focus.screen.tags[i]
                          if tag then
                              client.focus:move_to_tag(tag)
                          end
                     end
                  end,
                  {description = "move focused client to tag #"..i, group = "tag"}),
        -- Toggle tag on focused client.
        awful.key({ modkey, "Control", "Shift" }, "#" .. i + 9,
                  function ()
                      if client.focus then
                          local tag = client.focus.screen.tags[i]
                          if tag then
                              client.focus:toggle_tag(tag)
                          end
                      end
                  end,
                  {description = "toggle focused client on tag #" .. i, group = "tag"})
    )
end

clientbuttons = gears.table.join(
    awful.button({ }, 1, function (c)
        c:emit_signal("request::activate", "mouse_click", {raise = true})
    end),
    awful.button({ modkey }, 1, function (c)
        c:emit_signal("request::activate", "mouse_click", {raise = true})
        awful.mouse.client.move(c)
    end),
    awful.button({ modkey }, 3, function (c)
        c:emit_signal("request::activate", "mouse_click", {raise = true})
        awful.mouse.client.resize(c)
    end)
)

-- Set keys
root.keys(globalkeys)
-- }}}

-- {{{ Rules
-- Rules to apply to new clients (through the "manage" signal).
awful.rules.rules = {
    -- All clients will match this rule.
    { rule = { },
      properties = { border_width = beautiful.border_width,
                     border_color = beautiful.border_normal,
                     focus = awful.client.focus.filter,
                     raise = true,
                     keys = clientkeys,
                     buttons = clientbuttons,
                     screen = awful.screen.preferred,
                     placement = awful.placement.no_overlap+awful.placement.no_offscreen
     }
    },

    -- Floating clients.
    { rule_any = {
        instance = {
          "DTA",  -- Firefox addon DownThemAll.
          "copyq",  -- Includes session name in class.
          "pinentry",
        },
        class = {
          "Arandr",
          "Blueman-manager",
          "Gpick",
          "Kruler",
          "MessageWin",  -- kalarm.
          "Sxiv",
          "Tor Browser", -- Needs a fixed window size to avoid fingerprinting by screen size.
          "Wpa_gui",
          "veromix",
          "xtightvncviewer"},

        -- Note that the name property shown in xprop might be set slightly after creation of the client
        -- and the name shown there might not match defined rules here.
        name = {
          "Event Tester",  -- xev.
        },
        role = {
          "AlarmWindow",  -- Thunderbird's calendar.
          "ConfigManager",  -- Thunderbird's about:config.
          "pop-up",       -- e.g. Google Chrome's (detached) Developer Tools.
        }
      }, properties = { floating = true }},

    -- Add titlebars to normal clients and dialogs
    { rule_any = {type = { "normal", "dialog" }
      }, properties = { titlebars_enabled = true }
    },

    -- Set Firefox to always map on the tag named "2" on screen 1.
    -- { rule = { class = "Firefox" },
    --   properties = { screen = 1, tag = "2" } },
}
-- }}}

-- {{{ Signals
-- Signal function to execute when a new client appears.
client.connect_signal("manage", function (c)
    -- Set the windows at the slave,
    -- i.e. put it at the end of others instead of setting it master.
    -- if not awesome.startup then awful.client.setslave(c) end

    if awesome.startup
      and not c.size_hints.user_position
      and not c.size_hints.program_position then
        -- Prevent clients from being unreachable after screen count changes.
        awful.placement.no_offscreen(c)
    end
end)

-- Add a titlebar if titlebars_enabled is set to true in the rules.
client.connect_signal("request::titlebars", function(c)
    -- buttons for the titlebar
    local buttons = gears.table.join(
        awful.button({ }, 1, function()
            c:emit_signal("request::activate", "titlebar", {raise = true})
            awful.mouse.client.move(c)
        end),
        awful.button({ }, 3, function()
            c:emit_signal("request::activate", "titlebar", {raise = true})
            awful.mouse.client.resize(c)
        end)
    )

    awful.titlebar(c) : setup {
        { -- Left
            awful.titlebar.widget.iconwidget(c),
            buttons = buttons,
            layout  = wibox.layout.fixed.horizontal
        },
        { -- Middle
            { -- Title
                align  = "center",
                widget = awful.titlebar.widget.titlewidget(c)
            },
            buttons = buttons,
            layout  = wibox.layout.flex.horizontal
        },
        { -- Right
            awful.titlebar.widget.floatingbutton (c),
            awful.titlebar.widget.maximizedbutton(c),
            awful.titlebar.widget.stickybutton   (c),
            awful.titlebar.widget.ontopbutton    (c),
            awful.titlebar.widget.closebutton    (c),
            layout = wibox.layout.fixed.horizontal()
        },
        layout = wibox.layout.align.horizontal
    }
end)

-- Enable sloppy focus, so that focus follows mouse.
client.connect_signal("mouse::enter", function(c)
    c:emit_signal("request::activate", "mouse_enter", {raise = false})
end)

client.connect_signal("focus", function(c) c.border_color = beautiful.border_focus end)
client.connect_signal("unfocus", function(c) c.border_color = beautiful.border_normal end)
-- }}}

The relevant part are :

  • tasklist variables definition :
local tasklist_layout = {
  layout = wibox.layout.flex.horizontal,
  max_widget_size = 300
}

local function tasklist_create_function(self, c, index, objects) --luacheck: no unused
  -- adding close action on button
  local cbm = self:get_children_by_id('close_button_role')[1]
  cbm:buttons(
    awful.button({}, 1, nil,
      function()
        c:kill()
      end
    )
  )
end

local tasklist_template = {
  -- bgb
  id = "background_role",
  widget = wibox.container.background,
  create_callback = tasklist_create_function,
  {
    -- l
    layout = wibox.layout.align.horizontal,
    {
      -- ibm
      id = "icon_margin_role",
      widget = wibox.container.margin,
      left = 12,
      right = 12,
      {
        -- ib
        id = 'icon_role',
        widget = wibox.widget.imagebox,
      }
    },
    {
      -- tbm
      id = "text_margin_role",
      widget = wibox.container.margin,
      left = 4,
      right = 4,
      {
        -- tb
        id = "text_role",
        widget = wibox.widget.textbox,
        ellipsize = 'end'
      }
    },
    {
      -- cbm
      id = "close_button_role",
      widget = wibox.container.margin,
      left = 4,
      right = 8,
      {
        widget = wibox.widget.textbox,
        text = "X"
      }
    }
  }
}
  • the panel configuration :
    -- Create a tasklist widget
    s.mytasklist = awful.widget.tasklist {
        screen  = s,
        filter  = awful.widget.tasklist.filter.currenttags,
        buttons = tasklist_buttons,
        layout = tasklist_layout,
        widget_template = tasklist_template
    }

    s.add_button = {
      widget = wibox.container.background,
      bg = "#FF0000",
      fg = "#FFFFFF",
      {
        widget = wibox.widget.textbox,
        text = "I don't want to be wrapped !"
      }
    }

    -- Create the wibox
    s.mywibox = awful.wibar({ position = "top", screen = s })

    -- Add widgets to the wibox
    s.mywibox:setup {
        layout = wibox.layout.align.horizontal,
        { -- Left widgets
            layout = wibox.layout.fixed.horizontal,
            mylauncher,
            s.mytaglist,
            s.mypromptbox,
        },
        { -- Middle widget
          layout = wibox.layout.fixed.horizontal,
          s.mytasklist,
          s.add_button,
        },
        { -- Right widgets
            layout = wibox.layout.fixed.horizontal,
            mykeyboardlayout,
            wibox.widget.systray(),
            mytextclock,
            s.mylayoutbox,
        },
    }

EDIT : After trying Uli Schlachter solution, the content of the textbox isn't shrinked, but it is stuck on the right side enter image description here enter image description here

2

There are 2 answers

0
Thel-Rico On BEST ANSWER

Finally, I could make it work with a custom layout : I duplicate the wibox.layout.align file and edited it to make it suit my needs. I ends up with a layout which can take up to 3 widgets : prepend, main and append The prepend and append widgets are fixed size and main take its own space or the remaining space.

NOTE1: This layout has a vertical mode that should work but I haven't test it yet.

NOTE2 : I haven't test what happened if prepend and append widget are wider than their parent ...

Here are some screenshots of the result : enter image description here enter image description here

The code of the layout :

local base = require("wibox.widget.base")

local layout = {}

-- Calculate the layout.
-- @param context The context in which we are drawn.
-- @param width The available width.
-- @param height The available height.
function layout:layout(context, width, height)
    local result = {}

    local prepend_width, prepend_height = 0, 0
    if self._private.prepend then
        prepend_width, prepend_height = base.fit_widget(self, context, self._private.prepend, width, height)
    end

    local append_width, append_height = 0, 0
    if self._private.append then
        append_width, append_height = base.fit_widget(self, context, self._private.append, width, height)
    end

    local width_remains = self._private.dir == "x" and (width - prepend_width - append_width) or width
    local height_remains = self._private.dir == "y" and (height - prepend_height - append_height) or height

    local main_width, main_height = base.fit_widget(self, context, self._private.main, width_remains, height_remains)

    if self._private.prepend then
        local x, y, w, h = 0, 0, width, height
        if self._private.dir == "x" then
            w = prepend_width
        elseif self._private.dir == "y" then
            h = prepend_height
        end
        table.insert(result, base.place_widget_at(self._private.prepend, x, y, w, h))
    end

    if self._private.main then
        local x, y, w, h = 0, 0, width, height
        if self._private.dir == "x" then
            x = prepend_width
            w = main_width
        elseif self._private.dir == "y" then
            y = prepend_height
            h = main_height
        end
        table.insert(result, base.place_widget_at(self._private.main, x, y, w, h))
    end

    if self._private.append then
        local x, y, w, h = 0, 0, width, height
        if self._private.dir == "x" then
            x = prepend_width + main_width
            w = append_width
        elseif self._private.dir == "y" then
            y = prepend_height + main_height
            h = append_height
        end
        table.insert(result, base.place_widget_at(self._private.append, x, y, w, h))
    end

    return result
end

--- Set the layout's prepend widget.
-- This is the widget which is before the main widget
-- @property prepend
-- @tparam widget prepend
-- @propemits true false

function layout:set_prepend(widget)
    if self._private.prepend == widget then
        return
    end
    self._private.prepend = widget
    self:emit_signal("widget::layout_changed")
    self:emit_signal("property::prepend", widget)
end

--- Set the layout's main widget. This is the centered one.
-- @property main
-- @tparam widget main
-- @propemits true false

function layout:set_main(widget)
    if self._private.main == widget then
        return
    end
    self._private.main = widget
    self:emit_signal("widget::layout_changed")
    self:emit_signal("property::main", widget)
end

--- Set the layout's append widget.
-- This is the widget which is after the main widget
-- @property append
-- @tparam widget append
-- @propemits true false

function layout:set_append(widget)
    if self._private.append == widget then
        return
    end
    self._private.append = widget
    self:emit_signal("widget::layout_changed")
    self:emit_signal("property::append", widget)
end

for _, prop in ipairs {"prepend", "main", "append" } do
    layout["get_"..prop] = function(self)
        return self._private[prop]
    end
end

function layout:get_children()
    return gears.table.from_sparse {self._private.prepend, self._private.main, self._private.append}
end

function layout:set_children(children)
    self:set_prepend(children[1])
    self:set_main(children[2])
    self:set_append(children[3])
end

-- Fit the layout into the given space. The layout will
-- ask for the sum of the sizes of its sub-widgets in its direction
-- and the largest sized sub widget in the other direction.
-- @param context The context in which we are fit.
-- @param orig_width The available width.
-- @param orig_height The available height.
function layout:fit(context, orig_width, orig_height)
    local used_in_dir = 0
    local used_in_other = 0

    for _, v in pairs{self._private.prepend, self._private.main, self._private.append} do
        local w, h = base.fit_widget(self, context, v, orig_width, orig_height)

        local max = self._private.dir == "y" and w or h
        if max > used_in_other then
            used_in_other = max
        end

        used_in_dir = used_in_dir + (self._private.dir == "y" and h or w)
    end

    if self._private.dir == "y" then
        return used_in_other, used_in_dir
    end
    return used_in_dir, used_in_other
end

function layout:reset()
    for _, v in pairs({ "prepend", "main", "append" }) do
        self[v] = nil
    end
    self:emit_signal("widget::layout_changed")
end

local function get_layout(dir, prepend, main, append)
    local ret = base.make_widget(nil, nil, {enable_properties = true})
    ret._private.dir = dir

    for k, v in pairs(layout) do
        if type(v) == "function" then
            rawset(ret, k, v)
        end
    end

    ret:set_prepend(prepend)
    ret:set_main(main)
    ret:set_append(append)

    -- The layout allow set_children to have empty entries
    ret.allow_empty_widget = true

    return ret
end

--- Returns a new horizontal layout. This layout can display up to three widgets.
-- :set_prepend() sets a widget with a fixed size at the beginning.
-- :set_append() sets a widget with a fixed size at the end.
-- :set_main() sets a widget which shrink if their is not enough space.
-- @tparam[opt] widget prepend Widget to be put to the beginning.
-- @tparam[opt] widget main Widget to be put to the middle.
-- @tparam[opt] widget append Widget to be put to the end.
function layout.horizontal(prepend, main, append)
    local ret = get_layout("x", prepend, main, append)
    return ret
end

--- Returns a new vertical layout. This layout can display up to three widgets.
-- :set_prepend() sets a widget with a fixed size at the beginning.
-- :set_append() sets a widget with a fixed size at the end.
-- :set_main() sets a widget which shrink if their is not enough space.
-- @tparam[opt] widget prepend Widget to be put to the beginning.
-- @tparam[opt] widget main Widget to be put to the middle.
-- @tparam[opt] widget append Widget to be put to the end.
function layout.vertical(prepend, main, append)
    local ret = get_layout("y", prepend, main, append)
    return ret
end

return layout

And a use case :

local customLayout = require('CustomLayout')

-- ...

awful.screen.connect_for_each_screen(function(s)
    -- ...

    -- Add widgets to the wibox
    s.mywibox:setup {
        layout = wibox.layout.align.horizontal,
        { -- Left widgets
            -- ...
        },
        { -- Middle widget
          layout = customLayout.horizontal,
          {
            widget = wibox.container.background,
            bg = "#FF0000",
            fg = "#FFFFFF",
            {
              widget = wibox.widget.textbox,
              text = "Prepend content"
            }
          },
          s.mytasklist,
          {
            widget = wibox.container.background,
            bg = "#FF0000",
            fg = "#FFFFFF",
            {
              widget = wibox.widget.textbox,
              text = "Append content"
            }
          },
        },
        { -- Right widgets
            -- ...
        },
    }
0
Uli Schlachter On

Random drive-by idea that I am too lazy to test:

    { -- Middle widget
      layout = wibox.layout.align.horizontal,
      expand = "inside",
      nil,
      s.mytasklist,
      s.add_button,
    },

I am not sure this does what you want. Sorry.