rewrite ui in hy, with python astal bindings
This commit is contained in:
2
widgets/__init__.hy
Normal file
2
widgets/__init__.hy
Normal file
@ -0,0 +1,2 @@
|
||||
(import .bar [bar])
|
||||
(import .notifications [notifications])
|
32
widgets/bar/__init__.hy
Normal file
32
widgets/bar/__init__.hy
Normal 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
27
widgets/bar/battery.hy
Normal 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
18
widgets/bar/clock.hy
Normal 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
81
widgets/bar/mpris.hy
Normal 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
22
widgets/bar/volume.hy
Normal 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
41
widgets/bar/workspaces.hy
Normal 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)])))
|
107
widgets/notifications/__init__.hy
Normal file
107
widgets/notifications/__init__.hy
Normal file
@ -0,0 +1,107 @@
|
||||
(import astal *)
|
||||
(import astal.gtk3 *)
|
||||
|
||||
(.require-version gi "AstalNotifd" "0.1")
|
||||
(.require-version gi "Pango" "1.0")
|
||||
|
||||
(import gi.repository [AstalNotifd Pango])
|
||||
|
||||
(let [
|
||||
ProgressBar (astalify Gtk.ProgressBar)
|
||||
notifd (.get-default AstalNotifd)
|
||||
notification-timeout 3
|
||||
notification-icon (fn [notification]
|
||||
(cond
|
||||
(.get-image notification) (Widget.Box
|
||||
:class-name "icon image"
|
||||
:css f"
|
||||
background-image: url(\"{(.get-image notification)}\");
|
||||
background-size: contain;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;")
|
||||
(. Astal Icon (lookup-icon (.get-app-icon notification))) (Widget.Icon
|
||||
:icon (.get-app-icon notification)
|
||||
:class-name "icon")
|
||||
True (Widget.Icon
|
||||
:icon "dialog-information-symbolic"
|
||||
:class-name "icon")))
|
||||
|
||||
make-notification (fn [notification]
|
||||
(let [
|
||||
layout (Widget.Button
|
||||
:on-clicked (fn [self] (. self (get-parent) (set-reveal-child False)))
|
||||
:child (Widget.Box
|
||||
:children [
|
||||
(Widget.Box
|
||||
:vertical True
|
||||
:css "min-width: 200px; min-height 50px;;"
|
||||
:children [
|
||||
(Widget.Box
|
||||
:children [
|
||||
(notification-icon notification)
|
||||
(Widget.Box
|
||||
:vertical True
|
||||
:children [
|
||||
(Widget.Label
|
||||
:class-name "title"
|
||||
:label (str (.get-summary notification))
|
||||
:xalign 0
|
||||
:justify 0
|
||||
:ellipsize Pango.EllipsizeMode.END
|
||||
)
|
||||
(Widget.Label
|
||||
:class-name "body"
|
||||
:label (str (.get-body notification))
|
||||
:xalign 0
|
||||
:justify 0
|
||||
:wrap True
|
||||
:wrap-mode Pango.WrapMode.WORD_CHAR
|
||||
:use-markup True
|
||||
)])])
|
||||
(ProgressBar
|
||||
:class-name "timeout-bar"
|
||||
:hexpand True
|
||||
:valign 2
|
||||
:fraction (bind (.poll (Variable 1) (// (+ (* (or (max (.get-expire-timeout notification) 0) notification-timeout) 1000) 250) 100) (fn [prev]
|
||||
(when (> prev .02)
|
||||
(return (- prev .01)))
|
||||
(return 0)))))])
|
||||
(Widget.Box :class-name f"urgency-indicator {(.get-urgency notification)}")]))]
|
||||
|
||||
(Widget.Revealer
|
||||
:transition-type Gtk.RevealerTransitionType.SLIDE_DOWN
|
||||
:transition-duration 250
|
||||
:class-name "notifications"
|
||||
:child (Widget.Revealer
|
||||
:transition-type Gtk.RevealerTransitionType.SLIDE_DOWN
|
||||
:transition-duration 250
|
||||
:child layout
|
||||
:setup (fn [self]
|
||||
(.hook self self "notify::reveal-child" (fn [#* _]
|
||||
(when (not (.get-reveal-child self))
|
||||
(timeout 250 (fn [] (. self (get-parent) (set-reveal-child False)))))))))
|
||||
:setup (fn [self]
|
||||
(.hook self self "notify::reveal-child" (fn [self revealed?]
|
||||
(when revealed?
|
||||
(when (.get-reveal-child self) (timeout 1 (fn [] (. self (get-child) (set-reveal-child True)))))
|
||||
(timeout (* 1000 (or (max (.get-expire-timeout notification) 0) notification-timeout)) (fn []
|
||||
(. self (get-child) (set-reveal-child False))
|
||||
(timeout (. self (get-child) (get-transition-duration)) (fn []
|
||||
(.set-reveal-child self False)
|
||||
(timeout (.get-transition-duration self) (fn []
|
||||
(.destroy self))))))))))
|
||||
|
||||
(.set-reveal-child self True)))))]
|
||||
|
||||
(setv notifications (Widget.Window
|
||||
:namespace "notifications"
|
||||
:name "notifications"
|
||||
:anchor (| Astal.WindowAnchor.TOP Astal.WindowAnchor.RIGHT)
|
||||
:exclusivity Astal.Exclusivity.EXCLUSIVE
|
||||
:margin-top 5
|
||||
:child (Widget.Box
|
||||
:vertical True
|
||||
:setup (fn [self]
|
||||
(.hook self notifd "notified" (fn [self notification _]
|
||||
(let [children (.get-children self)]
|
||||
(.add self (make-notification (.get-notification notifd notification)))))))))))
|
Reference in New Issue
Block a user