summaryrefslogtreecommitdiff
path: root/examples/gtk3/lua/simple-bar
diff options
context:
space:
mode:
Diffstat (limited to 'examples/gtk3/lua/simple-bar')
-rw-r--r--examples/gtk3/lua/simple-bar/README.md13
-rw-r--r--examples/gtk3/lua/simple-bar/init.lua24
-rw-r--r--examples/gtk3/lua/simple-bar/lib.lua25
-rw-r--r--examples/gtk3/lua/simple-bar/style.scss106
-rw-r--r--examples/gtk3/lua/simple-bar/widget/Bar.lua198
5 files changed, 366 insertions, 0 deletions
diff --git a/examples/gtk3/lua/simple-bar/README.md b/examples/gtk3/lua/simple-bar/README.md
new file mode 100644
index 0000000..48cc27c
--- /dev/null
+++ b/examples/gtk3/lua/simple-bar/README.md
@@ -0,0 +1,13 @@
+# Simple Bar Example
+
+![simple-bar](https://github.com/user-attachments/assets/a306c864-56b7-44c4-8820-81f424f32b9b)
+
+A simple bar for Hyprland using
+
+- [Battery library](https://aylur.github.io/astal/guide/libraries/battery).
+- [Hyprland library](https://aylur.github.io/astal/guide/libraries/hyprland).
+- [Mpris library](https://aylur.github.io/astal/guide/libraries/mpris).
+- [Network library](https://aylur.github.io/astal/guide/libraries/network).
+- [Tray library](https://aylur.github.io/astal/guide/libraries/tray).
+- [WirePlumber library](https://aylur.github.io/astal/guide/libraries/wireplumber).
+- [dart-sass](https://sass-lang.com/dart-sass/) as the css precompiler
diff --git a/examples/gtk3/lua/simple-bar/init.lua b/examples/gtk3/lua/simple-bar/init.lua
new file mode 100644
index 0000000..8c412fb
--- /dev/null
+++ b/examples/gtk3/lua/simple-bar/init.lua
@@ -0,0 +1,24 @@
+local astal = require("astal")
+local App = require("astal.gtk3.app")
+
+local Bar = require("widget.Bar")
+local src = require("lib").src
+
+local scss = src("style.scss")
+local css = "/tmp/style.css"
+
+astal.exec("sass " .. scss .. " " .. css)
+
+App:start({
+ instance_name = "lua",
+ css = css,
+ request_handler = function(msg, res)
+ print(msg)
+ res("ok")
+ end,
+ main = function()
+ for _, mon in pairs(App.monitors) do
+ Bar(mon)
+ end
+ end,
+})
diff --git a/examples/gtk3/lua/simple-bar/lib.lua b/examples/gtk3/lua/simple-bar/lib.lua
new file mode 100644
index 0000000..d94db5c
--- /dev/null
+++ b/examples/gtk3/lua/simple-bar/lib.lua
@@ -0,0 +1,25 @@
+local Variable = require("astal").Variable
+
+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 arr T[]
+---@param func fun(T, integer): R
+---@return R[]
+function M.map(arr, func)
+ local new_arr = {}
+ for i, v in ipairs(arr) do
+ new_arr[i] = func(v, i)
+ end
+ return new_arr
+end
+
+M.date = Variable(""):poll(1000, "date")
+
+return M
diff --git a/examples/gtk3/lua/simple-bar/style.scss b/examples/gtk3/lua/simple-bar/style.scss
new file mode 100644
index 0000000..f5f771a
--- /dev/null
+++ b/examples/gtk3/lua/simple-bar/style.scss
@@ -0,0 +1,106 @@
+@use "sass:color";
+
+$bg: #212223;
+$fg: #f1f1f1;
+$accent: #378DF7;
+$radius: 7px;
+
+window.Bar {
+ border: none;
+ box-shadow: none;
+ background-color: $bg;
+ color: $fg;
+ font-size: 1.1em;
+ font-weight: bold;
+
+ label {
+ margin: 0 8px;
+ }
+
+ .Workspaces {
+ button {
+ all: unset;
+ background-color: transparent;
+
+ &:hover label {
+ background-color: color.adjust($fg, $alpha: -0.84);
+ border-color: color.adjust($accent, $alpha: -0.8);
+ }
+
+ &:active label {
+ background-color: color.adjust($fg, $alpha: -0.8)
+ }
+ }
+
+ label {
+ transition: 200ms;
+ padding: 0 8px;
+ margin: 2px;
+ border-radius: $radius;
+ border: 1pt solid transparent;
+ }
+
+ .focused label {
+ color: $accent;
+ border-color: $accent;
+ }
+ }
+
+ .SysTray {
+ margin-right: 8px;
+
+ button {
+ padding: 0 4px;
+ }
+ }
+
+ .FocusedClient {
+ color: $accent;
+ }
+
+ .Media .Cover {
+ min-height: 1.2em;
+ min-width: 1.2em;
+ border-radius: $radius;
+ background-position: center;
+ background-size: contain;
+ }
+
+ .Battery label {
+ padding-left: 0;
+ margin-left: 0;
+ }
+
+ .AudioSlider {
+ * {
+ all: unset;
+ }
+
+ icon {
+ margin-right: .6em;
+ }
+
+ & {
+ margin: 0 1em;
+ }
+
+ trough {
+ background-color: color.adjust($fg, $alpha: -0.8);
+ border-radius: $radius;
+ }
+
+ highlight {
+ background-color: $accent;
+ min-height: .8em;
+ border-radius: $radius;
+ }
+
+ slider {
+ background-color: $fg;
+ border-radius: $radius;
+ min-height: 1em;
+ min-width: 1em;
+ margin: -.2em;
+ }
+ }
+}
diff --git a/examples/gtk3/lua/simple-bar/widget/Bar.lua b/examples/gtk3/lua/simple-bar/widget/Bar.lua
new file mode 100644
index 0000000..155b9b8
--- /dev/null
+++ b/examples/gtk3/lua/simple-bar/widget/Bar.lua
@@ -0,0 +1,198 @@
+local astal = require("astal")
+local Widget = require("astal.gtk3.widget")
+local Variable = astal.Variable
+local GLib = astal.require("GLib")
+local bind = astal.bind
+local Mpris = astal.require("AstalMpris")
+local Battery = astal.require("AstalBattery")
+local Wp = astal.require("AstalWp")
+local Network = astal.require("AstalNetwork")
+local Tray = astal.require("AstalTray")
+local Hyprland = astal.require("AstalHyprland")
+local map = require("lib").map
+
+local function SysTray()
+ local tray = Tray.get_default()
+
+ return Widget.Box({
+ class_name = "SysTray",
+ bind(tray, "items"):as(function(items)
+ return map(items, function(item)
+ return Widget.MenuButton({
+ tooltip_markup = bind(item, "tooltip_markup"),
+ use_popover = false,
+ menu_model = bind(item, "menu-model"),
+ action_group = bind(item, "action-group"):as(function(ag)
+ return { "dbusmenu", ag }
+ end),
+ Widget.Icon({
+ gicon = bind(item, "gicon"),
+ }),
+ })
+ end)
+ end),
+ })
+end
+
+local function FocusedClient()
+ local hypr = Hyprland.get_default()
+ local focused = bind(hypr, "focused-client")
+
+ return Widget.Box({
+ class_name = "Focused",
+ visible = focused,
+ focused:as(function(client)
+ return client and Widget.Label({
+ label = bind(client, "title"):as(tostring),
+ })
+ end),
+ })
+end
+
+local function Wifi()
+ local network = Network.get_default()
+ local wifi = bind(network, "wifi")
+
+ return Widget.Box({
+ visible = wifi:as(function(v)
+ return v ~= nil
+ end),
+ wifi:as(function(w)
+ return Widget.Icon({
+ tooltip_text = bind(w, "ssid"):as(tostring),
+ class_name = "Wifi",
+ icon = bind(w, "icon-name"),
+ })
+ end),
+ })
+end
+
+local function AudioSlider()
+ local speaker = Wp.get_default().audio.default_speaker
+
+ return Widget.Box({
+ class_name = "AudioSlider",
+ css = "min-width: 140px;",
+ Widget.Icon({
+ icon = bind(speaker, "volume-icon"),
+ }),
+ Widget.Slider({
+ hexpand = true,
+ on_dragged = function(self)
+ speaker.volume = self.value
+ end,
+ value = bind(speaker, "volume"),
+ }),
+ })
+end
+
+local function BatteryLevel()
+ local bat = Battery.get_default()
+
+ return Widget.Box({
+ class_name = "Battery",
+ visible = bind(bat, "is-present"),
+ Widget.Icon({
+ icon = bind(bat, "battery-icon-name"),
+ }),
+ Widget.Label({
+ label = bind(bat, "percentage"):as(function(p)
+ return tostring(math.floor(p * 100)) .. " %"
+ end),
+ }),
+ })
+end
+
+local function Media()
+ local player = Mpris.Player.new("spotify")
+
+ return Widget.Box({
+ class_name = "Media",
+ visible = bind(player, "available"),
+ Widget.Box({
+ class_name = "Cover",
+ valign = "CENTER",
+ css = bind(player, "cover-art"):as(function(cover)
+ return "background-image: url('" .. (cover or "") .. "');"
+ end),
+ }),
+ Widget.Label({
+ label = bind(player, "metadata"):as(function()
+ return (player.title or "") .. " - " .. (player.artist or "")
+ end),
+ }),
+ })
+end
+
+local function Workspaces()
+ local hypr = Hyprland.get_default()
+
+ return Widget.Box({
+ class_name = "Workspaces",
+ bind(hypr, "workspaces"):as(function(wss)
+ table.sort(wss, function(a, b)
+ return a.id < b.id
+ end)
+
+ return map(wss, function(ws)
+ if not (ws.id >= -99 and ws.id <= -2) then -- filter out special workspaces
+ return Widget.Button({
+ class_name = bind(hypr, "focused-workspace"):as(function(fw)
+ return fw == ws and "focused" or ""
+ end),
+ on_clicked = function()
+ ws:focus()
+ end,
+ label = bind(ws, "id"):as(function(v)
+ return type(v) == "number" and string.format("%.0f", v) or v
+ end),
+ })
+ end
+ end)
+ end),
+ })
+end
+
+local function Time(format)
+ local time = Variable(""):poll(1000, function()
+ return GLib.DateTime.new_now_local():format(format)
+ end)
+
+ return Widget.Label({
+ class_name = "Time",
+ on_destroy = function()
+ time:drop()
+ end,
+ label = time(),
+ })
+end
+
+return function(gdkmonitor)
+ local WindowAnchor = astal.require("Astal", "3.0").WindowAnchor
+
+ return Widget.Window({
+ class_name = "Bar",
+ gdkmonitor = gdkmonitor,
+ anchor = WindowAnchor.TOP + WindowAnchor.LEFT + WindowAnchor.RIGHT,
+ exclusivity = "EXCLUSIVE",
+
+ Widget.CenterBox({
+ Widget.Box({
+ halign = "START",
+ Workspaces(),
+ FocusedClient(),
+ }),
+ Widget.Box({
+ Media(),
+ }),
+ Widget.Box({
+ halign = "END",
+ SysTray(),
+ Wifi(),
+ AudioSlider(),
+ BatteryLevel(),
+ Time("%H:%M - %A %e."),
+ }),
+ }),
+ })
+end