diff options
author | Freya Murphy <freya@freyacat.org> | 2025-06-18 11:34:06 -0400 |
---|---|---|
committer | Freya Murphy <freya@freyacat.org> | 2025-06-18 11:34:06 -0400 |
commit | f11ff2f2ea4bd8ad65aa8a4fbae6f4c083117672 (patch) | |
tree | a3b4425f1e87b1aef2f0e93d6570b1d3f3c69d8d /pkgs/astal | |
parent | update hyprland config (diff) | |
download | dotfiles-nix-f11ff2f2ea4bd8ad65aa8a4fbae6f4c083117672.tar.gz dotfiles-nix-f11ff2f2ea4bd8ad65aa8a4fbae6f4c083117672.tar.bz2 dotfiles-nix-f11ff2f2ea4bd8ad65aa8a4fbae6f4c083117672.zip |
add notifications to astal
Diffstat (limited to 'pkgs/astal')
-rw-r--r-- | pkgs/astal/src/init.lua | 2 | ||||
-rw-r--r-- | pkgs/astal/src/lib.lua | 67 | ||||
-rw-r--r-- | pkgs/astal/src/style/style.scss | 1 | ||||
-rw-r--r-- | pkgs/astal/src/style/widget/deck.scss | 89 | ||||
-rw-r--r-- | pkgs/astal/src/widget/bar/tray.lua | 2 | ||||
-rw-r--r-- | pkgs/astal/src/widget/deck/init.lua | 18 | ||||
-rw-r--r-- | pkgs/astal/src/widget/deck/notifications.lua | 136 |
7 files changed, 310 insertions, 5 deletions
diff --git a/pkgs/astal/src/init.lua b/pkgs/astal/src/init.lua index b350fbf..5bdfb03 100644 --- a/pkgs/astal/src/init.lua +++ b/pkgs/astal/src/init.lua @@ -4,6 +4,7 @@ local lib = require("lib") local Bar = require("widget.bar") local Corners = require("widget.corners") +local Deck = require("widget.deck") App:start({ css = lib.src("main.css"), @@ -11,6 +12,7 @@ App:start({ for _, mon in pairs(App.monitors) do Bar(mon) Corners(mon) + Deck(mon) end end, }) diff --git a/pkgs/astal/src/lib.lua b/pkgs/astal/src/lib.lua index 93f4bc1..992c0af 100644 --- a/pkgs/astal/src/lib.lua +++ b/pkgs/astal/src/lib.lua @@ -1,4 +1,7 @@ +local astal = require("astal") local Variable = require("astal").Variable +local Astal = require("astal.gtk3").Astal +local GLib = astal.require("GLib") local lib @@ -55,12 +58,31 @@ lib = { return new_arr end, - --- returns length of table - count = function(array) - if not array then + --- returns if value is an array + is_array = function(val) + return type(val) == 'table' + end, + + --- returns if value is an string + is_string = function(val) + return type(val) == 'string' + end, + + --- returns if value is nil + is_nil = function(val) + return val == nil + end, + + --- returns length of table or string + count = function(val) + if lib.is_array(val) then + return #val + elseif lib.is_string(val) then + return val:len() + elseif lib.is_nil(val) then return 0 else - return #array + return nil end end, @@ -73,6 +95,43 @@ lib = { neg = function(val) return not val end, + + --- better truthy function + is_true = function(val) + -- zero is false + if val == 0 then + return false + end + + -- empty array or string is false + if lib.empty(val) then + return false + end + + -- default truthyness + return val and true or false + end, + + --- better fasly function + is_false = function(val) + return not lib.is_true(val) + end, + + --- checks if a file exists + file_exists = function(path) + return GLib.file_test(path, "EXISTS") + end, + + --- checks if a string is an icon + is_icon = function(icon) + return Astal.Icon.lookup_icon(icon) ~= nil + end, + + --- formats a time + time = function(time, format) + format = format or "%H:%M" + return GLib.DateTime.new_from_unix_local(time):format(format) + end, } return lib diff --git a/pkgs/astal/src/style/style.scss b/pkgs/astal/src/style/style.scss index 7be2b2f..0c66051 100644 --- a/pkgs/astal/src/style/style.scss +++ b/pkgs/astal/src/style/style.scss @@ -1,3 +1,4 @@ +@use "sass:string"; @use "sass:color"; @use "sass:math"; diff --git a/pkgs/astal/src/style/widget/deck.scss b/pkgs/astal/src/style/widget/deck.scss new file mode 100644 index 0000000..cd926f0 --- /dev/null +++ b/pkgs/astal/src/style/widget/deck.scss @@ -0,0 +1,89 @@ + +@function gtkalpha($c, $a) { + @return string.unquote("alpha(#{$c},#{$a})"); +} + +$deck-scale: $font-size * 5; + +.deck { + + > box { + padding: $outer-gap; + } + + .notification { + > box { + color: $fg; + background: $bg; + border-radius: $outer-radius; + margin-bottom: $outer-gap; + min-width: $deck-scale * 5; + } + } +} + +.notification { + > box { + &.critical { + .app-name { + color: gtkalpha($error, .8); + } + + .app-icon { + color: gtkalpha($error, .6); + } + } + } + + .header { + padding: $outer-gap; + color: gtkalpha($fg, 0.5); + + .app-icon { + margin-left: $inner-gap; + margin-right: $outer-gap; + } + + .app-name { + margin-right: $inner-gap; + font-weight: bold; + + &:first-child { + margin-left: $outer-gap; + } + } + + .time { + margin: 0 $outer-gap; + } + + button { + padding: $inner-gap; + border-radius: $inner-radius; + min-width: 0; + min-height: 0; + } + } + + separator { + margin: 0 $outer-gap; + background: gtkalpha($fg, .1); + } + + .content { + margin: $outer-gap; + + .body { + color: gtkalpha($fg, 0.8); + } + + .image { + margin-right: $outer-gap; + border-radius: $inner-gap; + min-width: $deck-scale; + min-height: $deck-scale; + background-size: cover; + background-position: center; + } + } +} diff --git a/pkgs/astal/src/widget/bar/tray.lua b/pkgs/astal/src/widget/bar/tray.lua index 9046494..f0460b9 100644 --- a/pkgs/astal/src/widget/bar/tray.lua +++ b/pkgs/astal/src/widget/bar/tray.lua @@ -28,7 +28,7 @@ return function() return Widget.Box({ class_name = "tray", - visible = bind(tray, "items"):as(lib.empty):as(lib.neg), + visible = bind(tray, "items"):as(lib.is_true), bind(tray, "items"):as(Items) }) end diff --git a/pkgs/astal/src/widget/deck/init.lua b/pkgs/astal/src/widget/deck/init.lua new file mode 100644 index 0000000..0df5bb4 --- /dev/null +++ b/pkgs/astal/src/widget/deck/init.lua @@ -0,0 +1,18 @@ +local astal = require("astal") +local Widget = require("astal.gtk3.widget") + +local Notifications = require("widget.deck.notifications") + +return function(gdkmonitor) + local Anchor = astal.require('Astal').WindowAnchor + + return Widget.Window({ + class_name = "deck", + gdkmonitor = gdkmonitor, + anchor = Anchor.TOP + Anchor.RIGHT, + Widget.Box({ + vertical = true, + Notifications(), + }), + }) +end diff --git a/pkgs/astal/src/widget/deck/notifications.lua b/pkgs/astal/src/widget/deck/notifications.lua new file mode 100644 index 0000000..e40a815 --- /dev/null +++ b/pkgs/astal/src/widget/deck/notifications.lua @@ -0,0 +1,136 @@ +local astal = require("astal") +local Widget = require("astal.gtk3").Widget +local Gtk = require("astal.gtk3").Gtk +local Variable = require("astal").Variable +local Notifd = astal.require("AstalNotifd") +local lib = require("lib") +local bind = astal.bind +local timeout = astal.timeout + +local TIMEOUT_DELAY = 5000 + +local notifd = Notifd.get_default() +local notifs = Variable({}) +local map = {} + +function update() + local arr = {} + for id,_ in pairs(map) do + table.insert(arr, id) + end + notifs:set(arr) +end + +function set(_, id) + map[id] = true + update() +end + +function delete(id) + map[id] = nil + update() +end + +notifd.on_notified = set + +function Header(notif) + local show_icon = lib.is_true(notif.app_icon) or + lib.is_true(notif.desktop_entry) + + return Widget.Box({ + class_name = "header", + show_icon and Widget.Icon({ + class_name = "app-icon", + icon = notif.app_icon or notif.desktop_entry, + }), + Widget.Label({ + class_name = "app-name", + halign = "START", + ellipsize = "END", + label = notif.app_name or "", + }), + Widget.Label({ + class_name = "time", + hexpand = true, + halign = "END", + label = lib.time(notif.time), + }), + Widget.Button({ + on_clicked = function() delete(notif.id) end, + Widget.Icon({ icon = "window-close-symbolic" }), + }), + }) +end + +function Content(notif) + return Widget.Box({ + class_name = "content", + (notif.image and lib.file_exists(notif.image)) and Widget.Box({ + valign = "START", + class_name = "image", + css = string.format("background-image: url('%s')", notif.image), + }), + (notif.image and lib.is_icon(notif.image)) and Widget.Box({ + valign = "START", + class_name = "icon-image", + Widget.Icon({ + icon = notif.image, + hexpand = true, + vexpand = true, + halign = "CENTER", + valign = "CENTER", + }), + }), + Widget.Box({ + vertical = true, + Widget.Label({ + class_name = "summary", + halign = "START", + xalign = 0, + ellipsize = "END", + label = notif.summary, + }), + Widget.Label({ + class_name = "body", + wrap = true, + use_markup = true, + halign = "START", + xalign = 0, + justify = "FILL", + label = notif.body, + }), + }), + }) +end + +function Notification(id) + local notif = notifd:get_notification(id) + + local function destroy() + delete(id) + end + + local function setup() + timeout(TIMEOUT_DELAY, destroy) + end + + return Widget.EventBox({ + class_name = "notification", + setup = setup, + on_hover_lost = destroy, + Widget.Box({ + vertical = true, + Header(notif), + Gtk.Separator({ visible = true }), + Content(notif), + }), + }) +end + +function Notifications(ids) + return lib.map(ids, Notification) +end + +return function() + return bind(notifs):as(Notifications) +end |