aboutsummaryrefslogtreecommitdiff
path: root/.config/ags/widget/osd
diff options
context:
space:
mode:
Diffstat (limited to '.config/ags/widget/osd')
-rw-r--r--.config/ags/widget/osd/OSD.ts111
-rw-r--r--.config/ags/widget/osd/Progress.ts74
-rw-r--r--.config/ags/widget/osd/osd.scss26
3 files changed, 211 insertions, 0 deletions
diff --git a/.config/ags/widget/osd/OSD.ts b/.config/ags/widget/osd/OSD.ts
new file mode 100644
index 0000000..8239a08
--- /dev/null
+++ b/.config/ags/widget/osd/OSD.ts
@@ -0,0 +1,111 @@
+import { icon } from "lib/utils"
+import icons from "lib/icons"
+import Progress from "./Progress"
+import brightness from "service/brightness"
+import options from "options"
+
+const audio = await Service.import("audio")
+const { progress, microphone } = options.osd
+
+const DELAY = 2500
+
+function OnScreenProgress(vertical: boolean) {
+ const indicator = Widget.Icon({
+ size: 42,
+ vpack: "start",
+ })
+ const progress = Progress({
+ vertical,
+ width: vertical ? 42 : 300,
+ height: vertical ? 300 : 42,
+ child: indicator,
+ })
+
+ const revealer = Widget.Revealer({
+ transition: "slide_left",
+ child: progress,
+ })
+
+ let count = 0
+ function show(value: number, icon: string) {
+ revealer.reveal_child = true
+ indicator.icon = icon
+ progress.setValue(value)
+ count++
+ Utils.timeout(DELAY, () => {
+ count--
+
+ if (count === 0)
+ revealer.reveal_child = false
+ })
+ }
+
+ return revealer
+ .hook(brightness, () => show(
+ brightness.screen,
+ icons.brightness.screen,
+ ), "notify::screen")
+ .hook(brightness, () => show(
+ brightness.kbd,
+ icons.brightness.keyboard,
+ ), "notify::kbd")
+ .hook(audio.speaker, () => show(
+ audio.speaker.volume,
+ icon(audio.speaker.icon_name || "", icons.audio.type.speaker),
+ ), "notify::volume")
+}
+
+function MicrophoneMute() {
+ const icon = Widget.Icon({
+ class_name: "microphone",
+ })
+
+ const revealer = Widget.Revealer({
+ transition: "slide_up",
+ child: icon,
+ })
+
+ let count = 0
+ let mute = audio.microphone.stream?.is_muted ?? false
+
+ return revealer.hook(audio.microphone, () => Utils.idle(() => {
+ if (mute !== audio.microphone.stream?.is_muted) {
+ mute = audio.microphone.stream!.is_muted
+ icon.icon = icons.audio.mic[mute ? "muted" : "high"]
+ revealer.reveal_child = true
+ count++
+
+ Utils.timeout(DELAY, () => {
+ count--
+ if (count === 0)
+ revealer.reveal_child = false
+ })
+ }
+ }))
+}
+
+export default (monitor: number) => Widget.Window({
+ monitor,
+ name: `indicator${monitor}`,
+ class_name: "indicator",
+ layer: "overlay",
+ click_through: true,
+ anchor: ["right", "left", "top", "bottom"],
+ child: Widget.Box({
+ css: "padding: 2px;",
+ expand: true,
+ child: Widget.Overlay(
+ { child: Widget.Box({ expand: true }) },
+ Widget.Box({
+ hpack: progress.pack.h.bind(),
+ vpack: progress.pack.v.bind(),
+ child: progress.vertical.bind().as(OnScreenProgress),
+ }),
+ Widget.Box({
+ hpack: microphone.pack.h.bind(),
+ vpack: microphone.pack.v.bind(),
+ child: MicrophoneMute(),
+ }),
+ ),
+ }),
+})
diff --git a/.config/ags/widget/osd/Progress.ts b/.config/ags/widget/osd/Progress.ts
new file mode 100644
index 0000000..bcf27da
--- /dev/null
+++ b/.config/ags/widget/osd/Progress.ts
@@ -0,0 +1,74 @@
+import type Gtk from "gi://Gtk?version=3.0"
+import GLib from "gi://GLib?version=2.0"
+import { range } from "lib/utils"
+import options from "options"
+
+type ProgressProps = {
+ height?: number
+ width?: number
+ vertical?: boolean
+ child: Gtk.Widget
+}
+
+export default ({
+ height = 18,
+ width = 180,
+ vertical = false,
+ child,
+}: ProgressProps) => {
+ const fill = Widget.Box({
+ class_name: "fill",
+ hexpand: vertical,
+ vexpand: !vertical,
+ hpack: vertical ? "fill" : "start",
+ vpack: vertical ? "end" : "fill",
+ child,
+ })
+
+ const container = Widget.Box({
+ class_name: "progress",
+ child: fill,
+ css: `
+ min-width: ${width}px;
+ min-height: ${height}px;
+ `,
+ })
+
+ let fill_size = 0
+ let animations: number[] = []
+
+ return Object.assign(container, {
+ setValue(value: number) {
+ if (value < 0)
+ return
+
+ if (animations.length > 0) {
+ for (const id of animations)
+ GLib.source_remove(id)
+
+ animations = []
+ }
+
+ const axis = vertical ? "height" : "width"
+ const axisv = vertical ? height : width
+ const min = vertical ? width : height
+ const preferred = (axisv - min) * value + min
+
+ if (!fill_size) {
+ fill_size = preferred
+ fill.css = `min-${axis}: ${preferred}px;`
+ return
+ }
+
+ const frames = options.transition.value / 10
+ const goal = preferred - fill_size
+ const step = goal / frames
+
+ animations = range(frames, 0).map(i => Utils.timeout(5 * i, () => {
+ fill_size += step
+ fill.css = `min-${axis}: ${fill_size}px`
+ animations.shift()
+ }))
+ },
+ })
+}
diff --git a/.config/ags/widget/osd/osd.scss b/.config/ags/widget/osd/osd.scss
new file mode 100644
index 0000000..111a486
--- /dev/null
+++ b/.config/ags/widget/osd/osd.scss
@@ -0,0 +1,26 @@
+window.indicator {
+ .progress {
+ @include floating-widget;
+ padding: $padding * .5;
+ border-radius: if($radius >0, calc($radius + $padding*.5), 0);
+ @debug $radius;
+
+ .fill {
+ border-radius: $radius;
+ background-color: $primary-bg;
+ color: $primary-fg;
+
+ image {
+ -gtk-icon-transform: scale(0.7);
+ }
+ }
+ }
+
+ .microphone {
+ @include floating-widget;
+ margin: $spacing * 2;
+ padding: $popover-padding * 2;
+ font-size: 58px;
+ color: transparentize($fg, .1)
+ }
+}