New bars
parent
4ecf1e471f
commit
9b2b73e865
|
@ -22,6 +22,7 @@
|
||||||
../../modules/zen-browser.nix
|
../../modules/zen-browser.nix
|
||||||
../../modules/steam.nix
|
../../modules/steam.nix
|
||||||
../../modules/nvim
|
../../modules/nvim
|
||||||
|
../../modules/ags
|
||||||
../../modules/grayjay.nix
|
../../modules/grayjay.nix
|
||||||
../../modules/signal.nix
|
../../modules/signal.nix
|
||||||
];
|
];
|
||||||
|
@ -87,7 +88,7 @@
|
||||||
];
|
];
|
||||||
|
|
||||||
autoStart = [
|
autoStart = [
|
||||||
"${pkgs.ags}/bin/ags"
|
"uwsm-app -- ags run"
|
||||||
(mkUwsmApp inputs.zen-browser.packages.x86_64-linux.default "zen")
|
(mkUwsmApp inputs.zen-browser.packages.x86_64-linux.default "zen")
|
||||||
(mkUwsmApp pkgs.discord "discord")
|
(mkUwsmApp pkgs.discord "discord")
|
||||||
];
|
];
|
||||||
|
|
|
@ -1,10 +1,15 @@
|
||||||
import { App } from "astal/gtk3"
|
import { App } from "astal/gtk3"
|
||||||
import style from "./style.scss"
|
import style from "./style.scss"
|
||||||
import LeftBar from "./widget/LeftBar"
|
import LeftBar from "./widget/LeftBar"
|
||||||
|
import RightBar from "./widget/RightBar"
|
||||||
|
|
||||||
|
const LEFT_DISPLAY = "HF237"
|
||||||
|
const RIGHT_DISPLAY = "LCDTV16"
|
||||||
|
|
||||||
App.start({
|
App.start({
|
||||||
css: style,
|
css: style,
|
||||||
main() {
|
main() {
|
||||||
App.get_monitors().map(LeftBar)
|
LeftBar(App.get_monitors().find(it => it.get_model() == LEFT_DISPLAY)!);
|
||||||
|
RightBar(App.get_monitors().find(it => it.get_model() == RIGHT_DISPLAY)!);
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,18 +1,97 @@
|
||||||
// https://gitlab.gnome.org/GNOME/gtk/-/blob/gtk-3-24/gtk/theme/Adwaita/_colors-public.scss
|
// https://gitlab.gnome.org/GNOME/gtk/-/blob/gtk-3-24/gtk/theme/Adwaita/_colors-public.scss
|
||||||
$fg-color: #{"@theme_fg_color"};
|
@use 'sass:color';
|
||||||
$bg-color: #{"@theme_bg_color"};
|
|
||||||
|
@import "themes/catpuccin.scss";
|
||||||
|
|
||||||
window.Bar {
|
window.Bar {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
color: $fg-color;
|
color: $ctp-text;
|
||||||
|
|
||||||
>centerbox {
|
>centerbox {
|
||||||
background: $bg-color;
|
background: $ctp-base;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
margin: 8px;
|
margin: 8px;
|
||||||
|
padding: 8px;
|
||||||
>box {
|
|
||||||
margin: 4px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.Time {
|
||||||
|
background: $ctp-surface-0;
|
||||||
|
border-radius: 5px;
|
||||||
|
|
||||||
|
>.icon {
|
||||||
|
background: $ctp-red;
|
||||||
|
color: $ctp-surface-0;
|
||||||
|
padding: 6px 8px;
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.icon-left>.icon {
|
||||||
|
border-radius: 5px 0 0 5px;
|
||||||
|
}
|
||||||
|
&.icon-right>.icon {
|
||||||
|
border-radius: 0 5px 5px 0;
|
||||||
|
}
|
||||||
|
>.label {
|
||||||
|
padding: 0 8px;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.Workspaces {
|
||||||
|
background: $ctp-surface-0;
|
||||||
|
border-radius: 5px;
|
||||||
|
|
||||||
|
>.icon {
|
||||||
|
background: $ctp-blue;
|
||||||
|
color: $ctp-surface-0;
|
||||||
|
padding: 6px 8px;
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.icon-left>.icon {
|
||||||
|
border-radius: 5px 0 0 5px;
|
||||||
|
}
|
||||||
|
&.icon-right>.icon {
|
||||||
|
border-radius: 0 5px 5px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
>.labels {
|
||||||
|
padding: 0 8px;
|
||||||
|
font-size: 16px;
|
||||||
|
|
||||||
|
button {
|
||||||
|
all: unset;
|
||||||
|
|
||||||
|
&.add {
|
||||||
|
font-size: 12px;
|
||||||
|
margin-left: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.Systray {
|
||||||
|
background: $ctp-surface-0;
|
||||||
|
border-radius: 5px;
|
||||||
|
|
||||||
|
>.icon {
|
||||||
|
background: $ctp-green;
|
||||||
|
color: $ctp-surface-0;
|
||||||
|
padding: 4px 8px 0 8px;
|
||||||
|
font-size: 20px;
|
||||||
|
border-radius: 5px 0 0 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.icon-left>.icon {
|
||||||
|
border-radius: 5px 0 0 5px;
|
||||||
|
}
|
||||||
|
&.icon-right>.icon {
|
||||||
|
border-radius: 0 5px 5px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
>.item {
|
||||||
|
all: unset;
|
||||||
|
padding: 8px 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
$ctp-rosewater: #f5e0dc;
|
||||||
|
$ctp-flamingo: #f2cdcd;
|
||||||
|
$ctp-pink: #f5c2e7;
|
||||||
|
$ctp-mauve: #cba6f7;
|
||||||
|
$ctp-red: #f38ba8;
|
||||||
|
$ctp-maroon: #eba0ac;
|
||||||
|
$ctp-peach: #fab387;
|
||||||
|
$ctp-yellow: #f9e2af;
|
||||||
|
$ctp-green: #a6e3a1;
|
||||||
|
$ctp-teal: #94e2d5;
|
||||||
|
$ctp-sky: #89dceb;
|
||||||
|
$ctp-sapphire: #74c7ec;
|
||||||
|
$ctp-blue: #89b4fa;
|
||||||
|
$ctp-lavender: #b4befe;
|
||||||
|
$ctp-text: #cdd6f4;
|
||||||
|
$ctp-subtext-1: #bac2de;
|
||||||
|
$ctp-subtext-0: #a6adc8;
|
||||||
|
$ctp-overlay-2: #9399b2;
|
||||||
|
$ctp-overlay-1: #7f849c;
|
||||||
|
$ctp-overlay-0: #6c7086;
|
||||||
|
$ctp-surface-2: #585b70;
|
||||||
|
$ctp-surface-1: #45475a;
|
||||||
|
$ctp-surface-0: #313244;
|
||||||
|
$ctp-base: #1e1e2e;
|
||||||
|
$ctp-mantle: #181825;
|
||||||
|
$ctp-crust: #11111b;
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
import { GLib, Variable } from "astal"
|
||||||
|
|
||||||
|
const TIME_FORMAT = "%H:%M"
|
||||||
|
|
||||||
|
export default function Clock(props: { iconSide: "left" | "right" }) {
|
||||||
|
const time = Variable<string>("").poll(1000, () =>
|
||||||
|
GLib.DateTime.new_now_local().format(TIME_FORMAT)!)
|
||||||
|
|
||||||
|
return <box className={`Time icon-${props.iconSide}`}>
|
||||||
|
{props.iconSide == "left" && <label className="icon" label="" />}
|
||||||
|
<label
|
||||||
|
className="label"
|
||||||
|
onDestroy={() => time.drop()}
|
||||||
|
label={time()}
|
||||||
|
/>
|
||||||
|
{props.iconSide == "right" && <label className="icon" label="" />}
|
||||||
|
</box>
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
import { App, Astal, Gdk, Gtk } from "astal/gtk3";
|
import { App, Astal, Gdk, Gtk } from "astal/gtk3";
|
||||||
import Systray from "./Systray";
|
import Clock from "./Clock";
|
||||||
|
import Workspaces from "./Workspaces";
|
||||||
|
|
||||||
export default function LeftBar(gdkmonitor: Gdk.Monitor) {
|
export default function LeftBar(gdkmonitor: Gdk.Monitor) {
|
||||||
const { TOP, LEFT, RIGHT } = Astal.WindowAnchor;
|
const { TOP, LEFT, RIGHT } = Astal.WindowAnchor;
|
||||||
|
@ -11,25 +12,14 @@ export default function LeftBar(gdkmonitor: Gdk.Monitor) {
|
||||||
anchor={TOP | LEFT | RIGHT}
|
anchor={TOP | LEFT | RIGHT}
|
||||||
application={App}>
|
application={App}>
|
||||||
<centerbox>
|
<centerbox>
|
||||||
<Left />
|
<box halign={Gtk.Align.START}>
|
||||||
<Center />
|
</box>
|
||||||
<Right />
|
<box halign={Gtk.Align.CENTER}>
|
||||||
|
<Workspaces monitor="HDMI-A-2" iconSide="right" />
|
||||||
|
</box>
|
||||||
|
<box halign={Gtk.Align.END}>
|
||||||
|
<Clock iconSide="right" />
|
||||||
|
</box>
|
||||||
</centerbox>
|
</centerbox>
|
||||||
</window>
|
</window>
|
||||||
}
|
}
|
||||||
|
|
||||||
function Left() {
|
|
||||||
return <box halign={Gtk.Align.START}>
|
|
||||||
</box>
|
|
||||||
}
|
|
||||||
|
|
||||||
function Center() {
|
|
||||||
return <box halign={Gtk.Align.CENTER}>
|
|
||||||
</box>
|
|
||||||
}
|
|
||||||
|
|
||||||
function Right() {
|
|
||||||
return <box halign={Gtk.Align.END}>
|
|
||||||
<Systray />
|
|
||||||
</box>
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
import { App, Astal, Gdk, Gtk } from "astal/gtk3";
|
||||||
|
import Clock from "./Clock";
|
||||||
|
import Workspaces from "./Workspaces";
|
||||||
|
import Systray from "./Systray";
|
||||||
|
|
||||||
|
export default function RightBar(gdkmonitor: Gdk.Monitor) {
|
||||||
|
const { TOP, LEFT, RIGHT } = Astal.WindowAnchor;
|
||||||
|
|
||||||
|
return <window
|
||||||
|
className="RightBar Bar"
|
||||||
|
gdkmonitor={gdkmonitor}
|
||||||
|
exclusivity={Astal.Exclusivity.EXCLUSIVE}
|
||||||
|
anchor={TOP | LEFT | RIGHT}
|
||||||
|
application={App}>
|
||||||
|
<centerbox>
|
||||||
|
<box halign={Gtk.Align.START}>
|
||||||
|
<Clock iconSide="left" />
|
||||||
|
</box>
|
||||||
|
<box halign={Gtk.Align.CENTER}>
|
||||||
|
<Workspaces monitor="HDMI-A-1" iconSide="left" />
|
||||||
|
</box>
|
||||||
|
<box halign={Gtk.Align.END}>
|
||||||
|
<Systray iconSide="left" />
|
||||||
|
</box>
|
||||||
|
</centerbox>
|
||||||
|
</window>
|
||||||
|
}
|
|
@ -1,23 +1,23 @@
|
||||||
import { bind } from "astal"
|
import { bind } from "astal"
|
||||||
import { Button, Icon } from "astal/gtk3/widget"
|
|
||||||
import Tray from "gi://AstalTray"
|
import Tray from "gi://AstalTray"
|
||||||
|
|
||||||
const tray = Tray.get_default()
|
const tray = Tray.get_default()
|
||||||
|
|
||||||
export default function Systray() {
|
export default function Systray(props: { iconSide: "left" | "right" }) {
|
||||||
for (const item of tray.get_items()) {
|
return <box className={`Systray icon-${props.iconSide}`}>
|
||||||
print(item.title)
|
{props.iconSide == "left" && <label className="icon" label="" />}
|
||||||
}
|
{bind(tray, "items").as(items => items.map(item => <SystrayItem item={item} />))}
|
||||||
return <box>
|
{props.iconSide == "right" && <label className="icon" label="" />}
|
||||||
{tray.get_items().map(item => <SystrayItem item={item} />)}
|
|
||||||
</box>
|
</box>
|
||||||
}
|
}
|
||||||
|
|
||||||
function SystrayItem({ item }: { item: Tray.TrayItem }) {
|
function SystrayItem({ item }: { item: Tray.TrayItem }) {
|
||||||
return <Button
|
return <menubutton
|
||||||
onClick={() => item.activate(0, 0) /* NOTE: Figure out what these numbers do */}
|
className="item"
|
||||||
tooltipMarkup={bind(item, "tooltip_markup")}
|
tooltipMarkup={bind(item, "tooltipMarkup")}
|
||||||
>
|
usePopover={false}
|
||||||
<Icon gicon={bind(item, "gicon")} />
|
actionGroup={bind(item, "actionGroup").as(ag => ["dbusmenu", ag])}
|
||||||
</Button>
|
menuModel={bind(item, "menuModel")}>
|
||||||
|
<icon gicon={bind(item, "gicon")} />
|
||||||
|
</menubutton>
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
import { bind } from "astal"
|
||||||
|
import Hyprland from "gi://AstalHyprland"
|
||||||
|
|
||||||
|
export default function Workspaces(props: { monitor: string, iconSide: "left" | "right" }) {
|
||||||
|
const hyprland = Hyprland.get_default();
|
||||||
|
|
||||||
|
return <box className={`Workspaces icon-${props.iconSide}`}>
|
||||||
|
{props.iconSide == "left" && <label className="icon" label="" />}
|
||||||
|
<box className="labels">
|
||||||
|
{bind(hyprland, "workspaces").as(wss => wss
|
||||||
|
.filter(it => it && it.get_monitor && it.get_monitor().name == props.monitor)
|
||||||
|
.sort((a, b) => a.get_id() - b.get_id())
|
||||||
|
.map(workspace => <Workspace workspace={workspace} />)
|
||||||
|
)}
|
||||||
|
<button
|
||||||
|
className="add"
|
||||||
|
onClick={() => {
|
||||||
|
hyprland.dispatch("workspace", "emptynm");
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<label label="" />
|
||||||
|
</button>
|
||||||
|
</box>
|
||||||
|
{props.iconSide == "right" && <label className="icon" label="" />}
|
||||||
|
</box>
|
||||||
|
}
|
||||||
|
|
||||||
|
function Workspace(props: { workspace: Hyprland.Workspace }) {
|
||||||
|
const hyprland = Hyprland.get_default();
|
||||||
|
|
||||||
|
return <box>
|
||||||
|
{bind(props.workspace.get_monitor(), "activeWorkspace").as(ws => {
|
||||||
|
if (ws == props.workspace) {
|
||||||
|
return <label
|
||||||
|
label=""
|
||||||
|
/>
|
||||||
|
} else {
|
||||||
|
return <button
|
||||||
|
onClick={() => {
|
||||||
|
const name = props.workspace.name
|
||||||
|
if (name) {
|
||||||
|
hyprland.dispatch("workspace", `name:${name}`);
|
||||||
|
} else {
|
||||||
|
hyprland.dispatch("workspace", `id:${props.workspace.get_id()}`);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<label
|
||||||
|
label=""
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
})}
|
||||||
|
</box>
|
||||||
|
}
|
|
@ -1,46 +0,0 @@
|
||||||
import { GTK_ALIGN_CENTER, GTK_ALIGN_END, GTK_ALIGN_START } from "../constants.js"
|
|
||||||
import { Clock } from "./Clock.js"
|
|
||||||
import { SysTray } from "./Systray.js"
|
|
||||||
|
|
||||||
function BarStart() {
|
|
||||||
return Widget.Box({
|
|
||||||
halign: GTK_ALIGN_START,
|
|
||||||
children: [
|
|
||||||
Widget.Label({ label: "Start" }),
|
|
||||||
Widget.Button({ label: "Button", onClicked: () => App.ToggleWindow("media2") }),
|
|
||||||
],
|
|
||||||
})
|
|
||||||
}
|
|
||||||
function BarCenter() {
|
|
||||||
return Widget.Box({
|
|
||||||
halign: GTK_ALIGN_CENTER,
|
|
||||||
children: [
|
|
||||||
Clock(),
|
|
||||||
],
|
|
||||||
})
|
|
||||||
}
|
|
||||||
function BarEnd() {
|
|
||||||
return Widget.Box({
|
|
||||||
halign: GTK_ALIGN_END,
|
|
||||||
children: [
|
|
||||||
SysTray(),
|
|
||||||
],
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export function Bar(monitor = 0) {
|
|
||||||
return Widget.Window({
|
|
||||||
monitor,
|
|
||||||
exclusivity: "exclusive",
|
|
||||||
className: "bar",
|
|
||||||
margins: [5, 5, 0, 5],
|
|
||||||
name: `bar${monitor}`,
|
|
||||||
anchor: ["left", "top", "right"],
|
|
||||||
child: Widget.CenterBox({
|
|
||||||
vertical: false,
|
|
||||||
startWidget: BarStart(),
|
|
||||||
centerWidget: BarCenter(),
|
|
||||||
endWidget: BarEnd(),
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -1,24 +0,0 @@
|
||||||
const time = Variable("", {
|
|
||||||
poll: [1000, 'date "+%H:%M"'],
|
|
||||||
})
|
|
||||||
|
|
||||||
const date = Variable("", {
|
|
||||||
poll: [1000, 'date "+%Y-%m-%d"'],
|
|
||||||
})
|
|
||||||
|
|
||||||
export function Clock() {
|
|
||||||
return Widget.Box({
|
|
||||||
className: "clock",
|
|
||||||
vertical: true,
|
|
||||||
children: [
|
|
||||||
Widget.Label({
|
|
||||||
className: "time",
|
|
||||||
label: time.bind(),
|
|
||||||
}),
|
|
||||||
Widget.Label({
|
|
||||||
className: "date",
|
|
||||||
label: date.bind(),
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -1,16 +0,0 @@
|
||||||
const systemtray = await Service.import("systemtray")
|
|
||||||
|
|
||||||
export function SysTray() {
|
|
||||||
const items = systemtray.bind("items")
|
|
||||||
.as(items => items.map(item => Widget.Button({
|
|
||||||
child: Widget.Icon({ icon: item.bind("icon") }),
|
|
||||||
on_primary_click: (_, event) => item.activate(event),
|
|
||||||
on_secondary_click: (_, event) => item.openMenu(event),
|
|
||||||
tooltip_markup: item.bind("tooltip_markup"),
|
|
||||||
})))
|
|
||||||
|
|
||||||
return Widget.Box({
|
|
||||||
className: "systray",
|
|
||||||
children: items,
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -1,35 +0,0 @@
|
||||||
@define-color ctp-rosewater #f5e0dc;
|
|
||||||
@define-color ctp-flamingo #f2cdcd;
|
|
||||||
@define-color ctp-pink #f5c2e7;
|
|
||||||
@define-color ctp-mauve #cba6f7;
|
|
||||||
@define-color ctp-red #f38ba8;
|
|
||||||
@define-color ctp-maroon #eba0ac;
|
|
||||||
@define-color ctp-peach #fab387;
|
|
||||||
@define-color ctp-yellow #f9e2af;
|
|
||||||
@define-color ctp-green #a6e3a1;
|
|
||||||
@define-color ctp-teal #94e2d5;
|
|
||||||
@define-color ctp-sky #89dceb;
|
|
||||||
@define-color ctp-sapphire #74c7ec;
|
|
||||||
@define-color ctp-blue #89b4fa;
|
|
||||||
@define-color ctp-lavender #b4befe;
|
|
||||||
@define-color ctp-text #cdd6f4;
|
|
||||||
@define-color ctp-subtext1 #bac2de;
|
|
||||||
@define-color ctp-subtext0 #a6adc8;
|
|
||||||
@define-color ctp-overlay2 #9399b2;
|
|
||||||
@define-color ctp-overlay1 #7f849c;
|
|
||||||
@define-color ctp-overlay0 #6c7086;
|
|
||||||
@define-color ctp-surface2 #585b70;
|
|
||||||
@define-color ctp-surface1 #45475a;
|
|
||||||
@define-color ctp-surface0 #313244;
|
|
||||||
@define-color ctp-base #1e1e2e;
|
|
||||||
@define-color ctp-mantle #181825;
|
|
||||||
@define-color ctp-crust #11111b;
|
|
||||||
|
|
||||||
button {
|
|
||||||
background: @ctp-surface0;
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
button:active, button:hover, button:focus {
|
|
||||||
background: @ctp-surface1;
|
|
||||||
}
|
|
|
@ -1,14 +0,0 @@
|
||||||
import { Media } from "./media/Media.js";
|
|
||||||
import { Bar } from "./bar/Bar.js";
|
|
||||||
import { Notifications } from "./notifications/Notifications.js";
|
|
||||||
|
|
||||||
|
|
||||||
App.config({
|
|
||||||
style: "./style.css",
|
|
||||||
windows: [
|
|
||||||
Bar(2),
|
|
||||||
Media(2),
|
|
||||||
Notifications(2),
|
|
||||||
]
|
|
||||||
})
|
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
export const GTK_ALIGN_FILL = 0;
|
|
||||||
export const GTK_ALIGN_START = 1;
|
|
||||||
export const GTK_ALIGN_END = 2;
|
|
||||||
export const GTK_ALIGN_CENTER = 3;
|
|
||||||
export const GTK_ALIGN_BASELINE = 4;
|
|
|
@ -1,160 +0,0 @@
|
||||||
import { GTK_ALIGN_CENTER, GTK_ALIGN_FILL } from '../constants.js'
|
|
||||||
|
|
||||||
const mpris = await Service.import("mpris")
|
|
||||||
const players = mpris.bind("players")
|
|
||||||
|
|
||||||
const FALLBACK_ICON = "audio-x-generic-symbolic"
|
|
||||||
const PLAY_ICON = "media-playback-start-symbolic"
|
|
||||||
const PAUSE_ICON = "media-playback-pause-symbolic"
|
|
||||||
const PREV_ICON = "media-skip-backward-symbolic"
|
|
||||||
const NEXT_ICON = "media-skip-forward-symbolic"
|
|
||||||
|
|
||||||
/** @param {number} length */
|
|
||||||
function lengthStr(length) {
|
|
||||||
const min = Math.floor(length / 60)
|
|
||||||
const sec = Math.floor(length % 60)
|
|
||||||
const sec0 = sec < 10 ? "0" : ""
|
|
||||||
return `${min}:${sec0}${sec}`
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @param {import('types/service/mpris').MprisPlayer} player */
|
|
||||||
function Player(player) {
|
|
||||||
const artists = player.bind("track_artists").transform(a => a.join(", "))
|
|
||||||
|
|
||||||
const playPause = Widget.Button({
|
|
||||||
className: "play-pause",
|
|
||||||
onClicked: () => player.playPause(),
|
|
||||||
visible: player.bind("can_play"),
|
|
||||||
child: Widget.Icon({
|
|
||||||
icon: player.bind("play_back_status").transform(s => {
|
|
||||||
switch (s) {
|
|
||||||
case "Playing": return PAUSE_ICON
|
|
||||||
case "Paused":
|
|
||||||
case "Stopped": return PLAY_ICON
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
|
|
||||||
const prev = Widget.Button({
|
|
||||||
onClicked: () => player.previous(),
|
|
||||||
visible: player.bind("can_go_prev"),
|
|
||||||
child: Widget.Icon(PREV_ICON),
|
|
||||||
})
|
|
||||||
|
|
||||||
const next = Widget.Button({
|
|
||||||
onClicked: () => player.next(),
|
|
||||||
visible: player.bind("can_go_next"),
|
|
||||||
child: Widget.Icon(NEXT_ICON),
|
|
||||||
})
|
|
||||||
|
|
||||||
const positionSlider = Widget.Slider({
|
|
||||||
className: "position",
|
|
||||||
drawValue: false,
|
|
||||||
onChange: ({ value }) => player.position = value * player.length,
|
|
||||||
visible: player.bind("length").as(l => l > 0),
|
|
||||||
setup: self => {
|
|
||||||
function update() {
|
|
||||||
const value = player.position / player.length
|
|
||||||
self.value = value > 0 ? value : 0
|
|
||||||
}
|
|
||||||
self.hook(player, update)
|
|
||||||
self.hook(player, update, "position")
|
|
||||||
self.poll(1000, update)
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
const positionLabel = Widget.Label({
|
|
||||||
className: "position",
|
|
||||||
hpack: "start",
|
|
||||||
setup: self => {
|
|
||||||
const update = (_, time) => {
|
|
||||||
self.label = lengthStr(time || player.position)
|
|
||||||
self.visible = player.length > 0
|
|
||||||
}
|
|
||||||
|
|
||||||
self.hook(player, update, "position")
|
|
||||||
self.poll(1000, update)
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
const lengthLabel = Widget.Label({
|
|
||||||
className: "length",
|
|
||||||
hpack: "end",
|
|
||||||
visible: player.bind("length").transform(l => l > 0),
|
|
||||||
label: player.bind("length").transform(lengthStr),
|
|
||||||
})
|
|
||||||
|
|
||||||
return Widget.Overlay({
|
|
||||||
className: "player",
|
|
||||||
child: Widget.Box({
|
|
||||||
className: "bg-img",
|
|
||||||
vpack: "start",
|
|
||||||
css: player.bind("cover_path").transform(p => `background-image: url('${p}');`),
|
|
||||||
}),
|
|
||||||
overlays: [
|
|
||||||
Widget.Box({
|
|
||||||
className: "bg-cover"
|
|
||||||
}),
|
|
||||||
Widget.Box({
|
|
||||||
className: "overlay",
|
|
||||||
vertical: true,
|
|
||||||
halign: GTK_ALIGN_CENTER,
|
|
||||||
children: [
|
|
||||||
Widget.Box({
|
|
||||||
className: "info",
|
|
||||||
vertical: true,
|
|
||||||
children: [
|
|
||||||
Widget.Label({ className: "title", label: player.bind("track_title") }),
|
|
||||||
Widget.Label({ className: "album", label: player.bind("track_album") }),
|
|
||||||
Widget.Label({ className: "artist", label: artists }),
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
Widget.Box({
|
|
||||||
vexpand: true,
|
|
||||||
}),
|
|
||||||
Widget.CenterBox({
|
|
||||||
className: "controls",
|
|
||||||
startWidget: positionLabel,
|
|
||||||
centerWidget: Widget.Box({
|
|
||||||
halign: GTK_ALIGN_CENTER,
|
|
||||||
spacing: 20,
|
|
||||||
children: [
|
|
||||||
prev,
|
|
||||||
playPause,
|
|
||||||
next,
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
endWidget: lengthLabel,
|
|
||||||
}),
|
|
||||||
positionSlider,
|
|
||||||
],
|
|
||||||
})
|
|
||||||
],
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function MediaContent() {
|
|
||||||
const currentIdx = Variable(0);
|
|
||||||
const currentPlayer = Utils.merge([currentIdx.bind(), players], (currentIdx, players) => {
|
|
||||||
const idx = Math.min(currentIdx, players.length - 1);
|
|
||||||
return players[idx];
|
|
||||||
})
|
|
||||||
|
|
||||||
return Widget.Box({
|
|
||||||
children: currentPlayer.as(p => [Player(p)]),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export function Media(monitor = 0) {
|
|
||||||
return Widget.Window({
|
|
||||||
monitor,
|
|
||||||
visible: false,
|
|
||||||
exclusivity: "normal",
|
|
||||||
className: "media",
|
|
||||||
margins: [10, 10, 0, 10],
|
|
||||||
name: `media${monitor}`,
|
|
||||||
anchor: ["left", "top"],
|
|
||||||
child: MediaContent(),
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -1,59 +0,0 @@
|
||||||
.media > box {
|
|
||||||
border-radius: 5px;
|
|
||||||
|
|
||||||
background-color: transparent;
|
|
||||||
color: @ctp-text;
|
|
||||||
}
|
|
||||||
|
|
||||||
.media .player .bg-img {
|
|
||||||
border-radius: 5px;
|
|
||||||
min-width: 400px;
|
|
||||||
min-height: 200px;
|
|
||||||
background-size: cover;
|
|
||||||
}
|
|
||||||
|
|
||||||
.media .player .bg-cover {
|
|
||||||
background: linear-gradient(alpha(@ctp-base, 0.5), @ctp-base);
|
|
||||||
}
|
|
||||||
|
|
||||||
.media .player .overlay {
|
|
||||||
border-radius: 5px;
|
|
||||||
min-width: 400px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.media .player .info {
|
|
||||||
padding: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.media .player .controls {
|
|
||||||
padding: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.media .player .info .title {
|
|
||||||
font-size: 1.2em;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.media .player scale.position {
|
|
||||||
padding: 0;
|
|
||||||
border-radius: 0;
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.media .player scale.position trough {
|
|
||||||
min-height: 4px;
|
|
||||||
border-radius: 0;
|
|
||||||
border: none;
|
|
||||||
background-color: @ctp-overlay0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.media .player scale.position highlight {
|
|
||||||
border-radius: 0;
|
|
||||||
border: none;
|
|
||||||
background-color: @ctp-lavender;
|
|
||||||
}
|
|
||||||
|
|
||||||
.media .player scale.position slider {
|
|
||||||
all: unset;
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,129 +0,0 @@
|
||||||
const notifications = await Service.import("notifications")
|
|
||||||
|
|
||||||
/** @param {import('resource:///com/github/Aylur/ags/service/notifications.js').Notification} n */
|
|
||||||
function NotificationIcon({ app_entry, app_icon, image }) {
|
|
||||||
if (image) {
|
|
||||||
return Widget.Box({
|
|
||||||
css: `background-image: url("${image}");`
|
|
||||||
+ "background-size: contain;"
|
|
||||||
+ "background-repeat: no-repeat;"
|
|
||||||
+ "background-position: center;",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
let icon = "dialog-information-symbolic"
|
|
||||||
if (Utils.lookUpIcon(app_icon))
|
|
||||||
icon = app_icon
|
|
||||||
|
|
||||||
if (app_entry && Utils.lookUpIcon(app_entry))
|
|
||||||
icon = app_entry
|
|
||||||
|
|
||||||
return Widget.Box({
|
|
||||||
child: Widget.Icon(icon),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @param {import('resource:///com/github/Aylur/ags/service/notifications.js').Notification} n */
|
|
||||||
function Notification(n) {
|
|
||||||
const icon = Widget.Box({
|
|
||||||
vpack: "start",
|
|
||||||
class_name: "icon",
|
|
||||||
child: NotificationIcon(n),
|
|
||||||
})
|
|
||||||
|
|
||||||
const title = Widget.Label({
|
|
||||||
class_name: "title",
|
|
||||||
xalign: 0,
|
|
||||||
justification: "left",
|
|
||||||
hexpand: true,
|
|
||||||
max_width_chars: 24,
|
|
||||||
label: n.summary,
|
|
||||||
use_markup: true,
|
|
||||||
})
|
|
||||||
|
|
||||||
const body = Widget.Label({
|
|
||||||
class_name: "body",
|
|
||||||
hexpand: true,
|
|
||||||
use_markup: true,
|
|
||||||
xalign: 0,
|
|
||||||
justification: "left",
|
|
||||||
label: n.body,
|
|
||||||
wrap: true,
|
|
||||||
})
|
|
||||||
|
|
||||||
const content = Widget.Box({
|
|
||||||
className: "content",
|
|
||||||
children: [
|
|
||||||
icon,
|
|
||||||
Widget.Box({
|
|
||||||
vertical: true,
|
|
||||||
children: [
|
|
||||||
title,
|
|
||||||
body,
|
|
||||||
]
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
})
|
|
||||||
|
|
||||||
const actions = Widget.Box({
|
|
||||||
className: "actions",
|
|
||||||
children: n.actions.map(({ id, label }) => Widget.Button({
|
|
||||||
className: "action",
|
|
||||||
on_clicked: () => {
|
|
||||||
n.invoke(id)
|
|
||||||
n.dismiss()
|
|
||||||
},
|
|
||||||
hexpand: true,
|
|
||||||
child: Widget.Label(label),
|
|
||||||
})),
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
return Widget.EventBox({
|
|
||||||
attribute: { id: n.id },
|
|
||||||
onPrimaryClick: n.dismiss,
|
|
||||||
child: Widget.Box({
|
|
||||||
classNames: ["notification", n.urgency],
|
|
||||||
vertical: true,
|
|
||||||
children: [
|
|
||||||
content,
|
|
||||||
actions,
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function NotificationList() {
|
|
||||||
const list = Widget.Box({
|
|
||||||
css: "min-width: 2px; min-height: 2px;",
|
|
||||||
vertical: true,
|
|
||||||
spacing: 10,
|
|
||||||
children: notifications.popups.map(Notification),
|
|
||||||
})
|
|
||||||
|
|
||||||
function onNotified(_, /** @type {number} */ id) {
|
|
||||||
const n = notifications.getNotification(id)
|
|
||||||
if (n)
|
|
||||||
list.children = [Notification(n), ...list.children]
|
|
||||||
}
|
|
||||||
|
|
||||||
function onDismissed(_, /** @type {number} */ id) {
|
|
||||||
list.children.find(n => n.attribute.id === id)?.destroy()
|
|
||||||
}
|
|
||||||
|
|
||||||
list.hook(notifications, onNotified, "notified")
|
|
||||||
.hook(notifications, onDismissed, "dismissed")
|
|
||||||
return list
|
|
||||||
}
|
|
||||||
|
|
||||||
export function Notifications(monitor = 0) {
|
|
||||||
return Widget.Window({
|
|
||||||
monitor,
|
|
||||||
exclusivity: "normal",
|
|
||||||
className: "notifications",
|
|
||||||
margins: [10, 10, 10, 10],
|
|
||||||
name: `notifications${monitor}`,
|
|
||||||
anchor: ["left", "top"],
|
|
||||||
child: NotificationList(),
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -1,59 +0,0 @@
|
||||||
.notification {
|
|
||||||
min-width: 300px;
|
|
||||||
padding: 10px;
|
|
||||||
background-color: alpha(@ctp-base, 0.7);
|
|
||||||
border-radius: 5px;
|
|
||||||
|
|
||||||
border: 1px solid;
|
|
||||||
border-left: 5px solid;
|
|
||||||
border-color: @ctp-overlay1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.notification.low {
|
|
||||||
border-color: @ctp-base;
|
|
||||||
}
|
|
||||||
|
|
||||||
.notification.critical {
|
|
||||||
border-color: @ctp-red;
|
|
||||||
}
|
|
||||||
|
|
||||||
.notification .icon {
|
|
||||||
min-width: 68px;
|
|
||||||
min-height: 68px;
|
|
||||||
margin-right: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.notification .icon image {
|
|
||||||
font-size: 58px;
|
|
||||||
/* to center the icon */
|
|
||||||
margin: 5px;
|
|
||||||
color: @ctp-text;
|
|
||||||
}
|
|
||||||
|
|
||||||
.notification .icon box {
|
|
||||||
min-width: 68px;
|
|
||||||
min-height: 68px;
|
|
||||||
border-radius: 7px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.notification .actions .action {
|
|
||||||
margin: 0 .4em;
|
|
||||||
margin-top: .8em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.notification .actions .action:first-child {
|
|
||||||
margin-left: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.notification .actions .action:last-child {
|
|
||||||
margin-right: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.notification .title {
|
|
||||||
color: @ctp-text;
|
|
||||||
font-size: 1.4em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.notification .body {
|
|
||||||
color: @ctp-subtext0;
|
|
||||||
}
|
|
|
@ -1,22 +0,0 @@
|
||||||
@import url("colors.css");
|
|
||||||
@import url("notifications/style.css");
|
|
||||||
@import url("media/style.css");
|
|
||||||
|
|
||||||
|
|
||||||
.bar > box {
|
|
||||||
padding: 10px;
|
|
||||||
border-radius: 5px;
|
|
||||||
|
|
||||||
background-color: alpha(@ctp-base, 0.95);
|
|
||||||
color: @ctp-text;
|
|
||||||
}
|
|
||||||
|
|
||||||
.clock .time {
|
|
||||||
font-weight: bold;
|
|
||||||
font-size: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.clock .date {
|
|
||||||
font-size: 0.8em;
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,18 +0,0 @@
|
||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"target": "ES2022",
|
|
||||||
"module": "ES2022",
|
|
||||||
"lib": [
|
|
||||||
"ES2022"
|
|
||||||
],
|
|
||||||
"allowJs": true,
|
|
||||||
"checkJs": true,
|
|
||||||
"strict": true,
|
|
||||||
"noImplicitAny": false,
|
|
||||||
"baseUrl": ".",
|
|
||||||
"typeRoots": [
|
|
||||||
"./types"
|
|
||||||
],
|
|
||||||
"skipLibCheck": true
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1 +0,0 @@
|
||||||
/home/kalle/.local/share/com.github.Aylur.ags/types
|
|
|
@ -1,15 +1,20 @@
|
||||||
{
|
{
|
||||||
inputs,
|
inputs,
|
||||||
|
pkgs,
|
||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
||||||
home-manager.users.kalle = {
|
home-manager.users.kalle = {
|
||||||
imports = [ inputs.ags.homeManagerModules.default ];
|
imports = [ inputs.ags.homeManagerModules.default ];
|
||||||
programs.ags = {
|
programs.ags = {
|
||||||
enable = true;
|
enable = true;
|
||||||
configDir = ./config;
|
configDir = ./config;
|
||||||
|
extraPackages = with inputs.ags.packages.${pkgs.system}; [
|
||||||
|
tray
|
||||||
|
hyprland
|
||||||
|
mpris
|
||||||
|
wireplumber
|
||||||
|
];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue