✨ ags: make osd work, new window management system, lots of improvements
This commit is contained in:
+27
-15
@@ -1,36 +1,48 @@
|
|||||||
import { App } from "astal/gtk3"
|
import { App } from "astal/gtk3"
|
||||||
import { Bar } from "./window/Bar";
|
|
||||||
import { OSD } from "./window/OSD";
|
import { OSD, OSDModes, setOSDMode } from "./window/OSD";
|
||||||
import { ControlCenter } from "./window/ControlCenter";
|
import { ControlCenter } from "./window/ControlCenter";
|
||||||
|
|
||||||
import { runStyleHandler } from "./scripts/style-handler";
|
import { runStyleHandler } from "./scripts/style-handler";
|
||||||
import { handleArguments } from "./scripts/arg-handler";
|
import { handleArguments } from "./scripts/arg-handler";
|
||||||
import { monitorPaths } from "./scripts/reload-handler";
|
import { Wireplumber } from "./scripts/volume";
|
||||||
|
import { Windows } from "./windows";
|
||||||
|
import { Time, timeout } from "astal/time";
|
||||||
|
|
||||||
|
let osdTimer: (Time|undefined);
|
||||||
export const astalInstanceName = "astal"
|
|
||||||
|
|
||||||
App.start({
|
App.start({
|
||||||
instanceName: astalInstanceName || "astal",
|
instanceName: "astal",
|
||||||
requestHandler(request: string, res: (result: any) => void) {
|
requestHandler(request: string, res: (result: any) => void) {
|
||||||
console.log(`[LOG] Arguments received: ${request}`)
|
console.log(`[LOG] Arguments received: ${request}`)
|
||||||
res(handleArguments(request));
|
res(handleArguments(request));
|
||||||
},
|
},
|
||||||
main() {
|
main() {
|
||||||
console.log(`[LOG] Initialized astal instance as: ${ astalInstanceName || "astal" }`);
|
console.log(`[LOG] Initialized astal instance as: ${ App.instanceName || "astal" }`);
|
||||||
console.log(`[LOG] Running Stylesheet handler`);
|
console.log(`[LOG] Running Stylesheet handler`);
|
||||||
runStyleHandler();
|
runStyleHandler();
|
||||||
//console.log(`[LOG] Starting to monitor scripts to automatically reload instance`);
|
//console.log(`[LOG] Starting to monitor scripts to automatically reload instance`);
|
||||||
//monitorPaths(); // Only for debugging purposes(testing new widgets and stuff)
|
//monitorPaths(); // Only for debugging purposes(testing new widgets and stuff)
|
||||||
|
|
||||||
|
Wireplumber.getDefault().getDefaultSink().connect("notify::volume", () =>
|
||||||
|
!Windows.isVisible(ControlCenter) && triggerOSD(OSDModes.SINK));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Windows list
|
function triggerOSD(osdModeParam: OSDModes) {
|
||||||
export function getWindowsMap(): Object {
|
setOSDMode(osdModeParam);
|
||||||
return {
|
|
||||||
"bar": Bar,
|
Windows.open(OSD);
|
||||||
"osd": OSD,
|
if(!osdTimer) {
|
||||||
"control-center": ControlCenter,
|
osdTimer = timeout(3000, () => {
|
||||||
//"floating-notifications": FloatingNotifications
|
Windows.close(OSD);
|
||||||
};
|
osdTimer = undefined;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
osdTimer.cancel();
|
||||||
|
osdTimer = timeout(3000, () => {
|
||||||
|
Windows.close(OSD);
|
||||||
|
osdTimer = undefined;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+56
-18
@@ -1,5 +1,7 @@
|
|||||||
import { Windows } from "./windows";
|
import { Gtk } from "astal/gtk3";
|
||||||
|
import { Windows } from "../windows";
|
||||||
import { restartInstance } from "./reload-handler";
|
import { restartInstance } from "./reload-handler";
|
||||||
|
import { Wireplumber } from "./volume";
|
||||||
|
|
||||||
export function handleArguments(request: string): any {
|
export function handleArguments(request: string): any {
|
||||||
const args: Array<string> = request.split(" ");
|
const args: Array<string> = request.split(" ");
|
||||||
@@ -13,8 +15,11 @@ export function handleArguments(request: string): any {
|
|||||||
case "h":
|
case "h":
|
||||||
return getHelp(); // stop it, get some help
|
return getHelp(); // stop it, get some help
|
||||||
|
|
||||||
|
case "volume":
|
||||||
|
return handleVolumeArgs(args);
|
||||||
|
|
||||||
case "reload":
|
case "reload":
|
||||||
restartInstance({ log: true, instanceName: "astal" });
|
restartInstance({ log: false, instanceName: "astal" });
|
||||||
return "Reloading instance..."
|
return "Reloading instance..."
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@@ -23,48 +28,80 @@ export function handleArguments(request: string): any {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Didn't want to bloat the switch statement, so I just separated it into functions
|
// Didn't want to bloat the switch statement, so I just separated it into functions
|
||||||
export function handleWindowArgs(args: Array<string>): string {
|
function handleWindowArgs(args: Array<string>): string {
|
||||||
const windows = Windows.getDefault().getWindows();
|
const specifiedWindow: (Gtk.Window|undefined) = Windows.getWindow(args[1]);
|
||||||
const window = windows[args[1] as never];
|
|
||||||
|
|
||||||
if(args[1] == undefined || args[1] == "")
|
if(!specifiedWindow)
|
||||||
return "Window argument not specified!";
|
return "Window argument not specified!";
|
||||||
|
|
||||||
if(!Object.hasOwn(windows, args[1]!))
|
if(!Windows.getList().has(args[1]))
|
||||||
return `Window "${args[1]}" not found windows list!`
|
return `Name "${args[1]}" not found windows map! Make sure to add new Windows on the Map!`
|
||||||
|
|
||||||
switch(args[0]) {
|
switch(args[0]) {
|
||||||
case "open":
|
case "open":
|
||||||
if(!Windows.getDefault().isVisible(window)) {
|
if(!Windows.isVisible(specifiedWindow)) {
|
||||||
Windows.getDefault().open(window);
|
Windows.open(specifiedWindow);
|
||||||
return `Setting visibility of window "${args[1]}" to true`;
|
return `Setting visibility of window "${args[1]}" to true`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return `Window is already open, ignored`;
|
return `Window is already open, ignored`;
|
||||||
|
|
||||||
case "close":
|
case "close":
|
||||||
if(Windows.getDefault().isVisible(window)) {
|
if(Windows.isVisible(specifiedWindow)) {
|
||||||
Windows.getDefault().close(window);
|
Windows.close(specifiedWindow);
|
||||||
return `Setting visibility of window "${args[1]}" to false`
|
return `Setting visibility of window "${args[1]}" to false`
|
||||||
}
|
}
|
||||||
|
|
||||||
return `Window is already closed, ignored`
|
return `Window is already closed, ignored`
|
||||||
|
|
||||||
case "toggle":
|
case "toggle":
|
||||||
if(!Windows.getDefault().isVisible(window)) {
|
if(!Windows.isVisible(specifiedWindow)) {
|
||||||
Windows.getDefault().open(window);
|
Windows.open(specifiedWindow);
|
||||||
return `Toggle opening window "${args[1]}"`;
|
return `Toggle opening window "${args[1]}"`;
|
||||||
}
|
}
|
||||||
|
|
||||||
Windows.getDefault().close(window);
|
Windows.close(specifiedWindow);
|
||||||
return `Toggle closing window "${args[1]}"`
|
return `Toggle closing window "${args[1]}"`
|
||||||
}
|
}
|
||||||
|
|
||||||
return "Couldn't handle window management arguments"
|
return "Couldn't handle window management arguments"
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getHelp(): string {
|
function handleVolumeArgs(args: Array<string>) {
|
||||||
return `Manage Astal Windows and do more stuff. From
|
if(!args[1])
|
||||||
|
return `Please specify what you want to do!\n\n${volumeHelp()}`
|
||||||
|
|
||||||
|
if(!/(sink|source)\-mute/.test(args[1]) && !args[2])
|
||||||
|
return `You forgot to add a value to be set!`;
|
||||||
|
|
||||||
|
const command: Array<string> = args[1].split('-');
|
||||||
|
|
||||||
|
switch(command[1]) {
|
||||||
|
case "set":
|
||||||
|
return `Done! Set ${command[0]} volume to ${args[2]}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return `Couldn't resolve arguments! "${args.join(' ').replace(new RegExp(`^${args[0]}`), "")}"`;
|
||||||
|
|
||||||
|
function volumeHelp(): string {
|
||||||
|
return `
|
||||||
|
Control speaker and microphone volumes easily!
|
||||||
|
Options:
|
||||||
|
sink-set [number]: set sink(speaker) volume with [number], 0 to ${Wireplumber.getDefault().getMaxSinkVolume()}.
|
||||||
|
sink-mute: toggle mute for the sink(speaker) device.
|
||||||
|
sink-increase [number]: increases sink(speaker) volume with [number].
|
||||||
|
sink-decrease [number]: decreases sink(speaker) volume with [number].
|
||||||
|
source-set [number]: set source(microphone) volume with [number], 0 to ${Wireplumber.getDefault().getMaxSourceVolume()}.
|
||||||
|
source-mute: toggle mute for the source(microphone) device.
|
||||||
|
source-increase [number]: increases source(microphone) volume with [number].
|
||||||
|
source-decrease [number]: decreases source(microphone) volume with [number]
|
||||||
|
`.trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getHelp(): string {
|
||||||
|
return `
|
||||||
|
Manage Astal Windows and do more stuff. From
|
||||||
retrozinndev's Hyprland Dots, using Astal and AGS by Aylur.
|
retrozinndev's Hyprland Dots, using Astal and AGS by Aylur.
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
@@ -75,5 +112,6 @@ Options:
|
|||||||
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.
|
2024 (c) retrozinndev's Hyprland-Dots, licensed under the MIT License.
|
||||||
https://github.com/retrozinndev/Hyprland-Dots`
|
https://github.com/retrozinndev/Hyprland-Dots
|
||||||
|
`.trim();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import AstalNotifd from "gi://AstalNotifd";
|
import AstalNotifd from "gi://AstalNotifd";
|
||||||
import { Windows } from "./windows";
|
|
||||||
import { timeout } from "astal/time";
|
import { timeout } from "astal/time";
|
||||||
|
|
||||||
const notifd: AstalNotifd.Notifd = new AstalNotifd.Notifd({
|
const notifd: AstalNotifd.Notifd = new AstalNotifd.Notifd({
|
||||||
@@ -7,13 +6,10 @@ const notifd: AstalNotifd.Notifd = new AstalNotifd.Notifd({
|
|||||||
dontDisturb: false
|
dontDisturb: false
|
||||||
});
|
});
|
||||||
|
|
||||||
const windows = Windows.getDefault();
|
export let notifications: Array<AstalNotifd.Notification> = getNotifd().notifications;
|
||||||
|
|
||||||
export let notifications: Array<AstalNotifd.Notification> = [];
|
|
||||||
export let notificationHistory: Array<AstalNotifd.Notification> = [];
|
export let notificationHistory: Array<AstalNotifd.Notification> = [];
|
||||||
|
|
||||||
notifd.connect("notified", (_source: AstalNotifd.Notifd, id: number, _replaced: boolean) => {
|
notifd.connect("notified", (_source: AstalNotifd.Notifd, id: number, _replaced: boolean) => {
|
||||||
windows.isVisible(windows.getWindows().floating_notifications) && windows.open(windows.getWindows().floating_notifications);
|
|
||||||
addNotification(getNotifd().get_notification(id));
|
addNotification(getNotifd().get_notification(id));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { monitorFile, Process } from "astal";
|
import { monitorFile, Process } from "astal";
|
||||||
import { astalInstanceName } from "../app";
|
|
||||||
import { getUserDirs } from "./user";
|
import { getUserDirs } from "./user";
|
||||||
|
import { App } from "astal/gtk3";
|
||||||
|
|
||||||
const monitoringPaths = [ "./scripts", "./window", "./app.ts", "env.d.ts" ];
|
const monitoringPaths = [ "./scripts", "./window", "./app.ts", "env.d.ts" ];
|
||||||
|
|
||||||
@@ -12,7 +12,7 @@ export interface InstanceProps {
|
|||||||
export function restartInstance(props: InstanceProps = { instanceName: "astal", log: false }): void {
|
export function restartInstance(props: InstanceProps = { instanceName: "astal", log: false }): void {
|
||||||
Process.exec_async(`astal -q ${props.instanceName}`, () => {});
|
Process.exec_async(`astal -q ${props.instanceName}`, () => {});
|
||||||
Process.exec_async(`ags run ${ props.log && `--log-file
|
Process.exec_async(`ags run ${ props.log && `--log-file
|
||||||
${ getUserDirs().cache}/ags-${ astalInstanceName || "astal" }.log` }`.replaceAll('\n', ' ').trim(),
|
${ getUserDirs().cache}/ags-${ App.instanceName || "astal" }.log` }`.replaceAll('\n', ' ').trim(),
|
||||||
() => {}
|
() => {}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -22,7 +22,7 @@ export function monitorPaths(): void {
|
|||||||
monitorFile(
|
monitorFile(
|
||||||
path,
|
path,
|
||||||
() => restartInstance({
|
() => restartInstance({
|
||||||
instanceName: astalInstanceName || "astal",
|
instanceName: App.instanceName || "astal",
|
||||||
log: true
|
log: true
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|||||||
+34
-12
@@ -1,23 +1,45 @@
|
|||||||
|
import { GObject } from "astal";
|
||||||
import AstalWp from "gi://AstalWp";
|
import AstalWp from "gi://AstalWp";
|
||||||
|
|
||||||
export class Wireplumber {
|
export const Wireplumber = GObject.registerClass({
|
||||||
private astalWireplumber: (AstalWp.Wp|null) = AstalWp.get_default();
|
GTypeName: "Wireplumber",
|
||||||
private defaultSink: AstalWp.Endpoint = this.astalWireplumber!.get_default_speaker()!;
|
Signals: {}
|
||||||
private defaultSource: AstalWp.Endpoint = this.astalWireplumber!.get_default_microphone()!;
|
}, class WireplumberClass extends GObject.Object {
|
||||||
private static inst: Wireplumber = new Wireplumber();
|
private static astalWireplumber: (AstalWp.Wp|null) = AstalWp.get_default();
|
||||||
|
private static inst: WireplumberClass;
|
||||||
|
|
||||||
|
private defaultSink: AstalWp.Endpoint = WireplumberClass.astalWireplumber!.get_default_speaker()!;
|
||||||
|
private defaultSource: AstalWp.Endpoint = WireplumberClass.astalWireplumber!.get_default_microphone()!;
|
||||||
|
|
||||||
private maxSinkVolume: number = 100;
|
private maxSinkVolume: number = 100;
|
||||||
private maxSourceVolume: number = 100;
|
private maxSourceVolume: number = 100;
|
||||||
|
|
||||||
constructor() {
|
_init(...props: any[]) {
|
||||||
if(!this.astalWireplumber)
|
super._init(props);
|
||||||
|
|
||||||
|
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", {
|
||||||
cause: "Wireplumber library not found"
|
cause: "Wireplumber library not found"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public static getDefault(): Wireplumber {
|
public static getDefault(): WireplumberClass {
|
||||||
return Wireplumber.inst;
|
if(!WireplumberClass.inst)
|
||||||
|
WireplumberClass.inst = new WireplumberClass();
|
||||||
|
|
||||||
|
return WireplumberClass.inst;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static getWireplumber(): AstalWp.Wp {
|
||||||
|
return WireplumberClass.astalWireplumber!;
|
||||||
|
}
|
||||||
|
|
||||||
|
public getMaxSinkVolume(): number {
|
||||||
|
return this.maxSinkVolume;
|
||||||
|
}
|
||||||
|
|
||||||
|
public getMaxSourceVolume(): number {
|
||||||
|
return this.maxSourceVolume;
|
||||||
}
|
}
|
||||||
|
|
||||||
public getDefaultSink(): AstalWp.Endpoint {
|
public getDefaultSink(): AstalWp.Endpoint {
|
||||||
@@ -29,11 +51,11 @@ export class Wireplumber {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public getSinkVolume(): number {
|
public getSinkVolume(): number {
|
||||||
return this.getDefaultSink().get_volume() * 100;
|
return Math.floor(this.getDefaultSink().get_volume() * 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
public getSourceVolume(): number {
|
public getSourceVolume(): number {
|
||||||
return this.getDefaultSource().get_volume() * 100;
|
return Math.floor(this.getDefaultSource().get_volume() * 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
public setSinkVolume(newSinkVolume: number): void {
|
public setSinkVolume(newSinkVolume: number): void {
|
||||||
@@ -123,4 +145,4 @@ export class Wireplumber {
|
|||||||
|
|
||||||
return this.muteSource();
|
return this.muteSource();
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
|||||||
@@ -1,34 +0,0 @@
|
|||||||
// get open windows / interact with windows(e.g.: close, open or toggle)
|
|
||||||
|
|
||||||
import { Widget } from "astal/gtk3";
|
|
||||||
import { getWindowsMap } from "../app";
|
|
||||||
|
|
||||||
export class Windows {
|
|
||||||
private static inst: Windows = new Windows();
|
|
||||||
|
|
||||||
private readonly windows = getWindowsMap();
|
|
||||||
|
|
||||||
public static getDefault(): Windows {
|
|
||||||
return Windows.inst;
|
|
||||||
}
|
|
||||||
|
|
||||||
public getWindows(): typeof this.windows {
|
|
||||||
return this.windows;
|
|
||||||
}
|
|
||||||
|
|
||||||
public open(window: Widget.Window): void {
|
|
||||||
window.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
public isVisible(window: Widget.Window): boolean {
|
|
||||||
return window.get_visible();
|
|
||||||
}
|
|
||||||
|
|
||||||
public close(window: Widget.Window): void {
|
|
||||||
window.hide();
|
|
||||||
}
|
|
||||||
|
|
||||||
public toggle(window: Widget.Window): void {
|
|
||||||
window.is_visible() ? this.close(window) : this.open(window);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+3
-2
@@ -7,6 +7,7 @@
|
|||||||
* {
|
* {
|
||||||
all: unset;
|
all: unset;
|
||||||
transition: 120ms linear;
|
transition: 120ms linear;
|
||||||
|
color: color.adjust($color: wal.$foreground, $lightness: 15%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.button-row {
|
.button-row {
|
||||||
@@ -36,12 +37,12 @@ menu {
|
|||||||
background: wal.$background;
|
background: wal.$background;
|
||||||
border-radius: 14px;
|
border-radius: 14px;
|
||||||
|
|
||||||
& > separator {
|
& separator {
|
||||||
margin: 0 4px;
|
margin: 0 4px;
|
||||||
color: wal.$background;
|
color: wal.$background;
|
||||||
}
|
}
|
||||||
|
|
||||||
& > menuitem {
|
& menuitem {
|
||||||
padding: 8px 0px;
|
padding: 8px 0px;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
|
|||||||
+18
-12
@@ -1,3 +1,4 @@
|
|||||||
|
@use "sass:color";
|
||||||
@use "./wal";
|
@use "./wal";
|
||||||
@use "./mixins";
|
@use "./mixins";
|
||||||
|
|
||||||
@@ -5,7 +6,7 @@
|
|||||||
padding: 6px;
|
padding: 6px;
|
||||||
padding-bottom: 0px;
|
padding-bottom: 0px;
|
||||||
|
|
||||||
& * {
|
& {
|
||||||
button {
|
button {
|
||||||
padding: 6px 8px;
|
padding: 6px 8px;
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
@@ -73,9 +74,9 @@
|
|||||||
& > .text-content {
|
& > .text-content {
|
||||||
& > .class {
|
& > .class {
|
||||||
font-size: 9px;
|
font-size: 9px;
|
||||||
font-weight: 500;
|
|
||||||
font-family: monospace;
|
font-family: monospace;
|
||||||
color: adjust-hue($color: wal.$foreground, $degrees: 100deg);
|
font-weight: 600;
|
||||||
|
color: color.adjust($color: wal.$foreground, $lightness: -11%);
|
||||||
margin-top: 1px;
|
margin-top: 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,7 +101,7 @@
|
|||||||
& > .media > box {
|
& > .media > box {
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
background: wal.$color1;
|
background: wal.$color1;
|
||||||
padding: 0 6px;
|
padding: 0 7px;
|
||||||
|
|
||||||
& .icon {
|
& .icon {
|
||||||
margin-right: 6px;
|
margin-right: 6px;
|
||||||
@@ -110,19 +111,25 @@
|
|||||||
|
|
||||||
&.reveal {
|
&.reveal {
|
||||||
& .media > box {
|
& .media > box {
|
||||||
|
transition: 50ms linear;
|
||||||
border-top-right-radius: 0;
|
border-top-right-radius: 0;
|
||||||
border-bottom-right-radius: 0;
|
border-bottom-right-radius: 0;
|
||||||
}
|
}
|
||||||
& .media-controls {
|
& .media-controls {
|
||||||
padding-left: 3px;
|
padding-left: 6px;
|
||||||
border-top-right-radius: 12px;
|
border-top-right-radius: 12px;
|
||||||
border-bottom-right-radius: 12px;
|
border-bottom-right-radius: 12px;
|
||||||
background: scale-color($color: wal.$color1, $lightness: -20%);
|
transition: unset;
|
||||||
|
background: linear-gradient(to left, color.adjust($color: wal.$color1, $lightness: -15%) 45px, wal.$color1);
|
||||||
|
|
||||||
& > button {
|
& > button {
|
||||||
margin: 0px 1px;
|
margin: 0px 1px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: wal.$color2;
|
||||||
|
}
|
||||||
|
|
||||||
&:first-child {
|
&:first-child {
|
||||||
border-top-left-radius: 12px;
|
border-top-left-radius: 12px;
|
||||||
border-bottom-left-radius: 12px;
|
border-bottom-left-radius: 12px;
|
||||||
@@ -167,6 +174,11 @@
|
|||||||
background: wal.$color1;
|
background: wal.$color1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
& .notification-bell {
|
||||||
|
padding-left: 10px;
|
||||||
|
padding-right: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
& > box {
|
& > box {
|
||||||
padding: 0 9px;
|
padding: 0 9px;
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
@@ -180,10 +192,4 @@
|
|||||||
margin-right: 4px;
|
margin-right: 4px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.cc-toggle button {
|
|
||||||
$padding-inline: 12px;
|
|
||||||
padding-left: $padding-inline;
|
|
||||||
padding-right: calc($padding-inline + 2px);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,23 +4,65 @@
|
|||||||
.control-center-container {
|
.control-center-container {
|
||||||
background: rgba(wal.$background, .65);
|
background: rgba(wal.$background, .65);
|
||||||
border-radius: 24px;
|
border-radius: 24px;
|
||||||
padding: 24px 22px;
|
padding: 20px 14px;
|
||||||
|
|
||||||
& {
|
& > box {
|
||||||
& button {
|
margin: 9px;
|
||||||
padding: 4px 6px;
|
|
||||||
|
&:first-child {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
& .quickactions {
|
||||||
|
& .hostname {
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
& .quickactions {
|
& .uptime {
|
||||||
background: wal.$color1;
|
font-size: 12px;
|
||||||
& .hostname {
|
}
|
||||||
font-size: 15px;
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
& .uptime {
|
& .button-row {
|
||||||
font-size: 12px;
|
& button {
|
||||||
|
padding: 4px 6px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
& .sliders {
|
||||||
|
padding: 2px 6px;
|
||||||
|
|
||||||
|
& > box {
|
||||||
|
margin: 8px 0;
|
||||||
|
|
||||||
|
&:first-child {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
icon {
|
||||||
|
background-size: 48px;
|
||||||
|
}
|
||||||
|
|
||||||
|
trough {
|
||||||
|
background: color.adjust($color: wal.$color1, $lightness: -20%);
|
||||||
|
min-height: .8em;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
trough highlight {
|
||||||
|
background: wal.$color1;
|
||||||
|
min-height: inherit;
|
||||||
|
border-radius: inherit;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
@use "sass:color";
|
||||||
|
@use "./wal";
|
||||||
|
|
||||||
|
@mixin reset-props {
|
||||||
|
all: unset;
|
||||||
|
transition: 120ms linear;
|
||||||
|
color: color.adjust($color: wal.$foreground, $lightness: -15%);
|
||||||
|
}
|
||||||
|
|||||||
+32
-24
@@ -1,37 +1,45 @@
|
|||||||
|
@use "sass:color";
|
||||||
@use "./wal";
|
@use "./wal";
|
||||||
|
|
||||||
.osd-window {
|
.osd {
|
||||||
all: unset;
|
background: color.change($color: wal.$background, $alpha: 65%);
|
||||||
|
padding: 14px 16px;
|
||||||
|
border-radius: 20px;
|
||||||
|
|
||||||
.osd {
|
.icon {
|
||||||
margin-bottom: 100px;
|
margin-right: 14px;
|
||||||
background: rgba($color: wal.$background, $alpha: .5);
|
font-size: 24px;
|
||||||
padding: 14px 16px;
|
}
|
||||||
border-radius: 20px;
|
|
||||||
|
|
||||||
.icon {
|
.volume {
|
||||||
margin-right: 14px;
|
margin-top: -6px;
|
||||||
font-size: 24px;
|
|
||||||
|
.device {
|
||||||
|
margin-bottom: 5px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
.volume {
|
levelbar {
|
||||||
margin-top: -6px;
|
trough block {
|
||||||
|
border-radius: 2px;
|
||||||
|
background: color.adjust($color: wal.$color1, $lightness: -36%);
|
||||||
|
|
||||||
.value {
|
&.empty {
|
||||||
margin-bottom: 5px;
|
border-radius: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
levelbar {
|
&.filled {
|
||||||
trough block {
|
padding: 3px 0;
|
||||||
border-radius: 6px;
|
background: wal.$color1;
|
||||||
background: wal.$background;
|
|
||||||
|
|
||||||
&.filled {
|
|
||||||
padding: 3px 0;
|
|
||||||
background: wal.$color1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.value {
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 400;
|
||||||
|
padding: 0 4px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+12
-6
@@ -1,6 +1,6 @@
|
|||||||
import { bind, Process } from "astal";
|
import { bind, Process } from "astal";
|
||||||
import { Widget } from "astal/gtk3";
|
import { Widget } from "astal/gtk3";
|
||||||
import AstalWp from "gi://AstalWp?version=0.1";
|
import AstalWp from "gi://AstalWp";
|
||||||
import { Wireplumber } from "../../scripts/volume";
|
import { Wireplumber } from "../../scripts/volume";
|
||||||
|
|
||||||
const wp = AstalWp.get_default();
|
const wp = AstalWp.get_default();
|
||||||
@@ -26,8 +26,8 @@ export function Audio() {
|
|||||||
} as Widget.LabelProps),
|
} as Widget.LabelProps),
|
||||||
new Widget.Label({
|
new Widget.Label({
|
||||||
className: "icon nf",
|
className: "icon nf",
|
||||||
label: bind(wp!.defaultSpeaker, "volume").as((volume: number) =>
|
label: bind(Wireplumber.getDefault().getDefaultSink(), "volume").as((volume: number) =>
|
||||||
Math.round(volume * 100).toString() + "%")
|
Math.floor(volume * 100) + "%")
|
||||||
} as Widget.LabelProps)
|
} as Widget.LabelProps)
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
@@ -46,12 +46,18 @@ export function Audio() {
|
|||||||
label: ""
|
label: ""
|
||||||
} as Widget.LabelProps),
|
} as Widget.LabelProps),
|
||||||
new Widget.Label({
|
new Widget.Label({
|
||||||
label: bind(wp!.defaultMicrophone, "volume").as((volume: number) =>
|
label: bind(Wireplumber.getDefault().getDefaultSource(), "volume").as((volume: number) =>
|
||||||
Math.round(volume * 100).toString() + "%")
|
Math.floor(volume * 100) + "%")
|
||||||
} as Widget.LabelProps)
|
} as Widget.LabelProps)
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
} as Widget.EventBoxProps)
|
} as Widget.EventBoxProps),
|
||||||
|
new Widget.Box({
|
||||||
|
className: "notification-bell",
|
||||||
|
child: new Widget.Label({
|
||||||
|
label: ""
|
||||||
|
} as Widget.LabelProps)
|
||||||
|
} as Widget.BoxProps)
|
||||||
]
|
]
|
||||||
} as Widget.BoxProps)
|
} as Widget.BoxProps)
|
||||||
} as Widget.EventBoxProps);
|
} as Widget.EventBoxProps);
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
import { Widget } from "astal/gtk3";
|
import { Widget } from "astal/gtk3";
|
||||||
import { getDateTime } from "../../scripts/time";
|
import { getDateTime } from "../../scripts/time";
|
||||||
import { GLib } from "astal";
|
import { GLib } from "astal";
|
||||||
|
import { Windows } from "../../windows";
|
||||||
|
import { CenterWindow } from "../../window/CenterWindow";
|
||||||
|
|
||||||
export function Clock(): JSX.Element {
|
export function Clock(): JSX.Element {
|
||||||
return new Widget.Box({
|
return new Widget.Box({
|
||||||
className: "clock",
|
className: "clock",
|
||||||
child: new Widget.Button({
|
child: new Widget.Button({
|
||||||
|
onClick: () => Windows.toggle(CenterWindow),
|
||||||
label: getDateTime().as((dateTime: GLib.DateTime) => {
|
label: getDateTime().as((dateTime: GLib.DateTime) => {
|
||||||
return dateTime.format("%A %d, %H:%M")
|
return dateTime.format("%A %d, %H:%M")
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import { Widget } from "astal/gtk3";
|
import { Widget } from "astal/gtk3";
|
||||||
import { Box, Button } from "astal/gtk3/widget";
|
|
||||||
import AstalHyprland from "gi://AstalHyprland";
|
import AstalHyprland from "gi://AstalHyprland";
|
||||||
import { tr } from "../../i18n/intl";
|
|
||||||
|
|
||||||
export function Logo() {
|
export function Logo() {
|
||||||
return new Widget.Box({
|
return new Widget.Box({
|
||||||
className: "logo",
|
className: "logo",
|
||||||
//tooltipText: tr("bar.logo.tooltip"),
|
//tooltipText: tr("bar.logo.tooltip"),
|
||||||
child:
|
child: new Widget.Button({
|
||||||
<Button onClick={ () => AstalHyprland.get_default().dispatch("exec", "anyrun") } label={""} />
|
onClick: () => AstalHyprland.get_default().dispatch("exec", "anyrun"),
|
||||||
|
label: ""
|
||||||
|
} as Widget.ButtonProps)
|
||||||
} as Widget.BoxProps);
|
} as Widget.BoxProps);
|
||||||
}
|
}
|
||||||
+10
-3
@@ -1,4 +1,4 @@
|
|||||||
import { bind } from "astal";
|
import { bind, Process } from "astal";
|
||||||
import { Gtk, Widget } from "astal/gtk3";
|
import { Gtk, Widget } from "astal/gtk3";
|
||||||
import AstalMpris from "gi://AstalMpris";
|
import AstalMpris from "gi://AstalMpris";
|
||||||
import { Separator, SeparatorProps } from "../Separator";
|
import { Separator, SeparatorProps } from "../Separator";
|
||||||
@@ -13,7 +13,7 @@ const playerIcons = {
|
|||||||
firefox: ''
|
firefox: ''
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Media(): JSX.Element {
|
export function Media(): Gtk.Widget {
|
||||||
const mediaControlsRevealer: Widget.Revealer = new Widget.Revealer({
|
const mediaControlsRevealer: Widget.Revealer = new Widget.Revealer({
|
||||||
transitionType: Gtk.RevealerTransitionType.SLIDE_RIGHT,
|
transitionType: Gtk.RevealerTransitionType.SLIDE_RIGHT,
|
||||||
transitionDuration: 260,
|
transitionDuration: 260,
|
||||||
@@ -24,6 +24,13 @@ export function Media(): JSX.Element {
|
|||||||
homogeneous: false,
|
homogeneous: false,
|
||||||
children: bind(mpris, "players").as((players: Array<AstalMpris.Player>) =>
|
children: bind(mpris, "players").as((players: Array<AstalMpris.Player>) =>
|
||||||
players[0] ? [
|
players[0] ? [
|
||||||
|
new Widget.Button({
|
||||||
|
className: "link",
|
||||||
|
label: "",
|
||||||
|
visible: bind(players[0], "metadata").as(metadata =>
|
||||||
|
metadata?.["xesam:url"] ? true : false),
|
||||||
|
onClick: () => Process.exec(`echo ${players[0].metadata.url}"`)
|
||||||
|
} as Widget.ButtonProps),
|
||||||
new Widget.Button({
|
new Widget.Button({
|
||||||
className: "previous",
|
className: "previous",
|
||||||
label: "",
|
label: "",
|
||||||
@@ -82,7 +89,7 @@ export function Media(): JSX.Element {
|
|||||||
label: bind(players[0], "artist").as((artist: string) => artist || "No Artist")
|
label: bind(players[0], "artist").as((artist: string) => artist || "No Artist")
|
||||||
} as Widget.LabelProps)
|
} as Widget.LabelProps)
|
||||||
] : new Widget.Label({
|
] : new Widget.Label({
|
||||||
label: "Crazy to think this widget didn't disappear yet!"
|
label: "Crazy to think this widget haven't disappeared yet!"
|
||||||
} as Widget.LabelProps)
|
} as Widget.LabelProps)
|
||||||
)
|
)
|
||||||
} as Widget.BoxProps),
|
} as Widget.BoxProps),
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ export const QuickActions: Widget.Box = new Widget.Box({
|
|||||||
orientation: Gtk.Orientation.VERTICAL,
|
orientation: Gtk.Orientation.VERTICAL,
|
||||||
halign: Gtk.Align.START,
|
halign: Gtk.Align.START,
|
||||||
hexpand: true,
|
hexpand: true,
|
||||||
|
className: "left",
|
||||||
children: [
|
children: [
|
||||||
new Widget.Label({
|
new Widget.Label({
|
||||||
className: "hostname",
|
className: "hostname",
|
||||||
@@ -77,7 +78,7 @@ export const QuickActions: Widget.Box = new Widget.Box({
|
|||||||
} as Widget.BoxProps),
|
} as Widget.BoxProps),
|
||||||
new Widget.Box({
|
new Widget.Box({
|
||||||
orientation: Gtk.Orientation.HORIZONTAL,
|
orientation: Gtk.Orientation.HORIZONTAL,
|
||||||
className: "button-row",
|
className: "right button-row",
|
||||||
halign: Gtk.Align.END,
|
halign: Gtk.Align.END,
|
||||||
hexpand: true,
|
hexpand: true,
|
||||||
children: [
|
children: [
|
||||||
|
|||||||
@@ -0,0 +1,54 @@
|
|||||||
|
import { bind } from "astal";
|
||||||
|
import { Gtk, Widget } from "astal/gtk3";
|
||||||
|
import { Wireplumber } from "../../scripts/volume";
|
||||||
|
|
||||||
|
export const Sliders: Gtk.Widget = new Widget.Box({
|
||||||
|
className: "sliders",
|
||||||
|
orientation: Gtk.Orientation.VERTICAL,
|
||||||
|
expand: true,
|
||||||
|
children: [
|
||||||
|
new Widget.Box({
|
||||||
|
className: "sink speaker",
|
||||||
|
children: [
|
||||||
|
new Widget.Icon({
|
||||||
|
icon: "audio-volume-high-symbolic"
|
||||||
|
} as Widget.IconProps),
|
||||||
|
new Widget.Slider({
|
||||||
|
drawValue: false,
|
||||||
|
hexpand: true,
|
||||||
|
value: bind(Wireplumber.getDefault().getDefaultSink(), "volume").as((volume: number) =>
|
||||||
|
Math.floor(volume * 100)),
|
||||||
|
max: Wireplumber.getDefault().getMaxSinkVolume(),
|
||||||
|
onDragged: (slider: Gtk.Scale) => Wireplumber.getDefault().setSinkVolume(slider.get_value())
|
||||||
|
} as Widget.SliderProps)
|
||||||
|
]
|
||||||
|
} as Widget.BoxProps),
|
||||||
|
new Widget.Box({
|
||||||
|
className: "source microphone",
|
||||||
|
children: [
|
||||||
|
new Widget.Icon({
|
||||||
|
icon: "microphone-sensitivity-high-symbolic"
|
||||||
|
} as Widget.IconProps),
|
||||||
|
new Widget.Slider({
|
||||||
|
drawValue: false,
|
||||||
|
hexpand: true,
|
||||||
|
value: bind(Wireplumber.getDefault().getDefaultSource(), "volume").as((volume: number) =>
|
||||||
|
Math.floor(volume * 100)),
|
||||||
|
max: Wireplumber.getDefault().getMaxSourceVolume(),
|
||||||
|
onDragged: (slider: Gtk.Scale) => Wireplumber.getDefault().setSourceVolume(slider.get_value())
|
||||||
|
} as Widget.SliderProps)
|
||||||
|
]
|
||||||
|
} as Widget.BoxProps),
|
||||||
|
/*new Widget.Box({
|
||||||
|
className: "brightness screen",
|
||||||
|
children: [
|
||||||
|
new Widget.Slider({
|
||||||
|
drawValue: false,
|
||||||
|
hexpand: true,
|
||||||
|
value: 216,
|
||||||
|
max: 255
|
||||||
|
} as Widget.SliderProps)
|
||||||
|
]
|
||||||
|
} as Widget.BoxProps)*/
|
||||||
|
]
|
||||||
|
} as Widget.BoxProps);
|
||||||
@@ -9,7 +9,6 @@ import { FocusedClient } from "../widget/bar/FocusedClient";
|
|||||||
import { Media } from "../widget/bar/Media";
|
import { Media } from "../widget/bar/Media";
|
||||||
|
|
||||||
export const Bar: Widget.Window = new Widget.Window({
|
export const Bar: Widget.Window = new Widget.Window({
|
||||||
className: "bar",
|
|
||||||
monitor: 0,
|
monitor: 0,
|
||||||
namespace: "top-bar",
|
namespace: "top-bar",
|
||||||
anchor: Astal.WindowAnchor.TOP,
|
anchor: Astal.WindowAnchor.TOP,
|
||||||
@@ -18,8 +17,6 @@ export const Bar: Widget.Window = new Widget.Window({
|
|||||||
canFocus: false,
|
canFocus: false,
|
||||||
visible: true,
|
visible: true,
|
||||||
widthRequest: Gdk.Screen.get_default()?.get_monitor_geometry(0)?.width,
|
widthRequest: Gdk.Screen.get_default()?.get_monitor_geometry(0)?.width,
|
||||||
hexpand: false,
|
|
||||||
vexpand: false,
|
|
||||||
child: new Widget.Box({
|
child: new Widget.Box({
|
||||||
className: "bar-container",
|
className: "bar-container",
|
||||||
child: new Widget.CenterBox({
|
child: new Widget.CenterBox({
|
||||||
|
|||||||
@@ -0,0 +1,58 @@
|
|||||||
|
import { Astal, Gtk, Widget } from "astal/gtk3";
|
||||||
|
import { GLib } from "astal";
|
||||||
|
|
||||||
|
import { getDateTime } from "../scripts/time";
|
||||||
|
|
||||||
|
|
||||||
|
export const CenterWindow: Widget.Window = new Widget.Window({
|
||||||
|
className: "center-window",
|
||||||
|
namespace: "center-window",
|
||||||
|
canFocus: true,
|
||||||
|
monitor: 0,
|
||||||
|
layer: Astal.Layer.OVERLAY,
|
||||||
|
exclusivity: Astal.Exclusivity.NORMAL,
|
||||||
|
visible: false,
|
||||||
|
height_request: 600,
|
||||||
|
anchor: Astal.WindowAnchor.TOP,
|
||||||
|
child: new Widget.Box({
|
||||||
|
className: "center-window-container",
|
||||||
|
children: [
|
||||||
|
new Widget.Box({
|
||||||
|
className: "vertical left",
|
||||||
|
orientation: Gtk.Orientation.VERTICAL,
|
||||||
|
width_request: 300,
|
||||||
|
children: [
|
||||||
|
new Widget.Box({
|
||||||
|
className: "top time date",
|
||||||
|
orientation: Gtk.Orientation.VERTICAL,
|
||||||
|
children: [
|
||||||
|
new Widget.Label({
|
||||||
|
className: "time",
|
||||||
|
label: getDateTime().as((dateTime: GLib.DateTime) =>
|
||||||
|
dateTime.format("%H:%M"))
|
||||||
|
} as Widget.LabelProps),
|
||||||
|
new Widget.Label({
|
||||||
|
className: "date",
|
||||||
|
label: getDateTime().as((dateTime: GLib.DateTime) =>
|
||||||
|
dateTime.format("%A, %B %d %Y"))
|
||||||
|
} as Widget.LabelProps)
|
||||||
|
]
|
||||||
|
} as Widget.BoxProps)
|
||||||
|
]
|
||||||
|
} as Widget.BoxProps),
|
||||||
|
new Widget.Box({
|
||||||
|
className: "vertical right",
|
||||||
|
children: [
|
||||||
|
new Widget.Box({
|
||||||
|
className: "calendar-box",
|
||||||
|
child: new Gtk.Calendar({
|
||||||
|
show_heading: true,
|
||||||
|
show_day_names: true,
|
||||||
|
show_week_numbers: false
|
||||||
|
} as Gtk.Calendar.ConstructorProps)
|
||||||
|
} as Widget.BoxProps)
|
||||||
|
]
|
||||||
|
} as Widget.BoxProps)
|
||||||
|
]
|
||||||
|
} as Widget.BoxProps)
|
||||||
|
} as Widget.WindowProps);
|
||||||
@@ -1,12 +1,14 @@
|
|||||||
import { Astal, Gdk, Gtk, Widget } from "astal/gtk3";
|
import { Astal, Gtk, Widget } from "astal/gtk3";
|
||||||
import { QuickActions } from "../widget/control-center/QuickActions";
|
import { QuickActions } from "../widget/control-center/QuickActions";
|
||||||
import { Tiles } from "../widget/control-center/Tiles";
|
import { Tiles } from "../widget/control-center/Tiles";
|
||||||
|
import { Sliders } from "../widget/control-center/Sliders";
|
||||||
|
|
||||||
const widgetsContainer: Widget.Box = new Widget.Box({
|
const widgetsContainer: Widget.Box = new Widget.Box({
|
||||||
className: "control-center-container",
|
className: "control-center-container",
|
||||||
orientation: Gtk.Orientation.VERTICAL,
|
orientation: Gtk.Orientation.VERTICAL,
|
||||||
} as Widget.BoxProps,
|
} as Widget.BoxProps,
|
||||||
QuickActions,
|
QuickActions,
|
||||||
|
Sliders,
|
||||||
Tiles);
|
Tiles);
|
||||||
|
|
||||||
export const ControlCenter: Widget.Window = new Widget.Window({
|
export const ControlCenter: Widget.Window = new Widget.Window({
|
||||||
@@ -15,10 +17,10 @@ export const ControlCenter: Widget.Window = new Widget.Window({
|
|||||||
canFocus: true,
|
canFocus: true,
|
||||||
exclusivity: Astal.Exclusivity.NORMAL,
|
exclusivity: Astal.Exclusivity.NORMAL,
|
||||||
anchor: Astal.WindowAnchor.TOP | Astal.WindowAnchor.RIGHT,
|
anchor: Astal.WindowAnchor.TOP | Astal.WindowAnchor.RIGHT,
|
||||||
|
layer: Astal.Layer.OVERLAY,
|
||||||
margin_top: 10,
|
margin_top: 10,
|
||||||
margin_right: 10,
|
margin_right: 10,
|
||||||
width_request: 450,
|
width_request: 450,
|
||||||
height_request: 400,
|
|
||||||
monitor: 0,
|
monitor: 0,
|
||||||
visible: false
|
visible: false
|
||||||
} as Widget.WindowProps, widgetsContainer);
|
} as Widget.WindowProps, widgetsContainer);
|
||||||
|
|||||||
@@ -1,58 +1,47 @@
|
|||||||
import { Astal, Gtk, Widget } from "astal/gtk3";
|
import { Astal, Gtk, Widget } from "astal/gtk3";
|
||||||
import { getNotifd, removeNotification } from "../scripts/notification-handler";
|
import { getNotifd, notifications, removeNotification } from "../scripts/notification-handler";
|
||||||
import { notifications as popupNotifications } from "../scripts/notification-handler";
|
|
||||||
import AstalNotifd from "gi://AstalNotifd";
|
import AstalNotifd from "gi://AstalNotifd";
|
||||||
|
import { bind } from "astal";
|
||||||
|
|
||||||
export const FloatingNotifications: Widget.Window = FloatingNotificationsWindow();
|
export const FloatingNotifications: Widget.Window = new Widget.Window({
|
||||||
let gtkNotificationPopups: Array<Widget.Box> = [];
|
className: "floating-notifications",
|
||||||
|
namespace: "floating-notifications",
|
||||||
function FloatingNotificationsWindow(): Widget.Window {
|
canFocus: false,
|
||||||
|
anchor: Astal.WindowAnchor.RIGHT,
|
||||||
const notificationsBox = new Widget.Box({
|
monitor: 0,
|
||||||
|
layer: Astal.Layer.OVERLAY,
|
||||||
|
visible: false,
|
||||||
|
exclusivity: Astal.Exclusivity.NORMAL,
|
||||||
|
child: new Widget.Box({
|
||||||
className: "notifications",
|
className: "notifications",
|
||||||
orientation: Gtk.Orientation.VERTICAL,
|
orientation: Gtk.Orientation.VERTICAL,
|
||||||
homogeneous: false
|
homogeneous: false,
|
||||||
} as Widget.BoxProps);
|
children: bind(getNotifd(), "notifications").as(() => {
|
||||||
|
notifications.length > 0 ? notifications.map((notification: AstalNotifd.Notification) =>
|
||||||
getNotifd().connect("notified", () => {
|
new Widget.Box({
|
||||||
for(let i = 0; i < popupNotifications.length; i++) {
|
className: "notification",
|
||||||
const notification: AstalNotifd.Notification = popupNotifications[i];
|
homogeneous: false,
|
||||||
|
children: [
|
||||||
gtkNotificationPopups[i] = new Widget.Box({
|
new Widget.Box({
|
||||||
className: "notification",
|
className: "top",
|
||||||
homogeneous: false,
|
orientation: Gtk.Orientation.HORIZONTAL,
|
||||||
children: [
|
hexpand: true,
|
||||||
new Widget.Box({
|
vexpand: false,
|
||||||
className: "top",
|
children: [
|
||||||
orientation: Gtk.Orientation.HORIZONTAL,
|
new Widget.Label({
|
||||||
hexpand: true,
|
className: "app-name",
|
||||||
vexpand: false,
|
halign: Gtk.Align.START,
|
||||||
children: [
|
label: notification.appName || "Unknown Application"
|
||||||
new Widget.Label({
|
} as Widget.LabelProps),
|
||||||
className: "app-name",
|
new Widget.Button({
|
||||||
halign: Gtk.Align.START,
|
className: "close-button",
|
||||||
label: notification.appName || "Unknown Application"
|
onClick: () => removeNotification(notification.id)
|
||||||
} as Widget.LabelProps),
|
} as Widget.ButtonProps)
|
||||||
new Widget.Button({
|
]
|
||||||
className: "close-button",
|
} as Widget.BoxProps)
|
||||||
onClick: () => removeNotification(notification.id)
|
]
|
||||||
} as Widget.ButtonProps)
|
} as Widget.BoxProps)
|
||||||
]
|
) : new Widget.Box({})
|
||||||
} as Widget.BoxProps)
|
})
|
||||||
]
|
} as Widget.BoxProps)
|
||||||
} as Widget.BoxProps);
|
} as Widget.WindowProps);
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return new Widget.Window({
|
|
||||||
className: "window floating-notifications",
|
|
||||||
namespace: "floating-notifications",
|
|
||||||
canFocus: false,
|
|
||||||
anchor: Astal.WindowAnchor.RIGHT,
|
|
||||||
monitor: 0,
|
|
||||||
layer: Astal.Layer.OVERLAY,
|
|
||||||
visible: false,
|
|
||||||
exclusivity: Astal.Exclusivity.NORMAL,
|
|
||||||
child: notificationsBox
|
|
||||||
} as Widget.WindowProps);
|
|
||||||
}
|
|
||||||
|
|||||||
+77
-45
@@ -1,48 +1,80 @@
|
|||||||
import { bind } from "astal";
|
import { bind, Binding, Variable } from "astal";
|
||||||
import { Astal, Gtk, Widget } from "astal/gtk3";
|
import { Astal, Gtk, Widget } from "astal/gtk3";
|
||||||
import { Time } from "astal/time";
|
import { Wireplumber } from "../scripts/volume";
|
||||||
import AstalWp from "gi://AstalWp";
|
import AstalWp from "gi://AstalWp?version=0.1";
|
||||||
import { Windows } from "../scripts/windows";
|
|
||||||
|
|
||||||
export const OSD: Widget.Window = OSDWindow();
|
export enum OSDModes {
|
||||||
|
SINK,
|
||||||
function OSDWindow() {
|
BRIGHTNESS
|
||||||
return new Widget.Window({
|
|
||||||
className: "osd-window",
|
|
||||||
namespace: "osd",
|
|
||||||
layer: Astal.Layer.OVERLAY,
|
|
||||||
anchor: Astal.WindowAnchor.BOTTOM,
|
|
||||||
canFocus: false,
|
|
||||||
monitor: 0,
|
|
||||||
visible: false,
|
|
||||||
child: new Widget.Box({
|
|
||||||
className: "osd",
|
|
||||||
children: [
|
|
||||||
new Widget.Label({
|
|
||||||
className: "icon",
|
|
||||||
label: "",
|
|
||||||
css: ".icon { color: white; }"
|
|
||||||
} as Widget.LabelProps),
|
|
||||||
new Widget.Box({
|
|
||||||
className: "volume",
|
|
||||||
orientation: Gtk.Orientation.VERTICAL,
|
|
||||||
valign: Gtk.Align.CENTER,
|
|
||||||
children: [
|
|
||||||
new Widget.Label({
|
|
||||||
className: "value",
|
|
||||||
label: bind(AstalWp.get_default()?.defaultSpeaker!, "volume").as((volume: number) => `${Math.round(volume * 100)}%`),
|
|
||||||
halign: Gtk.Align.CENTER
|
|
||||||
} as Widget.LabelProps),
|
|
||||||
new Widget.LevelBar({
|
|
||||||
className: "levelbar",
|
|
||||||
width_request: 120,
|
|
||||||
value: bind(AstalWp.get_default()?.defaultSpeaker!, "volume").as((volume: number) => Math.round(volume * 100)),
|
|
||||||
maxValue: 100,
|
|
||||||
halign: Gtk.Align.CENTER
|
|
||||||
} as Widget.LevelBarProps)
|
|
||||||
]
|
|
||||||
} as Widget.BoxProps)
|
|
||||||
]
|
|
||||||
} as Widget.BoxProps)
|
|
||||||
} as Widget.WindowProps);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let osdMode: Variable<OSDModes> = new Variable<OSDModes>(OSDModes.SINK);
|
||||||
|
let osdIcon: Binding<string | undefined> = osdMode().as((mode: OSDModes) => {
|
||||||
|
switch(mode) {
|
||||||
|
case OSDModes.SINK: return "";
|
||||||
|
case OSDModes.BRIGHTNESS: return "";
|
||||||
|
default: return "";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export function setOSDMode(newMode: OSDModes): void {
|
||||||
|
osdMode.set(newMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const OSD: Widget.Window = new Widget.Window({
|
||||||
|
namespace: "osd",
|
||||||
|
layer: Astal.Layer.OVERLAY,
|
||||||
|
anchor: Astal.WindowAnchor.BOTTOM,
|
||||||
|
canFocus: false,
|
||||||
|
margin_bottom: 80,
|
||||||
|
monitor: 0,
|
||||||
|
visible: false,
|
||||||
|
focusOnClick: false,
|
||||||
|
child: new Widget.Box({
|
||||||
|
className: "osd",
|
||||||
|
children: [
|
||||||
|
new Widget.Label({
|
||||||
|
className: "icon",
|
||||||
|
label: osdIcon
|
||||||
|
} as Widget.LabelProps),
|
||||||
|
new Widget.Box({
|
||||||
|
className: "volume",
|
||||||
|
orientation: Gtk.Orientation.VERTICAL,
|
||||||
|
valign: Gtk.Align.CENTER,
|
||||||
|
children: [
|
||||||
|
new Widget.Label({
|
||||||
|
className: "device",
|
||||||
|
label: bind(Wireplumber.getDefault().getDefaultSink(), "name").as((name: string) =>
|
||||||
|
name || "Speaker"),
|
||||||
|
halign: Gtk.Align.CENTER
|
||||||
|
} as Widget.LabelProps),
|
||||||
|
new Widget.Box({
|
||||||
|
vexpand: false,
|
||||||
|
expand: false,
|
||||||
|
children: [
|
||||||
|
new Widget.LevelBar({
|
||||||
|
className: "levelbar",
|
||||||
|
width_request: 120,
|
||||||
|
value: bind(Wireplumber.getDefault().getDefaultSink(), "volume").as((volume: number) =>
|
||||||
|
Math.floor(volume * 100)),
|
||||||
|
maxValue: bind(Wireplumber.getWireplumber(), "defaultSpeaker").as(() =>
|
||||||
|
Wireplumber.getDefault().getMaxSinkVolume()),
|
||||||
|
vexpand: false,
|
||||||
|
expand: false,
|
||||||
|
halign: Gtk.Align.CENTER
|
||||||
|
} as Widget.LevelBarProps),
|
||||||
|
/*new Widget.Label({
|
||||||
|
className: "value",
|
||||||
|
label: bind(Wireplumber.getDefault().getDefaultSink(), "volume").as((volume: number) =>
|
||||||
|
`${Math.floor(volume * 100)}%`),
|
||||||
|
vexpand: false,
|
||||||
|
expand: false,
|
||||||
|
halign: Gtk.Align.CENTER
|
||||||
|
} as Widget.LabelProps)*/
|
||||||
|
]
|
||||||
|
} as Widget.BoxProps)
|
||||||
|
]
|
||||||
|
} as Widget.BoxProps)
|
||||||
|
]
|
||||||
|
} as Widget.BoxProps)
|
||||||
|
} as Widget.WindowProps);
|
||||||
|
|||||||
@@ -0,0 +1,57 @@
|
|||||||
|
import { Gtk, Widget } from "astal/gtk3";
|
||||||
|
|
||||||
|
import { Bar } from "./window/Bar";
|
||||||
|
import { OSD } from "./window/OSD";
|
||||||
|
import { ControlCenter } from "./window/ControlCenter";
|
||||||
|
import { CenterWindow } from "./window/CenterWindow";
|
||||||
|
import { FloatingNotifications } from "./window/FloatingNotifications";
|
||||||
|
import { GObject } from "astal";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get open windows / interact with windows(e.g.: close, open or toggle)
|
||||||
|
*/
|
||||||
|
export const Windows = GObject.registerClass({
|
||||||
|
GTypeName: "Windows"
|
||||||
|
}, class WindowsClass extends GObject.Object {
|
||||||
|
private static windowsMap: Map<string, Gtk.Window> = new Map<string, Gtk.Window>();
|
||||||
|
|
||||||
|
static {
|
||||||
|
WindowsClass.windowsMap.set("bar", Bar);
|
||||||
|
WindowsClass.windowsMap.set("osd", OSD);
|
||||||
|
WindowsClass.windowsMap.set("control-center", ControlCenter);
|
||||||
|
WindowsClass.windowsMap.set("center-window", CenterWindow);
|
||||||
|
WindowsClass.windowsMap.set("floating-notifications", FloatingNotifications);
|
||||||
|
}
|
||||||
|
|
||||||
|
public _init(...args: any[]) {
|
||||||
|
super._init(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static setWindow(name: string, window: Gtk.Window): void {
|
||||||
|
WindowsClass.windowsMap.set(name, window);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static getWindow(name: string): (Gtk.Window|undefined) {
|
||||||
|
return WindowsClass.windowsMap.get(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static getList(): Map<string, Gtk.Window> {
|
||||||
|
return WindowsClass.windowsMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static open(window: Gtk.Window): void {
|
||||||
|
!WindowsClass.isVisible(window) && window.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static isVisible(window: Gtk.Window): boolean {
|
||||||
|
return window.get_visible();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static close(window: Gtk.Window): void {
|
||||||
|
WindowsClass.isVisible(window) && window.hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static toggle(window: Gtk.Window): void {
|
||||||
|
window.is_visible() ? WindowsClass.close(window) : WindowsClass.open(window);
|
||||||
|
}
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user