ags(bar,scripts,i18n): added i18n system(wip), changed some bar stuff and started doing control center
This commit is contained in:
@@ -0,0 +1,30 @@
|
||||
import AstalApps from "gi://AstalApps";
|
||||
|
||||
const astalApps: AstalApps.Apps = new AstalApps.Apps();
|
||||
let appsList: Array<AstalApps.Application> = astalApps.get_list();
|
||||
|
||||
export function getApps(): Array<AstalApps.Application> {
|
||||
return appsList;
|
||||
}
|
||||
|
||||
export function updateApps(): void {
|
||||
astalApps.reload();
|
||||
appsList = astalApps.get_list();
|
||||
}
|
||||
|
||||
export function getAppsByName(appName: string): (Array<AstalApps.Application>|undefined) {
|
||||
let found: Array<AstalApps.Application> = [];
|
||||
|
||||
getApps().map((app: AstalApps.Application) => {
|
||||
if(app.get_name().trim().toLowerCase() === appName.trim().toLowerCase()
|
||||
|| (app?.wmClass && app.wmClass.trim().toLowerCase() === appName.trim().toLowerCase()))
|
||||
found.push(app);
|
||||
});
|
||||
|
||||
return (found.length > 0 ? found : undefined);
|
||||
}
|
||||
|
||||
export function getAppIcon(appName: string): (string|undefined) {
|
||||
const found: (Array<AstalApps.Application>|undefined) = getAppsByName(appName);
|
||||
return found ? found[0]?.iconName : undefined;
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
import { Windows } from "./windows";
|
||||
import { restartInstance } from "./reload-handler";
|
||||
|
||||
export function handleArguments(request: string): any {
|
||||
const args: Array<string> = request.split(" ");
|
||||
switch(args[0]) {
|
||||
case "open":
|
||||
case "close":
|
||||
case "toggle":
|
||||
return handleWindowArgs(args);
|
||||
|
||||
case "help":
|
||||
case "h":
|
||||
return getHelp(); // stop it, get some help
|
||||
|
||||
case "reload":
|
||||
restartInstance({ log: true, instanceName: "astal" });
|
||||
return "Reloading instance..."
|
||||
|
||||
default:
|
||||
return "command not found! try checking help";
|
||||
}
|
||||
}
|
||||
|
||||
// Didn't want to bloat the switch statement, so I just separated it into functions
|
||||
export function handleWindowArgs(args: Array<string>): string {
|
||||
const windows = Windows.getDefault().getWindows();
|
||||
const window = windows[args[1] as never];
|
||||
|
||||
if(args[1] == undefined || args[1] == "")
|
||||
return "Window argument not specified!";
|
||||
|
||||
if(!Object.hasOwn(windows, args[1]!))
|
||||
return `Window "${args[1]}" not found windows list!`
|
||||
|
||||
switch(args[0]) {
|
||||
case "open":
|
||||
if(!Windows.getDefault().isVisible(window)) {
|
||||
Windows.getDefault().open(window);
|
||||
return `Setting visibility of window "${args[1]}" to true`;
|
||||
}
|
||||
|
||||
return `Window is already open, ignored`;
|
||||
|
||||
case "close":
|
||||
if(Windows.getDefault().isVisible(window)) {
|
||||
Windows.getDefault().close(window);
|
||||
return `Setting visibility of window "${args[1]}" to false`
|
||||
}
|
||||
|
||||
return `Window is already closed, ignored`
|
||||
|
||||
case "toggle":
|
||||
if(!Windows.getDefault().isVisible(window)) {
|
||||
Windows.getDefault().open(window);
|
||||
return `Toggle opening window "${args[1]}"`;
|
||||
}
|
||||
|
||||
Windows.getDefault().close(window);
|
||||
return `Toggle closing window "${args[1]}"`
|
||||
}
|
||||
|
||||
return "Couldn't handle window management arguments"
|
||||
}
|
||||
|
||||
export function getHelp(): string {
|
||||
return `Manage Astal Windows and do more stuff. From
|
||||
retrozinndev's Hyprland Dots, using Astal and AGS by Aylur.
|
||||
|
||||
Options:
|
||||
open [window_name]: sets specified window's visibility to true.
|
||||
close [window_name]: sets specified window's visibility to false.
|
||||
toggle [window_name]: toggles visibility of specified window.
|
||||
reload: creates a new astal instance and removes this one.
|
||||
help, -h, --help: shows this help message.
|
||||
|
||||
2024 (c) retrozinndev's Hyprland-Dots, licensed under the MIT License.
|
||||
https://github.com/retrozinndev/Hyprland-Dots`
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
import AstalNotifd from "gi://AstalNotifd";
|
||||
import { Windows } from "./windows";
|
||||
import { timeout } from "astal/time";
|
||||
|
||||
const notifd: AstalNotifd.Notifd = new AstalNotifd.Notifd({
|
||||
ignoreTimeout: false,
|
||||
dontDisturb: false
|
||||
});
|
||||
|
||||
const windows = Windows.getDefault();
|
||||
|
||||
export let notifications: Array<AstalNotifd.Notification> = [];
|
||||
export let notificationHistory: Array<AstalNotifd.Notification> = [];
|
||||
|
||||
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));
|
||||
});
|
||||
|
||||
function addNotification(notification: AstalNotifd.Notification) {
|
||||
prependArray(notifications, getNotifd().get_notification(notification.id));
|
||||
|
||||
// default timeout if undefined
|
||||
let notificationTimeout = 4000;
|
||||
|
||||
switch(notification.urgency) {
|
||||
case AstalNotifd.Urgency.LOW:
|
||||
notificationTimeout = 2000;
|
||||
break;
|
||||
case AstalNotifd.Urgency.NORMAL:
|
||||
notificationTimeout = 4000;
|
||||
break;
|
||||
}
|
||||
|
||||
notification.urgency !== AstalNotifd.Urgency.CRITICAL && timeout(notificationTimeout, () => {
|
||||
notificationTimeout--;
|
||||
if(notificationTimeout === 0) {
|
||||
removeNotification(notification.id);
|
||||
addToNotificationHistory(notification);
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
export function removeNotification(notificationId: number) {
|
||||
notifications = notifications.filter((notification: AstalNotifd.Notification) =>
|
||||
notification.id !== notificationId);
|
||||
}
|
||||
|
||||
function addToNotificationHistory(notification: AstalNotifd.Notification) {
|
||||
prependArray(notificationHistory, notification);
|
||||
}
|
||||
|
||||
export function removeFromNotificationHistory(notificationId: number) {
|
||||
notifications = notifications.filter((curNotification: AstalNotifd.Notification) =>
|
||||
curNotification.id !== notificationId);
|
||||
}
|
||||
|
||||
function prependArray(array: Array<any>, item: any) {
|
||||
let tmpArray = array;
|
||||
tmpArray.reverse();
|
||||
tmpArray.push(item);
|
||||
array = tmpArray.reverse();
|
||||
}
|
||||
|
||||
export function getNotifd(): AstalNotifd.Notifd {
|
||||
return notifd;
|
||||
}
|
||||
@@ -1,17 +1,30 @@
|
||||
import { monitorFile, Process } from "astal";
|
||||
import { astalInstanceName } from "../app";
|
||||
import { getUserDirs } from "./user";
|
||||
|
||||
const monitoringPaths = [ "./scripts", "./widget", "./app.ts", "env.d.ts" ];
|
||||
const monitoringPaths = [ "./scripts", "./window", "./app.ts", "env.d.ts" ];
|
||||
|
||||
function restartInstance(instanceName?: string) {
|
||||
Process.exec_async(`ags run`, () => {});
|
||||
Process.exec_async(`astal -q ${ instanceName ? instanceName : "astal" }`, () => {});
|
||||
interface InstanceProps {
|
||||
instanceName?: string;
|
||||
log?: boolean;
|
||||
}
|
||||
|
||||
monitoringPaths.map((path: string) => {
|
||||
monitorFile(
|
||||
path,
|
||||
() => {
|
||||
restartInstance("astal");
|
||||
}
|
||||
export function restartInstance(props: InstanceProps = { instanceName: "astal", log: false }): void {
|
||||
Process.exec_async(`astal -q ${props.instanceName}`, () => {});
|
||||
Process.exec_async(`ags run ${ props.log && `--log-file
|
||||
${ getUserDirs().cache}/ags-${ astalInstanceName || "astal" }.log` }`.replaceAll('\n', ' ').trim(),
|
||||
() => {}
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
export function monitorPaths(): void {
|
||||
monitoringPaths.map((path: string) => {
|
||||
monitorFile(
|
||||
path,
|
||||
() => restartInstance({
|
||||
instanceName: astalInstanceName || "astal",
|
||||
log: true
|
||||
})
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// handles reloading stylesheet and pywal colors
|
||||
|
||||
import { readFile, monitorFile, Process, Gio } from "astal";
|
||||
import { readFile, monitorFile, Process } from "astal";
|
||||
import { App } from "astal/gtk3";
|
||||
import { getUserDirs } from "./user";
|
||||
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
import { GLib, Variable } from "astal";
|
||||
|
||||
const time = new Variable<GLib.DateTime>(GLib.DateTime.new_now_local()).poll(600, () =>
|
||||
GLib.DateTime.new_now_local())();
|
||||
|
||||
export const getDateTime = () => time;
|
||||
@@ -0,0 +1,68 @@
|
||||
import AstalWp from "gi://AstalWp";
|
||||
|
||||
export class Wireplumber {
|
||||
private astalWireplumber: (AstalWp.Wp|null) = AstalWp.get_default();
|
||||
private defaultSink: AstalWp.Endpoint = this.astalWireplumber!.get_default_speaker()!;
|
||||
private defaultSource: AstalWp.Endpoint = this.astalWireplumber!.get_default_microphone()!;
|
||||
private static inst: Wireplumber = new Wireplumber();
|
||||
|
||||
private maxSinkVolume: number = 100;
|
||||
private maxSourceVolume: number = 100;
|
||||
|
||||
constructor() {
|
||||
if(!this.astalWireplumber)
|
||||
throw new Error("Audio features will not work correctly! Please install wireplumber first", {
|
||||
cause: "Wireplumber library not found"
|
||||
});
|
||||
}
|
||||
|
||||
public static getDefault(): Wireplumber {
|
||||
return Wireplumber.inst;
|
||||
}
|
||||
|
||||
public getDefaultSink(): AstalWp.Endpoint {
|
||||
return this.defaultSink;
|
||||
}
|
||||
|
||||
public getDefaultSource(): AstalWp.Endpoint {
|
||||
return this.defaultSource;
|
||||
}
|
||||
|
||||
public getSinkVolume(): number {
|
||||
return this.getDefaultSink().get_volume() * 100;
|
||||
}
|
||||
|
||||
public getSourceVolume(): number {
|
||||
return this.getDefaultSource().get_volume() * 100;
|
||||
}
|
||||
|
||||
public setSinkVolume(newSinkVolume: number) {
|
||||
this.defaultSink.set_volume(
|
||||
(newSinkVolume > this.maxSinkVolume ? this.maxSinkVolume : newSinkVolume) / 100
|
||||
);
|
||||
}
|
||||
|
||||
public setSourceVolume(newSourceVolume: number) {
|
||||
this.defaultSource.set_volume(
|
||||
newSourceVolume > this.maxSourceVolume ? this.maxSourceVolume : newSourceVolume / 100
|
||||
);
|
||||
}
|
||||
|
||||
public increaseSinkVolume(volumeIncrease: number) {
|
||||
if(volumeIncrease > this.maxSinkVolume
|
||||
|| (this.maxSinkVolume + volumeIncrease) > this.maxSinkVolume) {
|
||||
this.setSinkVolume(this.maxSinkVolume);
|
||||
}
|
||||
|
||||
this.setSinkVolume(this.getSinkVolume() + volumeIncrease);
|
||||
}
|
||||
|
||||
public increaseSourceVolume(volumeIncrease: number) {
|
||||
if(volumeIncrease > this.maxSourceVolume //TODO
|
||||
|| (this.maxSinkVolume + volumeIncrease) > this.maxSinkVolume) {
|
||||
this.setSinkVolume(this.maxSinkVolume);
|
||||
}
|
||||
|
||||
this.setSinkVolume(this.getSinkVolume() + volumeIncrease);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
// get open windows / interact with windows(e.g.: close, open or toggle)
|
||||
|
||||
import { Widget } from "astal/gtk3";
|
||||
import { Bar } from "../window/Bar";
|
||||
import { OSD } from "../window/OSD";
|
||||
import { ControlCenter } from "../window/ControlCenter";
|
||||
//import { FloatingNotifications } from "../window/FloatingNotifications";
|
||||
|
||||
export class Windows {
|
||||
private static inst: Windows = new Windows();
|
||||
|
||||
/* Windows List(js object):
|
||||
* add all windows here */
|
||||
private readonly windows = {
|
||||
"bar": Bar,
|
||||
"osd": OSD,
|
||||
"control-center": ControlCenter
|
||||
//"floating-notifications": FloatingNotifications
|
||||
};
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user