diff options
author | delta <darkussdelta@gmail.com> | 2025-07-04 00:38:29 +0200 |
---|---|---|
committer | delta <darkussdelta@gmail.com> | 2025-07-04 00:38:29 +0200 |
commit | b3530d7c4a102935fa26498a160ee1dc6c1e9c03 (patch) | |
tree | d7751206a694bc5de2d6b34b0c077cfcd1855798 /.config/awesome/ui | |
parent | df75ec5ed5e3848c497f0439acb43ec9246ad3e7 (diff) |
:3
Diffstat (limited to '.config/awesome/ui')
26 files changed, 1197 insertions, 1192 deletions
diff --git a/.config/awesome/ui/conductor/init.lua b/.config/awesome/ui/conductor/init.lua index e6fe552..402945c 100644 --- a/.config/awesome/ui/conductor/init.lua +++ b/.config/awesome/ui/conductor/init.lua @@ -1,8 +1,64 @@ local awful = require "awful" +local gshape = require "gears.shape" +local phosphor = require "assets.phosphor" local q = require "quarrel" local qui = require "quarrel.ui" local qvars = require "quarrel.vars" +local wibox = require "wibox" -local conductor = {} +local M = {} -return conductor +M._popup = qui.popup { + -- visible = false, + ontop = true, + placement = "right", + shape = function(cr, w, h) + gshape.partially_rounded_rect(cr, w, h, true, false, false, true, qui.BORDER_RADIUS) + end, + -- x = awful.screen.focused().geometry.width, + -- minimum_width = width, + -- maximum_width = width, + -- maximum_height = max_height, + widget = awful.widget.tasklist { + screen = awful.screen.focused(), + filter = awful.widget.tasklist.filter.allscreen, + layout = { + spacing = qui.BIG_PADDING, + layout = wibox.layout.fixed.vertical, + }, + widget_template = qui.styled { + widget = wibox.container.background, + { + { + widget = wibox.container.constraint, + strategy = "max", + height = qui.CHAR_HEIGHT, + width = qui.CHAR_HEIGHT, + { + widget = wibox.widget.imagebox, + id = "icon_role", + }, + }, + { + widget = wibox.container.constraint, + strategy = "max", + width = qui.CHAR_WIDTH * 24, + { + widget = wibox.widget.textbox, + id = "client_name", + }, + }, + layout = wibox.layout.fixed.horizontal, + spacing = qui.PADDING, + }, + create_callback = function(self, c) + self:get_children_by_id("client_name")[1].text = c.icon_name + end, + update_callback = function(self, c) + self:get_children_by_id("client_name")[1].text = c.name + end, + }, + }, +} + +return M diff --git a/.config/awesome/ui/decorations/wallpaper.lua b/.config/awesome/ui/decorations/wallpaper.lua index 3a204c2..cb01745 100644 --- a/.config/awesome/ui/decorations/wallpaper.lua +++ b/.config/awesome/ui/decorations/wallpaper.lua @@ -1,12 +1,12 @@ +local qcolor = require "quarrel.color" local qmarkup = require "quarrel.markup" local qui = require "quarrel.ui" -local qvars = require "quarrel.vars" local wallpaper = require "awful.wallpaper" local wibox = require "wibox" screen.connect_signal("request::wallpaper", function(s) wallpaper { - bg = qvars.colors.dim.bg, + bg = qcolor.palette.bg.low, screen = s, widget = { widget = wibox.container.place, @@ -16,12 +16,12 @@ screen.connect_signal("request::wallpaper", function(s) widget = wibox.widget.textbox, font = qui.font(1.5), markup = table.concat({ - qmarkup(" ___", { fg = qvars.colors.red }), - qmarkup(" /\\ \\", { fg = qvars.colors.green }), - qmarkup(" /::\\ \\", { fg = qvars.colors.yellow }), - qmarkup("/::\\:\\__\\", { fg = qvars.colors.blue }), - qmarkup("\\/\\::/ /", { fg = qvars.colors.pink }), - qmarkup(" \\/__/", { fg = qvars.colors.cyan }), + qmarkup(" ___", { fg = qcolor.palette.blue() }), + qmarkup(" /\\ \\", { fg = qcolor.palette.green() }), + qmarkup(" /::\\ \\", { fg = qcolor.palette.yellow() }), + qmarkup("/::\\:\\__\\", { fg = qcolor.palette.orange() }), + qmarkup("\\/\\::/ /", { fg = qcolor.palette.red() }), + qmarkup(" \\/__/", { fg = qcolor.palette.pink() }), }, "\n"), }, }, diff --git a/.config/awesome/ui/fresnel/init.lua b/.config/awesome/ui/fresnel/init.lua index 9905741..5d091b6 100644 --- a/.config/awesome/ui/fresnel/init.lua +++ b/.config/awesome/ui/fresnel/init.lua @@ -1,8 +1,10 @@ local al_prompt = require "lib.bling.widget.app_launcher.prompt" local awful = require "awful" -local beautiful = require "beautiful" local cfg = require "misc.cfg" +local gshape = require "gears.shape" local gtable = require "gears.table" +local qanim = require "quarrel.animation" +local qcolor = require "quarrel.color" local qnative = require "quarrel.native" local qtable = require "quarrel.table" local qui = require "quarrel.ui" @@ -42,8 +44,8 @@ function fresnel:_update(query, scrolled) self._entries_offset = 0 end - local layout = self._widget.widget:get_children_by_id("entry_layout")[1] - local status = self._widget.widget:get_children_by_id("status")[1] + local layout = self._popup.widget:get_children_by_id("entry_layout")[1] + local status = self._popup.widget:get_children_by_id("status")[1] local all_providers = {} local entries_count = 0 @@ -71,18 +73,18 @@ function fresnel:_update(query, scrolled) local entry_widget = wibox.widget { widget = wibox.container.background, - shape = qvars.shape, + shape = qui.shape, { widget = wibox.container.margin, - margins = qvars.padding, + margins = qui.PADDING, { widget = wibox.container.constraint, strategy = "max", - height = qvars.char_height, + height = qui.CHAR_HEIGHT, { { widget = wibox.container.background, - fg = qvars.colors.dim.fg, + fg = qcolor.palette.low, { widget = wibox.widget.textbox, text = entry.provider .. " | ", @@ -92,7 +94,7 @@ function fresnel:_update(query, scrolled) widget = wibox.widget.textbox, text = entry.message, }, - spacing = qvars.padding, + spacing = qui.PADDING, layout = wibox.layout.fixed.horizontal, }, }, @@ -111,21 +113,21 @@ function fresnel:_update(query, scrolled) if self._selected_index + self._entries_offset == i then entry_widget._selected = true - entry_widget.bg = qvars.colors.black + entry_widget.bg = qcolor.palette.bg.high end entry_widget:connect_signal("mouse::enter", function() if entry_widget._selected == true then return end - entry_widget.bg = qvars.colors.black + entry_widget.bg = qcolor.palette.bg.high end) entry_widget:connect_signal("mouse::leave", function() if entry_widget._selected == true then return end - entry_widget.bg = qvars.colors.bg + entry_widget.bg = qcolor.palette.bg() end) layout:add(entry_widget) @@ -143,7 +145,7 @@ fresnel._prompt = al_prompt { prompt = "", reset_on_stop = true, ul_cursor = "low", - bg_cursor = qvars.colors.black, + bg_cursor = qcolor.palette.bg.high, changed_callback = function(text) if fresnel._text == text then return @@ -192,27 +194,31 @@ fresnel._prompt = al_prompt { end, } -fresnel._widget = qui.popup { - visible = false, +local max_height = qui.BIG_PADDING * 2 + (qui.BIG_PADDING * 2 + qui.CHAR_HEIGHT) * 10 +-- + qui.PADDING +-- + qui.PADDING * 9 + +local width = awful.screen.focused().geometry.width / 2 + +fresnel._popup = qui.popup { + -- visible = false, ontop = true, - placement = function(d) - awful.placement.top(d, { - margins = { - top = beautiful.useless_gap * 2, - }, - }) + placement = false, + shape = function(cr, w) + gshape.partially_rounded_rect(cr, w, 0, false, false, true, true, qui.BORDER_RADIUS) end, - minimum_width = awful.screen.focused().geometry.width / 2, - maximum_width = awful.screen.focused().geometry.width / 2, + x = width / 2, + minimum_width = width, + maximum_width = width, + -- maximum_height = max_height, widget = { - { + qui.styled { widget = wibox.container.background, - bg = qvars.colors.black, - fg = qvars.colors.dim.fg, - shape = qvars.shape, + fg = qcolor.palette.fg.low, + bg = qcolor.palette.bg.high, { widget = wibox.container.margin, - margins = qvars.padding, + margins = qui.BIG_PADDING, { { widget = wibox.widget.textbox, @@ -221,16 +227,16 @@ fresnel._widget = qui.popup { { widget = wibox.container.margin, margins = { - left = qvars.padding, - right = qvars.padding, + left = qui.PADDING, + right = qui.PADDING, }, { widget = wibox.container.constraint, strategy = "max", - height = qvars.char_height, + height = qui.CHAR_HEIGHT, { widget = wibox.container.background, - fg = qvars.colors.fg, + fg = qcolor.palette.fg(), fresnel._prompt.textbox, }, }, @@ -247,10 +253,10 @@ fresnel._widget = qui.popup { { widget = wibox.container.margin, margins = { - top = qvars.padding, + top = qui.PADDING, }, { - spacing = qvars.padding, + spacing = qui.PADDING, layout = wibox.layout.fixed.vertical, id = "entry_layout", }, @@ -258,35 +264,43 @@ fresnel._widget = qui.popup { layout = wibox.layout.align.vertical, }, } -fresnel._widget.maximum_height = qvars.big_padding * 2 - + (qvars.padding * 2 + qvars.char_height) * (1 + 10) - + qvars.padding - + qvars.padding * 9 - function fresnel:show() self._toggled = true - self._timed.target = 1 + self._opacity_timed.target = 1 + self._height_timed:set(max_height) self:_update() self._prompt:start() end function fresnel:hide() self._toggled = false - self._timed.target = 0 + self._opacity_timed.target = 0 + self._height_timed:set(0) self._prompt:stop() end -fresnel._timed = rubato.timed { +fresnel._height_timed = qanim:new { + duration = qvars.anim_duration, + pos = 0, + easing = qvars.easing, + subscribed = function(pos) + fresnel._popup.shape = function(cr, w) + gshape.partially_rounded_rect(cr, w, pos, false, false, true, true, qui.BORDER_RADIUS) + end + end, +} + +-- TODO: optimize the search algo to be more efficient and not require making fresnel invisible +fresnel._opacity_timed = rubato.timed { duration = qvars.anim_duration, - intro = qvars.anim_intro, pos = 0, subscribed = function(pos) - fresnel._widget.opacity = pos + fresnel._popup.opacity = pos if pos == 0 then - fresnel._widget.visible = false + fresnel._popup.visible = false else - fresnel._widget.visible = true + fresnel._popup.visible = true end end, } diff --git a/.config/awesome/ui/init.lua b/.config/awesome/ui/init.lua index dd93ad1..6ad1c7b 100644 --- a/.config/awesome/ui/init.lua +++ b/.config/awesome/ui/init.lua @@ -1,4 +1,4 @@ require "ui.statusbar" require "ui.decorations" -require "ui.conductor" -require "ui.osd" +-- require "ui.conductor" +-- require "ui.osd" diff --git a/.config/awesome/ui/insightful/init.lua b/.config/awesome/ui/insightful/init.lua index 3dcbbea..be072d5 100644 --- a/.config/awesome/ui/insightful/init.lua +++ b/.config/awesome/ui/insightful/init.lua @@ -1,6 +1,6 @@ local awful = require "awful" local beautiful = require "beautiful" -local qstore = require "quarrel.store" +local qbind = require "quarrel.bind" local qtable = require "quarrel.table" local qui = require "quarrel.ui" local qvars = require "quarrel.vars" @@ -81,7 +81,7 @@ insightful._widget = qui.popup { minimum_width = awful.screen.focused().geometry.width / 2, widget = { layout = wibox.layout.fixed.vertical, - spacing = qvars.big_padding, + spacing = qui.BIG_PADDING, id = "layout_container", }, } @@ -90,7 +90,7 @@ function insightful:_generate() local grouped_binds = {} local layout_container = insightful._widget.widget.layout_container - for _, keybind in ipairs(qstore.bindings) do + for _, keybind in ipairs(qbind.bindings) do local group = keybind.group or "general" local group_exists = grouped_binds[group] ~= nil @@ -107,7 +107,7 @@ function insightful:_generate() for group, keybinds in qtable.opairs(grouped_binds) do local group_layout = { - spacing = qvars.padding, + spacing = qui.PADDING, layout = wibox.layout.fixed.vertical, } @@ -129,11 +129,11 @@ function insightful:_generate() 1, qui.styled { widget = wibox.container.background, - bg = qvars.colors.bright.black, + bg = qcolor.palette.bright.black, border_width = 0, { widget = wibox.container.margin, - margins = qvars.padding, + margins = qui.PADDING, { widget = wibox.widget.textbox, text = insightful._keymap[mod] or mod, @@ -166,7 +166,7 @@ function insightful:_generate() if keybind.desc then key_and_desc_layout[3] = { widget = wibox.container.background, - fg = qvars.colors.dim.fg, + fg = qcolor.palette.dim.fg, { widget = wibox.widget.textbox, text = keybind.desc, @@ -183,12 +183,12 @@ function insightful:_generate() { { widget = wibox.container.background, - bg = qvars.colors.yellow, - fg = qvars.colors.bg, - shape = qvars.shape, + bg = qcolor.palette.yellow, + fg = qcolor.palette.bg, + shape = qui.shape, { widget = wibox.container.margin, - margins = qvars.padding, + margins = qui.PADDING, { widget = wibox.widget.textbox, text = group, @@ -199,7 +199,7 @@ function insightful:_generate() layout = wibox.layout.align.horizontal, }, group_layout, - spacing = qvars.padding, + spacing = qui.PADDING, layout = wibox.layout.fixed.vertical, } end diff --git a/.config/awesome/ui/osd.lua b/.config/awesome/ui/osd.lua deleted file mode 100644 index fc74ed9..0000000 --- a/.config/awesome/ui/osd.lua +++ /dev/null @@ -1,102 +0,0 @@ ---- Heavy inspiration from a design made by https://github.com/tsukki9696 - -local awful = require "awful" -local beautiful = require "beautiful" -local gtimer = require "gears.timer" -local phosphor = require "assets.phosphor" -local qmath = require "quarrel.math" -local qui = require "quarrel.ui" -local qvars = require "quarrel.vars" -local rubato = require "lib.rubato" -local wibox = require "wibox" - -local osd = {} - -local widget = qui.popup { - ontop = true, - visible = false, - placement = function(d) - awful.placement.top(d, { - margins = { - top = beautiful.useless_gap * 2, - }, - }) - end, - minimum_height = qvars.char_height * 2, - -- minimum_width = awful.screen.focused().geometry.width / 2, - widget = { - { - widget = wibox.container.place, - - qui.icon { - icon = phosphor.speaker_simple_none_fill, - widget = { - forced_height = qvars.char_height * 1.2, - forced_width = qvars.char_height * 1.2, - id = "icon", - }, - }, - }, - { - widget = wibox.container.place, - { - widget = wibox.widget.progressbar, - max_value = 100, - value = 20, - forced_height = qvars.char_height / 1.5, - forced_width = qvars.expanded_bar_size - - (qvars.big_padding + qvars.big_padding * 2 + qvars.padding * 2) - - (qvars.char_height / 1.25 + qvars.padding) * 3, - color = qvars.colors.yellow, - background_color = qvars.colors.black, - shape = qvars.shape, - id = "progress", - }, - }, - - { - widget = wibox.widget.textbox, - text = "20%", - font = qui.font(1.2), - id = "percentage", - }, - layout = wibox.layout.fixed.horizontal, - spacing = qvars.big_padding, - }, -} - -local timer - -local anim = rubato.timed { - duration = qvars.anim_duration, - intro = qvars.anim_intro, - pos = 0, - subscribed = function(pos) - widget.opacity = pos - if pos == 0 then - widget.visible = false - elseif not widget.visible then - widget.visible = true - elseif pos == 1 then - timer:start() - end - end, -} - -timer = gtimer { - timeout = 1, - callback = function() - anim.target = 0 - end, - single_shot = true, -} - -function osd.notify(icon, value, max) - anim.target = 1 - widget:get_children_by_id("icon")[1].image = icon - widget:get_children_by_id("progress")[1].value = value - widget:get_children_by_id("progress")[1].max_value = max - widget:get_children_by_id("percentage")[1].text = tostring(qmath.percentage(value, max)) .. "%" -end - -return osd diff --git a/.config/awesome/ui/osd/init.lua b/.config/awesome/ui/osd/init.lua new file mode 100644 index 0000000..4310f17 --- /dev/null +++ b/.config/awesome/ui/osd/init.lua @@ -0,0 +1,102 @@ +local awful = require "awful" +local beautiful = require "beautiful" +local gtimer = require "gears.timer" +local phosphor = require "assets.phosphor" +local qmath = require "quarrel.math" +local qui = require "quarrel.ui" +local qvars = require "quarrel.vars" +local rubato = require "lib.rubato" +local wibox = require "wibox" +-- local mpris_widget = require "ui.osd.mpris" + +local osd = {} + +local widget = awful.popup(qui.styled { + ontop = true, + visible = true, + placement = function(d) + awful.placement.top(d, { + margins = { + top = beautiful.useless_gap * 2, + }, + }) + end, + minimum_height = qui.CHAR_HEIGHT * 3, + -- widget = mpris_widget + -- minimum_width = awful.screen.focused().geometry.width / 2, + -- widget = { + -- { + -- widget = wibox.container.place, + -- + -- qui.icon { + -- icon = phosphor.speaker_simple_none_fill, + -- widget = { + -- forced_height = qui.CHAR_HEIGHT * 1.2, + -- forced_width = qui.CHAR_HEIGHT * 1.2, + -- id = "icon", + -- }, + -- }, + -- }, + -- { + -- widget = wibox.container.place, + -- { + -- widget = wibox.widget.progressbar, + -- max_value = 100, + -- value = 20, + -- forced_height = qui.CHAR_HEIGHT / 1.5, + -- forced_width = qvars.expanded_bar_size + -- - (qui.BIG_PADDING + qui.BIG_PADDING * 2 + qui.PADDING * 2) + -- - (qui.CHAR_HEIGHT / 1.25 + qui.PADDING) * 3, + -- color = qcolor.palette.yellow(), + -- background_color = qcolor.palette.border.variant, + -- shape = qui.shape, + -- id = "progress", + -- }, + -- }, + -- + -- { + -- widget = wibox.widget.textbox, + -- text = "20%", + -- font = qui.font(1.2), + -- id = "percentage", + -- }, + -- layout = wibox.layout.fixed.horizontal, + -- spacing = qui.BIG_PADDING, + -- }, +}) + +local timer + +local anim = rubato.timed { + duration = qvars.anim_duration, + intro = qvars.anim_intro, + pos = 1, + subscribed = function(pos) + widget.opacity = pos + if pos == 0 then + widget.visible = false + elseif not widget.visible then + widget.visible = true + elseif pos == 1 then + -- timer:start() + end + end, +} + +timer = gtimer { + timeout = 1, + callback = function() + -- anim.target = 0 + end, + single_shot = true, +} + +function osd.notify(icon, value, max) + anim.target = 1 + widget:get_children_by_id("icon")[1].image = icon + widget:get_children_by_id("progress")[1].value = value + widget:get_children_by_id("progress")[1].max_value = max + widget:get_children_by_id("percentage")[1].text = tostring(qmath.percentage(value, max)) .. "%" +end + +return osd diff --git a/.config/awesome/ui/powermenu/init.lua b/.config/awesome/ui/powermenu/init.lua index 81d2ea3..2e2b4af 100644 --- a/.config/awesome/ui/powermenu/init.lua +++ b/.config/awesome/ui/powermenu/init.lua @@ -1,5 +1,6 @@ local awful = require "awful" local phosphor = require "assets.phosphor" +local qcolor = require "quarrel.color" local qui = require "quarrel.ui" local qvars = require "quarrel.vars" local rubato = require "lib.rubato" @@ -8,21 +9,21 @@ local wibox = require "wibox" local function create_button(title, icon, color, exec) return wibox.widget { widget = wibox.container.background, - shape = qvars.shape, + shape = qui.shape, { widget = wibox.container.margin, - margins = qvars.big_padding, + margins = qui.BIG_PADDING, { qui.styled { widget = wibox.container.background, border_color = color, { widget = wibox.container.margin, - margins = qvars.big_padding, + margins = qui.BIG_PADDING, qui.icon { widget = { - forced_height = qvars.char_height * 4 - qvars.big_padding * 2, - forced_width = qvars.char_height * 4 - qvars.big_padding * 2, + forced_height = qui.CHAR_HEIGHT * 4 - qui.BIG_PADDING * 2, + forced_width = qui.CHAR_HEIGHT * 4 - qui.BIG_PADDING * 2, }, icon = icon, color = color, @@ -37,12 +38,12 @@ local function create_button(title, icon, color, exec) }, }, layout = wibox.layout.fixed.vertical, - spacing = qvars.big_padding, + spacing = qui.BIG_PADDING, exec = exec, }, }, select = function(self) - self.bg = qvars.colors.black + self.bg = qcolor.palette.bg.high end, } end @@ -56,7 +57,7 @@ screen.connect_signal("request::desktop_decoration", function(s) visible = false, minimum_width = s.geometry.width, minimum_height = s.geometry.height, - bg = qvars.colors.bg .. "ee", + bg = qcolor.palette.bg() .. "ee", border_width = 0, widget = { widget = wibox.container.place, @@ -64,10 +65,10 @@ screen.connect_signal("request::desktop_decoration", function(s) widget = wibox.container.background, { widget = wibox.container.margin, - margins = qvars.big_padding, + margins = qui.BIG_PADDING, { layout = wibox.layout.fixed.horizontal, - spacing = qvars.big_padding * 2, + spacing = qui.BIG_PADDING * 2, id = "list", }, }, @@ -77,16 +78,16 @@ screen.connect_signal("request::desktop_decoration", function(s) local layout = powermenu._widget.widget:get_children_by_id("list")[1] - layout:add(create_button("Shutdown", phosphor.power_bold, qvars.colors.red, function() + layout:add(create_button("Shutdown", phosphor.power_bold, qcolor.palette.red(), function() awful.spawn "poweroff" end)) - layout:add(create_button("Log out", phosphor.sign_out_bold, qvars.colors.green, function() + layout:add(create_button("Log out", phosphor.sign_out_bold, qcolor.palette.green(), function() awesome.quit() end)) - layout:add(create_button("Lock", phosphor.lock_simple_fill, qvars.colors.blue, function() + layout:add(create_button("Lock", phosphor.lock_simple_fill, qcolor.palette.blue(), function() require("quarrel").debug "locked!l" end)) - layout:add(create_button("Restart", phosphor.arrow_clockwise_bold, qvars.colors.pink, function() + layout:add(create_button("Restart", phosphor.arrow_clockwise_bold, qcolor.palette.pink(), function() awful.spawn "reboot" end)) end) diff --git a/.config/awesome/ui/statusbar/consts.lua b/.config/awesome/ui/statusbar/consts.lua new file mode 100644 index 0000000..00d01bf --- /dev/null +++ b/.config/awesome/ui/statusbar/consts.lua @@ -0,0 +1,8 @@ +local qui = require "quarrel.ui" +local C = {} + +C.BAR_SIZE = 24 + qui.BIG_PADDING * 2 +C.ELEMENT_SIZE = 12 +C.EXPANDED_BAR_SIZE = qui.BIG_PADDING + (qui.BIG_PADDING * 2 + C.ELEMENT_SIZE * 4) * 4 + qui.PADDING * 2 + +return C diff --git a/.config/awesome/ui/statusbar/init.lua b/.config/awesome/ui/statusbar/init.lua index 77a28f7..137ffb8 100644 --- a/.config/awesome/ui/statusbar/init.lua +++ b/.config/awesome/ui/statusbar/init.lua @@ -1,18 +1,17 @@ local awful = require "awful" local beautiful = require "beautiful" local panel = require "ui.statusbar.panel" -local phosphor = require "assets.phosphor" -local qstore = require "quarrel.store" +local qanim = require "quarrel.animation" local qui = require "quarrel.ui" local qvars = require "quarrel.vars" -local rubato = require "lib.rubato" local wibox = require "wibox" +local M = require "ui.statusbar.consts" + local clock = require "ui.statusbar.widgets.clock" local displays = require "ui.statusbar.widgets.displays" local keyboardlayout = require "ui.statusbar.widgets.keyboardlayout" -local taglist = require "ui.statusbar.widgets.taglist" -local tasklist = require "ui.statusbar.widgets.tasklist" +local taglist = require "ui.statusbar.widgets.taglist_new" screen.connect_signal("request::desktop_decoration", function(s) local bar = qui.popup { @@ -21,17 +20,16 @@ screen.connect_signal("request::desktop_decoration", function(s) margins = beautiful.useless_gap * 2, }) end, - minimum_height = s.geometry.height - (beautiful.useless_gap * 4 + qvars.border_width * 2), + minimum_height = s.geometry.height - (beautiful.useless_gap * 4 + qui.BORDER_WIDTH * 2), widget = { { { taglist(s), layout = wibox.layout.fixed.vertical, - spacing = qvars.padding * 2, + spacing = qui.PADDING * 2, id = "top", }, nil, - -- tasklist(s), { widget = wibox.container.place, valign = "bottom", @@ -44,14 +42,14 @@ screen.connect_signal("request::desktop_decoration", function(s) widget = wibox.container.place, { widget = wibox.container.constraint, - height = qvars.char_height, - width = qvars.char_height, + height = qui.CHAR_HEIGHT, + width = qui.CHAR_HEIGHT, keyboardlayout, }, }, clock, layout = wibox.layout.fixed.vertical, - spacing = qvars.padding * 2, + spacing = qui.PADDING * 2, }, }, layout = wibox.layout.align.vertical, @@ -61,51 +59,47 @@ screen.connect_signal("request::desktop_decoration", function(s) nil, layout = wibox.layout.align.horizontal, }, + + toggled = false, } - local bar_width = bar.width + qvars.border_width * 2 + local bar_width = bar.width + qui.BORDER_WIDTH * 2 + + bar.shape = function(cr, _, h) + qui.shape(cr, bar_width, h) + end bar:struts { left = bar_width + beautiful.useless_gap * 4, } - local panel_width - local panel_toggle = { toggled = false } -- hacky but it works + bar.widget.widget.third = panel - local timed = rubato.timed { + local timed = qanim:new { duration = qvars.anim_duration, - intro = qvars.anim_intro, pos = bar_width, + easing = qvars.easing, subscribed = function(pos) - if pos ~= bar_width and panel_toggle.toggled then - bar.widget.widget.third = panel - if panel_width == nil then - panel_width = bar.widget.widget.third.width - end + if pos ~= bar_width and bar.toggled then bar.ontop = true - elseif pos == bar_width and not panel_toggle.toggled then - bar.widget.widget.third = nil + elseif pos == bar_width and not bar.toggled then bar.ontop = false end bar.shape = function(cr, _, h) - qvars.shape(cr, pos, h) + qui.shape(cr, pos, h) end end, } - panel_toggle = qui.toggle { - off = phosphor.arrows_out_simple_fill, - on = phosphor.arrows_in_simple_fill, - press = function(self) - if not self.toggled then - timed.target = bar_width - else - timed.target = bar_width + qvars.expanded_bar_size - end - end, - } + function bar:toggle() + self.toggled = not self.toggled + if self.toggled then + timed:set(bar_width + M.EXPANDED_BAR_SIZE) + else + timed:set(bar_width) + end + end - bar.widget:get_children_by_id("top")[1]:insert(1, panel_toggle) - qstore.panel_toggle = panel_toggle + s.bar = bar end) diff --git a/.config/awesome/ui/statusbar/panel/init.lua b/.config/awesome/ui/statusbar/panel/init.lua index 31c2860..814a5a1 100644 --- a/.config/awesome/ui/statusbar/panel/init.lua +++ b/.config/awesome/ui/statusbar/panel/init.lua @@ -1,51 +1,61 @@ -local qvars = require "quarrel.vars" +local C = require "ui.statusbar.consts" +local qui = require "quarrel.ui" local wibox = require "wibox" local battery = require "ui.statusbar.panel.widgets.battery" local calendar = require "ui.statusbar.panel.widgets.calendar" local displays = require "ui.statusbar.panel.widgets.displays" -local music = require "ui.statusbar.panel.widgets.music" -local power_menu = require "ui.statusbar.panel.widgets.power_menu" +local music = require "ui.statusbar.panel.widgets.mpris" local wifi = require "ui.statusbar.panel.widgets.wifi" local panel = wibox.widget { - widget = wibox.container.margin, - margins = { - left = qvars.big_padding, - }, + widget = wibox.container.constraint, + strategy = "max", + width = C.EXPANDED_BAR_SIZE, + -- visible = false, { + widget = wibox.container.background, { - widget = wibox.container.place, - valign = "top", + widget = wibox.container.margin, + margins = { + left = qui.BIG_PADDING, + }, { { - displays.battery, - displays.audio, - displays.brightness, - layout = wibox.layout.fixed.horizontal, - spacing = qvars.padding, + widget = wibox.container.place, + valign = "top", + { + { + displays.battery, + displays.audio, + -- displays.brightness, + -- displays.brightness, + layout = wibox.layout.flex.horizontal, + spacing = qui.PADDING, + }, + wifi, + -- battery, + music.widget, + calendar, + layout = wibox.layout.fixed.vertical, + spacing = qui.PADDING, + }, }, - wifi, - -- battery, - -- music, - calendar, - layout = wibox.layout.fixed.vertical, - spacing = qvars.padding, - }, - }, - { - widget = wibox.container.background, - { - widget = wibox.widget.textbox, - text = ":)", + -- { + -- widget = wibox.container.background, + -- { + -- widget = wibox.widget.textbox, + -- text = ":)", + -- }, + -- }, + -- { + -- widget = wibox.container.place, + -- valign = "bottom", + -- power_menu, + -- }, + layout = wibox.layout.align.vertical, }, }, - { - widget = wibox.container.place, - valign = "bottom", - power_menu, - }, - layout = wibox.layout.align.vertical, }, } diff --git a/.config/awesome/ui/statusbar/panel/widgets/battery.lua b/.config/awesome/ui/statusbar/panel/widgets/battery.lua index 52685b7..52cf5da 100644 --- a/.config/awesome/ui/statusbar/panel/widgets/battery.lua +++ b/.config/awesome/ui/statusbar/panel/widgets/battery.lua @@ -1,6 +1,5 @@ local gears = require "gears" local lit = require "lib.lit" -local qbezier = require "quarrel.bezier" local qui = require "quarrel.ui" local wibox = require "wibox" diff --git a/.config/awesome/ui/statusbar/panel/widgets/calendar.lua b/.config/awesome/ui/statusbar/panel/widgets/calendar.lua index 8933543..ddbffc0 100644 --- a/.config/awesome/ui/statusbar/panel/widgets/calendar.lua +++ b/.config/awesome/ui/statusbar/panel/widgets/calendar.lua @@ -1,58 +1,123 @@ local awful = require "awful" local phosphor = require "assets.phosphor" local qbind = require "quarrel.bind" +local qcolor = require "quarrel.color" local qmarkup = require "quarrel.markup" local qui = require "quarrel.ui" -local qvars = require "quarrel.vars" local wibox = require "wibox" local weekday_map = { 7, 1, 2, 3, 4, 5, 6 } local calendar = wibox.widget(qui.styled { widget = wibox.container.background, + bg = qcolor.palette.bg.high, { widget = wibox.container.margin, - margins = qvars.big_padding, + margins = qui.BORDER_WIDTH, { { { - widget = wibox.container.place, - qui.icon { - icon = phosphor.caret_left_bold, + layout = wibox.layout.grid, + forced_num_rows = 7, + forced_num_cols = 7, + -- spacing = (qui.PADDING) / 2, + border_width = { + inner = qui.BORDER_WIDTH, + outer = 0, }, + border_color = qcolor.palette.border.variant, + id = "grid", }, { - widget = wibox.container.place, - { - widget = wibox.widget.textbox, - text = "January 2024", - }, + widget = wibox.container.background, + bg = qcolor.palette.border(), + forced_width = qui.BORDER_WIDTH, }, + layout = wibox.layout.fixed.horizontal, + }, + { + widget = wibox.container.margin, + margins = qui.BIG_PADDING, { - widget = wibox.container.place, - qui.icon { - icon = phosphor.caret_right_bold, + { + widget = wibox.container.place, + fill_horizontal = true, + { + widget = wibox.widget.textbox, + text = "05\n25", + }, }, + nil, + qui.styled { + widget = wibox.container.background, + bg = qcolor.palette.bg.highest, + { + { + widget = wibox.container.margin, + margins = qui.PADDING, + { + widget = wibox.container.place, + qui.icon { + icon = phosphor.caret_up_bold, + widget = { + forced_height = qui.CHAR_HEIGHT / 2, + forced_width = qui.CHAR_HEIGHT / 2, + }, + }, + }, + }, + { + widget = wibox.container.background, + bg = qcolor.palette.border.variant, + forced_height = qui.BORDER_WIDTH, + }, + { + widget = wibox.container.margin, + margins = qui.PADDING, + { + widget = wibox.container.place, + qui.icon { + icon = phosphor.caret_down_bold, + widget = { + forced_height = qui.CHAR_HEIGHT / 2, + forced_width = qui.CHAR_HEIGHT / 2, + }, + }, + }, + }, + layout = wibox.layout.fixed.vertical, + -- spacing = qui.PADDING + }, + }, + layout = wibox.layout.align.vertical, }, - layout = wibox.layout.align.horizontal, - expand = "outside", - }, - { - layout = wibox.layout.grid, - forced_num_rows = 7, - forced_num_cols = 7, - spacing = qvars.padding, - id = "grid", }, - layout = wibox.layout.fixed.vertical, - spacing = qvars.big_padding, + layout = wibox.layout.fixed.horizontal, }, }, }) +local grid = calendar:get_children_by_id("grid")[1] + -- Logic heavily inspired by https://github.com/Sinomor/dotfiles/blob/e409f9a84bf40daf1e39c0179ec749232ed827c9/home/.config/awesome/ui/control/moment/calendar.lua#L134-L173 function calendar:compute_grid(year, month) - local calendar_table = { {}, {}, {}, {}, {}, {} } + local calendar_table = { + { + { "Mo", false, true }, + { "Tu", false, true }, + { "We", false, true }, + { "Th", false, true }, + { "Fr", false, true }, + { "Sa", false, true }, + { "Su", false, true }, + }, + {}, + {}, + {}, + {}, + {}, + {}, + } local current = os.date("*t", os.time()) local first_day = os.date("*t", os.time { year = year, month = month, day = 1 }) local last_day = os.date("*t", os.time { year = year, month = month + 1, day = 0 }) @@ -68,14 +133,14 @@ function calendar:compute_grid(year, month) for offset = prev_offset, 1, -1 do local day = prev_days.day - offset + 1 table.insert( - calendar_table[1], + calendar_table[2], { day, day == current.day and prev_month == current.month and prev_year == current.year, false } ) end end do - local row = 1 + local row = 2 local weekday = weekday_map[first_day.wday] for day = 1, days do table.insert( @@ -108,14 +173,22 @@ function calendar:compute_grid(year, month) for i, row in ipairs(calendar_table) do for j, col in ipairs(row) do - local widget = self:get_children_by_id("grid")[1]:get_widgets_at(i + 1, j)[1] - widget.widget.widget.text = col[1] + local widget = grid:get_widgets_at(i, j)[1] + widget.widget.widget.widget.text = col[1] if col[2] then - widget.bg = qvars.colors.yellow - widget.fg = qvars.colors.bg + widget.bg = qcolor.palette.yellow() + widget.fg = qcolor.palette.bg() + elseif j % 6 == 0 or j % 7 == 0 then + widget.bg = col[3] and qcolor.palette.bg.highest or qcolor.palette.bg.high else - widget.bg = qvars.colors.bg - widget.fg = col[3] and qvars.colors.fg or qvars.colors.dim.fg + widget.bg = col[3] and qcolor.palette.bg.high or qcolor.palette.bg() + end + + if i == 1 then + widget.fg = qcolor.palette.fg() + elseif not col[2] then + -- widget.fg = col[3] and qcolor.palette.fg() or qcolor.palette.fg.low + widget.fg = qcolor.palette.fg.low end end end @@ -125,32 +198,31 @@ local cells = {} local function cell(content) local widget = wibox.widget { widget = wibox.container.background, - shape = qvars.shape, + bg = qcolor.palette.bg.high, + -- shape = qui.shape, { - widget = wibox.container.place, - forced_height = qvars.char_height * 1.5, - forced_width = qvars.char_height * 1.5, + widget = wibox.container.margin, + margins = qui.PADDING * 1.5, { - widget = wibox.widget.textbox, - markup = content, + widget = wibox.container.place, + forced_height = qui.CHAR_HEIGHT, + forced_width = qui.CHAR_HEIGHT, + { + widget = wibox.widget.textbox, + markup = content, + }, }, }, } table.insert(cells, widget) end -cell "Mo" -cell "Tu" -cell "We" -cell "Th" -cell "Fr" -cell(qmarkup("Sa", { bold = true })) -cell(qmarkup("Su", { bold = true })) - -for _ = 1, 42 do +for _ = 1, 49 do cell() end -calendar:get_children_by_id("grid")[1]:add(table.unpack(cells)) -calendar:compute_grid(2024, 1) +grid:add(table.unpack(cells)) +local current_time = os.date "*t" +calendar:compute_grid(current_time.year, current_time.month) +grid:add_row_border(2, qui.BORDER_WIDTH, { color = qcolor.palette.border() }) return calendar diff --git a/.config/awesome/ui/statusbar/panel/widgets/displays.lua b/.config/awesome/ui/statusbar/panel/widgets/displays.lua index b52c986..bfb9113 100644 --- a/.config/awesome/ui/statusbar/panel/widgets/displays.lua +++ b/.config/awesome/ui/statusbar/panel/widgets/displays.lua @@ -1,37 +1,43 @@ -local backlight = require "services.backlight" +-- local backlight = require "services.backlight" local battery = require "services.battery" local phosphor = require "assets.phosphor" -local qmath = require "quarrel.math" +-- local qmath = require "quarrel.math" +local C = require "ui.statusbar.consts" +local qcolor = require "quarrel.color" local qui = require "quarrel.ui" -local qvars = require "quarrel.vars" local wibox = require "wibox" local function create_display(icon, color) + -- the reason this is done is because ids can't be found with `get_children_by_id` across widget boundaries, + -- so we return it as a second value in order to manipulate the icon + local d_icon = qui.icon { + icon = icon, + color = color, + widget = { + id = "icon", + }, + } + return wibox.widget(qui.styled { widget = wibox.container.background, + bg = qcolor.palette.bg.high, { widget = wibox.container.margin, - margins = qvars.big_padding, + margins = qui.BIG_PADDING, { widget = wibox.container.place, { { widget = wibox.container.radialprogressbar, max_value = 100, - border_color = qvars.colors.black, + border_color = qcolor.palette.bg.lowest, color = color, - border_width = qvars.border_width * 2, - forced_height = qvars.element_size * 4, - forced_width = qvars.element_size * 4, + border_width = qui.BORDER_WIDTH * 2, + forced_height = C.ELEMENT_SIZE * 4, + forced_width = C.ELEMENT_SIZE * 4, { widget = wibox.container.place, - qui.icon { - icon = icon, - color = color, - widget = { - id = "icon", - }, - }, + d_icon, }, id = "indicator", }, @@ -44,14 +50,15 @@ local function create_display(icon, color) }, }, layout = wibox.layout.fixed.vertical, - spacing = qvars.big_padding, + spacing = qui.BIG_PADDING, }, }, }, - }) + }), + d_icon end -local d_audio = create_display(phosphor.speaker_simple_high_fill, qvars.colors.fg) +local d_audio, d_audio_icon = create_display(phosphor.speaker_simple_high_fill, qcolor.palette.fg()) awesome.connect_signal("services::audio", function(volume) if not volume then return @@ -61,30 +68,33 @@ awesome.connect_signal("services::audio", function(volume) end) awesome.connect_signal("services::audio::icon", function(icon, color) d_audio:get_children_by_id("indicator")[1].color = color - d_audio:get_children_by_id("icon")[1].image = icon - d_audio:get_children_by_id("icon")[1].stylesheet = qui.recolor(color) + d_audio_icon.image = icon + d_audio_icon.stylesheet = qui.recolor(color) end) -local d_battery = create_display(phosphor.battery_vertical_warning_fill, qvars.colors.red) +local d_battery, d_battery_icon = create_display(phosphor.battery_vertical_warning_fill, qcolor.palette.red()) battery:connect_signal("level", function(_, level) d_battery:get_children_by_id("indicator")[1].value = level d_battery:get_children_by_id("text")[1].text = level .. "%" end) battery:connect_signal("icon", function(_, icon, color) d_battery:get_children_by_id("indicator")[1].color = color - d_battery:get_children_by_id("icon")[1].image = icon - d_battery:get_children_by_id("icon")[1].stylesheet = qui.recolor(color) + d_battery_icon.image = icon + d_battery_icon.stylesheet = qui.recolor(color) end) -local d_brightness = create_display(phosphor.sun_fill, qvars.colors.fg) -backlight:connect_signal("value", function(_, brightness) - brightness = math.floor(qmath.translate_range(brightness, 0, 255, 0, 100)) - d_brightness:get_children_by_id("indicator")[1].value = brightness - d_brightness:get_children_by_id("text")[1].text = brightness .. "%" -end) -backlight:connect_signal("icon", function(_, icon, color) - d_brightness:get_children_by_id("icon")[1].image = icon - d_brightness:get_children_by_id("icon")[1].stylesheet = qui.recolor(color) -end) +-- local d_brightness, d_brightness_icon = create_display(phosphor.sun_fill, qcolor.palette.fg()) +-- backlight:connect_signal("value", function(_, brightness) +-- brightness = math.floor(qmath.translate_range(brightness, 0, 255, 0, 100)) +-- d_brightness:get_children_by_id("indicator")[1].value = brightness +-- d_brightness:get_children_by_id("text")[1].text = brightness .. "%" +-- end) +-- backlight:connect_signal("icon", function(_, icon, color) +-- d_brightness_icon.image = icon +-- d_brightness_icon.stylesheet = qui.recolor(color) +-- end) -return { audio = d_audio, battery = d_battery, brightness = d_brightness } +return { + audio = d_audio, + battery = d_battery, --[[brightness = d_brightness]] +} diff --git a/.config/awesome/ui/statusbar/panel/widgets/imagebox.lua b/.config/awesome/ui/statusbar/panel/widgets/imagebox.lua deleted file mode 100644 index 5caadc5..0000000 --- a/.config/awesome/ui/statusbar/panel/widgets/imagebox.lua +++ /dev/null @@ -1,720 +0,0 @@ ---------------------------------------------------------------------------- --- A widget to display an image. --- --- The `wibox.widget.imagebox` is part of the Awesome WM's widget system --- (see @{03-declarative-layout.md}). --- --- This widget displays an image. The image can be a file, --- a cairo image surface, or an rsvg handle object (see the --- [image property](#image)). --- --- Examples using a `wibox.widget.imagebox`: --- --- --- --- @DOC_wibox_widget_defaults_imagebox_EXAMPLE@ --- --- Alternatively, you can declare the `imagebox` widget using the --- declarative pattern (both variants are strictly equivalent): --- --- @DOC_wibox_widget_declarative-pattern_imagebox_EXAMPLE@ --- --- @author Uli Schlachter --- @copyright 2010 Uli Schlachter --- @widgetmod wibox.widget.imagebox --- @supermodule wibox.widget.base ---------------------------------------------------------------------------- - -local lgi = require "lgi" -local cairo = lgi.cairo - -local base = require "wibox.widget.base" -local gdebug = require "gears.debug" -local gtable = require "gears.table" -local surface = require "gears.surface" -local setmetatable = setmetatable -local type = type -local math = math - -local unpack = unpack or table.unpack -- luacheck: globals unpack (compatibility with Lua 5.1) - --- Safe load for optional Rsvg module -local Rsvg = nil -do - local success, err = pcall(function() - Rsvg = lgi.Rsvg - end) - if not success then - gdebug.print_warning(debug.traceback("Could not load Rsvg: " .. tostring(err))) - end -end - -local imagebox = { mt = {} } - -local rsvg_handle_cache = setmetatable({}, { __mode = "k" }) - ----Load rsvg handle form image file --- @tparam string file Path to svg file. --- @return Rsvg handle --- @treturn table A table where cached data can be stored. -local function load_rsvg_handle(file) - if not Rsvg then - return - end - - local cache = (rsvg_handle_cache[file] or {})["handle"] - - if cache then - return cache, rsvg_handle_cache[file] - end - - local handle, err - - if file:match "<[?]?xml" or file:match "<svg" then - handle, err = Rsvg.Handle.new_from_data(file) - else - handle, err = Rsvg.Handle.new_from_file(file) - end - - if not err then - rsvg_handle_cache[file] = rsvg_handle_cache[file] or {} - rsvg_handle_cache[file]["handle"] = handle - return handle, rsvg_handle_cache[file] - end -end - ----Apply cairo surface for given imagebox widget -local function set_surface(ib, surf) - local is_surf_valid = surf.width > 0 and surf.height > 0 - if not is_surf_valid then - return false - end - - ib._private.default = { width = surf.width, height = surf.height } - ib._private.handle = nil - ib._private.image = surf - return true -end - ----Apply RsvgHandle for given imagebox widget -local function set_handle(ib, handle, cache) - local dim = handle:get_dimensions() - local is_handle_valid = dim.width > 0 and dim.height > 0 - if not is_handle_valid then - return false - end - - ib._private.default = { width = dim.width, height = dim.height } - ib._private.handle = handle - ib._private.cache = cache - ib._private.image = nil - - return true -end - ----Try to load some image object from file then apply it to imagebox. ----@tparam table ib Imagebox ----@tparam string file Image file name ----@tparam function image_loader Function to load image object from file ----@tparam function image_setter Function to set image object to imagebox ----@treturn boolean True if image was successfully applied -local function load_and_apply(ib, file, image_loader, image_setter) - local image_applied - local object, cache = image_loader(file) - - if object then - image_applied = image_setter(ib, object, cache) - end - return image_applied -end - ----Update the cached size depending on the stylesheet and dpi. --- --- It's necessary because a single RSVG handle can be used by --- many imageboxes. So DPI and Stylesheet need to be set each time. -local function update_dpi(self, ctx) - if not self._private.handle then - return - end - - local dpi = self._private.auto_dpi and ctx.dpi or self._private.dpi or nil - - local need_dpi = dpi and self._private.last_dpi ~= dpi - - local need_style = self._private.handle.set_stylesheet and self._private.stylesheet - - local old_size = self._private.default and self._private.default.width - - if dpi and dpi ~= self._private.cache.dpi then - if type(dpi) == "table" then - self._private.handle:set_dpi_x_y(dpi.x, dpi.y) - else - self._private.handle:set_dpi(dpi) - end - end - - if need_style and self._private.cache.stylesheet ~= self._private.stylesheet then - self._private.handle:set_stylesheet(self._private.stylesheet) - end - - -- Reload the size. - if need_dpi or (need_style and self._private.stylesheet ~= self._private.last_stylesheet) then - set_handle(self, self._private.handle, self._private.cache) - end - - self._private.last_dpi = dpi - self._private.cache.dpi = dpi - self._private.last_stylesheet = self._private.stylesheet - self._private.cache.stylesheet = self._private.stylesheet - - -- This can happen in the constructor when `dpi` is set after `image`. - if old_size and old_size ~= self._private.default.width then - self:emit_signal "widget::redraw_needed" - self:emit_signal "widget::layout_changed" - end -end - --- Draw an imagebox with the given cairo context in the given geometry. -function imagebox:draw(ctx, cr, width, height) - if width == 0 or height == 0 or not self._private.default then - return - end - - -- For valign = "top" and halign = "left" - local translate = { - x = 0, - y = 0, - } - - update_dpi(self, ctx) - - local w, h = self._private.default.width, self._private.default.height - - if self._private.resize then - -- That's for the "fit" policy. - local aspects = { - w = width / w, - h = height / h, - } - - local policy = { - w = self._private.horizontal_fit_policy or "auto", - h = self._private.vertical_fit_policy or "auto", - } - - for _, aspect in ipairs { "w", "h" } do - if self._private.upscale == false and (w < width and h < height) then - aspects[aspect] = 1 - elseif self._private.downscale == false and (w >= width and h >= height) then - aspects[aspect] = 1 - elseif policy[aspect] == "none" then - aspects[aspect] = 1 - elseif policy[aspect] == "auto" then - aspects[aspect] = math.min(width / w, height / h) - elseif policy[aspect] == "cover" then - aspects[aspect] = math.max(width / w, height / h) - end - end - - if self._private.halign == "center" then - translate.x = math.floor((width - w * aspects.w) / 2) - elseif self._private.halign == "right" then - translate.x = math.floor(width - (w * aspects.w)) - end - - if self._private.valign == "center" then - translate.y = math.floor((height - h * aspects.h) / 2) - elseif self._private.valign == "bottom" then - translate.y = math.floor(height - (h * aspects.h)) - end - - cr:translate(translate.x, translate.y) - - -- Before using the scale, make sure it is below the threshold. - local threshold, max_factor = self._private.max_scaling_factor, math.max(aspects.w, aspects.h) - - if threshold and threshold > 0 and threshold < max_factor then - aspects.w = (aspects.w * threshold) / max_factor - aspects.h = (aspects.h * threshold) / max_factor - end - - -- Set the clip - if self._private.clip_shape then - cr:clip(self._private.clip_shape(cr, w * aspects.w, h * aspects.h, unpack(self._private.clip_args))) - end - - cr:scale(aspects.w, aspects.h) - else - if self._private.halign == "center" then - translate.x = math.floor((width - w) / 2) - elseif self._private.halign == "right" then - translate.x = math.floor(width - w) - end - - if self._private.valign == "center" then - translate.y = math.floor((height - h) / 2) - elseif self._private.valign == "bottom" then - translate.y = math.floor(height - h) - end - - cr:translate(translate.x, translate.y) - - -- Set the clip - if self._private.clip_shape then - cr:clip(self._private.clip_shape(cr, w, h, unpack(self._private.clip_args))) - end - end - - if self._private.handle then - self._private.handle:render_cairo(cr) - else - cr:set_source_surface(self._private.image, 0, 0) - - local filter = self._private.scaling_quality - - if filter then - cr:get_source():set_filter(cairo.Filter[filter:upper()]) - end - - cr:paint() - end -end - --- Fit the imagebox into the given geometry -function imagebox:fit(ctx, width, height) - if not self._private.default then - return 0, 0 - end - - update_dpi(self, ctx) - - local w, h = self._private.default.width, self._private.default.height - - if w <= width and h <= height and self._private.upscale == false then - return w, h - end - - if (w < width or h < height) and self._private.downscale == false then - return w, h - end - - if self._private.resize or w > width or h > height then - local aspect = math.min(width / w, height / h) - return w * aspect, h * aspect - end - - return w, h -end - ---- The image rendered by the `imagebox`. --- --- @property image --- @tparam[opt=nil] image|nil image --- @propemits false false - ---- Set the `imagebox` image. --- --- The image can be a file, a cairo image surface, or an rsvg handle object --- (see the [image property](#image)). --- @method set_image --- @hidden --- @tparam image image The image to render. --- @treturn boolean `true` on success, `false` if the image cannot be used. --- @usage my_imagebox:set_image(beautiful.awesome_icon) --- @usage my_imagebox:set_image('/usr/share/icons/theme/my_icon.png') --- @see image -function imagebox:set_image(image) - local setup_succeed - - -- Keep the original to prevent the cache from being GCed. - self._private.original_image = image - - if type(image) == "userdata" and not (Rsvg and Rsvg.Handle:is_type_of(image)) then - -- This function is not documented to handle userdata objects, but - -- historically it did, and it did by just assuming they refer to a - -- cairo surface. - image = surface.load(image) - end - - if type(image) == "string" then - -- try to load rsvg handle from file - setup_succeed = load_and_apply(self, image, load_rsvg_handle, set_handle) - - if not setup_succeed then - -- rsvg handle failed, try to load cairo surface with pixbuf - setup_succeed = load_and_apply(self, image, surface.load, set_surface) - end - elseif Rsvg and Rsvg.Handle:is_type_of(image) then - -- try to apply given rsvg handle - rsvg_handle_cache[image] = rsvg_handle_cache[image] or {} - setup_succeed = set_handle(self, image, rsvg_handle_cache[image]) - elseif cairo.Surface:is_type_of(image) then - -- try to apply given cairo surface - setup_succeed = set_surface(self, image) - elseif not image then - -- nil as argument mean full imagebox reset - setup_succeed = true - self._private.handle = nil - self._private.image = nil - self._private.default = nil - end - - if not setup_succeed then - return false - end - - self:emit_signal "widget::redraw_needed" - self:emit_signal "widget::layout_changed" - self:emit_signal "property::image" - return true -end - ---- Set a clip shape for this imagebox. --- --- A clip shape defines an area and dimension to which the content should be --- trimmed. --- --- @DOC_wibox_widget_imagebox_clip_shape_EXAMPLE@ --- --- @property clip_shape --- @tparam[opt=gears.shape.rectangle] shape clip_shape A `gears.shape` compatible shape function. --- @propemits true false --- @see gears.shape - ---- Set a clip shape for this imagebox. --- --- A clip shape defines an area and dimensions to which the content should be --- trimmed. --- --- Additional parameters will be passed to the clip shape function. --- --- @tparam function|gears.shape clip_shape A `gears_shape` compatible shape function. --- @method set_clip_shape --- @hidden --- @see gears.shape --- @see clip_shape -function imagebox:set_clip_shape(clip_shape, ...) - self._private.clip_shape = clip_shape - self._private.clip_args = { ... } - self:emit_signal "widget::redraw_needed" - self:emit_signal("property::clip_shape", clip_shape) -end - ---- Should the image be resized to fit into the available space? --- --- Note that `upscale` and `downscale` can affect the value of `resize`. --- If conflicting values are passed to the constructor, then the result --- is undefined. --- --- @DOC_wibox_widget_imagebox_resize_EXAMPLE@ --- @property resize --- @propemits true false --- @tparam[opt=true] boolean resize - ---- Allow the image to be upscaled (made bigger). --- --- Note that `upscale` and `downscale` can affect the value of `resize`. --- If conflicting values are passed to the constructor, then the result --- is undefined. --- --- @DOC_wibox_widget_imagebox_upscale_EXAMPLE@ --- @property upscale --- @tparam[opt=self.resize] boolean upscale --- @see downscale --- @see resize - ---- Allow the image to be downscaled (made smaller). --- --- Note that `upscale` and `downscale` can affect the value of `resize`. --- If conflicting values are passed to the constructor, then the result --- is undefined. --- --- @DOC_wibox_widget_imagebox_downscale_EXAMPLE@ --- @property downscale --- @tparam[opt=self.resize] boolean downscale --- @see upscale --- @see resize - ---- Set the SVG CSS stylesheet. --- --- If the image is an SVG (vector graphics), this property allows to set --- a CSS stylesheet. It can be used to set colors and much more. --- --- Note that this property is a string, not a path. If the stylesheet is --- stored on disk, read the content first. --- ---@DOC_wibox_widget_imagebox_stylesheet_EXAMPLE@ --- --- @property stylesheet --- @tparam[opt=""] string stylesheet --- @propemits true false - ---- Set the SVG DPI (dot per inch). --- --- Force a specific DPI when rendering the `.svg`. For other file formats, --- this does nothing. --- --- It can either be a number of a table containing the `x` and `y` keys. --- --- Please note that DPI and `resize` can "fight" each other and end up --- making the image smaller instead of bigger. --- ---@DOC_wibox_widget_imagebox_dpi_EXAMPLE@ --- --- @property dpi --- @tparam[opt=96] number|table dpi --- @negativeallowed false --- @propemits true false --- @see auto_dpi - ---- Use the object DPI when rendering the SVG. --- --- By default, the SVG are interpreted as-is. When this property is set, --- the screen DPI will be passed to the SVG renderer. Depending on which --- tool was used to create the `.svg`, this may do nothing at all. However, --- for example, if the `.svg` uses `<text>` elements and doesn't have an --- hardcoded stylesheet, the result will differ. --- --- @property auto_dpi --- @tparam[opt=false] boolean auto_dpi --- @propemits true false --- @see dpi - -for _, prop in ipairs { "stylesheet", "dpi", "auto_dpi" } do - imagebox["set_" .. prop] = function(self, value) - -- It will be set in :fit and :draw. The handle is shared - -- by multiple imagebox, so it cannot be set just once. - self._private[prop] = value - - self:emit_signal "widget::redraw_needed" - self:emit_signal "widget::layout_changed" - self:emit_signal("property::" .. prop) - end -end - -function imagebox:set_resize(allowed) - self._private.resize = allowed - - if allowed then - self._private.downscale = true - self._private.upscale = true - self:emit_signal("property::downscale", allowed) - self:emit_signal("property::upscale", allowed) - end - - self:emit_signal "widget::redraw_needed" - self:emit_signal "widget::layout_changed" - self:emit_signal("property::resize", allowed) -end - -for _, prop in ipairs { "downscale", "upscale" } do - imagebox["set_" .. prop] = function(self, allowed) - self._private[prop] = allowed - - if self._private.resize ~= (self._private.upscale or self._private.downscale) then - self._private.resize = self._private.upscale or self._private.downscale - self:emit_signal("property::resize", self._private.resize) - end - - self:emit_signal "widget::redraw_needed" - self:emit_signal "widget::layout_changed" - self:emit_signal("property::" .. prop, allowed) - end -end - ---- Set the horizontal fit policy. --- --- Here is the result for a 22x32 image: --- --- @DOC_wibox_widget_imagebox_horizontal_fit_policy_EXAMPLE@ --- --- @property horizontal_fit_policy --- @tparam[opt="auto"] string horizontal_fit_policy --- @propertyvalue "auto" Honor the `resize` variable and preserve the aspect ratio. --- @propertyvalue "none" Do not resize at all. --- @propertyvalue "fit" Resize to the widget width. --- @propertyvalue "cover" Resize to fill widget and preserve the aspect ratio. --- @propemits true false --- @see vertical_fit_policy --- @see resize - ---- Set the vertical fit policy. --- --- Here is the result for a 32x22 image: --- --- @DOC_wibox_widget_imagebox_vertical_fit_policy_EXAMPLE@ --- --- @property vertical_fit_policy --- @tparam[opt="auto"] string vertical_fit_policy --- @propertyvalue "auto" Honor the `resize` variable and preserve the aspect ratio. --- @propertyvalue "none" Do not resize at all. --- @propertyvalue "fit" Resize to the widget height. --- @propertyvalue "cover" Resize to fill widget and preserve the aspect ratio. --- @propemits true false --- @see horizontal_fit_policy --- @see resize - ---- The vertical alignment. --- --- @DOC_wibox_widget_imagebox_valign_EXAMPLE@ --- --- @property valign --- @tparam[opt="center"] string valign --- @propertyvalue "top" --- @propertyvalue "center" --- @propertyvalue "bottom" --- @propemits true false --- @see wibox.container.place --- @see halign - ---- The horizontal alignment. --- --- @DOC_wibox_widget_imagebox_halign_EXAMPLE@ --- --- @property halign --- @tparam[opt="center"] string halign --- @propertyvalue "left" --- @propertyvalue "center" --- @propertyvalue "right" --- @propemits true false --- @see wibox.container.place --- @see valign - ---- The maximum scaling factor. --- --- If an image is scaled too much, it gets very blurry. This --- property allows to limit the scaling. --- Use the properties `valign` and `halign` to control how the image will be --- aligned. --- --- In the example below, the original size is 22x22 --- --- @DOC_wibox_widget_imagebox_max_scaling_factor_EXAMPLE@ --- --- @property max_scaling_factor --- @tparam[opt=0] number max_scaling_factor Use `0` for "no limit". --- @negativeallowed false --- @propemits true false --- @see valign --- @see halign --- @see scaling_quality - ---- Set the scaling aligorithm. --- --- Depending on how the image is used, what is the "correct" way to --- scale can change. For example, upscaling a pixel art image should --- not make it blurry. However, scaling up a photo should not make it --- blocky. --- ---<table class='widget_list' border=1> --- <tr style='font-weight: bold;'> --- <th align='center'>Value</th> --- <th align='center'>Description</th> --- </tr> --- <tr><td>fast</td><td>A high-performance filter</td></tr> --- <tr><td>good</td><td>A reasonable-performance filter</td></tr> --- <tr><td>best</td><td>The highest-quality available</td></tr> --- <tr><td>nearest</td><td>Nearest-neighbor filtering (blocky)</td></tr> --- <tr><td>bilinear</td><td>Linear interpolation in two dimensions</td></tr> ---</table> --- --- The image used in the example below has a resolution of 32x22 and is --- intentionally blocky to highlight the difference. --- It is zoomed by a factor of 3. --- --- @DOC_wibox_widget_imagebox_scaling_quality_EXAMPLE@ --- --- @property scaling_quality --- @tparam[opt="good"] string scaling_quality --- @propertyvalue "fast" A high-performance filter. --- @propertyvalue "good" A reasonable-performance filter. --- @propertyvalue "best" The highest-quality available. --- @propertyvalue "nearest" Nearest-neighbor filtering (blocky). --- @propertyvalue "bilinear" Linear interpolation in two dimensions. --- @propemits true false --- @see resize --- @see horizontal_fit_policy --- @see vertical_fit_policy --- @see max_scaling_factor - -local defaults = { - halign = "left", - valign = "top", - horizontal_fit_policy = "auto", - vertical_fit_policy = "auto", - max_scaling_factor = 0, - scaling_quality = "good", -} - -local function get_default(prop, value) - if value == nil then - return defaults[prop] - end - - return value -end - -for prop in pairs(defaults) do - imagebox["set_" .. prop] = function(self, value) - if value == self._private[prop] then - return - end - - self._private[prop] = get_default(prop, value) - self:emit_signal "widget::redraw_needed" - self:emit_signal("property::" .. prop, self._private[prop]) - end - - imagebox["get_" .. prop] = function(self) - if self._private[prop] == nil then - return defaults[prop] - end - - return self._private[prop] - end -end - ---- Returns a new `wibox.widget.imagebox` instance. --- --- This is the constructor of `wibox.widget.imagebox`. It creates a new --- instance of imagebox widget. --- --- Alternatively, the declarative layout syntax can handle --- `wibox.widget.imagebox` instanciation. --- --- The image can be a file, a cairo image surface, or an rsvg handle object --- (see the [image property](#image)). --- --- Any additional arguments will be passed to the clip shape function. --- @tparam[opt] image image The image to display (may be `nil`). --- @tparam[opt] boolean resize_allowed If `false`, the image will be --- clipped, else it will be resized to fit into the available space. --- @tparam[opt] function clip_shape A `gears.shape` compatible function. --- @treturn wibox.widget.imagebox A new `wibox.widget.imagebox` widget instance. --- @constructorfct wibox.widget.imagebox -local function new(image, resize_allowed, clip_shape, ...) - local ret = base.make_widget(nil, nil, { enable_properties = true }) - - gtable.crush(ret, imagebox, true) - ret._private.resize = true - - if image then - ret:set_image(image) - end - - if resize_allowed ~= nil then - ret.resize = resize_allowed - end - - ret._private.clip_shape = clip_shape - ret._private.clip_args = { ... } - - return ret -end - -function imagebox.mt:__call(...) - return new(...) -end - -return setmetatable(imagebox, imagebox.mt) - --- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 diff --git a/.config/awesome/ui/statusbar/panel/widgets/mpris.lua b/.config/awesome/ui/statusbar/panel/widgets/mpris.lua new file mode 100644 index 0000000..c27bf66 --- /dev/null +++ b/.config/awesome/ui/statusbar/panel/widgets/mpris.lua @@ -0,0 +1,429 @@ +local custom = require "assets.custom" +local gdebug = require "gears.debug" +local phosphor = require "assets.phosphor" +local playerctl = require "services.playerctl" +local qcolor = require "quarrel.color" +local qpersistent = require "quarrel.persistent" +local qui = require "quarrel.ui" +local qvars = require "quarrel.vars" +local simpleicons = require "assets.simpleicons" +local wibox = require "wibox" + +local M = {} + +local client_icons = { -- this is used to map the mpris clients to icons + firefox = simpleicons.librewolf, -- librewolf uses the same player id as firefox + spotify = simpleicons.spotify, + mpd = custom.vinyl_record_fill, + __generic = phosphor.waveform_fill, -- used for any client not in the map +} + +local DEFAULTS = { + ---@type string + title = "Nothing's playing", + artist = {}, + ---@type string + album = "", + progresstext = { + ---@type string + position = "~", + ---@type string + length = "~", + }, + ---@type number + position = 0, + ---@type number + length = math.huge, + client_icon = client_icons.__generic, + art = phosphor.music_notes_fill, +} + +local bg_icon = qui.icon { + icon = DEFAULTS.client_icon, + widget = { + forced_width = 0, + forced_height = 0, + }, + color = qcolor.palette.bg.highest, +} + +local function to_hms(time) + local format = "%i:%02i" + + local h = math.floor(time / 60 ^ 2) + local m = math.floor((time / 60) % 60) + local s = math.floor(time % 60) + + if h > 0 then + format = "%i:" .. format + return string.format(format, h, m, s) + end + + return string.format(format, m, s) +end + +local function is_empty(str) + return (str == nil or #str == 0) +end + +--- mirror of playerctl:list() +--- why bother mirroring? simple: we need to track changes to the player list +--- but vanished and appeared simply don't give us an old version to compare with +---@type Playerctl.data[] +local players = {} + +---@type number +M.active_player_index = qpersistent.get "active_player_index" --[[@as number]] +if not M.active_player_index then + M.active_player_index = 1 + gdebug.print_error "failed to get active_player_index from qpersistent, falling back..." + qpersistent.store("active_player_index", M.active_player_index) +end + +--- the reason we do this instead of some other hack is this is the only way to draw the icons *without* resizing the mpris container +--- this happens because in the panel, height is unlimited, and imagebox grows until a hard limit +---@class wibox.widget.base +local client_background = wibox.container.background() + +function client_background:before_draw_children(_, cr, width, height) + cr:save() + cr:translate(width - (height / 1.25), -(height * 0.125)) + ---@diagnostic disable-next-line: missing-parameter + wibox.widget.draw_to_cairo_context(bg_icon, cr, height * 1.25, height * 1.25) + cr:restore() +end + +M.widget = wibox.widget(qui.styled { + widget = wibox.container.background, + bg = qcolor.palette.bg.high, + { + widget = client_background, + { + widget = wibox.container.margin, + margins = qui.BIG_PADDING, + { + nil, + { + { + { + widget = wibox.container.background, + bg = qcolor.palette.bg(), + shape = qui.shape, + { + widget = wibox.widget.imagebox, + image = DEFAULTS.art, + forced_height = qui.CHAR_HEIGHT * 5, + forced_width = qui.CHAR_HEIGHT * 5, + valign = "center", + halign = "center", + stylesheet = qui.recolor(qcolor.palette.bg.highest), + id = "cover", + }, + }, + { + widget = wibox.container.margin, + right = qui.BIG_PADDING, + left = qui.BIG_PADDING, + { + { + widget = wibox.container.constraint, + height = qui.CHAR_HEIGHT * 2.5, + strategy = "max", + { + widget = wibox.widget.textbox, + text = DEFAULTS.title, -- Song + id = "song", + valign = "top", + }, + }, + { + widget = wibox.container.constraint, + height = qui.CHAR_HEIGHT * 2.5, + strategy = "max", + { + widget = wibox.container.background, + fg = qcolor.palette.fg.low, + { + widget = wibox.widget.textbox, + text = DEFAULTS.artist_album, -- Artist - Album Name + id = "artist_album", + valign = "top", + }, + }, + }, + layout = wibox.layout.fixed.vertical, + }, + }, + layout = wibox.layout.fixed.horizontal, + }, + nil, + { + widget = wibox.container.margin, + top = qui.BIG_PADDING, + right = qui.BIG_PADDING, + { + { + widget = wibox.widget.textbox, + text = DEFAULTS.progresstext.position .. " / " .. DEFAULTS.progresstext.length, -- position / length + id = "progresstext", + }, + { + widget = wibox.container.place, + { + widget = wibox.widget.progressbar, + forced_height = qui.PADDING, + color = qcolor.palette.yellow(), + value = DEFAULTS.position, + max_value = DEFAULTS.length, + background_color = qcolor.palette.bg.lowest, + bar_shape = qui.shape, + shape = qui.shape, + id = "progressbar", + }, + }, + layout = wibox.layout.fixed.horizontal, + spacing = qui.BIG_PADDING, + }, + }, + layout = wibox.layout.align.vertical, + }, + { + layout = wibox.layout.flex.vertical, + spacing = qui.BIG_PADDING, + id = "client_list", + }, + layout = wibox.layout.align.horizontal, + }, + }, + }, +}) + +local layout = M.widget:get_children_by_id("client_list")[1] --[[@as wibox.layout.flex]] +local progressbar = M.widget:get_children_by_id("progressbar")[1] --[[@as wibox.widget.progressbar]] +local progresstext = M.widget:get_children_by_id("progresstext")[1] --[[@as wibox.widget.textbox]] +local song = M.widget:get_children_by_id("song")[1] --[[@as wibox.widget.textbox]] +local artist_album = M.widget:get_children_by_id("artist_album")[1] --[[@as wibox.widget.textbox]] +local cover = M.widget:get_children_by_id("cover")[1] --[[@as wibox.widget.imagebox]] + +local function mirror_player_list() + players = playerctl:list() +end + +mirror_player_list() + +local function handle_metadata(_, player) + local title, album, artist, art + if player ~= playerctl:list()[M.active_player_index] then + return + end + if player then + if player.metadata.title then + title = player.metadata.title + else + title = DEFAULTS.title + end + + if player.metadata.album then + album = player.metadata.album + else + album = DEFAULTS.album + end + + if player.metadata.artist then + artist = player.metadata.artist + else + artist = DEFAULTS.artist + end + + if player.metadata.art and player.metadata.art:match "^file://" then + art = player.metadata.art:gsub("^file://", "") + else + art = DEFAULTS.art + end + else + title = DEFAULTS.title + album = DEFAULTS.album + artist = DEFAULTS.artist + art = DEFAULTS.art + end + + artist_album.text = table.concat(artist, ", ") .. ((is_empty(artist) or is_empty(album)) and "" or " - ") .. album + song.text = title + ---@diagnostic disable-next-line:inject-field + cover.image = art +end + +local function handle_position(_, player) + if player ~= playerctl:list()[M.active_player_index] then + return + end + local position, length + local content = "" + + if player then + if player.position then + position = player.position / playerctl.unit + content = content .. to_hms(position) + else + position = DEFAULTS.position + content = content .. DEFAULTS.progresstext.position + end + + content = content .. " / " + + if player.metadata.length then + length = player.metadata.length / playerctl.unit + content = content .. to_hms(length) + else + length = DEFAULTS.length + content = content .. DEFAULTS.progresstext.length + end + else + position = DEFAULTS.position + length = DEFAULTS.length + content = DEFAULTS.progresstext.position .. " / " .. DEFAULTS.progresstext.length + end + + progresstext.text = content + ---@diagnostic disable-next-line:inject-field + progressbar.value = position + ---@diagnostic disable-next-line:inject-field + progressbar.max_value = length +end + +playerctl:connect_signal("player::metadata", handle_metadata) +playerctl:connect_signal("player::position", handle_position) + +local function update_player(player) + handle_metadata(nil, player) + handle_position(nil, player) + qpersistent.store("active_player_index", M.active_player_index) + + local client_icon = client_icons[(player or { name = "__generic" }).name] + bg_icon.image = client_icon or client_icons.__generic + client_background:emit_signal "widget::redraw_needed" + + for i, child in ipairs(layout.children) do + ---@diagnostic disable-next-line:undefined-field + child:index_handler(i) + end +end + +function M.next_player() + local players_length = #layout.children + if players_length == 0 then + return + end + + if M.active_player_index + 1 > players_length then + M.active_player_index = 1 + else + M.active_player_index = M.active_player_index + 1 + end + + update_player(playerctl:list()[M.active_player_index]) +end + +function M.previous_player() + local players_length = #layout.children + if players_length == 0 then + return + end + + if M.active_player_index - 1 < 1 then + M.active_player_index = players_length + else + M.active_player_index = M.active_player_index - 1 + end + + update_player(playerctl:list()[M.active_player_index]) +end + +---@param diff_player Playerctl.data +local function recalculate_active_player(diff_player, vanished) + if type(diff_player) ~= "table" then + return + end + if #layout.children == 0 then + M.active_player_index = 1 + update_player() + return + end + + local active_player = players[M.active_player_index] + if not active_player then -- we're recovering from a state with no players + update_player(diff_player) + return + end + + if diff_player.instance == active_player.instance and vanished then -- active player vanished; fall back to previous player + M.previous_player() + else -- non-active player appeared/vanished; try to find active player + for i, p in ipairs(playerctl:list()) do + if p.instance == active_player.instance then + M.active_player_index = i + update_player(p) + return + end + end + + gdebug.print_warning( + "failed to find active player:\n " .. gdebug.dump_return(active_player, nil, 2):gsub("\n", "\n ") + ) + M.active_player_index = 1 + update_player(playerctl:list()[M.active_player_index]) + end +end + +local function register_player(player) + local widget = wibox.widget { + widget = wibox.container.constraint, + width = qui.PADDING, + strategy = "min", + { + widget = wibox.container.background, + shape = qui.shape, + bg = qcolor.palette.bg.lowest, + }, + index_handler = function(self, index) + if M.active_player_index == index then + self.widget.bg = qcolor.palette.yellow() + else + self.widget.bg = qcolor.palette.bg.lowest + end + end, + } + + ---@diagnostic disable-next-line:undefined-field + layout:add(widget) + + recalculate_active_player(player, false) + mirror_player_list() +end + +local function unregister_player(player) + ---@diagnostic disable-next-line:undefined-field + layout:remove(#layout.children) + recalculate_active_player(player, true) + mirror_player_list() +end + +for _, player in ipairs(playerctl:list()) do + register_player(player) +end + +-- recover state +---@diagnostic disable-next-line:undefined-field +local last_active_player = playerctl:list()[M.active_player_index] + +update_player(last_active_player) + +playerctl:connect_signal("player::appeared", function(_, player) + register_player(player) +end) + +playerctl:connect_signal("player::vanished", function(_, player) + unregister_player(player) +end) + +return M diff --git a/.config/awesome/ui/statusbar/panel/widgets/music.lua b/.config/awesome/ui/statusbar/panel/widgets/music.old.lua index eea7335..5631f66 100644 --- a/.config/awesome/ui/statusbar/panel/widgets/music.lua +++ b/.config/awesome/ui/statusbar/panel/widgets/music.old.lua @@ -1,3 +1,4 @@ +-- TODO: update to the new color format local qnative = require "quarrel.native" local qui = require "quarrel.ui" local qvars = require "quarrel.vars" @@ -12,7 +13,7 @@ local w_title = wibox.widget { local w_artist = wibox.widget { widget = wibox.container.background, - fg = qvars.colors.dim.fg, + fg = qcolor.palette.dim.fg, { widget = wibox.widget.textbox, text = "", @@ -23,35 +24,35 @@ local w_progress_bar = wibox.widget { widget = wibox.widget.progressbar, max_value = 0, value = 0, - forced_height = qvars.char_height / 2, + forced_height = qui.CHAR_HEIGHT / 2, forced_width = qvars.expanded_bar_size - - (qvars.big_padding + qvars.big_padding * 2 + qvars.padding * 2) - - (qvars.char_height / 1.25 + qvars.padding) * 3, - color = qvars.colors.yellow, - background_color = qvars.colors.black, - shape = qvars.shape, + - (qui.BIG_PADDING + qui.BIG_PADDING * 2 + qui.PADDING * 2) + - (qui.CHAR_HEIGHT / 1.25 + qui.PADDING) * 3, + color = qcolor.palette.yellow, + background_color = qcolor.palette.black, + shape = qui.shape, } local music = wibox.widget(qui.styled { widget = wibox.container.background, { widget = wibox.container.margin, - margins = qvars.big_padding, + margins = qui.BIG_PADDING, { { { widget = wibox.container.background, - bg = qvars.colors.bg, - shape = qvars.shape, + bg = qcolor.palette.bg, + shape = qui.shape, { widget = wibox.container.margin, - margins = qvars.padding, + margins = qui.PADDING, { { widget = wibox.container.constraint, width = qvars.expanded_bar_size - - (qvars.big_padding + qvars.big_padding * 2 + qvars.padding * 2), - height = qvars.char_height, + - (qui.BIG_PADDING + qui.BIG_PADDING * 2 + qui.PADDING * 2), + height = qui.CHAR_HEIGHT, { widget = wibox.container.scroll.horizontal, speed = 50, @@ -62,8 +63,8 @@ local music = wibox.widget(qui.styled { { widget = wibox.container.constraint, width = qvars.expanded_bar_size - - (qvars.big_padding + qvars.big_padding * 2 + qvars.padding * 2), - height = qvars.char_height, + - (qui.BIG_PADDING + qui.BIG_PADDING * 2 + qui.PADDING * 2), + height = qui.CHAR_HEIGHT, { widget = wibox.container.scroll.horizontal, speed = 50, @@ -82,8 +83,8 @@ local music = wibox.widget(qui.styled { nil, { widget = wibox.container.background, - bg = qvars.colors.bg, - shape = qvars.shape, + bg = qcolor.palette.bg, + shape = qui.shape, w_progress_bar, }, layout = wibox.layout.align.vertical, diff --git a/.config/awesome/ui/statusbar/panel/widgets/power_menu.lua b/.config/awesome/ui/statusbar/panel/widgets/power_menu.lua index feea829..01171df 100644 --- a/.config/awesome/ui/statusbar/panel/widgets/power_menu.lua +++ b/.config/awesome/ui/statusbar/panel/widgets/power_menu.lua @@ -1,19 +1,17 @@ local q = require "quarrel" local qbind = require "quarrel.bind" local qui = require "quarrel.ui" -local qvars = require "quarrel.vars" local wibox = require "wibox" return wibox.widget { qui.styled { widget = wibox.container.background, - bg = qvars.colors.black, { widget = wibox.widget.textbox, text = "1", buttons = { - qbind:new { - triggers = qvars.btns.left, + qbind { + triggers = qbind.btns.left, press = function() q.debug "from 1" end, @@ -24,13 +22,12 @@ return wibox.widget { }, qui.styled { widget = wibox.container.background, - bg = qvars.colors.black, { widget = wibox.widget.textbox, text = "2", buttons = { - qbind:new { - triggers = qvars.btns.left, + qbind { + triggers = qbind.btns.left, press = function() q.debug "from 2" end, @@ -41,13 +38,12 @@ return wibox.widget { }, qui.styled { widget = wibox.container.background, - bg = qvars.colors.black, { widget = wibox.widget.textbox, text = "3", buttons = { - qbind:new { - triggers = qvars.btns.left, + qbind { + triggers = qbind.btns.left, press = function() q.debug "from 3" end, diff --git a/.config/awesome/ui/statusbar/panel/widgets/weather.lua b/.config/awesome/ui/statusbar/panel/widgets/weather.lua new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/.config/awesome/ui/statusbar/panel/widgets/weather.lua diff --git a/.config/awesome/ui/statusbar/panel/widgets/wifi.lua b/.config/awesome/ui/statusbar/panel/widgets/wifi.lua index ad2234f..8ccff95 100644 --- a/.config/awesome/ui/statusbar/panel/widgets/wifi.lua +++ b/.config/awesome/ui/statusbar/panel/widgets/wifi.lua @@ -1,27 +1,30 @@ local lgi = require "lgi" local phosphor = require "assets.phosphor" +local qcolor = require "quarrel.color" local qui = require "quarrel.ui" -local qvars = require "quarrel.vars" local wibox = require "wibox" local glib = lgi.GLib +local wifi_icon = qui.icon { + icon = phosphor.wifi_x_fill, + color = qcolor.palette.red(), + widget = { + id = "icon", + }, +} + local wifi = wibox.widget(qui.styled { widget = wibox.container.background, + bg = qcolor.palette.bg.high, { widget = wibox.container.margin, - margins = qvars.big_padding, + margins = qui.BIG_PADDING, { { widget = wibox.container.place, valign = "center", halign = "center", - qui.icon { - icon = phosphor.wifi_x_fill, - color = qvars.colors.red, - widget = { - id = "icon", - }, - }, + wifi_icon, }, { widget = wibox.widget.textbox, @@ -29,7 +32,7 @@ local wifi = wibox.widget(qui.styled { id = "essid", }, layout = wibox.layout.fixed.horizontal, - spacing = qvars.padding, + spacing = qui.PADDING, }, }, }) @@ -43,8 +46,8 @@ awesome.connect_signal("services::wifi", function(essid, _, connected) end) awesome.connect_signal("services::wifi::icon", function(icon, color) - wifi:get_children_by_id("icon")[1].image = icon - wifi:get_children_by_id("icon")[1].stylesheet = qui.recolor(color) + wifi_icon.image = icon + wifi_icon.stylesheet = qui.recolor(color) end) return wifi diff --git a/.config/awesome/ui/statusbar/widgets/displays.lua b/.config/awesome/ui/statusbar/widgets/displays.lua index 62e4398..96cf84c 100644 --- a/.config/awesome/ui/statusbar/widgets/displays.lua +++ b/.config/awesome/ui/statusbar/widgets/displays.lua @@ -1,7 +1,7 @@ local battery = require "services.battery" local phosphor = require "assets.phosphor" +local qcolor = require "quarrel.color" local qui = require "quarrel.ui" -local qvars = require "quarrel.vars" local wibox = require "wibox" local function create_display(icon, color) @@ -16,28 +16,32 @@ local function create_display(icon, color) } end -local d_battery = create_display(phosphor.battery_vertical_warning_fill, qvars.colors.red) +local d_battery = create_display(phosphor.battery_vertical_warning_fill, qcolor.palette.red()) battery:connect_signal("icon", function(_, icon, color) d_battery.widget.image = icon d_battery.widget.stylesheet = qui.recolor(color) end) -local d_brightness = create_display(phosphor.moon_fill, qvars.colors.fg) -awesome.connect_signal("services::brightness::icon", function(icon, color) - d_brightness.widget.image = icon - d_brightness.widget.stylesheet = qui.recolor(color) -end) +-- local d_brightness = create_display(phosphor.moon_fill, qcolor.palette.fg()) +-- awesome.connect_signal("services::brightness::icon", function(icon, color) +-- d_brightness.widget.image = icon +-- d_brightness.widget.stylesheet = qui.recolor(color) +-- end) -local d_audio = create_display(phosphor.speaker_simple_slash_fill, qvars.colors.red) +local d_audio = create_display(phosphor.speaker_simple_slash_fill, qcolor.palette.red()) awesome.connect_signal("services::audio::icon", function(icon, color) d_audio.widget.image = icon d_audio.widget.stylesheet = qui.recolor(color) end) -local d_wifi = create_display(phosphor.wifi_x_fill, qvars.colors.red) +local d_wifi = create_display(phosphor.wifi_x_fill, qcolor.palette.red()) awesome.connect_signal("services::wifi::icon", function(icon, color) d_wifi.widget.image = icon d_wifi.widget.stylesheet = qui.recolor(color) end) -return { audio = d_audio, battery = d_battery, brightness = d_brightness, wifi = d_wifi } +return { + audio = d_audio, + battery = d_battery, --[[brightness = d_brightness,]] + wifi = d_wifi, +} diff --git a/.config/awesome/ui/statusbar/widgets/taglist.lua b/.config/awesome/ui/statusbar/widgets/taglist.lua index df114df..3b81173 100644 --- a/.config/awesome/ui/statusbar/widgets/taglist.lua +++ b/.config/awesome/ui/statusbar/widgets/taglist.lua @@ -10,7 +10,7 @@ return function(s) screen = s, filter = awful.widget.taglist.filter.all, layout = { - spacing = qvars.padding, + spacing = qui.PADDING, layout = wibox.layout.fixed.vertical, }, widget_template = { @@ -21,8 +21,8 @@ return function(s) self.widget = qui.icon { icon = phosphor[next(tag:clients()) and "circle_fill" or "circle_bold"], widget = { - forced_height = qvars.char_height / 1.5, - forced_width = qvars.char_height / 1.5, + forced_height = qui.CHAR_HEIGHT / 1.5, + forced_width = qui.CHAR_HEIGHT / 1.5, }, } -- self.widget = wibox.widget { @@ -33,44 +33,44 @@ return function(s) if tag.selected then return end - self.widget.stylesheet = qui.recolor(qvars.colors.yellow) + self.widget.stylesheet = qui.recolor(qcolor.palette.yellow()) end) self:connect_signal("mouse::leave", function() if tag.selected then return end - self.widget.stylesheet = qui.recolor(qvars.colors.fg) + self.widget.stylesheet = qui.recolor(qcolor.palette.fg()) end) if tag.selected then - self.widget.stylesheet = qui.recolor(qvars.colors.yellow) + self.widget.stylesheet = qui.recolor(qcolor.palette.yellow()) return end - self.widget.stylesheet = qui.recolor(qvars.colors.fg) + self.widget.stylesheet = qui.recolor(qcolor.palette.fg()) end, update_callback = function(self, tag) self.widget.image = phosphor[next(tag:clients()) and "circle_fill" or "circle_bold"] if tag.selected then - self.widget.stylesheet = qui.recolor(qvars.colors.yellow) + self.widget.stylesheet = qui.recolor(qcolor.palette.yellow()) else - self.widget.stylesheet = qui.recolor(qvars.colors.fg) + self.widget.stylesheet = qui.recolor(qcolor.palette.fg()) end end, }, buttons = { - qbind:new { - triggers = qvars.btns.left, + qbind { + triggers = qbind.btns.left, press = function(t) t:view_only() end, hidden = true, }, - qbind:new { - mods = qvars.mods.M, - triggers = qvars.btns.left, + qbind { + mods = qbind.mods.M, + triggers = qbind.btns.left, press = function(t) if client.focus then client.focus:move_to_tag(t) @@ -78,8 +78,8 @@ return function(s) end, hidden = true, }, - qbind:new { - triggers = qvars.btns.right, + qbind { + triggers = qbind.btns.right, press = awful.tag.viewtoggle, hidden = true, }, diff --git a/.config/awesome/ui/statusbar/widgets/taglist_new.lua b/.config/awesome/ui/statusbar/widgets/taglist_new.lua new file mode 100644 index 0000000..79b8a96 --- /dev/null +++ b/.config/awesome/ui/statusbar/widgets/taglist_new.lua @@ -0,0 +1,147 @@ +local awful = require "awful" +local qbind = require "quarrel.bind" +local qcolor = require "quarrel.color" +local qui = require "quarrel.ui" +local qvars = require "quarrel.vars" +local wibox = require "wibox" + +local empty_indicator = wibox.widget { + widget = wibox.container.place, + { + widget = wibox.container.constraint, + strategy = "exact", + height = qui.PADDING, + width = qui.PADDING, + { + widget = wibox.container.background, + bg = qcolor.palette.border.variant, + shape = qui.shape, + }, + }, +} + +local client_indicator = wibox.widget { + widget = wibox.container.place, + { + widget = wibox.container.constraint, + height = qui.CHAR_HEIGHT, + width = qui.PADDING, + strategy = "exact", + { + widget = wibox.container.background, + shape = qui.shape, + bg = qcolor.palette.fg(), + }, + }, +} + +local function update_tasklist(layout, tag, indicated_empty) + local client_delta = #tag:clients() - #layout.children + if client_delta < 0 then + for _ = 1, -(client_delta + (indicated_empty and 1 or 0)) do + layout:remove(1) + end + elseif client_delta > 0 then + for _ = 1, client_delta do + layout:add(client_indicator) + end + end +end + +return function(s) + return awful.widget.taglist { + screen = s, + filter = awful.widget.taglist.filter.all, + layout = { + spacing = qui.PADDING, + layout = wibox.layout.fixed.vertical, + }, + widget_template = { + widget = wibox.container.constraint, + strategy = "min", + height = qui.CHAR_HEIGHT, + qui.styled { + widget = wibox.container.background, + bg = qcolor.palette.bg.high, + { + widget = wibox.container.margin, + margins = qui.PADDING, + { + layout = wibox.layout.flex.vertical, + spacing = qui.PADDING, + }, + }, + }, + create_callback = function( + self, + tag --[[@as tag]] + ) + if tag.selected then + self.widget.border_color = qcolor.palette.yellow() + end + + if #tag:clients() ~= 0 then + self.widget.widget.spacing = qui.PADDING + if self.indicated_empty then + self.widget.widget.widget:remove(1) + self.indicated_empty = false + end + else + if not self.indicated_empty then + self.widget.widget.widget:add(empty_indicator) + self.indicated_empty = true + end + end + + update_tasklist(self.widget.widget.widget, tag, self.indicated_empty) + end, + update_callback = function(self, tag) + if tag.selected then + self.widget.border_color = qcolor.palette.yellow() + else + self.widget.border_color = qcolor.palette.border() + end + + if #tag:clients() ~= 0 then + self.widget.widget.spacing = qui.PADDING + if self.indicated_empty then + self.widget.widget.widget:remove(1) + self.indicated_empty = false + end + else + self.widget.widget.spacing = nil + if not self.indicated_empty then + self.widget.widget.widget:add(empty_indicator) + self.indicated_empty = true + end + end + + update_tasklist(self.widget.widget.widget, tag, self.indicated_empty) + end, + }, + buttons = { + qbind { + triggers = qbind.btns.left, + press = function(t) + t:view_only() + end, + hidden = true, + }, + qbind { + mods = qbind.mods.M, + triggers = qbind.btns.left, + press = function(t) + if client.focus then + client.focus:move_to_tag(t) + end + end, + hidden = true, + }, + qbind { + triggers = qbind.btns.right, + press = awful.tag.viewtoggle, + hidden = true, + }, + }, + } +end diff --git a/.config/awesome/ui/statusbar/widgets/tasklist.lua b/.config/awesome/ui/statusbar/widgets/tasklist.lua deleted file mode 100644 index 9656185..0000000 --- a/.config/awesome/ui/statusbar/widgets/tasklist.lua +++ /dev/null @@ -1,30 +0,0 @@ -local awful = require "awful" -local gears = require "gears" -local qvars = require "quarrel.vars" -local wibox = require "wibox" - -return function(s) - return awful.widget.tasklist { - screen = s, - filter = awful.widget.tasklist.filter.currenttags, - -- buttons = tasklist_buttons, - layout = { - spacing = qvars.padding, - layout = wibox.layout.flex.vertical, - }, - widget_template = { - widget = wibox.container.place, - valign = "center", - halign = "center", - -- { - -- widget = awful.widget.clienticon, - - -- } - { - widget = awful.widget.clienticon, - forced_width = qvars.char_height, - forced_height = qvars.char_height, - }, - }, - } -end diff --git a/.config/awesome/ui/wicked/consts.lua b/.config/awesome/ui/wicked/consts.lua new file mode 100644 index 0000000..e00cae5 --- /dev/null +++ b/.config/awesome/ui/wicked/consts.lua @@ -0,0 +1,5 @@ +local C = {} + +C.NOTIF_TIMEOUT = 3 + +return C diff --git a/.config/awesome/ui/wicked/init.lua b/.config/awesome/ui/wicked/init.lua index ddbf912..dea28d5 100644 --- a/.config/awesome/ui/wicked/init.lua +++ b/.config/awesome/ui/wicked/init.lua @@ -2,19 +2,22 @@ local awful = require "awful" local beautiful = require "beautiful" local gshape = require "gears.shape" local naughty = require "naughty" +local qanim = require "quarrel.animation" local qui = require "quarrel.ui" local qvars = require "quarrel.vars" local wibox = require "wibox" local rtimed = require("lib.rubato").timed local easing = require("lib.rubato").easing local gtimer = require "gears.timer" +local qcolor = require "quarrel.color" local qmarkup = require "quarrel.markup" -return function(n) - local intertext_margin = (n.title ~= "" or n.message ~= "") and qvars.padding or 0 - local title_height = n.title ~= "" and qvars.char_height or 0 - local message_height = n.message ~= "" and qvars.char_height or 0 - -- local app_name_height = n.app_name ~= "" and +local M = require "ui.wicked.consts" + +function M.new(n) + local intertext_margin = (n.title ~= "" or n.message ~= "") and qui.PADDING or 0 + local title_height = n.title ~= "" and qui.CHAR_HEIGHT or 0 + local message_height = n.message ~= "" and qui.CHAR_HEIGHT or 0 local app_name if n.app_name == "" then app_name = n._private._foreign and "Unknown" or "Awesome" @@ -29,22 +32,19 @@ return function(n) margins = beautiful.useless_gap * 2, }) end, - bg = qvars.colors.transparent, + bg = qcolor.palette.transparent, border_width = 0, shape = gshape.rectangle, widget_template = { widget = wibox.container.constraint, - height = qvars.big_padding * 2 - + qvars.char_height - + qvars.border_width - + qvars.big_padding * 2 + height = qui.BIG_PADDING * 2 + + qui.CHAR_HEIGHT + + qui.BORDER_WIDTH + + qui.BIG_PADDING * 2 -- + title_height -- + message_height -- + intertext_margin, - + ( - n.icon and qvars.char_height * 2 + qvars.padding - or (title_height + message_height + intertext_margin) - ), + + (n.icon and qui.CHAR_HEIGHT * 2 + qui.PADDING or (title_height + message_height + intertext_margin)), strategy = "exact", { @@ -59,7 +59,7 @@ return function(n) { { widget = wibox.container.margin, - margins = qvars.big_padding, + margins = qui.BIG_PADDING, { { widget = wibox.widget.textbox, @@ -69,12 +69,12 @@ return function(n) { widget = wibox.container.radialprogressbar, - max_value = qvars.notif_timeout, - border_color = qvars.colors.black, - color = qvars.colors.yellow, - border_width = qvars.border_width, - forced_height = qvars.char_height, - forced_width = qvars.char_height, + max_value = M.NOTIF_TIMEOUT, + border_color = qcolor.palette.bg.lowest, + color = qcolor.palette.yellow(), + border_width = qui.BORDER_WIDTH, + forced_height = qui.CHAR_HEIGHT, + forced_width = qui.CHAR_HEIGHT, id = "progress", }, layout = wibox.layout.align.horizontal, @@ -82,20 +82,20 @@ return function(n) }, { widget = wibox.container.constraint, - height = qvars.border_width, + height = qui.BORDER_WIDTH, width = beautiful.notification_max_width, strategy = "exact", { widget = wibox.container.background, - bg = qvars.colors.bright.black, + bg = qcolor.palette.border(), }, }, { widget = wibox.container.margin, - margins = qvars.big_padding, + margins = qui.BIG_PADDING, { widget = wibox.container.constraint, - height = n.icon and qvars.char_height * 2 + qvars.padding + height = n.icon and qui.CHAR_HEIGHT * 2 + qui.PADDING or (title_height + message_height + intertext_margin), strategy = "exact", { @@ -103,7 +103,7 @@ return function(n) widget = wibox.container.background, { widget = naughty.widget.icon, - shape = qvars.shape, + shape = qui.shape, notification = n, }, }, @@ -130,7 +130,7 @@ return function(n) layout = wibox.layout.fixed.vertical, }, fill_space = true, - spacing = n.icon and qvars.big_padding or nil, + spacing = n.icon and qui.BIG_PADDING or nil, layout = wibox.layout.fixed.horizontal, }, }, @@ -158,12 +158,10 @@ return function(n) end, } - local position = rtimed { - duration = qvars.anim_duration * 2, - intro = qvars.anim_intro * 2, + local position = qanim:new { + duration = qvars.anim_duration, pos = 0, - easing = easing.quadratic, - clamp_position = true, + easing = qvars.easing, subscribed = function(pos) gtimer.delayed_call(function() notif.widget.widget:move(1, function(geo, args) @@ -179,8 +177,8 @@ return function(n) end, } local opacity = rtimed { - duration = qvars.anim_duration * 2, - intro = qvars.anim_intro * 2, + duration = qvars.anim_duration, + intro = qvars.anim_intro, easing = easing.quadratic, pos = 0, clamp_position = true, @@ -192,12 +190,20 @@ return function(n) n:disconnect_signal("destroyed", notif._private.destroy_callback) function notif._private.destroy_callback() opacity.target = 0 - position.target = 0 + position:set(0) hiding = true end n:weak_connect_signal("destroyed", notif._private.destroy_callback) + --- for some reason when urgency is critical, it somehow makes it not disappear + --- why? dunno, FIXME opacity.target = 1 - position.target = qvars.char_width * 48 - progress.target = qvars.notif_timeout + position:set(beautiful.notification_max_width) + progress.target = M.NOTIF_TIMEOUT end + +return setmetatable(M, { + __call = function(_, ...) + return M.new(...) + end, +}) |