rewrite ui in hy, with python astal bindings

This commit is contained in:
gnat
2025-02-14 01:56:09 -08:00
parent afc9ff29e7
commit ad3e377963
27 changed files with 1106 additions and 0 deletions

32
widgets/bar/__init__.hy Normal file
View File

@ -0,0 +1,32 @@
(import astal.gtk3 *)
(import astal *)
(import .workspaces [workspaces])
(import .mpris [mpris-controls])
(import .clock [clock])
(import .battery [battery-dial])
(import .volume [volume])
(setv bar (Widget.Window
:namespace "bar"
:name "bar"
:anchor (| Astal.WindowAnchor.TOP Astal.WindowAnchor.LEFT Astal.WindowAnchor.RIGHT)
:exclusivity Astal.Exclusivity.EXCLUSIVE
:child (Widget.CenterBox
:start-widget (Widget.Box
:class-name "left"
:children [
workspaces
mpris-controls])
:end-widget (Widget.Box
:class-name "right"
:halign Gtk.Align.END
:children [
(Widget.Box
:class-name "sliders"
:vertical True
:children [
volume])
battery-dial
((astalify Gtk.Separator))
clock]))))

27
widgets/bar/battery.hy Normal file
View File

@ -0,0 +1,27 @@
(import astal *)
(import astal.gtk3 *)
(.require-version gi "AstalBattery" "0.1")
(import gi.repository [AstalBattery :as Battery])
(let [
battery (.get-default Battery)
icons [
["󰂎" "󰁺" "󰁻" "󰁼" "󰁽" "󰁾" "󰁿" "󰂀" "󰂁" "󰂂" "󰁹"]
["󰢟" "󰢜" "󰂆" "󰂇" "󰂈" "󰢝" "󰂉" "󰢞" "󰂊" "󰂋" "󰂅"]]]
(setv battery-dial (Widget.Box
:class-name "battery-container"
:children [
(Widget.CircularProgress
:class-name "battery-dial"
:rounded False
:inverted False
:start-at -.25
:end-at .75
:value (bind battery "percentage")
:child (Widget.Label
:halign Gtk.Align.CENTER
:hexpand True
:justify 2
:label (.transform (bind battery "percentage") (fn [percentage] (get (get icons (.get-charging battery)) (round (* percentage 10)))))))])))

18
widgets/bar/clock.hy Normal file
View File

@ -0,0 +1,18 @@
(import astal *)
(import astal.gtk3 *)
(let [
datetime (.poll (Variable "") 1000 "date +'%d %b %H:%M:%S'" (fn [out _] out))
unix-seconds (.poll (Variable "") 1000 "date +%s" (fn [out _] out))]
(setv clock (Widget.Box
:class-name "clock"
:vertical True
:valign Gtk.Align.CENTER
:children [
(Widget.Label
:halign Gtk.Align.START
:label (bind datetime))
(Widget.Label
:halign Gtk.Align.START
:label (bind unix-seconds))])))

81
widgets/bar/mpris.hy Normal file
View File

