diff options
author | Kevin <[email protected]> | 2025-01-23 09:34:46 -0300 |
---|---|---|
committer | GitHub <[email protected]> | 2025-01-23 13:34:46 +0100 |
commit | 907230e479ba6c9489463797f81c7348ed754302 (patch) | |
tree | 4f12e013cebcff9097ae6a0e23885f1236b8b3d7 /examples/gtk3/lua/notifications | |
parent | 4d6d85562be7fe25cf659f1f1898244e1bdb44ca (diff) |
add: lua gtk3 examples (#156)
Diffstat (limited to 'examples/gtk3/lua/notifications')
-rw-r--r-- | examples/gtk3/lua/notifications/README.md | 5 | ||||
-rw-r--r-- | examples/gtk3/lua/notifications/init.lua | 20 | ||||
-rw-r--r-- | examples/gtk3/lua/notifications/lib.lua | 74 | ||||
-rw-r--r-- | examples/gtk3/lua/notifications/notifications/Notification.lua | 105 | ||||
-rw-r--r-- | examples/gtk3/lua/notifications/notifications/Notification.scss | 126 | ||||
-rw-r--r-- | examples/gtk3/lua/notifications/notifications/NotificationPopups.lua | 57 | ||||
-rw-r--r-- | examples/gtk3/lua/notifications/style.scss | 1 |
7 files changed, 388 insertions, 0 deletions
diff --git a/examples/gtk3/lua/notifications/README.md b/examples/gtk3/lua/notifications/README.md new file mode 100644 index 0000000..60dad60 --- /dev/null +++ b/examples/gtk3/lua/notifications/README.md @@ -0,0 +1,5 @@ +# Notifications Popups + + + +A replacement for dunst and other daemons using [Notifd](https://aylur.github.io/astal/guide/libraries/notifd). diff --git a/examples/gtk3/lua/notifications/init.lua b/examples/gtk3/lua/notifications/init.lua new file mode 100644 index 0000000..886e9ab --- /dev/null +++ b/examples/gtk3/lua/notifications/init.lua @@ -0,0 +1,20 @@ +local astal = require("astal") +local App = require("astal.gtk3.app") + +local NotificationPopups = require("notifications.NotificationPopups") +local src = require("lib").src + +local scss = src("style.scss") +local css = "/tmp/style.css" + +astal.exec("sass " .. scss .. " " .. css) + +App:start({ + instance_name = "notifications", + css = css, + main = function() + for _, mon in pairs(App.monitors) do + NotificationPopups(mon) + end + end, +}) diff --git a/examples/gtk3/lua/notifications/lib.lua b/examples/gtk3/lua/notifications/lib.lua new file mode 100644 index 0000000..289fc7e --- /dev/null +++ b/examples/gtk3/lua/notifications/lib.lua @@ -0,0 +1,74 @@ +local astal = require("astal") +local Variable = require("astal").Variable +local Gtk = require("astal.gtk3").Gtk +local GLib = astal.require("GLib") + +local M = {} + +function M.src(path) + local str = debug.getinfo(2, "S").source:sub(2) + local src = str:match("(.*/)") or str:match("(.*\\)") or "./" + return src .. path +end + +---@generic T, R +---@param array T[] +---@param func fun(T, i: integer): R +---@return R[] +function M.map(array, func) + local new_arr = {} + for i, v in ipairs(array) do + new_arr[i] = func(v, i) + end + return new_arr +end + +---@param path string +---@return boolean +function M.file_exists(path) return GLib.file_test(path, "EXISTS") end + +function M.varmap(initial) + local map = initial + local var = Variable() + + local function notify() + local arr = {} + for _, value in pairs(map) do + table.insert(arr, value) + end + var:set(arr) + end + + local function delete(key) + if Gtk.Widget:is_type_of(map[key]) then map[key]:destroy() end + + map[key] = nil + end + + notify() + + return setmetatable({ + set = function(key, value) + delete(key) + map[key] = value + notify() + end, + delete = function(key) + delete(key) + notify() + end, + get = function() return var:get() end, + subscribe = function(callback) return var:subscribe(callback) end, + }, { + __call = function() return var() end, + }) +end + +---@param time number +---@param format? string +function M.time(time, format) + format = format or "%H:%M" + return GLib.DateTime.new_from_unix_local(time):format(format) +end + +return M diff --git a/examples/gtk3/lua/notifications/notifications/Notification.lua b/examples/gtk3/lua/notifications/notifications/Notification.lua new file mode 100644 index 0000000..39d36f5 --- /dev/null +++ b/examples/gtk3/lua/notifications/notifications/Notification.lua @@ -0,0 +1,105 @@ +local Widget = require("astal.gtk3").Widget +local Gtk = require("astal.gtk3").Gtk +local Astal = require("astal.gtk3").Astal + +local map = require("lib").map +local time = require("lib").time +local file_exists = require("lib").file_exists + +local function is_icon(icon) return Astal.Icon.lookup_icon(icon) ~= nil end + +---@param props { setup?: function, on_hover_lost?: function, notification: any } +return function(props) + local n = props.notification + + local header = Widget.Box({ + class_name = "header", + (n.app_icon or n.desktop_entry) and Widget.Icon({ + class_name = "app-icon", + icon = n.app_icon or n.desktop_entry, + }), + Widget.Label({ + class_name = "app-name", + halign = "START", + ellipsize = "END", + label = n.app_name or "Unknown", + }), + Widget.Label({ + class_name = "time", + hexpand = true, + halign = "END", + label = time(n.time), + }), + Widget.Button({ + on_clicked = function() n:dismiss() end, + Widget.Icon({ icon = "window-close-symbolic" }), + }), + }) + + local content = Widget.Box({ + class_name = "content", + (n.image and file_exists(n.image)) and Widget.Box({ + valign = "START", + class_name = "image", + css = string.format("background-image: url('%s')", n.image), + }), + n.image and is_icon(n.image) and Widget.Box({ + valign = "START", + class_name = "icon-image", + Widget.Icon({ + icon = n.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 = n.summary, + }), + Widget.Label({ + class_name = "body", + wrap = true, + use_markup = true, + halign = "START", + xalign = 0, + justify = "FILL", + label = n.body, + }), + }), + }) + + return Widget.EventBox({ + class_name = string.format("Notification %s", string.lower(n.urgency)), + setup = props.setup, + on_hover_lost = props.on_hover_lost, + Widget.Box({ + vertical = true, + header, + Gtk.Separator({ visible = true }), + content, + #n.actions > 0 and Widget.Box({ + class_name = "actions", + map(n.actions, function(action) + local label, id = action.label, action.id + + return Widget.Button({ + hexpand = true, + on_clicked = function() return n:invoke(id) end, + Widget.Label({ + label = label, + halign = "CENTER", + hexpand = true, + }), + }) + end), + }), + }), + }) +end diff --git a/examples/gtk3/lua/notifications/notifications/Notification.scss b/examples/gtk3/lua/notifications/notifications/Notification.scss new file mode 100644 index 0000000..089d587 --- /dev/null +++ b/examples/gtk3/lua/notifications/notifications/Notification.scss @@ -0,0 +1,126 @@ +@use "sass:string"; + +@function gtkalpha($c, $a) { + @return string.unquote("alpha(#{$c},#{$a})"); +} + +// https://gitlab.gnome.org/GNOME/gtk/-/blob/gtk-3-24/gtk/theme/Adwaita/_colors-public.scss +$fg-color: #{"@theme_fg_color"}; +$bg-color: #{"@theme_bg_color"}; +$error: red; + +window.NotificationPopups { + all: unset; +} + +eventbox.Notification { + + &:first-child>box { + margin-top: 1rem; + } + + &:last-child>box { + margin-bottom: 1rem; + } + + // eventboxes can not take margins so we style its inner box instead + >box { + min-width: 400px; + border-radius: 13px; + background-color: $bg-color; + margin: .5rem 1rem .5rem 1rem; + box-shadow: 2px 3px 8px 0 gtkalpha(black, .4); + border: 1pt solid gtkalpha($fg-color, .03); + } + + &.critical>box { + border: 1pt solid gtkalpha($error, .4); + + .header { + + .app-name { + color: gtkalpha($error, .8); + + } + + .app-icon { + color: gtkalpha($error, .6); + } + } + } + + .header { + padding: .5rem; + color: gtkalpha($fg-color, 0.5); + + .app-icon { + margin: 0 .4rem; + } + + .app-name { + margin-right: .3rem; + font-weight: bold; + + &:first-child { + margin-left: .4rem; + } + } + + .time { + margin: 0 .4rem; + } + + button { + padding: .2rem; + min-width: 0; + min-height: 0; + } + } + + separator { + margin: 0 .4rem; + background-color: gtkalpha($fg-color, .1); + } + + .content { + margin: 1rem; + margin-top: .5rem; + + .summary { + font-size: 1.2em; + color: $fg-color; + } + + .body { + color: gtkalpha($fg-color, 0.8); + } + + .image { + border: 1px solid gtkalpha($fg-color, .02); + margin-right: .5rem; + border-radius: 9px; + min-width: 100px; + min-height: 100px; + background-size: cover; + background-position: center; + } + } + + .actions { + margin: 1rem; + margin-top: 0; + padding: .2rem; + + button { + margin: 0 .3rem; + + &:first-child { + margin-left: 0; + } + + &:last-child { + margin-right: 0; + } + } + } +} diff --git a/examples/gtk3/lua/notifications/notifications/NotificationPopups.lua b/examples/gtk3/lua/notifications/notifications/NotificationPopups.lua new file mode 100644 index 0000000..c5f9e1b --- /dev/null +++ b/examples/gtk3/lua/notifications/notifications/NotificationPopups.lua @@ -0,0 +1,57 @@ +local astal = require("astal") +local Widget = require("astal.gtk3").Widget + +local Notifd = astal.require("AstalNotifd") +local Notification = require("notifications.Notification") +local timeout = astal.timeout + +local TIMEOUT_DELAY = 5000 + +local varmap = require("lib").varmap +local notifd = Notifd.get_default() + +local function NotificationMap() + local notif_map = varmap({}) + + notifd.on_notified = function(_, id) + notif_map.set( + id, + Notification({ + notification = notifd:get_notification(id), + -- once hovering over the notification is done + -- destroy the widget without calling notification.dismiss() + -- so that it acts as a "popup" and we can still display it + -- in a notification center like widget + -- but clicking on the close button will close it + on_hover_lost = function() notif_map.delete(id) end, + setup = function() + timeout(TIMEOUT_DELAY, function() + -- uncomment this if you want to "hide" the notifications + -- after TIMEOUT_DELAY + + -- NotificationMap.delete(id) + end) + end, + }) + ) + end + + notifd.on_resolved = function(_, id) notif_map.delete(id) end + + return notif_map +end + +return function(gdkmonitor) + local Anchor = astal.require("Astal").WindowAnchor + local notifs = NotificationMap() + + return Widget.Window({ + class_name = "NotificationPopups", + gdkmonitor = gdkmonitor, + anchor = Anchor.TOP + Anchor.RIGHT, + Widget.Box({ + vertical = true, + notifs(), + }), + }) +end diff --git a/examples/gtk3/lua/notifications/style.scss b/examples/gtk3/lua/notifications/style.scss new file mode 100644 index 0000000..7ef0168 --- /dev/null +++ b/examples/gtk3/lua/notifications/style.scss @@ -0,0 +1 @@ +@use "./notifications/Notification.scss"; |