✨ ags: add brightness class, media widget on center-window and more
This commit is contained in:
@@ -71,14 +71,47 @@ function handleVolumeArgs(args: Array<string>) {
|
|||||||
if(!args[1])
|
if(!args[1])
|
||||||
return `Please specify what you want to do!\n\n${volumeHelp()}`
|
return `Please specify what you want to do!\n\n${volumeHelp()}`
|
||||||
|
|
||||||
if(!/(sink|source)\-mute/.test(args[1]) && !args[2])
|
if(/^(sink|source)(\-increase|\-decrease|\-set)$/.test(args[1]) && !args[2])
|
||||||
return `You forgot to add a value to be set!`;
|
return `You forgot to add a value to be set!`;
|
||||||
|
|
||||||
|
if(Number.isNaN(Number.parseFloat(args[2])) && Number.isSafeInteger(Number.parseFloat(args[2])))
|
||||||
|
return `Argument "${args[2]} is not a valid number! Please use integers"`;
|
||||||
|
|
||||||
const command: Array<string> = args[1].split('-');
|
const command: Array<string> = args[1].split('-');
|
||||||
|
|
||||||
|
if(/help/.test(args[1]))
|
||||||
|
return volumeHelp();
|
||||||
|
|
||||||
switch(command[1]) {
|
switch(command[1]) {
|
||||||
case "set":
|
case "set":
|
||||||
|
command[0] === "sink" ?
|
||||||
|
Wireplumber.getDefault().setSinkVolume(Number.parseInt(args[2]))
|
||||||
|
:
|
||||||
|
Wireplumber.getDefault().setSourceVolume(Number.parseInt(args[2]))
|
||||||
return `Done! Set ${command[0]} volume to ${args[2]}`;
|
return `Done! Set ${command[0]} volume to ${args[2]}`;
|
||||||
|
|
||||||
|
case "mute":
|
||||||
|
command[0] === "sink" ?
|
||||||
|
Wireplumber.getDefault().toggleMuteSink()
|
||||||
|
:
|
||||||
|
Wireplumber.getDefault().toggleMuteSource()
|
||||||
|
return `Done toggling mute!`;
|
||||||
|
|
||||||
|
case "increase":
|
||||||
|
command[0] === "sink" ?
|
||||||
|
Wireplumber.getDefault().increaseSinkVolume(Number.parseInt(args[2]))
|
||||||
|
:
|
||||||
|
Wireplumber.getDefault().increaseSourceVolume(Number.parseInt(args[2]))
|
||||||
|
|
||||||
|
return `Done increasing volume by ${args[2]}`;
|
||||||
|
|
||||||
|
case "decrease":
|
||||||
|
command[0] === "sink" ?
|
||||||
|
Wireplumber.getDefault().decreaseSinkVolume(Number.parseInt(args[2]))
|
||||||
|
:
|
||||||
|
Wireplumber.getDefault().decreaseSourceVolume(Number.parseInt(args[2]))
|
||||||
|
|
||||||
|
return `Done decreasing volume to ${args[2]}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return `Couldn't resolve arguments! "${args.join(' ').replace(new RegExp(`^${args[0]}`), "")}"`;
|
return `Couldn't resolve arguments! "${args.join(' ').replace(new RegExp(`^${args[0]}`), "")}"`;
|
||||||
@@ -109,9 +142,10 @@ Options:
|
|||||||
close [window_name]: sets specified window's visibility to false.
|
close [window_name]: sets specified window's visibility to false.
|
||||||
toggle [window_name]: toggles visibility of specified window.
|
toggle [window_name]: toggles visibility of specified window.
|
||||||
reload: creates a new astal instance and removes this one.
|
reload: creates a new astal instance and removes this one.
|
||||||
|
volume: wireplumber volume controller, see "volume help".
|
||||||
help, -h, --help: shows this help message.
|
help, -h, --help: shows this help message.
|
||||||
|
|
||||||
2024 (c) retrozinndev's Hyprland-Dots, licensed under the MIT License.
|
2025 (c) retrozinndev's Hyprland-Dots, licensed under the MIT License.
|
||||||
https://github.com/retrozinndev/Hyprland-Dots
|
https://github.com/retrozinndev/Hyprland-Dots
|
||||||
`.trim();
|
`.trim();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
import { exec, execAsync, GObject, monitorFile, Process, readFileAsync, register, signal } from "astal";
|
||||||
|
import { Connectable } from "astal/binding";
|
||||||
|
|
||||||
|
|
||||||
|
/** !!TODO!! Needs more work and testing
|
||||||
|
* I(retrozinndev) don't have a monitor that has software-controlled brightness
|
||||||
|
*/
|
||||||
|
@register({ GTypeName: "Brightness" })
|
||||||
|
class Brightness extends GObject.Object implements Connectable {
|
||||||
|
private readonly backlight: string|undefined;
|
||||||
|
private max: number;
|
||||||
|
private brightness: number;
|
||||||
|
|
||||||
|
@signal(Number)
|
||||||
|
declare brightnessChanged: (value: number) => void;
|
||||||
|
|
||||||
|
constructor(backlightDevice?: string) {
|
||||||
|
super();
|
||||||
|
this.backlight = backlightDevice || "";
|
||||||
|
this.max = Number.parseInt(exec(`brightnessctl -d ${backlightDevice} max`))
|
||||||
|
this.brightness = Number.parseInt(exec(`brightnessctl -d ${backlightDevice} get`))
|
||||||
|
|
||||||
|
readFileAsync(`/sys/class/backlight/${backlightDevice}/brightness`).catch(() => {
|
||||||
|
throw new Error(`Couldn't find backlight ${backlightDevice}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
monitorFile(`/sys/class/backlight/${backlightDevice}/brightness`, async () => {
|
||||||
|
this.brightness = Number.parseInt(await execAsync(`brightnessctl -d ${backlightDevice} get`));
|
||||||
|
this.max = Number.parseInt(await execAsync(`brightnessctl -d ${backlightDevice} max`));
|
||||||
|
|
||||||
|
this.emit("brightness-changed", this.brightness);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public setBrightness(newBrightness: number): void {
|
||||||
|
execAsync(`brightnessctl -d ${this.backlight} set ${newBrightness || this.brightness}`).catch(() => {
|
||||||
|
throw new Error(`Couldn't set brightness of backlight ${this.backlight}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.emit("brightness-changed", newBrightness);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,29 +1,32 @@
|
|||||||
import AstalNotifd from "gi://AstalNotifd";
|
import AstalNotifd from "gi://AstalNotifd";
|
||||||
import { timeout } from "astal/time";
|
import { timeout } from "astal/time";
|
||||||
import { Connectable } from "astal/binding";
|
import { Connectable } from "astal/binding";
|
||||||
import { GObject, register, property, signal } from "astal";
|
import { GObject, register, signal } from "astal";
|
||||||
import { Windows } from "../windows";
|
import { Windows } from "../windows";
|
||||||
|
|
||||||
@register()
|
@register({ GTypeName: "Notifications" })
|
||||||
class Notifications extends GObject.Object implements Connectable {
|
class NotificationsClass extends GObject.Object implements Connectable {
|
||||||
|
|
||||||
private static instance: Notifications;
|
private static instance: NotificationsClass;
|
||||||
private notifd: AstalNotifd.Notifd;
|
private notifd: AstalNotifd.Notifd;
|
||||||
|
|
||||||
public notifications: Array<AstalNotifd.Notification> = [];
|
public notifications: Array<AstalNotifd.Notification> = [];
|
||||||
public notificationHistory: Array<AstalNotifd.Notification> = [];
|
public notificationHistory: Array<AstalNotifd.Notification> = [];
|
||||||
|
|
||||||
@signal()
|
@signal(AstalNotifd.Notification)
|
||||||
declare "notification-added": (notification: AstalNotifd.Notification) => void;
|
declare notificationAdded: (added: AstalNotifd.Notification) => void;
|
||||||
|
|
||||||
|
@signal(Number)
|
||||||
|
declare notificationRemoved: (id: number) => void;
|
||||||
|
|
||||||
|
|
||||||
public static getDefault(): Notifications {
|
public static getDefault(): NotificationsClass {
|
||||||
if(!Notifications.instance) {
|
if(!NotificationsClass.instance) {
|
||||||
Notifications.instance = new Notifications();
|
NotificationsClass.instance = new NotificationsClass();
|
||||||
this.instance._init();
|
this.instance._init();
|
||||||
}
|
}
|
||||||
|
|
||||||
return Notifications.instance;
|
return NotificationsClass.instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
@@ -95,3 +98,5 @@ class Notifications extends GObject.Object implements Connectable {
|
|||||||
return this.notifd;
|
return this.notifd;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const Notifications = new NotificationsClass();
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
import { GObject } from "astal";
|
import { GObject, register } from "astal";
|
||||||
import AstalWp from "gi://AstalWp";
|
import AstalWp from "gi://AstalWp";
|
||||||
|
|
||||||
export const Wireplumber = GObject.registerClass({
|
export { WireplumberClass as Wireplumber };
|
||||||
GTypeName: "Wireplumber",
|
|
||||||
Signals: {}
|
|
||||||
}, class WireplumberClass extends GObject.Object {
|
@register({ GTypeName: "Wireplumber" })
|
||||||
|
class WireplumberClass extends GObject.Object {
|
||||||
private static astalWireplumber: (AstalWp.Wp|null) = AstalWp.get_default();
|
private static astalWireplumber: (AstalWp.Wp|null) = AstalWp.get_default();
|
||||||
private static inst: WireplumberClass;
|
private static inst: WireplumberClass;
|
||||||
|
|
||||||
@@ -14,8 +15,8 @@ export const Wireplumber = GObject.registerClass({
|
|||||||
private maxSinkVolume: number = 100;
|
private maxSinkVolume: number = 100;
|
||||||
private maxSourceVolume: number = 100;
|
private maxSourceVolume: number = 100;
|
||||||
|
|
||||||
_init(...props: any[]) {
|
constructor() {
|
||||||
super._init(props);
|
super();
|
||||||
|
|
||||||
if(!WireplumberClass.astalWireplumber)
|
if(!WireplumberClass.astalWireplumber)
|
||||||
throw new Error("Audio features will not work correctly! Please install wireplumber first", {
|
throw new Error("Audio features will not work correctly! Please install wireplumber first", {
|
||||||
@@ -145,4 +146,4 @@ export const Wireplumber = GObject.registerClass({
|
|||||||
|
|
||||||
return this.muteSource();
|
return this.muteSource();
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|||||||
+1
-18
@@ -112,27 +112,10 @@
|
|||||||
|
|
||||||
& > button {
|
& > button {
|
||||||
margin: 4px 1px;
|
margin: 4px 1px;
|
||||||
border-radius: 4px;
|
|
||||||
|
|
||||||
label {
|
& label {
|
||||||
font-size: 8px;
|
font-size: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background: colors.$bg-secondary;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:first-child {
|
|
||||||
border-top-left-radius: 12px;
|
|
||||||
border-bottom-left-radius: 12px;
|
|
||||||
margin-left: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:last-child {
|
|
||||||
border-top-right-radius: 12px;
|
|
||||||
border-bottom-right-radius: 12px;
|
|
||||||
margin-right: 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,38 +1,75 @@
|
|||||||
@use "sass:color";
|
@use "sass:color";
|
||||||
@use "./wal";
|
@use "./wal";
|
||||||
@use "./functions" as funs; // Did you know that you can use the 'as' keyword? I just found out!
|
@use "./colors";
|
||||||
|
@use "./functions" as funs;
|
||||||
|
|
||||||
.center-window-container {
|
.center-window-container {
|
||||||
background: wal.$background;
|
background: colors.$bg-translucent;
|
||||||
border-radius: 18px;
|
border-radius: 18px;
|
||||||
padding: 12px;
|
padding: 12px;
|
||||||
|
|
||||||
& .left {
|
& .left {
|
||||||
.top {
|
& > .top {
|
||||||
.time {
|
padding-bottom: 10px;
|
||||||
font-size: 22px;
|
|
||||||
|
& .time {
|
||||||
|
font-size: 28px;
|
||||||
font-weight: 800;
|
font-weight: 800;
|
||||||
}
|
}
|
||||||
|
|
||||||
.date {
|
& .date {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
color: funs.toRGB(color.adjust($color: wal.$foreground, $lightness: -15%));
|
color: colors.$fg-disabled;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
& .big-media {
|
||||||
|
padding: 6px 16px;
|
||||||
|
|
||||||
|
& > box > .image {
|
||||||
|
background-size: cover;
|
||||||
|
background-position: center center;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
& > .info {
|
||||||
|
padding: {
|
||||||
|
top: 4px;
|
||||||
|
bottom: 6px;
|
||||||
|
};
|
||||||
|
|
||||||
|
& .title {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
& .artist {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: colors.$fg-disabled;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
& > .controls {
|
||||||
|
padding: 8px 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
& .calendar-box {
|
& .right {
|
||||||
padding: 5px;
|
& .calendar-box {
|
||||||
& calendar {
|
padding: 5px;
|
||||||
border-radius: 6px;
|
& calendar {
|
||||||
padding: 2px;
|
border-radius: 6px;
|
||||||
|
padding: 2px;
|
||||||
|
|
||||||
&.view {
|
&.view {
|
||||||
background: funs.toRGB(color.adjust($color: wal.$color1, $lightness: -35%));
|
background: funs.toRGB(color.adjust($color: wal.$color1, $lightness: -35%));
|
||||||
|
|
||||||
& header {
|
& header {
|
||||||
background: funs.toRGB(color.adjust($color: wal.$background, $lightness: -20%));
|
background: funs.toRGB(color.adjust($color: wal.$background, $lightness: -20%));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,14 @@
|
|||||||
@use "sass:color";
|
@use "sass:color";
|
||||||
@use "./wal";
|
@use "./wal";
|
||||||
|
@use "./colors";
|
||||||
@use "./functions" as funs;
|
@use "./functions" as funs;
|
||||||
|
@use "./mixins";
|
||||||
|
|
||||||
.control-center-container {
|
.control-center-container {
|
||||||
background: rgba(wal.$background, .65);
|
@include mixins.reset-props;
|
||||||
|
@include mixins.default-styles;
|
||||||
|
|
||||||
|
background: colors.$bg-translucent;
|
||||||
border-radius: 24px;
|
border-radius: 24px;
|
||||||
padding: 20px 14px;
|
padding: 20px 14px;
|
||||||
|
|
||||||
@@ -58,25 +63,5 @@
|
|||||||
margin-right: 8px;
|
margin-right: 8px;
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
trough {
|
|
||||||
background: funs.toRGB(color.adjust($color: wal.$color1, $lightness: -20%));
|
|
||||||
border-radius: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
trough highlight {
|
|
||||||
background: wal.$color1;
|
|
||||||
border-top-left-radius: inherit;
|
|
||||||
border-bottom-left-radius: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
trough slider {
|
|
||||||
min-width: 1.2em;
|
|
||||||
min-height: 1.2em;
|
|
||||||
border-radius: 50%;
|
|
||||||
margin: -3px 0;
|
|
||||||
background: wal.$foreground;
|
|
||||||
margin-left: -1px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+23
-1
@@ -19,7 +19,7 @@
|
|||||||
& > button {
|
& > button {
|
||||||
background: colors.$bg-secondary;
|
background: colors.$bg-secondary;
|
||||||
margin: 0 1px;
|
margin: 0 1px;
|
||||||
padding: 0 6px;
|
padding: 4px 6px;
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
@@ -78,4 +78,26 @@
|
|||||||
color: colors.$fg-primary;
|
color: colors.$fg-primary;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
& trough {
|
||||||
|
background: funs.toRGB(color.adjust($color: wal.$color1, $lightness: -20%));
|
||||||
|
border-radius: 8px;
|
||||||
|
margin: 2px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
& trough highlight {
|
||||||
|
background: wal.$color1;
|
||||||
|
min-height: .9em;
|
||||||
|
border-top-left-radius: inherit;
|
||||||
|
border-bottom-left-radius: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
& trough slider {
|
||||||
|
border-radius: 50%;
|
||||||
|
margin: -2px 0;
|
||||||
|
background: wal.$foreground;
|
||||||
|
margin-left: -1px;
|
||||||
|
min-width: 1.2em;
|
||||||
|
min-height: 1.2em;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -8,7 +8,7 @@
|
|||||||
border-radius: 20px;
|
border-radius: 20px;
|
||||||
|
|
||||||
.icon {
|
.icon {
|
||||||
margin-right: 14px;
|
margin-right: 10px;
|
||||||
font-size: 24px;
|
font-size: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import { Astal, Gtk, Widget } from "astal/gtk3";
|
||||||
|
|
||||||
|
|
||||||
|
const { TOP, BOTTOM, LEFT, RIGHT }: typeof Astal.WindowAnchor = Astal.WindowAnchor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a screen-size window and opens the provided window after it.
|
||||||
|
* When clicking in the transparent background window, it closes(hides)
|
||||||
|
* the provided window.
|
||||||
|
* @param window the window to be rendered and closed when clicking outside of it
|
||||||
|
*/
|
||||||
|
export function PopupWindow(window: Gtk.Window) {
|
||||||
|
const bgWindow: Gtk.Window = new Widget.Window({
|
||||||
|
namespace: "popup-bg-window",
|
||||||
|
anchor: TOP | BOTTOM | LEFT | RIGHT,
|
||||||
|
|
||||||
|
} as Widget.WindowProps);
|
||||||
|
}
|
||||||
@@ -29,11 +29,15 @@ export function FocusedClient() {
|
|||||||
new Widget.Label({
|
new Widget.Label({
|
||||||
className: "class",
|
className: "class",
|
||||||
xalign: 0,
|
xalign: 0,
|
||||||
|
max_width_chars: 65,
|
||||||
|
truncate: false,
|
||||||
label: bind(focusedClient, "class")
|
label: bind(focusedClient, "class")
|
||||||
} as Widget.LabelProps),
|
} as Widget.LabelProps),
|
||||||
new Widget.Label({
|
new Widget.Label({
|
||||||
className: "title",
|
className: "title",
|
||||||
xalign: 0,
|
xalign: 0,
|
||||||
|
max_width_chars: 48,
|
||||||
|
truncate: false,
|
||||||
label: bind(focusedClient, "title")
|
label: bind(focusedClient, "title")
|
||||||
} as Widget.LabelProps)
|
} as Widget.LabelProps)
|
||||||
] : []
|
] : []
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ export function Media(): Gtk.Widget {
|
|||||||
new Widget.Button({
|
new Widget.Button({
|
||||||
className: "next nf",
|
className: "next nf",
|
||||||
label: "",
|
label: "",
|
||||||
|
tooltipText: "Next",
|
||||||
onClick: () => players[0].canGoNext && players[0].next()
|
onClick: () => players[0].canGoNext && players[0].next()
|
||||||
} as Widget.ButtonProps)
|
} as Widget.ButtonProps)
|
||||||
] : new Widget.Label({
|
] : new Widget.Label({
|
||||||
|
|||||||
@@ -1,6 +1,116 @@
|
|||||||
|
import { AstalIO, bind, GLib, Process, timeout } from "astal";
|
||||||
import { Gtk, Widget } from "astal/gtk3";
|
import { Gtk, Widget } from "astal/gtk3";
|
||||||
|
import AstalMpris from "gi://AstalMpris";
|
||||||
|
|
||||||
|
let dragTimer: (AstalIO.Time|undefined);
|
||||||
|
|
||||||
export const BigMedia: Gtk.Widget = new Widget.Box({
|
export const BigMedia: Gtk.Widget = new Widget.Box({
|
||||||
className: "big-media",
|
className: "big-media",
|
||||||
//TODO
|
orientation: Gtk.Orientation.VERTICAL,
|
||||||
|
homogeneous: false,
|
||||||
|
children: bind(AstalMpris.get_default(), "players").as((players: Array<AstalMpris.Player>) =>
|
||||||
|
players[0] ? [
|
||||||
|
new Widget.Box({
|
||||||
|
halign: Gtk.Align.CENTER,
|
||||||
|
child: new Widget.Box({
|
||||||
|
className: "image",
|
||||||
|
hexpand: false,
|
||||||
|
orientation: Gtk.Orientation.VERTICAL,
|
||||||
|
visible: bind(players[0], "coverArt").as((coverArt: string) =>
|
||||||
|
coverArt !== ""),
|
||||||
|
css: bind(players[0], "coverArt").as((coverArt: string) =>
|
||||||
|
`.image { background-image: url('${coverArt}'); }`),
|
||||||
|
width_request: 132,
|
||||||
|
height_request: 128
|
||||||
|
} as Widget.BoxProps)
|
||||||
|
} as Widget.BoxProps),
|
||||||
|
new Widget.Box({
|
||||||
|
className: "info",
|
||||||
|
orientation: Gtk.Orientation.VERTICAL,
|
||||||
|
children: [
|
||||||
|
new Widget.Label({
|
||||||
|
className: "title",
|
||||||
|
tooltipText: bind(players[0], "title").as((title: string) => !title ? "No Title" : title),
|
||||||
|
label: bind(players[0], "title").as((title: string) => !title ? "No Title" : title),
|
||||||
|
truncate: true
|
||||||
|
} as Widget.LabelProps),
|
||||||
|
new Widget.Label({
|
||||||
|
className: "artist",
|
||||||
|
tooltipText: bind(players[0], "artist").as((artist: string) => !artist ? "No Artist" : artist),
|
||||||
|
label: bind(players[0], "artist").as((artist: string) => !artist ? "No Artist" : artist),
|
||||||
|
truncate: true
|
||||||
|
} as Widget.LabelProps)
|
||||||
|
]
|
||||||
|
} as Widget.BoxProps),
|
||||||
|
new Widget.Box({
|
||||||
|
className: "progress",
|
||||||
|
hexpand: true,
|
||||||
|
visible: bind(players[0], "canSeek"),
|
||||||
|
children: [
|
||||||
|
/*new Widget.Label({
|
||||||
|
className: "elapsed",
|
||||||
|
label: bind(players[0], "position").as((position: number) =>
|
||||||
|
Math.floor(position).toString())
|
||||||
|
}),*/
|
||||||
|
new Widget.Slider({
|
||||||
|
min: 0,
|
||||||
|
hexpand: true,
|
||||||
|
max: bind(players[0], "length").as((length: number) =>
|
||||||
|
Math.floor(length)),
|
||||||
|
value: bind(players[0], "position").as((position: number) =>
|
||||||
|
Math.floor(position)),
|
||||||
|
onDragged: (slider: Widget.Slider) => {
|
||||||
|
if(dragTimer === undefined)
|
||||||
|
dragTimer = timeout(600, () =>
|
||||||
|
players[0].set_position(Math.round(slider.value)));
|
||||||
|
else {
|
||||||
|
dragTimer.cancel();
|
||||||
|
dragTimer = timeout(600, () =>
|
||||||
|
players[0].set_position(Math.round(slider.value)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
]
|
||||||
|
}),
|
||||||
|
new Widget.Box({
|
||||||
|
className: "controls button-row",
|
||||||
|
hexpand: true,
|
||||||
|
halign: Gtk.Align.CENTER,
|
||||||
|
children: [
|
||||||
|
new Widget.Button({
|
||||||
|
className: "link nf",
|
||||||
|
label: "",
|
||||||
|
tooltipText: "Copy link to Clipboard",
|
||||||
|
visible: bind(players[0], "metadata").as((_metadata: GLib.HashTable) =>
|
||||||
|
players[0].get_meta("xesam:url") === null),
|
||||||
|
onClick: () => Process.exec(`wl-copy ${players[0].get_meta("xesam:url")?.get_string()[0]}`)
|
||||||
|
} as Widget.ButtonProps),
|
||||||
|
new Widget.Button({
|
||||||
|
className: "previous nf",
|
||||||
|
label: "",
|
||||||
|
tooltipText: "Previous",
|
||||||
|
onClick: () => players[0].canGoPrevious && players[0].previous()
|
||||||
|
} as Widget.ButtonProps),
|
||||||
|
new Widget.Button({
|
||||||
|
className: "pause nf",
|
||||||
|
tooltipText: bind(players[0], "playback_status").as((status: AstalMpris.PlaybackStatus) =>
|
||||||
|
status === AstalMpris.PlaybackStatus.PLAYING ? "Pause" : "Play"),
|
||||||
|
label: bind(players[0], "playbackStatus").as((status: AstalMpris.PlaybackStatus) =>
|
||||||
|
status === AstalMpris.PlaybackStatus.PLAYING ? "" : ""),
|
||||||
|
onClick: () => {
|
||||||
|
players[0].playbackStatus === AstalMpris.PlaybackStatus.PAUSED ?
|
||||||
|
players[0].play()
|
||||||
|
:
|
||||||
|
players[0].pause()
|
||||||
|
}
|
||||||
|
} as Widget.ButtonProps),
|
||||||
|
new Widget.Button({
|
||||||
|
className: "next nf",
|
||||||
|
label: "",
|
||||||
|
tooltipText: "Next",
|
||||||
|
onClick: () => players[0].canGoNext && players[0].next()
|
||||||
|
} as Widget.ButtonProps)
|
||||||
|
]
|
||||||
|
})
|
||||||
|
] : new Widget.Box({ className: "empty no-media" }))
|
||||||
} as Widget.BoxProps);
|
} as Widget.BoxProps);
|
||||||
|
|||||||
@@ -1,12 +1,22 @@
|
|||||||
import { Gtk, Widget } from "astal/gtk3";
|
import { Gtk, Widget } from "astal/gtk3";
|
||||||
|
|
||||||
export const tileList: Array<Gtk.Widget> = [
|
export const tileList: Array<Gtk.Widget> = [];
|
||||||
]
|
|
||||||
|
|
||||||
export const Tiles: Widget.Box = new Widget.Box({
|
export function TilesWidget(): Gtk.Widget {
|
||||||
child: new Gtk.Grid({
|
const tilesFlowBox: Gtk.FlowBox = new Gtk.FlowBox({
|
||||||
visible: true,
|
visible: true,
|
||||||
orientation: Gtk.Orientation.HORIZONTAL,
|
noShowAll: false,
|
||||||
rowHomogeneous: true
|
orientation: Gtk.Orientation.HORIZONTAL
|
||||||
} as Gtk.Grid.ConstructorProps)
|
} as Gtk.Grid.ConstructorProps);
|
||||||
} as Widget.BoxProps);
|
|
||||||
|
tileList.map((item: Gtk.Widget) =>
|
||||||
|
tilesFlowBox.insert(item, -1));
|
||||||
|
|
||||||
|
return new Widget.Box({
|
||||||
|
children: [
|
||||||
|
tilesFlowBox
|
||||||
|
]
|
||||||
|
} as Widget.BoxProps);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Tiles: Gtk.Widget = TilesWidget();
|
||||||
|
|||||||
@@ -1,60 +0,0 @@
|
|||||||
import { Binding } from "astal";
|
|
||||||
import { Gtk, Widget } from "astal/gtk3";
|
|
||||||
|
|
||||||
export interface MoreTileProps {
|
|
||||||
className?: string | Binding<string | undefined>;
|
|
||||||
iconName?: string | Binding<string | undefined>;
|
|
||||||
iconSize?: Gtk.IconSize;
|
|
||||||
title: string | Binding<string>;
|
|
||||||
description?: string | Binding<string | undefined>;
|
|
||||||
defaultToggleState?: boolean;
|
|
||||||
onToggledOn: Function;
|
|
||||||
onToggledOff: Function;
|
|
||||||
onClickMore: Function;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function MoreTile(props: MoreTileProps): Gtk.Widget {
|
|
||||||
|
|
||||||
let toggleState: boolean = props?.defaultToggleState !== undefined ?
|
|
||||||
props.defaultToggleState : false;
|
|
||||||
|
|
||||||
const mainEventBox = new Widget.EventBox({
|
|
||||||
onClick: () => toggleState ? props.onToggledOff() : props.onToggledOn(),
|
|
||||||
expand: true,
|
|
||||||
child: new Widget.Box({
|
|
||||||
className: props?.className || "",
|
|
||||||
expand: true,
|
|
||||||
children: [
|
|
||||||
new Widget.Icon({
|
|
||||||
iconName: props?.iconName,
|
|
||||||
visible: props.iconName !== undefined,
|
|
||||||
iconSize: props.iconSize || Gtk.IconSize.BUTTON
|
|
||||||
}),
|
|
||||||
new Widget.Box({
|
|
||||||
className: "text",
|
|
||||||
orientation: Gtk.Orientation.VERTICAL,
|
|
||||||
children: [
|
|
||||||
new Widget.Label({
|
|
||||||
className: "title",
|
|
||||||
label: props.title
|
|
||||||
} as Widget.LabelProps),
|
|
||||||
new Widget.Label({
|
|
||||||
className: "description",
|
|
||||||
visible: props?.description !== undefined,
|
|
||||||
label: props?.description
|
|
||||||
} as Widget.LabelProps)
|
|
||||||
]
|
|
||||||
} as Widget.BoxProps),
|
|
||||||
new Widget.Button({
|
|
||||||
onClick: () => props.onClickMore(),
|
|
||||||
child: new Widget.Icon({
|
|
||||||
iconName: "go-next",
|
|
||||||
iconSize: Gtk.IconSize.BUTTON
|
|
||||||
} as Widget.IconProps),
|
|
||||||
} as Widget.ButtonProps)
|
|
||||||
]
|
|
||||||
} as Widget.BoxProps)
|
|
||||||
} as Widget.EventBoxProps);
|
|
||||||
|
|
||||||
return mainEventBox;
|
|
||||||
}
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
import { Binding, Variable } from "astal";
|
|
||||||
import { Gtk, Widget } from "astal/gtk3";
|
|
||||||
|
|
||||||
export interface NormalTileProps {
|
|
||||||
className?: string | Binding<string | undefined>;
|
|
||||||
iconName?: string | Binding<string | undefined>;
|
|
||||||
iconSize?: Gtk.IconSize;
|
|
||||||
title: string | Binding<string>;
|
|
||||||
description?: string | Binding<string | undefined>;
|
|
||||||
toggleState?: boolean | Binding<boolean | undefined>;
|
|
||||||
onToggledOn: Function;
|
|
||||||
onToggledOff: Function;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function MoreTile(props: NormalTileProps): Gtk.Widget {
|
|
||||||
|
|
||||||
const mainEventBox = new Widget.EventBox({
|
|
||||||
onClick: () => toggleState ? props.onToggledOff() : props.onToggledOn(),
|
|
||||||
expand: true,
|
|
||||||
child: new Widget.Box({
|
|
||||||
className: props?.className || "",
|
|
||||||
expand: true,
|
|
||||||
children: [
|
|
||||||
new Widget.Icon({
|
|
||||||
iconName: props?.iconName,
|
|
||||||
visible: props.iconName !== undefined,
|
|
||||||
iconSize: props.iconSize || Gtk.IconSize.BUTTON
|
|
||||||
}),
|
|
||||||
new Widget.Box({
|
|
||||||
className: "text",
|
|
||||||
orientation: Gtk.Orientation.VERTICAL,
|
|
||||||
children: [
|
|
||||||
new Widget.Label({
|
|
||||||
className: "title",
|
|
||||||
label: props.title
|
|
||||||
} as Widget.LabelProps),
|
|
||||||
new Widget.Label({
|
|
||||||
className: "description",
|
|
||||||
visible: props?.description !== undefined,
|
|
||||||
label: props?.description
|
|
||||||
} as Widget.LabelProps)
|
|
||||||
]
|
|
||||||
} as Widget.BoxProps)
|
|
||||||
]
|
|
||||||
} as Widget.BoxProps)
|
|
||||||
} as Widget.EventBoxProps);
|
|
||||||
|
|
||||||
function toggleOn(): void {
|
|
||||||
mainEventBox.set_class_name(mainEventBox + "")
|
|
||||||
props.onToggledOn();
|
|
||||||
}
|
|
||||||
|
|
||||||
function toggleOff(): void {
|
|
||||||
props.onToggledOff();
|
|
||||||
}
|
|
||||||
|
|
||||||
return mainEventBox;
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
import { Binding } from "astal";
|
||||||
|
import { Gtk, Widget } from "astal/gtk3";
|
||||||
|
|
||||||
|
export type TileProps = {
|
||||||
|
className?: string | Binding<string | undefined>;
|
||||||
|
iconName?: string | Binding<string | undefined>;
|
||||||
|
visible?: boolean | Binding<boolean | undefined>;
|
||||||
|
iconSize?: number | Binding<number | undefined>;
|
||||||
|
title: string | Binding<string>;
|
||||||
|
description?: string | Binding<string | undefined>;
|
||||||
|
defaultToggleState?: boolean;
|
||||||
|
onToggledOn: () => void;
|
||||||
|
onToggledOff: () => void;
|
||||||
|
onClickMore?: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Tile(props: TileProps): Widget.Box {
|
||||||
|
|
||||||
|
const toggleButton = new Gtk.ToggleButton();
|
||||||
|
toggleButton.set_active(props.defaultToggleState || false);
|
||||||
|
|
||||||
|
const moreButton = new Widget.Button({
|
||||||
|
className: "more",
|
||||||
|
visible: props.onClickMore
|
||||||
|
});
|
||||||
|
|
||||||
|
return new Widget.Box({
|
||||||
|
className: (typeof Binding<string | undefined>) === (typeof props.className) ?
|
||||||
|
(props.className as Binding<string | undefined>).as((clsName: (string|undefined)) =>
|
||||||
|
`tile ${clsName || ""}`)
|
||||||
|
:
|
||||||
|
props.className,
|
||||||
|
visible: props.visible,
|
||||||
|
children: [
|
||||||
|
toggleButton,
|
||||||
|
moreButton
|
||||||
|
]
|
||||||
|
})
|
||||||
|
}
|
||||||
+2
-3
@@ -1,4 +1,4 @@
|
|||||||
import { Gdk, Astal, Gtk, Widget } from "astal/gtk3";
|
import { Astal, Gtk, Widget } from "astal/gtk3";
|
||||||
|
|
||||||
import { Clock } from "../widget/bar/Clock";
|
import { Clock } from "../widget/bar/Clock";
|
||||||
import { Logo } from "../widget/bar/Logo";
|
import { Logo } from "../widget/bar/Logo";
|
||||||
@@ -11,12 +11,11 @@ import { Media } from "../widget/bar/Media";
|
|||||||
export const Bar: Widget.Window = new Widget.Window({
|
export const Bar: Widget.Window = new Widget.Window({
|
||||||
monitor: 0,
|
monitor: 0,
|
||||||
namespace: "top-bar",
|
namespace: "top-bar",
|
||||||
anchor: Astal.WindowAnchor.TOP,
|
anchor: Astal.WindowAnchor.TOP | Astal.WindowAnchor.LEFT | Astal.WindowAnchor.RIGHT,
|
||||||
layer: Astal.Layer.TOP,
|
layer: Astal.Layer.TOP,
|
||||||
exclusivity: Astal.Exclusivity.EXCLUSIVE,
|
exclusivity: Astal.Exclusivity.EXCLUSIVE,
|
||||||
canFocus: false,
|
canFocus: false,
|
||||||
visible: true,
|
visible: true,
|
||||||
widthRequest: Gdk.Screen.get_default()?.get_monitor_geometry(0)?.width,
|
|
||||||
child: new Widget.Box({
|
child: new Widget.Box({
|
||||||
className: "bar-container",
|
className: "bar-container",
|
||||||
child: new Widget.CenterBox({
|
child: new Widget.CenterBox({
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { Astal, Gtk, Widget } from "astal/gtk3";
|
|||||||
import { GLib } from "astal";
|
import { GLib } from "astal";
|
||||||
|
|
||||||
import { getDateTime } from "../scripts/time";
|
import { getDateTime } from "../scripts/time";
|
||||||
|
import { BigMedia } from "../widget/center-window/BigMedia";
|
||||||
|
|
||||||
export const CenterWindow: Widget.Window = new Widget.Window({
|
export const CenterWindow: Widget.Window = new Widget.Window({
|
||||||
className: "center-window",
|
className: "center-window",
|
||||||
@@ -25,6 +26,7 @@ export const CenterWindow: Widget.Window = new Widget.Window({
|
|||||||
new Widget.Box({
|
new Widget.Box({
|
||||||
className: "top",
|
className: "top",
|
||||||
orientation: Gtk.Orientation.VERTICAL,
|
orientation: Gtk.Orientation.VERTICAL,
|
||||||
|
valign: Gtk.Align.START,
|
||||||
children: [
|
children: [
|
||||||
new Widget.Label({
|
new Widget.Label({
|
||||||
className: "time",
|
className: "time",
|
||||||
@@ -38,6 +40,7 @@ export const CenterWindow: Widget.Window = new Widget.Window({
|
|||||||
} as Widget.LabelProps)
|
} as Widget.LabelProps)
|
||||||
]
|
]
|
||||||
} as Widget.BoxProps),
|
} as Widget.BoxProps),
|
||||||
|
BigMedia
|
||||||
]
|
]
|
||||||
} as Widget.BoxProps),
|
} as Widget.BoxProps),
|
||||||
new Widget.Box({
|
new Widget.Box({
|
||||||
|
|||||||
@@ -3,10 +3,65 @@ import AstalNotifd from "gi://AstalNotifd";
|
|||||||
import { bind } from "astal";
|
import { bind } from "astal";
|
||||||
import { Notifications } from "../scripts/notification-handler";
|
import { Notifications } from "../scripts/notification-handler";
|
||||||
|
|
||||||
|
function NotificationWidget(notification: AstalNotifd.Notification): Gtk.Widget {
|
||||||
|
return new Widget.Box({
|
||||||
|
className: "notification",
|
||||||
|
homogeneous: false,
|
||||||
|
expand: false,
|
||||||
|
orientation: Gtk.Orientation.VERTICAL,
|
||||||
|
children: [
|
||||||
|
new Widget.Box({
|
||||||
|
className: "top",
|
||||||
|
orientation: Gtk.Orientation.HORIZONTAL,
|
||||||
|
hexpand: true,
|
||||||
|
vexpand: false,
|
||||||
|
children: [
|
||||||
|
new Widget.Label({
|
||||||
|
className: "app-name",
|
||||||
|
halign: Gtk.Align.START,
|
||||||
|
label: notification.appName || "Unknown Application"
|
||||||
|
} as Widget.LabelProps),
|
||||||
|
new Widget.Button({
|
||||||
|
className: "close-button",
|
||||||
|
onClick: () => Notifications.removeNotification(notification.id)
|
||||||
|
} as Widget.ButtonProps)
|
||||||
|
]
|
||||||
|
} as Widget.BoxProps),
|
||||||
|
new Widget.Box({
|
||||||
|
className: "content",
|
||||||
|
orientation: Gtk.Orientation.HORIZONTAL,
|
||||||
|
children: [
|
||||||
|
new Widget.Box({
|
||||||
|
className: "image",
|
||||||
|
visible: notification.image !== "",
|
||||||
|
css: `box.image { background-image: url('${notification.image}'); }`
|
||||||
|
} as Widget.BoxProps),
|
||||||
|
new Widget.Box({
|
||||||
|
className: "text",
|
||||||
|
orientation: Gtk.Orientation.VERTICAL,
|
||||||
|
children: [
|
||||||
|
new Widget.Label({
|
||||||
|
className: "summary",
|
||||||
|
useMarkup: true,
|
||||||
|
label: notification.summary
|
||||||
|
}),
|
||||||
|
new Widget.Label({
|
||||||
|
className: "body",
|
||||||
|
useMarkup: true,
|
||||||
|
label: notification.body
|
||||||
|
} as Widget.LabelProps)
|
||||||
|
]
|
||||||
|
} as Widget.BoxProps)
|
||||||
|
]
|
||||||
|
} as Widget.BoxProps)
|
||||||
|
]
|
||||||
|
} as Widget.BoxProps);
|
||||||
|
}
|
||||||
|
|
||||||
export const FloatingNotifications: Widget.Window = new Widget.Window({
|
export const FloatingNotifications: Widget.Window = new Widget.Window({
|
||||||
namespace: "floating-notifications",
|
namespace: "floating-notifications",
|
||||||
canFocus: false,
|
canFocus: false,
|
||||||
anchor: Astal.WindowAnchor.RIGHT,
|
anchor: Astal.WindowAnchor.TOP | Astal.WindowAnchor.RIGHT,
|
||||||
monitor: 0,
|
monitor: 0,
|
||||||
layer: Astal.Layer.OVERLAY,
|
layer: Astal.Layer.OVERLAY,
|
||||||
visible: false,
|
visible: false,
|
||||||
@@ -16,62 +71,8 @@ export const FloatingNotifications: Widget.Window = new Widget.Window({
|
|||||||
className: "floating-notifications-container",
|
className: "floating-notifications-container",
|
||||||
orientation: Gtk.Orientation.VERTICAL,
|
orientation: Gtk.Orientation.VERTICAL,
|
||||||
homogeneous: false,
|
homogeneous: false,
|
||||||
children: bind(Notifications, "notifications").as((notifications: Array<AstalNotifd.Notification>) => {
|
children: bind(Notifications, "notifications").as((notifications: Array<AstalNotifd.Notification>) =>
|
||||||
console.log("something changed!");
|
notifications.map((notification: AstalNotifd.Notification) =>
|
||||||
return notifications.map((notification: AstalNotifd.Notification) =>
|
NotificationWidget(notification)))
|
||||||
new Widget.Box({
|
|
||||||
className: "notification",
|
|
||||||
homogeneous: false,
|
|
||||||
expand: false,
|
|
||||||
orientation: Gtk.Orientation.VERTICAL,
|
|
||||||
children: [
|
|
||||||
new Widget.Box({
|
|
||||||
className: "top",
|
|
||||||
orientation: Gtk.Orientation.HORIZONTAL,
|
|
||||||
hexpand: true,
|
|
||||||
vexpand: false,
|
|
||||||
children: [
|
|
||||||
new Widget.Label({
|
|
||||||
className: "app-name",
|
|
||||||
halign: Gtk.Align.START,
|
|
||||||
label: notification.appName || "Unknown Application"
|
|
||||||
} as Widget.LabelProps),
|
|
||||||
new Widget.Button({
|
|
||||||
className: "close-button",
|
|
||||||
onClick: () => Notifications.removeNotification(notification.id)
|
|
||||||
} as Widget.ButtonProps)
|
|
||||||
]
|
|
||||||
} as Widget.BoxProps),
|
|
||||||
new Widget.Box({
|
|
||||||
className: "content",
|
|
||||||
orientation: Gtk.Orientation.HORIZONTAL,
|
|
||||||
children: [
|
|
||||||
new Widget.Box({
|
|
||||||
className: "image",
|
|
||||||
visible: notification.image !== "",
|
|
||||||
css: `.image { background-image: url('${notification.image}'); }`
|
|
||||||
} as Widget.BoxProps),
|
|
||||||
new Widget.Box({
|
|
||||||
className: "text",
|
|
||||||
orientation: Gtk.Orientation.VERTICAL,
|
|
||||||
children: [
|
|
||||||
new Widget.Label({
|
|
||||||
className: "summary",
|
|
||||||
useMarkup: true,
|
|
||||||
label: notification.summary
|
|
||||||
}),
|
|
||||||
new Widget.Label({
|
|
||||||
className: "body",
|
|
||||||
useMarkup: true,
|
|
||||||
label: notification.body
|
|
||||||
} as Widget.LabelProps)
|
|
||||||
]
|
|
||||||
} as Widget.BoxProps)
|
|
||||||
]
|
|
||||||
} as Widget.BoxProps)
|
|
||||||
]
|
|
||||||
} as Widget.BoxProps)
|
|
||||||
)
|
|
||||||
})
|
|
||||||
} as Widget.BoxProps)
|
} as Widget.BoxProps)
|
||||||
} as Widget.WindowProps);
|
} as Widget.WindowProps);
|
||||||
|
|||||||
@@ -12,8 +12,6 @@ export const LogoutMenu: Widget.Window = new Widget.Window({
|
|||||||
exclusivity: Astal.Exclusivity.IGNORE,
|
exclusivity: Astal.Exclusivity.IGNORE,
|
||||||
monitor: 0,
|
monitor: 0,
|
||||||
visible: false,
|
visible: false,
|
||||||
widthRequest: Gdk.Screen.get_default()?.get_monitor_geometry(0)?.width,
|
|
||||||
height_request: Gdk.Screen.get_default()?.get_monitor_geometry(0)?.height,
|
|
||||||
child: new Widget.EventBox({
|
child: new Widget.EventBox({
|
||||||
className: "logout-menu",
|
className: "logout-menu",
|
||||||
onClick: () => Process.exec_async("astal close logout-menu", () => {}),
|
onClick: () => Process.exec_async("astal close logout-menu", () => {}),
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { bind, Binding, Variable } from "astal";
|
import { bind, Binding, Variable } from "astal";
|
||||||
import { Astal, Gtk, Widget } from "astal/gtk3";
|
import { Astal, Gtk, Widget } from "astal/gtk3";
|
||||||
import { Wireplumber } from "../scripts/volume";
|
import { Wireplumber } from "../scripts/volume";
|
||||||
import AstalWp from "gi://AstalWp?version=0.1";
|
|
||||||
|
|
||||||
export enum OSDModes {
|
export enum OSDModes {
|
||||||
SINK,
|
SINK,
|
||||||
|
|||||||
Reference in New Issue
Block a user