From 225eeafcea67d63a608f9c666faf2a2ef014be4a Mon Sep 17 00:00:00 2001 From: delta Date: Mon, 19 Jan 2026 06:30:33 +0100 Subject: sync: electric boogaloo --- .../assets/phosphor/bell-simple-slash-fill.svg | 1 + .config/awesome/misc/autostart.lua | 1 + .config/awesome/misc/init.lua | 2 +- .config/awesome/misc/keys.lua | 8 + .config/awesome/quarrel/debug.lua | 39 + .config/awesome/quarrel/persistent.lua | 18 +- .config/awesome/quarrel/ui/init.lua | 3 +- .config/awesome/services/dnd.lua | 18 + .config/awesome/services/time.lua | 18 + .config/awesome/signals/client.lua | 26 + .config/awesome/test_rc.lua | 22 + .config/awesome/ui/fresnel/text_input.lua | 934 +++++++++++++++++++++ .config/awesome/ui/init.lua | 16 + .config/awesome/ui/osd/providers/mpris.lua | 3 + .config/awesome/ui/statusbar/init.lua | 32 +- .config/awesome/ui/statusbar/panel/init.lua | 14 +- .../ui/statusbar/panel/widgets/calendar.lua | 143 ++-- .../ui/statusbar/panel/widgets/notifs/consts.lua | 13 + .../ui/statusbar/panel/widgets/notifs/init.lua | 0 .../panel/widgets/notifs/widgets/notif.lua | 214 +++++ .config/awesome/ui/statusbar/widgets/dnd.lua | 34 + .config/awesome/ui/tidy/init.lua | 94 +++ .config/awesome/ui/wicked/consts.lua | 6 + .config/awesome/ui/wicked/init.lua | 16 +- 24 files changed, 1566 insertions(+), 109 deletions(-) create mode 100644 .config/awesome/assets/phosphor/bell-simple-slash-fill.svg create mode 100644 .config/awesome/quarrel/debug.lua create mode 100644 .config/awesome/services/dnd.lua create mode 100644 .config/awesome/services/time.lua create mode 100644 .config/awesome/test_rc.lua create mode 100644 .config/awesome/ui/fresnel/text_input.lua create mode 100644 .config/awesome/ui/osd/providers/mpris.lua create mode 100644 .config/awesome/ui/statusbar/panel/widgets/notifs/consts.lua create mode 100644 .config/awesome/ui/statusbar/panel/widgets/notifs/init.lua create mode 100644 .config/awesome/ui/statusbar/panel/widgets/notifs/widgets/notif.lua create mode 100644 .config/awesome/ui/statusbar/widgets/dnd.lua create mode 100644 .config/awesome/ui/tidy/init.lua diff --git a/.config/awesome/assets/phosphor/bell-simple-slash-fill.svg b/.config/awesome/assets/phosphor/bell-simple-slash-fill.svg new file mode 100644 index 0000000..f98e66f --- /dev/null +++ b/.config/awesome/assets/phosphor/bell-simple-slash-fill.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/.config/awesome/misc/autostart.lua b/.config/awesome/misc/autostart.lua index 291e39d..de79c65 100644 --- a/.config/awesome/misc/autostart.lua +++ b/.config/awesome/misc/autostart.lua @@ -5,6 +5,7 @@ if qpersistent.is_restart() then return end +-- local programs = {} local programs = { "picom -b", "sxhkd", diff --git a/.config/awesome/misc/init.lua b/.config/awesome/misc/init.lua index d3c36b2..069046a 100644 --- a/.config/awesome/misc/init.lua +++ b/.config/awesome/misc/init.lua @@ -1,3 +1,3 @@ require "misc.keys" require "misc.rules" -require "misc.autostart" +-- require "misc.autostart" diff --git a/.config/awesome/misc/keys.lua b/.config/awesome/misc/keys.lua index b36808b..b7dc4c4 100644 --- a/.config/awesome/misc/keys.lua +++ b/.config/awesome/misc/keys.lua @@ -12,6 +12,7 @@ local mpris = require "ui.statusbar.panel.widgets.mpris" local playerctl = require "services.playerctl" local powermenu = require "ui.powermenu" local qbind = require "quarrel.bind" +local dnd = require "services.dnd" local recording = { false, "" } @@ -371,4 +372,11 @@ awful.keyboard.append_global_keybindings { group = "misc", desc = "scroll up/down", }, + qbind { + mods = qbind.mods.M, + triggers = "`", + press = function() + dnd.dnd = not dnd.dnd + end + } } diff --git a/.config/awesome/quarrel/debug.lua b/.config/awesome/quarrel/debug.lua new file mode 100644 index 0000000..54fc317 --- /dev/null +++ b/.config/awesome/quarrel/debug.lua @@ -0,0 +1,39 @@ +local n = require("naughty").notification +local gdebug = require "gears.debug" + +---@class QuarrelDebug +local M = {} + +--- Send a notification with the specified message +---@param message any +function M.debug(message) + if type(message) == "table" then + gdebug.dump(message, "data", 8) + n { message = "Dumped table!", app_name = "QDebug", level = "debug" } + else + n { message = tostring(message), app_name = "QDebug", level = "debug" } + end +end + +--- Print an info message to stdout and send a notification at the same time +---@param message any +function M.info(message) + print("[QDebug]: " .. tostring(message)) + n { message = message, app_name = "QDebug", level = "info" } +end + +--- Print a warning to stderr and send a notification at the same time +---@param message any +function M.warn(message) + gdebug.print_warning("[QDebug]: " .. tostring(message)) + n { message = message, app_name = "QDebug", level = "warn" } +end + +--- Print an error to stderr and send a notification at the same time +---@param message any +function M.error(message) + gdebug.print_warning("[QDebug]: " .. tostring(message)) + n { message = message, app_name = "QDebug", level = "error" } +end + +return M diff --git a/.config/awesome/quarrel/persistent.lua b/.config/awesome/quarrel/persistent.lua index 2b15c89..193affc 100644 --- a/.config/awesome/quarrel/persistent.lua +++ b/.config/awesome/quarrel/persistent.lua @@ -2,7 +2,7 @@ local gdebug = require "gears.debug" local qfs = require "quarrel.fs" local qjson = require "quarrel.json" -awesome.register_xproperty("is_restart", "boolean") +-- awesome.register_xproperty("is_restart", "boolean") ---@alias QPersistentValue string|number|table|nil|boolean @@ -29,7 +29,13 @@ end --- Check if there was a restart ---@return boolean function qpersistent.is_restart() - return awesome.get_xproperty "is_restart" ~= nil + local f=io.open("/tmp/qpersistent-marker", "r") + if f then + io.close(f) + end + + return not not f + -- return awesome.get_xproperty "is_restart" ~= nil end do @@ -55,7 +61,13 @@ awesome.connect_signal("exit", function(restart) end if not qpersistent.is_restart() then - awesome.set_xproperty("is_restart", true) + local f = io.open("/tmp/qpersistent-marker", "w") + if not f then + gdebug.print_error "failed to create the marker file" + return + end + f:close() + -- awesome.set_xproperty("is_restart", true) end end) diff --git a/.config/awesome/quarrel/ui/init.lua b/.config/awesome/quarrel/ui/init.lua index 2b63694..5e68b1d 100644 --- a/.config/awesome/quarrel/ui/init.lua +++ b/.config/awesome/quarrel/ui/init.lua @@ -79,9 +79,10 @@ function M.recolor(color) end --- Generates icon widget ----@param args table +---@param args? table ---@return table function M.separator(args) + args = args or {} return wibox.widget(gtable.crush({ widget = wibox.container.background, bg = qcolor.palette.border(), diff --git a/.config/awesome/services/dnd.lua b/.config/awesome/services/dnd.lua new file mode 100644 index 0000000..d64583c --- /dev/null +++ b/.config/awesome/services/dnd.lua @@ -0,0 +1,18 @@ +local gobject = require "gears.object" + +local inner = false + +local M = gobject { + class = { + set_dnd = function(self, value) + inner = value + self:emit_signal("dnd", inner) + end, + get_dnd = function() + return inner + end + }, + enable_properties = true, +} + +return M diff --git a/.config/awesome/services/time.lua b/.config/awesome/services/time.lua new file mode 100644 index 0000000..ec33b31 --- /dev/null +++ b/.config/awesome/services/time.lua @@ -0,0 +1,18 @@ +local gobject = require "gears.object" + +local current = os.date "*t" + +local M = gobject { + class = { + update_current_date = function(self) + current = os.date "*t" + self:emit_signal("current", current) + end, + get_current_date = function() + return current + end + }, + enable_properties = true, +} + +return M diff --git a/.config/awesome/signals/client.lua b/.config/awesome/signals/client.lua index f0b5136..2a24863 100644 --- a/.config/awesome/signals/client.lua +++ b/.config/awesome/signals/client.lua @@ -1,5 +1,7 @@ local qui = require "quarrel.ui" local rectangle = require("gears.shape").rectangle +local qdebug = require "quarrel.debug" +local gtimer = require "gears.timer" -- local conductor = require "ui.conductor" client.connect_signal("request::manage", function(c) @@ -24,3 +26,27 @@ end client.connect_signal("property::maximized", handle_corners) client.connect_signal("property::fullscreen", handle_corners) + +client.connect_signal("focus", function(c) + if c.instance == "steamwebhelper" then + local was_maximized = c.maximized + c.maximized = false + + local geo = c:geometry() + c:geometry { + x = geo.x, + y = geo.y, + width = geo.width - 1, + height = geo.height - 1 + } + + gtimer { + callback = function() + c:geometry(geo) + c.maximized = was_maximized + end, + single_shot = true, + timeout = 0.5 + } + end +end) diff --git a/.config/awesome/test_rc.lua b/.config/awesome/test_rc.lua new file mode 100644 index 0000000..2882e7a --- /dev/null +++ b/.config/awesome/test_rc.lua @@ -0,0 +1,22 @@ +local awful = require "awful" +local gshape = require "gears.shape" +local wibox = require "wibox" + +awful.popup { + width = 300, + shape = function(cr, _, height) + gshape.rectangle(cr, 200, height) + end, + border_color = "#ff0000", + border_width = 10, + placement = awful.placement.centered, + widget = { + widget = wibox.container.background, + bg = "#00ff00", + fg = "#0000ff", + { + widget = wibox.widget.textbox, + text = ("testing"):rep(10, "\n") + } + } +} diff --git a/.config/awesome/ui/fresnel/text_input.lua b/.config/awesome/ui/fresnel/text_input.lua new file mode 100644 index 0000000..2dc7368 --- /dev/null +++ b/.config/awesome/ui/fresnel/text_input.lua @@ -0,0 +1,934 @@ +------------------------------------------- +-- @author https://github.com/Kasper24 +-- @copyright 2021-2025 Kasper24 +------------------------------------------- +local lgi = require('lgi') +local Gtk = lgi.require('Gtk', '3.0') +local Gdk = lgi.require('Gdk', '3.0') +local Pango = lgi.Pango +local awful = require("awful") +local gtable = require("gears.table") +local gtimer = require("gears.timer") +local gcolor = require("gears.color") +local wibox = require("wibox") +local qcolor = require "quarrel.color" +local abs = math.abs +local ipairs = ipairs +local string = string +local capi = { + awesome = awesome, + root = root, + tag = tag, + client = client, + mouse = mouse, + mousegrabber = mousegrabber +} + +local text_input = { + mt = {} +} + +local properties = { + "unfocus_keys", + "unfocus_on_root_clicked", "unfocus_on_client_clicked", "unfocus_on_client_focus", + "unfocus_on_mouse_leave", "unfocus_on_tag_change", + "focus_on_subject_mouse_enter", "unfocus_on_subject_mouse_leave", + "click_timeout", + "reset_on_unfocus", + "text_color", + "placeholder", "initial", + "pattern", "obscure", + "cursor_blink", "cursor_blink_rate","cursor_size", "cursor_bg", + "selection_bg" +} + +text_input.patterns = { + numbers = "[%d.]*", + numbers_one_decimal = "%d*%.?%d*", + round_numbers = "[0-9]*", + email = "%S+@%S+%.%S+", + time = "%d%d?:%d%d:%d%d?|%d%d?:%d%d", + date = "%d%d%d%d%-%d%d%-%d%d|%d%d?/%d%d?/%d%d%d%d|%d%d?%.%d%d?%.%d%d%d%d", + phone = "%+?%d[%d%-%s]+%d", + url = "https?://[%w-_%.]+%.[%w]+/?[%w-_%.?=%+]*", + email = "[%w._%-%+]+@[%w._%-]+%.%w+", + alphanumeric = "%w+", + letters = "[a-zA-Z]+" +} + +local function build_properties(prototype, prop_names) + for _, prop in ipairs(prop_names) do + if not prototype["set_" .. prop] then + prototype["set_" .. prop] = function(self, value) + if self._private[prop] ~= value then + self._private[prop] = value + self:emit_signal("widget::redraw_needed") + self:emit_signal("property::" .. prop, value) + end + return self + end + end + if not prototype["get_" .. prop] then + prototype["get_" .. prop] = function(self) + return self._private[prop] + end + end + end +end + +local function has_value(tab, val) + for _, value in ipairs(tab) do + if val:lower():find(value:lower(), 1, true) then + return true + end + end + return false +end + +local function is_word_char(c) + if string.find(c, "[{[(,.:;_-+=@/ ]") then + return false + else + return true + end +end + +local function cword_start(s, pos) + local i = pos + if i > 1 then + i = i - 1 + end + while i >= 1 and not is_word_char(s:sub(i, i)) do + i = i - 1 + end + while i >= 1 and is_word_char(s:sub(i, i)) do + i = i - 1 + end + if i <= #s then + i = i + 1 + end + return i +end + +local function cword_end(s, pos) + local i = pos + while i <= #s and not is_word_char(s:sub(i, i)) do + i = i + 1 + end + while i <= #s and is_word_char(s:sub(i, i)) do + i = i + 1 + end + return i +end + +local function set_mouse_cursor(cursor) + capi.root.cursor(cursor) + local wibox = capi.mouse.current_wibox + if wibox then + wibox.cursor = cursor + end +end + +local function single_double_triple_tap(self, args) + local wp = self._private + + if wp.click_timer == nil then + wp.click_timer = gtimer { + timeout = wp.click_timeout, + autostart = false, + call_now = false, + single_shot = true, + callback = function() + wp.click_count = 0 + end + } + end + + wp.click_timer:again() + wp.click_count = wp.click_count + 1 + if wp.click_count == 1 then + args.on_single_click() + elseif wp.click_count == 2 then + args.on_double_click() + elseif wp.click_count == 3 then + args.on_triple_click() + wp.click_count = 0 + end +end + +local function run_keygrabber(self) + local wp = self._private + wp.keygrabber = awful.keygrabber.run(function(modifiers, key, event) + if event ~= "press" then + self:emit_signal("key::release", modifiers, key, event) + return + end + self:emit_signal("key::press", modifiers, key, event) + + -- Convert index array to hash table + local mod = {} + for _, v in ipairs(modifiers) do + mod[v] = true + end + + if mod.Control then + if key == "a" then + self:select_all() + elseif key == "c" then + self:copy() + elseif key == "v" then + self:paste() + elseif key == "b" or key == "Left" then + self:set_cursor_index_to_word_start() + elseif key == "f" or key == "Right" then + self:set_cursor_index_to_word_end() + elseif key == "d" then + self:delete_next_word() + elseif key == "BackSpace" then + self:delete_previous_word() + end + elseif mod.Shift and key:wlen() ~= 1 then + if key =="Left" then + self:decremeant_selection_end_index() + elseif key == "Right" then + self:increamant_selection_end_index() + end + else + if has_value(wp.unfocus_keys, key) then + self:unfocus() + end + + if mod.Shift and key == "Insert" then + self:paste() + elseif key == "Home" then + self:set_cursor_index(0) + elseif key == "End" then + self:set_cursor_index_to_end() + elseif key == "BackSpace" then + self:delete_text() + elseif key == "Delete" then + self:delete_text_after_cursor() + elseif key == "Left" then + self:decremeant_cursor_index() + elseif key == "Right" then + self:increamant_cursor_index() + elseif not mod.Mod1 and not mod.Mod4 and key:wlen() == 1 then + self:update_text(key) + end + end + end) +end + +function text_input:set_widget_template(widget_template) + local wp = self._private + + wp.text_widget = widget_template:get_children_by_id("text_role")[1] + wp.text_widget.forced_width = math.huge + local text_draw = wp.text_widget.draw + if self:get_initial() then + self:replace_text(self:get_initial()) + end + + local placeholder_widget = widget_template:get_children_by_id("placeholder_role") + if placeholder_widget then + placeholder_widget = placeholder_widget[1] + end + + function wp.text_widget:draw(context, cr, width, height) + local _, logical_rect = self._private.layout:get_pixel_extents() + + -- Selection bg + cr:set_source(gcolor.change_opacity(wp.selection_bg, wp.selection_opacity)) + cr:rectangle( + wp.selection_start_x, + logical_rect.y - 3, + wp.selection_end_x - wp.selection_start_x, + logical_rect.y + logical_rect.height + 6 + ) + cr:fill() + + -- Cursor + cr:set_source(gcolor.change_opacity(wp.cursor_bg, wp.cursor_opacity)) + cr:set_line_width(wp.cursor_width) + cr:move_to(wp.cursor_x, logical_rect.y - 3) + cr:line_to(wp.cursor_x, logical_rect.y + logical_rect.height + 6) + cr:stroke() + + cr:set_source(gcolor(wp.text_color)) + text_draw(self, context, cr, width, height) + + if self:get_text() == "" and placeholder_widget then + placeholder_widget.visible = true + elseif placeholder_widget then + placeholder_widget.visible = false + end + end + + local function on_drag(_, lx, ly) + lx, ly = wp.hierarchy:get_matrix_from_device():transform_point(lx, ly) + if abs(lx - wp.press_pos.lx) > 2 or abs(ly - wp.press_pos.ly) > 2 then + if self:get_mode() ~= "overwrite" then + self:set_selection_start_index_from_x_y(wp.press_pos.lx, wp.press_pos.ly) + end + self:set_selection_end_index_from_x_y(lx, ly) + end + end + + wp.text_widget:connect_signal("button::press", function(_, lx, ly, button, mods, find_widgets_result) + if gtable.hasitem(mods, "Mod4") or button ~= 1 then + return + end + + single_double_triple_tap(self, { + on_single_click = function() + self:focus() + self:set_cursor_index_from_x_y(lx, ly) + end, + on_double_click = function() + self:set_selection_to_word() + end, + on_triple_click = function() + self:select_all() + end + }) + + wp.press_pos = { lx = lx, ly = ly } + wp.hierarchy = find_widgets_result.hierarchy + find_widgets_result.drawable:connect_signal("mouse::move", on_drag) + end) + + wp.text_widget:connect_signal("button::release", function(_, lx, ly, button, mods, find_widgets_result) + if button == 1 then + find_widgets_result.drawable:disconnect_signal("mouse::move", on_drag) + end + end) + + wp.text_widget:connect_signal("mouse::enter", function() + set_mouse_cursor("xterm") + end) + + wp.text_widget:connect_signal("mouse::leave", function(_, find_widgets_result) + if self:get_focused() == false then + set_mouse_cursor("left_ptr") + end + + find_widgets_result.drawable:disconnect_signal("mouse::move", on_drag) + if wp.unfocus_on_mouse_leave then + self:unfocus() + end + end) + + self:set_widget(widget_template) +end + +function text_input:get_mode() + return self._private.mode +end + +function text_input:set_focused(focused) + if focused == true then + self:focus() + else + self:unfocus() + end +end + +function text_input:set_pattern(pattern) + self._private.pattern = text_input.patterns[pattern] +end + +function text_input:set_obscure(obscure) + self._private.obscure = obscure + self:set_text(self:get_text()) +end + +function text_input:toggle_obscure() + self:set_obscure(not self._private.obscure) +end + +function text_input:set_initial(initial) + self._private.initial = initial + self:replace_text(initial) +end + +function text_input:update_text(text) + if self:get_mode() == "insert" then + self:insert_text(text) + else + self:overwrite_text(text) + end +end + +function text_input:set_text(text) + self._private.text_buffer = text + + if self:get_obscure() then + self:get_text_widget():set_text(string.rep("*", #text)) + else + self:get_text_widget():set_text(text) + end +end + +function text_input:replace_text(text) + self._private.text_buffer = text + + if self:get_obscure() then + self:set_text(string.rep("*", #text)) + else + self:set_text(text) + end + + self:set_cursor_index(#text) +end + +function text_input:insert_text(text) + local wp = self._private + + local old_text = self:get_text() + local cursor_index = self:get_cursor_index() + local left_text = old_text:sub(1, cursor_index) .. text + local right_text = old_text:sub(cursor_index + 1) + local new_text = left_text .. right_text + if wp.pattern then + new_text = new_text:match(wp.pattern) + if new_text then + self:set_text(new_text) + self:set_cursor_index(self:get_cursor_index() + #text) + self:emit_signal("property::text", self:get_text()) + end + else + self:set_text(new_text) + self:set_cursor_index(self:get_cursor_index() + #text) + self:emit_signal("property::text", self:get_text()) + end +end + +function text_input:overwrite_text(text) + local wp = self._private + + local start_pos = wp.selection_start + local end_pos = wp.selection_end + if start_pos > end_pos then + start_pos, end_pos = end_pos, start_pos + end + + local old_text = self:get_text() + local left_text = old_text:sub(1, start_pos) + local right_text = old_text:sub(end_pos + 1) + local new_text = left_text .. text .. right_text + + if wp.pattern then + new_text = new_text:match(wp.pattern) + if new_text then + self:set_text(new_text) + self:set_cursor_index(#left_text + 1) + self:emit_signal("property::text", self:get_text()) + end + else + self:set_text(new_text) + self:set_cursor_index(#left_text + 1) + self:emit_signal("property::text", self:get_text()) + end +end + +function text_input:copy() + local wp = self._private + if self:get_mode() == "overwrite" then + local text = self:get_text() + local start_pos = self._private.selection_start + local end_pos = self._private.selection_end + if start_pos > end_pos then + start_pos, end_pos = end_pos + 1, start_pos + end + text = text:sub(start_pos, end_pos) + wp.clipboard:set_text(text, -1) + end +end + +function text_input:paste() + local wp = self._private + + wp.clipboard:request_text(function(clipboard, text) + if text then + self:update_text(text) + end + end) +end + +function text_input:delete_next_word() + local old_text = self:get_text() + local cursor_index = self:get_cursor_index() + + local left_text = old_text:sub(1, cursor_index) + local right_text = old_text:sub(cword_end(old_text, cursor_index + 1)) + self:set_text(left_text .. right_text) + self:emit_signal("property::text", self:get_text()) +end + +function text_input:delete_previous_word() + local old_text = self:get_text() + local cursor_index = self:get_cursor_index() + local wstart = cword_start(old_text, cursor_index + 1) - 1 + local left_text = old_text:sub(1, wstart) + local right_text = old_text:sub(cursor_index + 1) + self:set_text(left_text .. right_text) + self:set_cursor_index(wstart) + self:emit_signal("property::text", self:get_text()) +end + +function text_input:delete_text() + if self:get_mode() == "insert" then + self:delete_text_before_cursor() + else + self:overwrite_text("") + end +end + +function text_input:delete_text_before_cursor() + local cursor_index = self:get_cursor_index() + if cursor_index > 0 then + local old_text = self:get_text() + local left_text = old_text:sub(1, cursor_index - 1) + local right_text = old_text:sub(cursor_index + 1) + self:set_text(left_text .. right_text) + self:set_cursor_index(cursor_index - 1) + self:emit_signal("property::text", self:get_text()) + end +end + +function text_input:delete_text_after_cursor() + local cursor_index = self:get_cursor_index() + if cursor_index < #self:get_text() then + local old_text = self:get_text() + local left_text = old_text:sub(1, cursor_index) + local right_text = old_text:sub(cursor_index + 2) + self:set_text(left_text .. right_text) + self:emit_signal("property::text", self:get_text()) + end +end + +function text_input:get_text() + return self._private.text_buffer or "" +end + +function text_input:get_text_widget() + return self._private.text_widget +end + +function text_input:show_selection() + self._private.selection_opacity = 1 + self:get_text_widget():emit_signal("widget::redraw_needed") +end + +function text_input:hide_selection() + self._private.selection_opacity = 0 + self:get_text_widget():emit_signal("widget::redraw_needed") +end + +function text_input:select_all() + if self:get_text() == "" then + return + end + + self:set_selection_start_index(0) + self:set_selection_end_index(#self:get_text()) +end + +function text_input:set_selection_to_word() + if self:get_text() == "" then + return + end + + local word_start_index = cword_start(self:get_text(), self:get_cursor_index() + 1) - 1 + local word_end_index = cword_end(self:get_text(), self:get_cursor_index() + 1) - 1 + + self:set_selection_start_index(word_start_index) + self:set_selection_end_index(word_end_index) +end + +function text_input:set_selection_start_index(index) + if #self:get_text() == 0 then + return + end + + index = math.max(math.min(index, #self:get_text()), 0) + + local layout = self:get_text_widget()._private.layout + local strong_pos, weak_pos = layout:get_caret_pos(index) + if strong_pos then + self._private.selection_start = index + self._private.selection_start_x = strong_pos.x / Pango.SCALE + self._private.selection_start_y = strong_pos.y / Pango.SCALE + self:get_text_widget():emit_signal("widget::redraw_needed") + end +end + +function text_input:set_selection_end_index(index) + if #self:get_text() == 0 then + return + end + + index = math.max(math.min(index, #self:get_text()), 0) + + local layout = self:get_text_widget()._private.layout + local strong_pos, weak_pos = layout:get_caret_pos(index) + if strong_pos then + if self:get_mode() ~= "overwrite" and index ~= self._private.selection_start then + self._private.mode = "overwrite" + self:show_selection() + self:hide_cursor() + end + + self._private.selection_end = index + self._private.selection_end_x = strong_pos.x / Pango.SCALE + self._private.selection_end_y = strong_pos.y / Pango.SCALE + self:get_text_widget():emit_signal("widget::redraw_needed") + end +end + +function text_input:increamant_selection_end_index() + if self:get_mode() == "insert" then + self:set_selection_start_index(self:get_cursor_index()) + self:set_selection_end_index(self:get_cursor_index() + 1) + else + self:set_selection_end_index(self._private.selection_end + 1) + end +end + +function text_input:decremeant_selection_end_index() + if self:get_mode() == "insert" then + self:set_selection_start_index(self:get_cursor_index()) + self:set_selection_end_index(self:get_cursor_index() - 1) + else + self:set_selection_end_index(self._private.selection_end - 1) + end +end + +function text_input:set_selection_start_index_from_x_y(x, y) + local layout = self:get_text_widget()._private.layout + local index, trailing = layout:xy_to_index(x * Pango.SCALE, y * Pango.SCALE) + if index then + self:set_selection_start_index(index) + else + local pixel_rect, logical_rect = self:get_text_widget()._private.layout:get_pixel_extents() + if x < logical_rect.x + logical_rect.width then + self:set_selection_start_index(0) + else + self:set_selection_start_index(#self:get_text()) + end + end +end + +function text_input:set_selection_end_index_from_x_y(x, y) + local layout = self:get_text_widget()._private.layout + local index, trailing = layout:xy_to_index(x * Pango.SCALE, y * Pango.SCALE) + if index then + self:set_selection_end_index(index + trailing) + else + local pixel_rect, logical_rect = self:get_text_widget()._private.layout:get_pixel_extents() + if x < logical_rect.x + logical_rect.width then + self:set_selection_end_index(0) + else + self:set_selection_end_index(#self:get_text()) + end + end +end + +function text_input:show_cursor() + self._private.cursor_opacity = 1 + self:get_text_widget():emit_signal("widget::redraw_needed") +end + +function text_input:hide_cursor() + self._private.cursor_opacity = 0 + self:get_text_widget():emit_signal("widget::redraw_needed") +end + +function text_input:set_cursor_index(index) + index = math.max(math.min(index, #self:get_text()), 0) + + local layout = self:get_text_widget()._private.layout + local strong_pos, weak_pos = layout:get_cursor_pos(index) + if strong_pos then + if strong_pos == self._private.cursor_index and self._private.mode == "insert" then + return + end + + if self:get_focused() and self:get_mode() ~= "insert" then + self:show_cursor() + end + + self._private.cursor_index = index + self._private.mode = "insert" + + self._private.cursor_x = strong_pos.x / Pango.SCALE + self._private.cursor_y = strong_pos.y / Pango.SCALE + + self:hide_selection() + + self:get_text_widget():emit_signal("widget::redraw_needed") + end +end + +function text_input:set_cursor_index_from_x_y(x, y) + local layout = self:get_text_widget()._private.layout + local index, trailing = layout:xy_to_index(x * Pango.SCALE, y * Pango.SCALE) + + if index then + self:set_cursor_index(index) + else + local pixel_rect, logical_rect = self:get_text_widget()._private.layout:get_pixel_extents() + if x < logical_rect.x + logical_rect.width then + self:set_cursor_index(0) + else + self:set_cursor_index(#self:get_text()) + end + end +end + +function text_input:set_cursor_index_to_word_start() + self:set_cursor_index(cword_start(self:get_text(), self:get_cursor_index() + 1) - 1) +end + +function text_input:set_cursor_index_to_word_end() + self:set_cursor_index(cword_end(self:get_text(), self:get_cursor_index() + 1) - 1) +end + +function text_input:set_cursor_index_to_end() + self:set_cursor_index(#self:get_text()) +end + +function text_input:increamant_cursor_index() + if self:get_mode() == "insert" then + self:set_cursor_index(self:get_cursor_index() + 1) + else + local start_pos = self._private.selection_start + local end_pos = self._private.selection_end + if start_pos > end_pos then + start_pos, end_pos = end_pos, start_pos + end + self:set_cursor_index(end_pos) + end +end + +function text_input:decremeant_cursor_index() + if self:get_mode() == "insert" then + self:set_cursor_index(self:get_cursor_index() - 1) + else + local start_pos = self._private.selection_start + local end_pos = self._private.selection_end + if start_pos > end_pos then + start_pos, end_pos = end_pos, start_pos + end + self:set_cursor_index(start_pos) + end +end + +function text_input:get_cursor_index() + return self._private.cursor_index +end + +function text_input:set_focus_on_subject_mouse_enter(subject) + subject:connect_signal("mouse::enter", function() + self:focus() + end) +end + +function text_input:set_unfocus_on_subject_mouse_leave(subject) + subject:connect_signal("mouse::leave", function() + self:unfocus() + end) +end + +function text_input:get_focused() + return self._private.focused +end + +function text_input:focus() + local wp = self._private + + if self:get_focused() == true then + return + end + + -- Do it first, so the cursor won't change back when unfocus was called on the focused text input + capi.awesome.emit_signal("text_input::focus", self) + + set_mouse_cursor("xterm") + + if self:get_mode() == "insert" then + self:show_cursor() + end + + run_keygrabber(self) + + if wp.cursor_blink then + if wp.cursor_blink_timer == nil then + wp.cursor_blink_timer = gtimer { + timeout = wp.cursor_blink_rate, + autostart = false, + call_now = false, + single_shot = false, + callback = function() + if self._private.cursor_opacity == 1 then + self:hide_cursor() + elseif self:get_mode() == "insert" then + self:show_cursor() + end + end + } + end + wp.cursor_blink_timer:start() + end + + + wp.focused = true + self:emit_signal("focus") +end + +function text_input:unfocus(context) + local wp = self._private + if self:get_focused() == false then + return + end + + set_mouse_cursor("left_ptr") + self:hide_cursor() + wp.cursor_blink_timer:stop() + self:hide_selection() + if self.reset_on_unfocus == true then + self:replace_text("") + end + + awful.keygrabber.stop(wp.keygrabber) + wp.focused = false + self:emit_signal("unfocus", context or "normal", self:get_text()) +end + +function text_input:toggle() + local wp = self._private + + if self:get_focused() == false then + self:focus() + else + self:unfocus() + end +end + +local function new() + local widget = wibox.container.background() + gtable.crush(widget, text_input, true) + + local wp = widget._private + + wp.focused = false + wp.clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD) + wp.cursor_index = 0 + wp.mode = "insert" + wp.click_count = 0 + + wp.cursor_x = 0 + wp.cursor_y = 0 + wp.cursor_opacity = 0 + wp.selection_start_x = 0 + wp.selection_end_x = 0 + wp.selection_start_y = 0 + wp.selection_end_y = 0 + wp.selection_opacity = 0 + + wp.click_timeout = 0.2 + + wp.unfocus_keys = { "Escape", "Return" } + wp.unfocus_on_root_clicked = true + wp.unfocus_on_client_clicked = true + wp.unfocus_on_mouse_leave = false + wp.unfocus_on_tag_change = true + wp.unfocus_on_other_text_input_focus = true + wp.unfocus_on_client_focus = true + + wp.focus_on_subject_mouse_enter = nil + wp.unfocus_on_subject_mouse_leave = nil + + wp.reset_on_unfocus = false + + wp.pattern = nil + wp.obscure = false + + wp.placeholder = "" + wp.text_color = qcolor.palette.fg() + wp.text = "" + + wp.cursor_width = 2 + wp.cursor_bg = qcolor.palette.fg() + wp.cursor_blink = true + wp.cursor_blink_rate = 0.6 + + wp.selection_bg = qcolor.palette.bg.high + + widget:set_widget_template(wibox.widget { + layout = wibox.layout.stack, + { + widget = wibox.widget.textbox, + id = "placeholder_role", + text = wp.placeholder + }, + { + widget = wibox.widget.textbox, + id = "text_role", + text = wp.text + } + }) + + capi.tag.connect_signal("property::selected", function() + if wp.unfocus_on_tag_change then + widget:unfocus() + end + end) + + capi.awesome.connect_signal("text_input::focus", function(text_input) + if wp.unfocus_on_other_text_input_focus and text_input ~= widget then + widget:unfocus() + end + end) + + capi.client.connect_signal("focus", function() + if wp.unfocus_on_client_focus then + widget:unfocus() + end + end) + + awful.mouse.append_global_mousebindings({ + awful.button({"Any"}, 1, function() + if wp.unfocus_on_root_clicked then + widget:unfocus() + end + end), + awful.button({"Any"}, 3, function() + if wp.unfocus_on_root_clicked then + widget:unfocus() + end + end) + }) + + capi.client.connect_signal("button::press", function() + if wp.unfocus_on_client_clicked then + widget:unfocus() + end + end) + + capi.awesome.connect_signal("colorscheme::changed", function(old_colorscheme_to_new_map) + wp.text_color = old_colorscheme_to_new_map[wp.text_color] + wp.cursor_bg = old_colorscheme_to_new_map[wp.cursor_bg] + wp.selection_bg = old_colorscheme_to_new_map[wp.selection_bg] + end) + + return widget +end + +function text_input.mt:__call(...) + return new() +end + +build_properties(text_input, properties) + +return setmetatable(text_input, text_input.mt) diff --git a/.config/awesome/ui/init.lua b/.config/awesome/ui/init.lua index 8ba64f9..efcdd2f 100644 --- a/.config/awesome/ui/init.lua +++ b/.config/awesome/ui/init.lua @@ -2,3 +2,19 @@ require "ui.statusbar" require "ui.decorations" -- require "ui.tidy" -- require "ui.osd" + +-- local awful = require "awful" +-- local wibox = require "wibox" +-- +-- awful.popup { +-- widget = { +-- widget = wibox.container.background, +-- bg = "#ff0000", +-- opacity = 0.5, +-- { +-- widget = wibox.widget.textbox, +-- text = "test" +-- } +-- } +-- } +-- diff --git a/.config/awesome/ui/osd/providers/mpris.lua b/.config/awesome/ui/osd/providers/mpris.lua new file mode 100644 index 0000000..babb0ff --- /dev/null +++ b/.config/awesome/ui/osd/providers/mpris.lua @@ -0,0 +1,3 @@ +local M = {} + + diff --git a/.config/awesome/ui/statusbar/init.lua b/.config/awesome/ui/statusbar/init.lua index 137ffb8..0a1198a 100644 --- a/.config/awesome/ui/statusbar/init.lua +++ b/.config/awesome/ui/statusbar/init.lua @@ -5,6 +5,7 @@ local qanim = require "quarrel.animation" local qui = require "quarrel.ui" local qvars = require "quarrel.vars" local wibox = require "wibox" +local dnd = require "ui.statusbar.widgets.dnd" local M = require "ui.statusbar.consts" @@ -31,29 +32,26 @@ screen.connect_signal("request::desktop_decoration", function(s) }, nil, { - widget = wibox.container.place, - valign = "bottom", + dnd, + displays.brightness, + displays.audio, + displays.battery, + displays.wifi, { - displays.brightness, - displays.audio, - displays.battery, - displays.wifi, + widget = wibox.container.place, { - widget = wibox.container.place, - { - widget = wibox.container.constraint, - height = qui.CHAR_HEIGHT, - width = qui.CHAR_HEIGHT, - keyboardlayout, - }, + widget = wibox.container.constraint, + height = qui.CHAR_HEIGHT, + width = qui.CHAR_HEIGHT, + keyboardlayout, }, - clock, - layout = wibox.layout.fixed.vertical, - spacing = qui.PADDING * 2, }, + clock, + layout = wibox.layout.fixed.vertical, + spacing = qui.PADDING * 2, }, layout = wibox.layout.align.vertical, - expand = "outside", + expand = "inside", }, nil, nil, diff --git a/.config/awesome/ui/statusbar/panel/init.lua b/.config/awesome/ui/statusbar/panel/init.lua index 814a5a1..7e54560 100644 --- a/.config/awesome/ui/statusbar/panel/init.lua +++ b/.config/awesome/ui/statusbar/panel/init.lua @@ -36,23 +36,11 @@ local panel = wibox.widget { wifi, -- battery, music.widget, - calendar, + calendar.widget, layout = wibox.layout.fixed.vertical, spacing = qui.PADDING, }, }, - -- { - -- widget = wibox.container.background, - -- { - -- widget = wibox.widget.textbox, - -- text = ":)", - -- }, - -- }, - -- { - -- widget = wibox.container.place, - -- valign = "bottom", - -- power_menu, - -- }, layout = wibox.layout.align.vertical, }, }, diff --git a/.config/awesome/ui/statusbar/panel/widgets/calendar.lua b/.config/awesome/ui/statusbar/panel/widgets/calendar.lua index ddbffc0..c3f0424 100644 --- a/.config/awesome/ui/statusbar/panel/widgets/calendar.lua +++ b/.config/awesome/ui/statusbar/panel/widgets/calendar.lua @@ -1,14 +1,36 @@ -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 wibox = require "wibox" -local weekday_map = { 7, 1, 2, 3, 4, 5, 6 } +local WEEKDAY_MAP = { 7, 1, 2, 3, 4, 5, 6 } -local calendar = wibox.widget(qui.styled { +local M = {} + +local l_grid = wibox.widget { + layout = wibox.layout.grid, + forced_num_rows = 7, + forced_num_cols = 7, + border_width = { + inner = qui.BORDER_WIDTH, + outer = 0, + }, + border_color = qcolor.palette.border.variant, +} + +local w_month = wibox.widget { + widget = wibox.widget.textbox, + text = "-", +} + +local w_year = wibox.widget { + widget = wibox.widget.textbox, + text = "-", +} + +local offset = 0 + +M.widget = wibox.widget(qui.styled { widget = wibox.container.background, bg = qcolor.palette.bg.high, { @@ -16,18 +38,7 @@ local calendar = wibox.widget(qui.styled { margins = qui.BORDER_WIDTH, { { - { - 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", - }, + l_grid, { widget = wibox.container.background, bg = qcolor.palette.border(), @@ -43,9 +54,17 @@ local calendar = wibox.widget(qui.styled { widget = wibox.container.place, fill_horizontal = true, { - widget = wibox.widget.textbox, - text = "05\n25", - }, + { + widget = wibox.container.place, + w_month, + }, + qui.separator { + size = qui.BORDER_WIDTH + }, + w_year, + layout = wibox.layout.fixed.vertical, + spacing = qui.PADDING + } }, nil, qui.styled { @@ -86,7 +105,6 @@ local calendar = wibox.widget(qui.styled { }, }, layout = wibox.layout.fixed.vertical, - -- spacing = qui.PADDING }, }, layout = wibox.layout.align.vertical, @@ -97,11 +115,32 @@ local calendar = wibox.widget(qui.styled { }, }) -local grid = calendar:get_children_by_id("grid")[1] +function M:render(grid) + for i, row in ipairs(grid) do + for j, col in ipairs(row) do + local widget = l_grid:get_widgets_at(i, j)[1] + widget.widget.widget.widget.text = col[1] + if col[2] then + 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 = 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 = qcolor.palette.fg.low + end + end + end +end -- 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 = { +function M:compute(year, month) + local grid = { { { "Mo", false, true }, { "Tu", false, true }, @@ -125,7 +164,7 @@ function calendar:compute_grid(year, month) local days = last_day.day local prev_days = os.date("*t", os.time { year = year, month = month, day = 0 }) - local prev_offset = weekday_map[first_day.wday] - 1 + local prev_offset = WEEKDAY_MAP[first_day.wday] - 1 local prev_month = month - 1 == -1 and 12 or month - 1 local prev_year = prev_month == 12 and year - 1 or year @@ -133,7 +172,7 @@ function calendar:compute_grid(year, month) for offset = prev_offset, 1, -1 do local day = prev_days.day - offset + 1 table.insert( - calendar_table[2], + grid[2], { day, day == current.day and prev_month == current.month and prev_year == current.year, false } ) end @@ -141,10 +180,10 @@ function calendar:compute_grid(year, month) do local row = 2 - local weekday = weekday_map[first_day.wday] + local weekday = WEEKDAY_MAP[first_day.wday] for day = 1, days do table.insert( - calendar_table[row], + grid[row], { day, day == current.day and month == current.month and year == current.year, true } ) if weekday == 7 then @@ -159,7 +198,7 @@ function calendar:compute_grid(year, month) local next_year = next_month == 1 and year + 1 or year for day = 1, 42 - prev_offset - days do table.insert( - calendar_table[row], + grid[row], { day, day == current.day and next_month + 1 == current.month and next_year == current.year, false } ) if weekday == 7 then @@ -171,35 +210,16 @@ function calendar:compute_grid(year, month) end end - for i, row in ipairs(calendar_table) do - for j, col in ipairs(row) do - local widget = grid:get_widgets_at(i, j)[1] - widget.widget.widget.widget.text = col[1] - if col[2] then - 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 = 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 + w_month.text = month + w_year.text = year + self:render(grid) end -local cells = {} -local function cell(content) - local widget = wibox.widget { +-- Set up the widget +for _ = 1, 49 do + local cell = wibox.widget { widget = wibox.container.background, bg = qcolor.palette.bg.high, - -- shape = qui.shape, { widget = wibox.container.margin, margins = qui.PADDING * 1.5, @@ -209,20 +229,15 @@ local function cell(content) forced_width = qui.CHAR_HEIGHT, { widget = wibox.widget.textbox, - markup = content, + markup = "", }, }, }, } - table.insert(cells, widget) -end - -for _ = 1, 49 do - cell() + l_grid:add(cell) end -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() }) +M:compute(current_time.year, current_time.month) +l_grid:add_row_border(2, qui.BORDER_WIDTH, { color = qcolor.palette.border() }) -return calendar +return M diff --git a/.config/awesome/ui/statusbar/panel/widgets/notifs/consts.lua b/.config/awesome/ui/statusbar/panel/widgets/notifs/consts.lua new file mode 100644 index 0000000..c864483 --- /dev/null +++ b/.config/awesome/ui/statusbar/panel/widgets/notifs/consts.lua @@ -0,0 +1,13 @@ +local qcolor = require "quarrel.color" + +local C = {} + +C.NOTIF_TIMEOUT = 3 +C.LEVEL_COLORS = { + debug = qcolor.palette.purple(), + info = qcolor.palette.blue(), + warn = qcolor.palette.orange(), + error = qcolor.palette.red(), +} + +return C diff --git a/.config/awesome/ui/statusbar/panel/widgets/notifs/init.lua b/.config/awesome/ui/statusbar/panel/widgets/notifs/init.lua new file mode 100644 index 0000000..e69de29 diff --git a/.config/awesome/ui/statusbar/panel/widgets/notifs/widgets/notif.lua b/.config/awesome/ui/statusbar/panel/widgets/notifs/widgets/notif.lua new file mode 100644 index 0000000..b2b66b2 --- /dev/null +++ b/.config/awesome/ui/statusbar/panel/widgets/notifs/widgets/notif.lua @@ -0,0 +1,214 @@ +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 consts = require "ui.wicked.consts" +local gtimer = require "gears.timer" +local qcolor = require "quarrel.color" +local qmarkup = require "quarrel.markup" + +local M = require "ui.wicked.consts" + +function M.new(n, _, n_args) + 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" + else + app_name = n.app_name + end + + local w_progress + + local level_color = consts.LEVEL_COLORS[n_args.level] or (n.urgency == "critical" and qcolor.palette.yellow()) + + if n.timeout > 0 then + w_progress = wibox.widget { + widget = wibox.container.radialprogressbar, + max_value = M.NOTIF_TIMEOUT, + border_color = qcolor.palette.bg.lowest, + color = qcolor.palette.yellow(), + border_width = qui.PADDING / 2, + forced_height = qui.CHAR_HEIGHT, + forced_width = qui.CHAR_HEIGHT, + } + end + + local w_notif = naughty.layout.box { + notification = n, + placement = function(d) + return awful.placement.right(d, { + margins = beautiful.useless_gap * 2, + }) + end, + bg = qcolor.palette.transparent, + border_width = 0, + shape = gshape.rectangle, + widget_template = { + widget = wibox.container.constraint, + height = qui.BIG_PADDING * 2 + + qui.CHAR_HEIGHT + + qui.BORDER_WIDTH + + qui.BIG_PADDING * 2 + + (n.icon and qui.CHAR_HEIGHT * 2 + qui.PADDING or (title_height + message_height + intertext_margin)), + + strategy = "exact", + { + qui.styled { + widget = wibox.container.background, + forced_width = beautiful.notification_max_width, + point = function(geo, args) + return { + x = args.parent.width, + y = args.parent.height - geo.height, + } + end, + { + widget = wibox.container.margin, + margins = qui.BIG_PADDING, + { + { + widget = wibox.container.constraint, + width = beautiful.notification_max_width - (qui.BIG_PADDING + qui.BORDER_WIDTH) * 2 - (level_color and qui.BIG_PADDING + qui.PADDING or 0), + strategy = "max", + { + widget = wibox.container.place, + content_fill_horizontal = true, + { + { + { + widget = wibox.widget.textbox, + markup = qmarkup(app_name, { bold = true }), + }, + nil, + w_progress, + layout = wibox.layout.align.horizontal, + }, + { + widget = wibox.container.constraint, + height = qui.BORDER_WIDTH, + strategy = "exact", + { + widget = wibox.container.background, + bg = qcolor.palette.border(), + }, + }, + { + widget = wibox.container.constraint, + height = n.icon and qui.CHAR_HEIGHT * 2 + qui.PADDING + or (title_height + message_height + intertext_margin), + strategy = "exact", + { + { + widget = naughty.widget.icon, + shape = qui.shape, + notification = n, + }, + { + { + widget = wibox.container.constraint, + height = title_height, + strategy = "exact", + { + widget = wibox.widget.textbox, + text = n.title or "", + }, + }, + { + widget = wibox.container.constraint, + height = message_height, + strategy = "exact", + { + widget = wibox.widget.textbox, + text = n.message or "", + }, + }, + spacing = intertext_margin, + layout = wibox.layout.fixed.vertical, + }, + fill_space = true, + spacing = n.icon and qui.BIG_PADDING, + layout = wibox.layout.fixed.horizontal, + }, + }, + layout = wibox.layout.fixed.vertical, + spacing = qui.BIG_PADDING, + }, + }, + }, + { + forced_width = qui.PADDING, + widget = wibox.container.background, + bg = level_color, + shape = qui.shape, + }, + layout = wibox.layout.fixed.horizontal, + spacing = level_color and qui.BIG_PADDING, + }, + }, + id = "bg", + }, + layout = wibox.layout.manual, + }, + }, + } + + local hiding = false + + local t_position = qanim:new { + duration = qvars.anim_duration, + pos = 0, + easing = qvars.easing, + subscribed = function(pos) + gtimer.delayed_call(function() + w_notif.widget.widget:move(1, function(geo, args) + if pos == 0 and hiding then + old_destroy() + end + return { + x = args.parent.width - pos, + y = args.parent.height - geo.height, + } + end) + end) + end, + } + local t_opacity = rtimed { + duration = qvars.anim_duration, + intro = qvars.anim_intro, + easing = easing.quadratic, + pos = 0, + clamp_position = true, + subscribed = function(pos) + w_notif.opacity = pos + end, + } + + n:disconnect_signal("destroyed", w_notif._private.destroy_callback) + function w_notif._private.destroy_callback() + t_opacity.target = 0 + t_position:set(0) + hiding = true + end + n:weak_connect_signal("destroyed", w_notif._private.destroy_callback) + + t_opacity.target = 1 + t_position:set(beautiful.notification_max_width) + if t_progress then + t_progress.target = M.NOTIF_TIMEOUT + end +end + +return setmetatable(M, { + __call = function(_, ...) + return M.new(...) + end, +}) diff --git a/.config/awesome/ui/statusbar/widgets/dnd.lua b/.config/awesome/ui/statusbar/widgets/dnd.lua new file mode 100644 index 0000000..37d96b7 --- /dev/null +++ b/.config/awesome/ui/statusbar/widgets/dnd.lua @@ -0,0 +1,34 @@ +local qvars = require "quarrel.vars" +local wibox = require "wibox" +local dnd = require "services.dnd" +local qcolor = require "quarrel.color" +local phosphor = require "assets.phosphor" +local qui = require "quarrel.ui" +local qdebug = require "quarrel.debug" + +local w = wibox.widget { + widget = wibox.container.background, + shape = qui.shape, + qui.padded { + widget = wibox.container.place, + qui.icon { + icon = phosphor.bell_simple_slash_fill, + color = qcolor.palette.bg(), + widget = { + forced_height = qui.CHAR_HEIGHT - qui.PADDING * 2, + forced_width = qui.CHAR_HEIGHT - qui.PADDING * 2 + } + } + }, + -- visible = false +} + +dnd:connect_signal("dnd", function (_, value) + -- this is a giant hack + -- why is it here? cause otherwise the align layout freaks out + -- and counts the hidden widget when reserving space *and* when drawing, offsetting the rest of the widgets in the process + -- w.visible = value + w.bg = qcolor.palette[value and "yellow" or "bg"]() +end) + +return w diff --git a/.config/awesome/ui/tidy/init.lua b/.config/awesome/ui/tidy/init.lua new file mode 100644 index 0000000..ff3b603 --- /dev/null +++ b/.config/awesome/ui/tidy/init.lua @@ -0,0 +1,94 @@ +local awful = require "awful" +local gshape = require "gears.shape" +local gtable = require "gears.table" +local phosphor = require "assets.phosphor" +local q = require "quarrel" +local qcolor = require "quarrel.color" +local qui = require "quarrel.ui" +local qvars = require "quarrel.vars" +local wibox = require "wibox" + +local M = {} + +M.last_focused_client = nil +M.class_map = { + ["org.wezfurlong.wezterm"] = "Wezterm", +} + +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, + source = function(s) + local ret = gtable.clone(s.all_clients, false) + -- q.debug(ret) + table.sort(ret, function(a, b) + return a.class == b.class and (a.pid or 0) < (b.pid or 0) or a.class < b.class + end) + return ret + end, + layout = { + spacing = qui.BIG_PADDING, + layout = wibox.layout.fixed.vertical, + }, + widget_template = qui.styled { + widget = wibox.container.background, + bg = qcolor.palette.bg.high, + qui.padded_big { + { + widget = wibox.container.place, + { + 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 = M.class_map[c.class] or c.class + if c.active then + c.wasactive = true + self.border_color = qcolor.palette.yellow() + end + end, + update_callback = function(self, c) + self:get_children_by_id("client_name")[1].text = M.class_map[c.class] or c.class + if c.active then + c.wasactive = true + self.border_color = qcolor.palette.yellow() + elseif c.wasactive then + c.wasactive = false + self.border_color = qcolor.palette.border() + end + end, + }, + }, +} + +return M diff --git a/.config/awesome/ui/wicked/consts.lua b/.config/awesome/ui/wicked/consts.lua index c864483..31e31d5 100644 --- a/.config/awesome/ui/wicked/consts.lua +++ b/.config/awesome/ui/wicked/consts.lua @@ -3,6 +3,12 @@ local qcolor = require "quarrel.color" local C = {} C.NOTIF_TIMEOUT = 3 +C.LEVEL_PRIORITIES = { + debug = 0, + info = 1, + warn = 2, + error = 3 +} C.LEVEL_COLORS = { debug = qcolor.palette.purple(), info = qcolor.palette.blue(), diff --git a/.config/awesome/ui/wicked/init.lua b/.config/awesome/ui/wicked/init.lua index dd3324d..e82d408 100644 --- a/.config/awesome/ui/wicked/init.lua +++ b/.config/awesome/ui/wicked/init.lua @@ -12,6 +12,7 @@ local consts = require "ui.wicked.consts" local gtimer = require "gears.timer" local qcolor = require "quarrel.color" local qmarkup = require "quarrel.markup" +local dnd = require "services.dnd" local M = require "ui.wicked.consts" @@ -28,6 +29,11 @@ function M.new(n, _, n_args) local w_progress, t_progress + if ((consts.LEVEL_PRIORITIES[n_args.level] or 0) < consts.LEVEL_PRIORITIES.warn and n.urgency ~= "critical") and dnd.dnd then + n:destroy() + return + end + local level_color = consts.LEVEL_COLORS[n_args.level] or (n.urgency == "critical" and qcolor.palette.yellow()) if n.timeout > 0 then @@ -68,9 +74,6 @@ function M.new(n, _, n_args) + qui.CHAR_HEIGHT + qui.BORDER_WIDTH + qui.BIG_PADDING * 2 - -- + title_height - -- + message_height - -- + intertext_margin, + (n.icon and qui.CHAR_HEIGHT * 2 + qui.PADDING or (title_height + message_height + intertext_margin)), strategy = "exact", @@ -78,7 +81,6 @@ function M.new(n, _, n_args) qui.styled { widget = wibox.container.background, forced_width = beautiful.notification_max_width, - -- border_color = border_color, point = function(geo, args) return { x = args.parent.width, @@ -158,20 +160,14 @@ function M.new(n, _, n_args) }, }, }, - -- { - -- widget = wibox.container.constraint, - -- strategy = "max", - -- widget = wibox.container.place, { forced_width = qui.PADDING, widget = wibox.container.background, bg = level_color, shape = qui.shape, - -- }, }, layout = wibox.layout.fixed.horizontal, spacing = level_color and qui.BIG_PADDING, - -- expand = "outside" }, }, id = "bg", -- cgit v1.2.3