ags(bar,scripts,i18n): added i18n system(wip), changed some bar stuff and started doing control center
This commit is contained in:
+13
-5
@@ -1,14 +1,22 @@
|
|||||||
import { App } from "astal/gtk3"
|
import { App } from "astal/gtk3"
|
||||||
|
|
||||||
import { Bar } from "./window/Bar";
|
|
||||||
import { runStyleHandler } from "./scripts/style-handler";
|
import { runStyleHandler } from "./scripts/style-handler";
|
||||||
import "./scripts/reload-handler";
|
//import { monitorPaths } from "./scripts/reload-handler"; // Only for debugging purposes(testing new widgets and stuff)
|
||||||
|
import { handleArguments } from "./scripts/arg-handler";
|
||||||
|
|
||||||
runStyleHandler();
|
export const astalInstanceName = "astal"
|
||||||
|
|
||||||
App.start({
|
App.start({
|
||||||
instanceName: "astal",
|
instanceName: astalInstanceName || "astal",
|
||||||
|
requestHandler(request: string, res: (result: any) => void) {
|
||||||
|
console.log(`[LOG] Arguments received: ${request}`)
|
||||||
|
res(handleArguments(request));
|
||||||
|
},
|
||||||
main() {
|
main() {
|
||||||
Bar(0);
|
console.log(`[LOG] Initialized astal instance as: ${ astalInstanceName || "astal" }`);
|
||||||
|
console.log(`[LOG] Running Stylesheet handler`);
|
||||||
|
runStyleHandler();
|
||||||
|
//console.log(`[LOG] Starting to monitor scripts to automatically reload instance`);
|
||||||
|
//monitorPaths();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -0,0 +1,53 @@
|
|||||||
|
//TODO use I18n system >.<
|
||||||
|
|
||||||
|
import { GLib } from "astal";
|
||||||
|
|
||||||
|
export const i18nKeys = {
|
||||||
|
"en_US": () => import("./lang/en_US"),
|
||||||
|
"pt_BR": () => import("./lang/pt_BR")
|
||||||
|
}
|
||||||
|
|
||||||
|
export const languages: Array<string> = (() =>
|
||||||
|
Object.keys(i18nKeys))()
|
||||||
|
|
||||||
|
const defaultLanguage: string = languages[0];
|
||||||
|
let language: string = defaultLanguage;
|
||||||
|
|
||||||
|
export function getSystemLanguage(): string {
|
||||||
|
const sysLanguage: (string|null|undefined) = GLib.getenv("LANG") || GLib.getenv("LANGUAGE");
|
||||||
|
|
||||||
|
if(!sysLanguage) {
|
||||||
|
console.log(`[WARNING] Couldn't get system language, fallback to default ${defaultLanguage || "en_US"}`);
|
||||||
|
console.log("[TIP] Please set the LANG or LANGUAGE environment variable");
|
||||||
|
|
||||||
|
return defaultLanguage || "en_US";
|
||||||
|
}
|
||||||
|
|
||||||
|
return sysLanguage.split('.')[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setLanguage(lang: keyof typeof i18nKeys): (string|Error) {
|
||||||
|
languages.map((cur: string) => {
|
||||||
|
if(cur === lang) {
|
||||||
|
language = lang;
|
||||||
|
return lang;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
throw new Error(`[i18n/intl] Couldn't set language: ${lang}`, {
|
||||||
|
cause: `Language ${lang} not found in languages of type ${typeof languages}`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function tr(key: string): (string|undefined) {
|
||||||
|
let langKeys: Object = i18nKeys[language as keyof typeof i18nKeys];
|
||||||
|
let result = i18nKeys[language as keyof typeof i18nKeys],
|
||||||
|
defResult = i18nKeys[defaultLanguage as keyof typeof i18nKeys];
|
||||||
|
|
||||||
|
key.split('.').map((keyString: string) => {
|
||||||
|
result = result[keyString as keyof typeof result];
|
||||||
|
defResult = defResult[keyString as keyof typeof defResult];
|
||||||
|
});
|
||||||
|
|
||||||
|
return (result as never) || (defResult as never) || undefined;
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
export default {
|
||||||
|
"language": "English (United States)",
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
export default {
|
||||||
|
"language": "Português (Brasil)"
|
||||||
|
}
|
||||||
@@ -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 { 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) {
|
interface InstanceProps {
|
||||||
Process.exec_async(`ags run`, () => {});
|
instanceName?: string;
|
||||||
Process.exec_async(`astal -q ${ instanceName ? instanceName : "astal" }`, () => {});
|
log?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
monitoringPaths.map((path: string) => {
|
export function restartInstance(props: InstanceProps = { instanceName: "astal", log: false }): void {
|
||||||
monitorFile(
|
Process.exec_async(`astal -q ${props.instanceName}`, () => {});
|
||||||
path,
|
Process.exec_async(`ags run ${ props.log && `--log-file
|
||||||
() => {
|
${ getUserDirs().cache}/ags-${ astalInstanceName || "astal" }.log` }`.replaceAll('\n', ' ').trim(),
|
||||||
restartInstance("astal");
|
() => {}
|
||||||
}
|
|
||||||
)
|
)
|
||||||
})
|
}
|
||||||
|
|
||||||
|
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
|
// 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 { App } from "astal/gtk3";
|
||||||
import { getUserDirs } from "./user";
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,2 +1,4 @@
|
|||||||
@use "./style/bar";
|
@use "./style/bar";
|
||||||
@use "./style/wal";
|
@use "./style/wal";
|
||||||
|
@use "./style/osd";
|
||||||
|
@use "./style/control-center"
|
||||||
|
|||||||
+40
-6
@@ -41,11 +41,15 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Style widget groups
|
||||||
& > .bar-centerbox > * {
|
& > .bar-centerbox > * {
|
||||||
background: rgba($color: wal.$background, $alpha: .6);
|
background: rgba($color: wal.$background, $alpha: .6);
|
||||||
padding: 6px;
|
padding: 5px;
|
||||||
border-radius: 18px;
|
border-radius: 18px;
|
||||||
|
|
||||||
|
// Style widgets
|
||||||
|
& > *,
|
||||||
|
& > * > button
|
||||||
& > * {
|
& > * {
|
||||||
margin: 0 2px;
|
margin: 0 2px;
|
||||||
|
|
||||||
@@ -68,17 +72,18 @@
|
|||||||
|
|
||||||
.workspaces {
|
.workspaces {
|
||||||
padding: 2px 2px;
|
padding: 2px 2px;
|
||||||
|
|
||||||
& button {
|
& button {
|
||||||
all: unset;
|
all: unset;
|
||||||
border-radius: 16px;
|
border-radius: 16px;
|
||||||
transition: 80ms linear;
|
transition: 80ms linear;
|
||||||
padding: 0 12px;
|
padding: 12px 12px;
|
||||||
background: wal.$color1;
|
background: wal.$color1;
|
||||||
margin: 1px 2px;
|
margin: 1px 2px;
|
||||||
|
|
||||||
&.focus {
|
&.focus {
|
||||||
background: wal.$foreground;
|
background: wal.$foreground;
|
||||||
padding: 0 20px;
|
padding: 12px 20px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -108,9 +113,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.logo button {
|
.logo button {
|
||||||
$padding-inline: 12px;
|
padding: 0 11px;
|
||||||
padding-left: $padding-inline;
|
padding-right: 16px;
|
||||||
padding-right: calc($padding-inline + 3.9px);
|
|
||||||
|
|
||||||
& label {
|
& label {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
@@ -128,6 +132,36 @@
|
|||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.reveal {
|
||||||
|
& .media > box {
|
||||||
|
border-top-right-radius: 0;
|
||||||
|
border-bottom-right-radius: 0;
|
||||||
|
}
|
||||||
|
& .media-controls {
|
||||||
|
padding-left: 3px;
|
||||||
|
border-top-right-radius: 12px;
|
||||||
|
border-bottom-right-radius: 12px;
|
||||||
|
background: scale-color($color: wal.$color3, $lightness: -12%);
|
||||||
|
|
||||||
|
& > button {
|
||||||
|
margin: 0px 1px;
|
||||||
|
border-radius: 4px;
|
||||||
|
|
||||||
|
&: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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.tray {
|
.tray {
|
||||||
|
|||||||
@@ -0,0 +1,54 @@
|
|||||||
|
@use "./wal";
|
||||||
|
|
||||||
|
.control-center-container {
|
||||||
|
all: unset;
|
||||||
|
background: rgba(wal.$background, .6);
|
||||||
|
border-top-left-radius: 16px;
|
||||||
|
border-bottom-left-radius: 16px;
|
||||||
|
margin: 32px 0;
|
||||||
|
|
||||||
|
& * {
|
||||||
|
all: unset;
|
||||||
|
transition: 120ms linear;
|
||||||
|
}
|
||||||
|
|
||||||
|
& {
|
||||||
|
& button {
|
||||||
|
padding: 4px 6px;
|
||||||
|
background: scale-color($color: wal.$color1, $lightness: -20%);
|
||||||
|
border-radius: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
& .quickactions {
|
||||||
|
padding: 10px 16px;
|
||||||
|
& .button-row {
|
||||||
|
& > button {
|
||||||
|
margin: 5px 2px;
|
||||||
|
border-radius: 2px;
|
||||||
|
padding: 0 8px;
|
||||||
|
background: rgba($color: scale-color($color: wal.$color1, $lightness: -20%), $alpha: .7);
|
||||||
|
|
||||||
|
& label {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: rgba($color: scale-color($color: wal.$color1, $lightness: -20%), $alpha: 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:first-child {
|
||||||
|
margin-left: 0;
|
||||||
|
border-top-left-radius: 8px;
|
||||||
|
border-bottom-left-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-right: 0;
|
||||||
|
border-top-right-radius: 8px;
|
||||||
|
border-bottom-right-radius: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
@use "./wal";
|
||||||
|
|
||||||
|
.osd-window {
|
||||||
|
all: unset;
|
||||||
|
|
||||||
|
.osd {
|
||||||
|
margin-bottom: 100px;
|
||||||
|
background: rgba($color: wal.$background, $alpha: .5);
|
||||||
|
padding: 14px 16px;
|
||||||
|
border-radius: 20px;
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
margin-right: 14px;
|
||||||
|
font-size: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.volume {
|
||||||
|
margin-top: -6px;
|
||||||
|
|
||||||
|
.value {
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
levelbar {
|
||||||
|
trough block {
|
||||||
|
border-radius: 6px;
|
||||||
|
background: wal.$background;
|
||||||
|
|
||||||
|
&.filled {
|
||||||
|
padding: 3px 0;
|
||||||
|
background: wal.$color1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+20
-20
@@ -1,26 +1,26 @@
|
|||||||
// SCSS Variables
|
// SCSS Variables
|
||||||
// Generated by 'wal'
|
// Generated by 'wal'
|
||||||
$wallpaper: "/home/joaov/wallpapers/Garden Kita.png";
|
$wallpaper: "/home/joaov/wallpapers/Miku, Rin and Luka Chibi.jpg";
|
||||||
|
|
||||||
// Special
|
// Special
|
||||||
$background: #101212;
|
$background: #3d2217;
|
||||||
$foreground: #c3c3c3;
|
$foreground: #cec7c5;
|
||||||
$cursor: #c3c3c3;
|
$cursor: #cec7c5;
|
||||||
|
|
||||||
// Colors
|
// Colors
|
||||||
$color0: #101212;
|
$color0: #3d2217;
|
||||||
$color1: #59662a;
|
$color1: #b38678;
|
||||||
$color2: #517047;
|
$color2: #a4998a;
|
||||||
$color3: #87863c;
|
$color3: #b39e8a;
|
||||||
$color4: #707b48;
|
$color4: #a5a09b;
|
||||||
$color5: #4b6266;
|
$color5: #aea299;
|
||||||
$color6: #84876e;
|
$color6: #b4aea2;
|
||||||
$color7: #8e9898;
|
$color7: #a39c99;
|
||||||
$color8: #596d6d;
|
$color8: #7f6f68;
|
||||||
$color9: #778839;
|
$color9: #EFB3A1;
|
||||||
$color10: #6C965F;
|
$color10: #DBCCB9;
|
||||||
$color11: #B4B350;
|
$color11: #EFD3B9;
|
||||||
$color12: #96A460;
|
$color12: #DDD6CF;
|
||||||
$color13: #658388;
|
$color13: #E8D8CD;
|
||||||
$color14: #B0B493;
|
$color14: #F1E8D9;
|
||||||
$color15: #c3c3c3;
|
$color15: #cec7c5;
|
||||||
|
|||||||
@@ -12,15 +12,15 @@ export function Separator(props: SeparatorProps) {
|
|||||||
return new Widget.Box({
|
return new Widget.Box({
|
||||||
className: `separator separator-${ props.orientation == Gtk.Orientation.VERTICAL ? "vertical" : "horizontal" } ${ props.class && props.class }`,
|
className: `separator separator-${ props.orientation == Gtk.Orientation.VERTICAL ? "vertical" : "horizontal" } ${ props.class && props.class }`,
|
||||||
css: `.separator {
|
css: `.separator {
|
||||||
background: ${ props.cssColor ? props.cssColor : "lightgray" };
|
background: ${ props.cssColor || "lightgray" };
|
||||||
opacity: ${ props.alpha ? props.alpha : 1 };
|
opacity: ${ props.alpha || 1 };
|
||||||
}
|
}
|
||||||
.separator-horizontal {
|
.separator-horizontal {
|
||||||
padding-right: ${props.size ? props.size : 1 }px;
|
padding-right: ${props.size || 1 }px;
|
||||||
margin: 7px 4px;
|
margin: 7px 4px;
|
||||||
}
|
}
|
||||||
.separator-vertical {
|
.separator-vertical {
|
||||||
padding-bottom: ${props.size ? props.size : 1 }px;
|
padding-bottom: ${props.size || 1 }px;
|
||||||
margin: 4px 7px;
|
margin: 4px 7px;
|
||||||
}`,
|
}`,
|
||||||
} as Widget.BoxProps);
|
} as Widget.BoxProps);
|
||||||
|
|||||||
@@ -19,7 +19,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) => Math.round(volume * 100).toString() + "%")
|
label: bind(wp!.defaultSpeaker, "volume").as((volume: number) =>
|
||||||
|
Math.round(volume * 100).toString() + "%")
|
||||||
} as Widget.LabelProps)
|
} as Widget.LabelProps)
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
@@ -33,7 +34,8 @@ 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) => Math.round(volume * 100).toString() + "%")
|
label: bind(wp!.defaultMicrophone, "volume").as((volume: number) =>
|
||||||
|
Math.round(volume * 100).toString() + "%")
|
||||||
} as Widget.LabelProps)
|
} as Widget.LabelProps)
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,16 +1,14 @@
|
|||||||
import { Box, Button } from "astal/gtk3/widget";
|
|
||||||
import { GLib, Variable } from "astal";
|
|
||||||
import { Widget } from "astal/gtk3";
|
import { Widget } from "astal/gtk3";
|
||||||
|
import { getDateTime } from "../../scripts/time";
|
||||||
const dateTimeFormat = "%A %d, %H:%M"
|
import { GLib } from "astal";
|
||||||
const time = new Variable<string>("").poll(600, () =>
|
|
||||||
GLib.DateTime.new_now_local().format(dateTimeFormat)!);
|
|
||||||
|
|
||||||
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({
|
||||||
label: time()
|
label: getDateTime().as((dateTime: GLib.DateTime) => {
|
||||||
|
return dateTime.format("%A %d, %H:%M")
|
||||||
|
})
|
||||||
} as Widget.ButtonProps)
|
} as Widget.ButtonProps)
|
||||||
} as Widget.BoxProps);
|
} as Widget.BoxProps);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { bind } from "astal";
|
import { bind } from "astal";
|
||||||
import { Gtk, Widget } from "astal/gtk3";
|
import { Gtk, Widget } from "astal/gtk3";
|
||||||
import AstalHyprland from "gi://AstalHyprland";
|
import AstalHyprland from "gi://AstalHyprland";
|
||||||
|
import { getAppIcon } from "../../scripts/apps";
|
||||||
|
|
||||||
const hyprland = AstalHyprland.get_default();
|
const hyprland = AstalHyprland.get_default();
|
||||||
|
|
||||||
@@ -11,14 +12,8 @@ export function FocusedWindow() {
|
|||||||
children: [
|
children: [
|
||||||
new Widget.Icon({
|
new Widget.Icon({
|
||||||
className: "icon",
|
className: "icon",
|
||||||
icon: bind(hyprland, "focusedClient").as(Boolean) && bind(hyprland, "focusedClient").as((client: AstalHyprland.Client) => {
|
icon: bind(hyprland, "focusedClient").as((client: AstalHyprland.Client) =>
|
||||||
switch(client.initialClass) {
|
getAppIcon(client.initialClass) || "image-missing"),
|
||||||
case "zen":
|
|
||||||
return "zen-browser";
|
|
||||||
|
|
||||||
default:
|
|
||||||
return client.initialClass;
|
|
||||||
}}),
|
|
||||||
iconSize: Gtk.IconSize.SMALL_TOOLBAR
|
iconSize: Gtk.IconSize.SMALL_TOOLBAR
|
||||||
}),
|
}),
|
||||||
new Widget.Box({
|
new Widget.Box({
|
||||||
@@ -29,12 +24,14 @@ export function FocusedWindow() {
|
|||||||
new Widget.Label({
|
new Widget.Label({
|
||||||
className: "class",
|
className: "class",
|
||||||
xalign: 0,
|
xalign: 0,
|
||||||
label: bind(hyprland, "focusedClient").as(Boolean) && bind(hyprland, "focusedClient").as((client: AstalHyprland.Client) => client.get_class())
|
label: bind(hyprland, "focusedClient").as((client: AstalHyprland.Client) =>
|
||||||
|
client?.["class"])
|
||||||
} as Widget.LabelProps),
|
} as Widget.LabelProps),
|
||||||
new Widget.Label({
|
new Widget.Label({
|
||||||
className: "title",
|
className: "title",
|
||||||
xalign: 0,
|
xalign: 0,
|
||||||
label: bind(hyprland, "focusedClient").as(Boolean) && bind(hyprland, "focusedClient").as((client: AstalHyprland.Client) => client.get_title())
|
label: bind(hyprland, "focusedClient").as((client: AstalHyprland.Client) =>
|
||||||
|
client?.["title"])
|
||||||
} as Widget.LabelProps)
|
} as Widget.LabelProps)
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { Box, Button } from "astal/gtk3/widget";
|
import { Box, Button } from "astal/gtk3/widget";
|
||||||
import { Process } from "astal";
|
import AstalHyprland from "gi://AstalHyprland";
|
||||||
|
|
||||||
export function Logo() {
|
export function Logo() {
|
||||||
return (
|
return (
|
||||||
<Box className={"logo"}>
|
<Box className={"logo"}>
|
||||||
<Button onClick={ () => Process.exec("hyprctl dispatch exec anyrun") } label={""} />
|
<Button onClick={ () => AstalHyprland.get_default().dispatch("exec", "anyrun") } label={""} />
|
||||||
</Box>
|
</Box>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import { bind } 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";
|
||||||
import { Wal } from "../../scripts/pywal";
|
|
||||||
|
|
||||||
const mpris: AstalMpris.Mpris = AstalMpris.get_default();
|
const mpris: AstalMpris.Mpris = AstalMpris.get_default();
|
||||||
let defaultPlayer: (AstalMpris.Player|undefined) = mpris.get_players()?.[0];
|
let defaultPlayer: (AstalMpris.Player|undefined) = mpris.get_players()?.[0];
|
||||||
@@ -10,6 +9,7 @@ let defaultPlayer: (AstalMpris.Player|undefined) = mpris.get_players()?.[0];
|
|||||||
const playerIcons = {
|
const playerIcons = {
|
||||||
spotify: '',
|
spotify: '',
|
||||||
clapper: '',
|
clapper: '',
|
||||||
|
mpv: '',
|
||||||
spotube: '',
|
spotube: '',
|
||||||
firefox: ''
|
firefox: ''
|
||||||
}
|
}
|
||||||
@@ -20,7 +20,46 @@ export function Media(): JSX.Element {
|
|||||||
defaultPlayer = players?.[0] as AstalMpris.Player;
|
defaultPlayer = players?.[0] as AstalMpris.Player;
|
||||||
});
|
});
|
||||||
|
|
||||||
return new Widget.EventBox({
|
const mediaControlsRevealer: Widget.Revealer = new Widget.Revealer({
|
||||||
|
transitionType: Gtk.RevealerTransitionType.SLIDE_RIGHT,
|
||||||
|
transitionDuration: 260,
|
||||||
|
revealChild: false,
|
||||||
|
child: new Widget.Box({
|
||||||
|
className: "media-controls",
|
||||||
|
homogeneous: false,
|
||||||
|
children: [
|
||||||
|
new Widget.Button({
|
||||||
|
className: "previous",
|
||||||
|
label: "",
|
||||||
|
onClick: () => {
|
||||||
|
if(bind(defaultPlayer!, "canGoPrevious").as(Boolean))
|
||||||
|
defaultPlayer?.previous();
|
||||||
|
}
|
||||||
|
} as Widget.ButtonProps),
|
||||||
|
new Widget.Button({
|
||||||
|
className: "pause",
|
||||||
|
label: bind(defaultPlayer!, "playback_status").as((status: AstalMpris.PlaybackStatus) => {
|
||||||
|
return status === AstalMpris.PlaybackStatus.PLAYING ? "" : ""
|
||||||
|
}),
|
||||||
|
onClick: () => {
|
||||||
|
if(bind(defaultPlayer!, "canPlay").as(Boolean)
|
||||||
|
|| bind(defaultPlayer!, "canPause").as(Boolean))
|
||||||
|
defaultPlayer?.play_pause();
|
||||||
|
}
|
||||||
|
} as Widget.ButtonProps),
|
||||||
|
new Widget.Button({
|
||||||
|
className: "next",
|
||||||
|
label: "",
|
||||||
|
onClick: () => {
|
||||||
|
if(bind(defaultPlayer!, "canGoNext").as(Boolean))
|
||||||
|
defaultPlayer?.next();
|
||||||
|
}
|
||||||
|
} as Widget.ButtonProps)
|
||||||
|
]
|
||||||
|
} as Widget.BoxProps)
|
||||||
|
} as Widget.RevealerProps);
|
||||||
|
|
||||||
|
const mediaWidget = new Widget.EventBox({
|
||||||
className: "media-eventbox",
|
className: "media-eventbox",
|
||||||
visible: bind(mpris, "players").as((players: Array<AstalMpris.Player>) => players?.[0]).as(Boolean),
|
visible: bind(mpris, "players").as((players: Array<AstalMpris.Player>) => players?.[0]).as(Boolean),
|
||||||
child: new Widget.Box({
|
child: new Widget.Box({
|
||||||
@@ -42,7 +81,7 @@ export function Media(): JSX.Element {
|
|||||||
} as Widget.LabelProps),
|
} as Widget.LabelProps),
|
||||||
Separator({
|
Separator({
|
||||||
size: 2,
|
size: 2,
|
||||||
cssColor: `rgb(150, 150, 150)`,
|
cssColor: `rgb(180, 180, 180)`,
|
||||||
alpha: 1
|
alpha: 1
|
||||||
} as SeparatorProps),
|
} as SeparatorProps),
|
||||||
new Widget.Label({
|
new Widget.Label({
|
||||||
@@ -51,12 +90,19 @@ export function Media(): JSX.Element {
|
|||||||
} as Widget.LabelProps)
|
} as Widget.LabelProps)
|
||||||
]
|
]
|
||||||
} as Widget.BoxProps),
|
} as Widget.BoxProps),
|
||||||
new Widget.Revealer({
|
mediaControlsRevealer
|
||||||
transitionType: Gtk.RevealerTransitionType.SLIDE_RIGHT,
|
|
||||||
transitionDuration: 400,
|
|
||||||
revealChild: false //FIXME
|
|
||||||
} as Widget.RevealerProps)
|
|
||||||
]
|
]
|
||||||
} as Widget.BoxProps)
|
} as Widget.BoxProps)
|
||||||
} as Widget.EventBoxProps);
|
} as Widget.EventBoxProps);
|
||||||
|
|
||||||
|
mediaWidget.connect("hover", () => {
|
||||||
|
mediaControlsRevealer.set_reveal_child(true);
|
||||||
|
mediaWidget.className = mediaWidget.className + " reveal";
|
||||||
|
});
|
||||||
|
mediaWidget.connect("hover-lost", () => {
|
||||||
|
mediaControlsRevealer.set_reveal_child(false);
|
||||||
|
mediaWidget.className = mediaWidget.className.replaceAll(" reveal", "");
|
||||||
|
})
|
||||||
|
|
||||||
|
return mediaWidget;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { bind } from "astal";
|
import { bind } from "astal";
|
||||||
import { Astal, Gtk, Widget } from "astal/gtk3";
|
import { Gtk, Widget } from "astal/gtk3";
|
||||||
import AstalTray from "gi://AstalTray"
|
import AstalTray from "gi://AstalTray"
|
||||||
|
|
||||||
const astalTray = AstalTray.get_default();
|
const astalTray = AstalTray.get_default();
|
||||||
@@ -7,6 +7,7 @@ const astalTray = AstalTray.get_default();
|
|||||||
export function Tray() {
|
export function Tray() {
|
||||||
return new Widget.Box({
|
return new Widget.Box({
|
||||||
className: "tray",
|
className: "tray",
|
||||||
|
visible: bind(astalTray, "items").as((items: Array<AstalTray.TrayItem>) => items.length > 0),
|
||||||
children: bind(astalTray, "items").as((items: Array<AstalTray.TrayItem>) =>
|
children: bind(astalTray, "items").as((items: Array<AstalTray.TrayItem>) =>
|
||||||
items.map((item: AstalTray.TrayItem) =>
|
items.map((item: AstalTray.TrayItem) =>
|
||||||
new Widget.MenuButton({
|
new Widget.MenuButton({
|
||||||
@@ -15,6 +16,8 @@ export function Tray() {
|
|||||||
menuModel: bind(item, "menuModel"),
|
menuModel: bind(item, "menuModel"),
|
||||||
usePopover: false,
|
usePopover: false,
|
||||||
actionGroup: bind(item, "actionGroup").as((actionGroup: any) => ["dbusmenu", actionGroup]),
|
actionGroup: bind(item, "actionGroup").as((actionGroup: any) => ["dbusmenu", actionGroup]),
|
||||||
|
direction: Gtk.ArrowType.DOWN,
|
||||||
|
halign: Gtk.Align.CENTER,
|
||||||
child: new Widget.Icon({
|
child: new Widget.Icon({
|
||||||
gIcon: bind(item, "gicon"),
|
gIcon: bind(item, "gicon"),
|
||||||
iconSize: Gtk.IconSize.SMALL_TOOLBAR
|
iconSize: Gtk.IconSize.SMALL_TOOLBAR
|
||||||
|
|||||||
@@ -1,21 +1,31 @@
|
|||||||
import { bind } from "astal";
|
import { bind } from "astal";
|
||||||
import { Widget } from "astal/gtk3";
|
import { Gdk, Gtk, Widget } from "astal/gtk3";
|
||||||
import AstalHyprland from "gi://AstalHyprland";
|
import AstalHyprland from "gi://AstalHyprland";
|
||||||
|
|
||||||
const hyprland = AstalHyprland.get_default();
|
const hyprland = AstalHyprland.get_default();
|
||||||
|
|
||||||
export function Workspaces() {
|
export function Workspaces() {
|
||||||
return new Widget.Box({
|
const workspacesEventBox = new Widget.EventBox({
|
||||||
className: "workspaces",
|
onScroll: (_, event) =>
|
||||||
children: bind(hyprland, "workspaces").as((workspaces) =>
|
event.delta_y > 0 ? hyprland.dispatch("workspace", "e-1") : hyprland.dispatch("workspace", "e+1"),
|
||||||
workspaces.sort((a: AstalHyprland.Workspace, b: AstalHyprland.Workspace) =>
|
|
||||||
a.get_id() - b.get_id())
|
child: new Widget.Box({
|
||||||
.map((workspace: AstalHyprland.Workspace) =>
|
className: "workspaces",
|
||||||
new Widget.Button({
|
vexpand: false,
|
||||||
className: bind(hyprland, "focusedWorkspace").as((focusedWs: AstalHyprland.Workspace) => workspace === focusedWs ? "focus" : ""),
|
valign: Gtk.Align.CENTER,
|
||||||
onClicked: () => workspace.focus()
|
children: bind(hyprland, "workspaces").as((workspaces) => {
|
||||||
} as Widget.ButtonProps)
|
const sortedWorkspaces = workspaces.sort((a: AstalHyprland.Workspace, b: AstalHyprland.Workspace) => a.get_id() - b.get_id());
|
||||||
)
|
return sortedWorkspaces.map((workspace: AstalHyprland.Workspace) =>
|
||||||
)
|
new Widget.Button({
|
||||||
} as Widget.BoxProps);
|
className: bind(hyprland, "focusedWorkspace").as((focusedWs: AstalHyprland.Workspace) => workspace === focusedWs ? "focus" : ""),
|
||||||
|
visible: true,
|
||||||
|
onClicked: () => workspace.focus()
|
||||||
|
} as Widget.ButtonProps)
|
||||||
|
)
|
||||||
|
})
|
||||||
|
} as Widget.BoxProps)
|
||||||
|
} as Widget.EventBoxProps);
|
||||||
|
|
||||||
|
|
||||||
|
return workspacesEventBox;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
import { Gtk, Widget } from "astal/gtk3";
|
||||||
|
|
||||||
|
export function ButtonGrid(): Widget.Box {
|
||||||
|
return new Widget.Box({
|
||||||
|
child: new Gtk.Grid({
|
||||||
|
orientation: Gtk.Orientation.HORIZONTAL,
|
||||||
|
rowHomogeneous: true
|
||||||
|
} as Gtk.Grid.ConstructorProps, BluetoothToggle())
|
||||||
|
} as Widget.BoxProps);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Buttons and Toggles!
|
||||||
|
|
||||||
|
export function BluetoothToggle(): Gtk.ToggleButton {
|
||||||
|
return new Gtk.ToggleButton({
|
||||||
|
child: new Widget.Box({
|
||||||
|
orientation: Gtk.Orientation.VERTICAL,
|
||||||
|
children: [
|
||||||
|
new Widget.Label({
|
||||||
|
className: "title",
|
||||||
|
label: "Bluetooth"
|
||||||
|
} as Widget.LabelProps),
|
||||||
|
new Widget.Label({
|
||||||
|
className: "extra",
|
||||||
|
label: "[dev] [dev_bat]"
|
||||||
|
} as Widget.LabelProps)
|
||||||
|
]
|
||||||
|
} as Widget.BoxProps)
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
import { Process, Variable } from "astal";
|
||||||
|
import { Gtk, Widget } from "astal/gtk3";
|
||||||
|
import AstalHyprland from "gi://AstalHyprland";
|
||||||
|
|
||||||
|
const hostname: string = Process.exec("cat /etc/hostname") || "GNU/Linux";
|
||||||
|
const uptime = new Variable<string>("Just turned on")
|
||||||
|
.poll(1000, () => {
|
||||||
|
return Process.exec("uptime -p").replace(/^up /, "")
|
||||||
|
})();
|
||||||
|
|
||||||
|
const quickActionsBox: Widget.Box = new Widget.Box({
|
||||||
|
className: "quickactions",
|
||||||
|
hexpand: true,
|
||||||
|
children: [
|
||||||
|
new Widget.Box({
|
||||||
|
orientation: Gtk.Orientation.VERTICAL,
|
||||||
|
halign: Gtk.Align.START,
|
||||||
|
children: [
|
||||||
|
new Widget.Label({
|
||||||
|
className: "hostname",
|
||||||
|
xalign: 0,
|
||||||
|
label: hostname.toString()
|
||||||
|
} as Widget.LabelProps),
|
||||||
|
new Widget.Label({
|
||||||
|
className: "uptime",
|
||||||
|
xalign: 0,
|
||||||
|
label: uptime.as((uptime: string) => ` ${uptime}`)
|
||||||
|
} as Widget.LabelProps)
|
||||||
|
]
|
||||||
|
} as Widget.BoxProps),
|
||||||
|
new Widget.Box({
|
||||||
|
orientation: Gtk.Orientation.HORIZONTAL,
|
||||||
|
className: "button-row",
|
||||||
|
halign: Gtk.Align.END,
|
||||||
|
children: [
|
||||||
|
LockButton(),
|
||||||
|
ColorPickerButton(),
|
||||||
|
ScreenshotButton(),
|
||||||
|
SelectWallpaperButton(),
|
||||||
|
LogoutButton()
|
||||||
|
]
|
||||||
|
} as Widget.BoxProps)
|
||||||
|
]
|
||||||
|
} as Widget.BoxProps);
|
||||||
|
|
||||||
|
export function QuickActionsWidget(): Widget.Box {
|
||||||
|
return quickActionsBox;
|
||||||
|
}
|
||||||
|
|
||||||
|
function LockButton(): Widget.Button {
|
||||||
|
return new Widget.Button({
|
||||||
|
label: "",
|
||||||
|
onClick: () => AstalHyprland.get_default().dispatch("exec", "hyprlock")
|
||||||
|
} as Widget.ButtonProps)
|
||||||
|
}
|
||||||
|
|
||||||
|
function ColorPickerButton(): Widget.Button {
|
||||||
|
return new Widget.Button({
|
||||||
|
label: "",
|
||||||
|
onClick: () => AstalHyprland.get_default().dispatch(
|
||||||
|
"exec",
|
||||||
|
"sh $HOME/.config/eww/scripts/color-picker.sh"
|
||||||
|
)
|
||||||
|
} as Widget.ButtonProps)
|
||||||
|
}
|
||||||
|
|
||||||
|
function ScreenshotButton(): Widget.Button {
|
||||||
|
return new Widget.Button({
|
||||||
|
label: "",
|
||||||
|
onClick: () => Process.exec_async(
|
||||||
|
"bash -c 'hyprshot -m region -o $HOME/Screenshots'",
|
||||||
|
() => {}
|
||||||
|
)
|
||||||
|
} as Widget.ButtonProps);
|
||||||
|
}
|
||||||
|
|
||||||
|
function SelectWallpaperButton(): Widget.Button {
|
||||||
|
return new Widget.Button({
|
||||||
|
label: "",
|
||||||
|
onClick: () => Process.exec_async(
|
||||||
|
"bash -c 'sh $HOME/.config/hypr/scripts/change-wallpaper.sh'",
|
||||||
|
() => {}
|
||||||
|
)
|
||||||
|
} as Widget.ButtonProps);
|
||||||
|
}
|
||||||
|
|
||||||
|
function LogoutButton(): Widget.Button {
|
||||||
|
return new Widget.Button({
|
||||||
|
label: "",
|
||||||
|
onClick: () => Process.exec_async(
|
||||||
|
"bash -c 'loginctl terminate-user $USER'",
|
||||||
|
() => {}
|
||||||
|
)
|
||||||
|
} as Widget.ButtonProps);
|
||||||
|
}
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
import { Gdk, Astal, Gtk, Widget } from "astal/gtk3";
|
||||||
|
|
||||||
|
import { Clock } from "../widget/bar/Clock";
|
||||||
|
import { Logo } from "../widget/bar/Logo";
|
||||||
|
import { CCToggle } from "../widget/bar/CCToggle";
|
||||||
|
import { Tray } from "../widget/bar/Tray";
|
||||||
|
import { Workspaces } from "../widget/bar/Workspaces";
|
||||||
|
import { Audio } from "../widget/bar/Audio";
|
||||||
|
import { FocusedWindow } from "../widget/bar/FocusedWindow";
|
||||||
|
//import { Media } from "../widget/bar/Media";
|
||||||
|
|
||||||
|
interface BarProps {
|
||||||
|
monitor: number;
|
||||||
|
width?: number;
|
||||||
|
height?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Bar: Widget.Window = newBar({
|
||||||
|
monitor: 0
|
||||||
|
} as BarProps);
|
||||||
|
|
||||||
|
function newBar(props: BarProps): Widget.Window {
|
||||||
|
return new Widget.Window({
|
||||||
|
className: "bar",
|
||||||
|
monitor: props.monitor,
|
||||||
|
namespace: "top-bar",
|
||||||
|
anchor: Astal.WindowAnchor.TOP,
|
||||||
|
layer: Astal.Layer.TOP,
|
||||||
|
exclusivity: Astal.Exclusivity.EXCLUSIVE,
|
||||||
|
canFocus: false,
|
||||||
|
visible: true, // Recommendation: set visible to false if you don't want this window to appear on app start
|
||||||
|
heightRequest: props.height || 0,
|
||||||
|
widthRequest: props.width || Gdk.Screen.get_default()?.get_monitor_geometry(props.monitor)?.width,
|
||||||
|
hexpand: false,
|
||||||
|
vexpand: false,
|
||||||
|
child: new Widget.Box({
|
||||||
|
className: "bar-container",
|
||||||
|
child: new Widget.CenterBox({
|
||||||
|
className: "bar-centerbox",
|
||||||
|
expand: true,
|
||||||
|
homogeneous: false,
|
||||||
|
startWidget: new Widget.Box({
|
||||||
|
className: "widgets-left",
|
||||||
|
homogeneous: false,
|
||||||
|
halign: Gtk.Align.START,
|
||||||
|
children: [
|
||||||
|
Logo(),
|
||||||
|
Workspaces(),
|
||||||
|
FocusedWindow()
|
||||||
|
]
|
||||||
|
} as Widget.BoxProps),
|
||||||
|
centerWidget: new Widget.Box({
|
||||||
|
className: "widgets-center",
|
||||||
|
homogeneous: false,
|
||||||
|
halign: Gtk.Align.CENTER,
|
||||||
|
children: [
|
||||||
|
Clock(),
|
||||||
|
/*<Media />*/
|
||||||
|
]
|
||||||
|
} as Widget.BoxProps),
|
||||||
|
endWidget: new Widget.Box({
|
||||||
|
className: "widgets-right",
|
||||||
|
homogeneous: false,
|
||||||
|
halign: Gtk.Align.END,
|
||||||
|
children: [
|
||||||
|
Tray(),
|
||||||
|
Audio(),
|
||||||
|
CCToggle()
|
||||||
|
]
|
||||||
|
} as Widget.BoxProps)
|
||||||
|
} as Widget.CenterBoxProps)
|
||||||
|
} as Widget.BoxProps)
|
||||||
|
} as Widget.WindowProps);
|
||||||
|
}
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
import { Box, CenterBox } from "astal/gtk3/widget";
|
|
||||||
import { Astal, Gtk } from "astal/gtk3";
|
|
||||||
import Gdk from "gi://Gdk?version=3.0";
|
|
||||||
|
|
||||||
import { Clock } from "../widget/bar/Clock";
|
|
||||||
import { Logo } from "../widget/bar/Logo";
|
|
||||||
import { CCToggle } from "../widget/bar/CCToggle";
|
|
||||||
import { Tray } from "../widget/bar/Tray";
|
|
||||||
import { Workspaces } from "../widget/bar/Workspaces";
|
|
||||||
import { Audio } from "../widget/bar/Audio";
|
|
||||||
import { FocusedWindow } from "../widget/bar/FocusedWindow";
|
|
||||||
import { Media } from "../widget/bar/Media";
|
|
||||||
|
|
||||||
export function Bar(monitor: number = 0, width: (number|undefined) = undefined, height: (number|undefined) = undefined) {
|
|
||||||
return (
|
|
||||||
<window className="bar" monitor={ monitor } namespace={ "top-bar" }
|
|
||||||
anchor={ Astal.WindowAnchor.TOP } layer={ Astal.Layer.TOP }
|
|
||||||
exclusivity={ Astal.Exclusivity.EXCLUSIVE } canFocus={ false }
|
|
||||||
heightRequest={ height ? height : 0 }
|
|
||||||
widthRequest={ width ? width : Gdk.Screen.get_default()?.get_monitor_geometry(monitor)?.width }>
|
|
||||||
|
|
||||||
<Box className={ "bar-container" } spacing={ 2 }>
|
|
||||||
<CenterBox className={ "bar-centerbox" } expand={ true }>
|
|
||||||
<Box className={ "widgets-left" } vertical={ false }
|
|
||||||
homogeneous={ false } halign={ Gtk.Align.START }>
|
|
||||||
|
|
||||||
<Logo />
|
|
||||||
<Workspaces />
|
|
||||||
<FocusedWindow />
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
<Box className={ "widgets-center" } halign={ Gtk.Align.CENTER }
|
|
||||||
vertical={ false } homogeneous={ false }>
|
|
||||||
|
|
||||||
<Clock />
|
|
||||||
<Media />
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
<Box className={ "widgets-right" } halign={ Gtk.Align.END }
|
|
||||||
vertical={ false } homogeneous={ false }>
|
|
||||||
|
|
||||||
<Tray />
|
|
||||||
<Audio />
|
|
||||||
<CCToggle />
|
|
||||||
</Box>
|
|
||||||
</CenterBox>
|
|
||||||
</Box>
|
|
||||||
</window>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
import { Astal, Gdk, Gtk, Widget } from "astal/gtk3";
|
||||||
|
import { QuickActionsWidget } from "../widget/control-center/QuickActions";
|
||||||
|
|
||||||
|
export const ControlCenter: Widget.Window = CC();
|
||||||
|
export const widgetsBox: Widget.Box = new Widget.Box({
|
||||||
|
visible: true,
|
||||||
|
className: "control-center-container",
|
||||||
|
orientation: Gtk.Orientation.VERTICAL,
|
||||||
|
children: [
|
||||||
|
QuickActionsWidget()
|
||||||
|
]
|
||||||
|
} as Widget.BoxProps);
|
||||||
|
|
||||||
|
widgetsBox.connect("add", (_: Widget.Box, widget: Gtk.Widget) => {
|
||||||
|
widget.set_size_request(widgetsBox.get_allocated_width(), widget.get_allocated_height());
|
||||||
|
});
|
||||||
|
|
||||||
|
function CC(): Widget.Window {
|
||||||
|
return new Widget.Window({
|
||||||
|
className: "control-center",
|
||||||
|
namespace: "control-center",
|
||||||
|
canFocus: true,
|
||||||
|
exclusivity: Astal.Exclusivity.NORMAL,
|
||||||
|
anchor: Astal.WindowAnchor.RIGHT,
|
||||||
|
width_request: 450,
|
||||||
|
height_request: Gdk.Screen.get_default()?.get_monitor_geometry(0)?.height || 800,
|
||||||
|
monitor: 0,
|
||||||
|
visible: false,
|
||||||
|
child: widgetsBox
|
||||||
|
} as Widget.WindowProps);
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
import { Astal, Gtk, Widget } from "astal/gtk3";
|
||||||
|
import { getNotifd, removeNotification } from "../scripts/notification-handler";
|
||||||
|
import { notifications as popupNotifications } from "../scripts/notification-handler";
|
||||||
|
import AstalNotifd from "gi://AstalNotifd";
|
||||||
|
|
||||||
|
export const FloatingNotifications: Widget.Window = FloatingNotificationsWindow();
|
||||||
|
let gtkNotificationPopups: Array<Widget.Box> = [];
|
||||||
|
|
||||||
|
function FloatingNotificationsWindow(): Widget.Window {
|
||||||
|
|
||||||
|
const notificationsBox = new Widget.Box({
|
||||||
|
className: "notifications",
|
||||||
|
orientation: Gtk.Orientation.VERTICAL,
|
||||||
|
homogeneous: false
|
||||||
|
} as Widget.BoxProps);
|
||||||
|
|
||||||
|
getNotifd().connect("notified", () => {
|
||||||
|
for(let i = 0; i < popupNotifications.length; i++) {
|
||||||
|
const notification: AstalNotifd.Notification = popupNotifications[i];
|
||||||
|
|
||||||
|
gtkNotificationPopups[i] = new Widget.Box({
|
||||||
|
className: "notification",
|
||||||
|
homogeneous: false,
|
||||||
|
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: () => removeNotification(notification.id)
|
||||||
|
} as Widget.ButtonProps)
|
||||||
|
]
|
||||||
|
} as Widget.BoxProps)
|
||||||
|
]
|
||||||
|
} as Widget.BoxProps);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
import { bind } from "astal";
|
||||||
|
import { Astal, Gtk, Widget } from "astal/gtk3";
|
||||||
|
import { Time } from "astal/time";
|
||||||
|
import AstalWp from "gi://AstalWp";
|
||||||
|
import { Windows } from "../scripts/windows";
|
||||||
|
|
||||||
|
export const OSD: Widget.Window = OSDWindow();
|
||||||
|
|
||||||
|
function OSDWindow() {
|
||||||
|
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);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user