@ -0,0 +1,81 @@
(import astal *)
(import astal.gtk3 *)
(import math)
(.require-version gi "AstalMpris" "0.1")
(import gi.repository [AstalMpris :as Mpris])
(let [mpris (.get-default Mpris)]
(setv mpris-controls
(Widget.Stack
:transition-type Gtk.StackTransitionType.SLIDE_UP_DOWN
:transition-duration 125
:children []
:setup (fn [self]
(.add-events self Gdk.EventMask.SCROLL_MASK)
(.add-events self Gdk.EventMask.SMOOTH_SCROLL_MASK)
(defn add-player [player]
(.add-named self
(Widget.Box
:class-name "player"
:vertical True
:hexpand False
:setup (fn [self] (.set-name self (.get-bus-name player)))
:css (.transform (bind player "cover-art") (fn [cover-uri]
(when cover-uri (return f"
background: linear-gradient(to right, rgba(0, 0, 0, 0), rgba(22, 22, 22, 0.925)), url(\"{cover-uri}\");
background-position: 50% 50%;
background-size: cover;
"))
(return "")))
:children [
(Widget.Box
:vertical True
:vexpand True
:valign Gtk.Align.CENTER
:halign Gtk.Align.END
:children [
(Widget.Button
:on-clicked (fn [#* _] (.previous player))
:child (Widget.Icon :icon "media-skip-backward-symbolic"))
(Widget.Button
:on-clicked (fn [#* _] (.play-pause player))
:child (Widget.Icon :icon (.transform (bind player "playback-status") (fn [status]
(when (= status Mpris.PlaybackStatus.PLAYING)
(return "media-playback-pause-symbolic"))
(return "media-playback-start-symbolic")))))
(Widget.Button
:on-clicked (fn [#* _] (.next player))
:child (Widget.Icon :icon "media-skip-forward-symbolic"))])
(Widget.Slider
:hexpand True
:class-name "media-progress"
:valign Gtk.Align.END
:halign Gtk.Align.START
:on-dragged (fn [self] (.set-position player (* (.get-value self) (.get-length player))))
:setup (fn [self]
(.poll (Variable) 500 (fn [#* _]
(when player
(.set-value self (/ (.get-position player) (.get-length player))))))))])
(.get-bus-name player)))
(for [player (.get-players mpris)] (add-player player))
(.hook self mpris "player-added" (fn [self player]
(add-player player)))
(.hook self mpris "player-closed" (fn [self player]
(.destroy (.get-named self (.get-bus-name player)))))
(setv accumulated-delta-y 0)
(.hook self self "scroll-event" (fn [_ event]
(nonlocal accumulated-delta-y)
(setv accumulated-delta-y (+ accumulated-delta-y (get (.get-scroll-deltas event) 2)))
(when (> (abs accumulated-delta-y) 10)
(.set-visible-child self (get (.get-children self) (% (+ (.index (lfor child (.get-children self) (.get-name child)) (.get-shown self)) (* 1 (round (/ accumulated-delta-y 10)))) (len (.get-children self)))))
(setv accumulated-delta-y 0))))))))

22
widgets/bar/volume.hy Normal file
View File

@ -0,0 +1,22 @@
(import astal *)
(import astal.gtk3 *)
(.require-version gi "AstalWp" "0.1")
(import gi.repository [AstalWp])
(let [
endpoint (. AstalWp (get-default) (get-audio) (get-default-speaker))]
(setv volume (Widget.Box
:class-name "volume-slider"
:children [
(Widget.Button
:child (Widget.Icon :icon (bind endpoint "volume-icon"))
:on-clicked (fn [#* _] (.set-mute endpoint (not (.get-mute endpoint)))))
(Widget.Slider
:class-name "volume-slider"
:hexpand True
:draw-value False
:value (bind endpoint "volume")
:on-dragged (fn [self]
(.set-volume endpoint (.get-value self))))])))

41
widgets/bar/workspaces.hy Normal file
View File

@ -0,0 +1,41 @@
(import astal *)
(import astal.gtk3 *)
(.require-version gi "AstalHyprland" "0.1")
(import gi.repository [AstalHyprland :as Hyprland])
(let [
hyprland (.get-default Hyprland)
workspace-row (fn [start stop]
(Widget.Box
:children (lfor i (range start stop)
(Widget.Button
:class-name "workspace"
:attribute (+ i 1)
:on-clicked (fn [self] (.message-async hyprland f"dispatch workspace {self.attribute}"))
:setup (fn [self]
(.hook self hyprland "notify::focused-workspace" (fn [self, w] (.toggle-class-name self "focused" (= (.get-id w) self.attribute))))
(defn update [#* _]
(let [workspace (.get-workspace hyprland self.attribute)]
(when (!= workspace None)
(.toggle-class-name self "occupied" (< 0 (len (.get-clients workspace)))))))
(.hook self hyprland "notify::workspaces" update)
(.hook self hyprland "notify::clients" update)
(.hook self hyprland "client-moved" update)
(update)
(when (= (.get-id (.get-focused-workspace hyprland)) self.attribute)
(.toggle-class-name self "focused")))))))]
(setv workspaces (Widget.Box
:class_name "workspaces"
:vertical True
:hexpand False
:halign Gtk.Align.START
:valign Gtk.Align.CENTER
:children [
(workspace-row 0 5)
(workspace-row 5 10)])))