✨ ags: new window management system, adjustments, use adwaita sans
This commit is contained in:
+39
-15
@@ -1,22 +1,25 @@
|
|||||||
import { App } from "astal/gtk3"
|
import { App } from "astal/gtk3"
|
||||||
import { Windows } from "./windows";
|
|
||||||
import { Wireplumber } from "./scripts/volume";
|
import { Wireplumber } from "./scripts/volume";
|
||||||
|
|
||||||
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 { Time, timeout } from "astal/time";
|
import { Time, timeout } from "astal/time";
|
||||||
|
|
||||||
import { OSD, OSDModes, setOSDMode } from "./window/OSD";
|
import { OSDModes, setOSDMode } from "./window/OSD";
|
||||||
import { ControlCenter } from "./window/ControlCenter";
|
|
||||||
|
|
||||||
import { Runner } from "./runner/Runner";
|
import { Runner } from "./runner/Runner";
|
||||||
import { PluginApps } from "./runner/plugins/apps";
|
import { PluginApps } from "./runner/plugins/apps";
|
||||||
import { PluginShell } from "./runner/plugins/shell";
|
import { PluginShell } from "./runner/plugins/shell";
|
||||||
import { PluginWebSearch } from "./runner/plugins/websearch";
|
import { PluginWebSearch } from "./runner/plugins/websearch";
|
||||||
import { PluginMedia } from "./runner/plugins/media";
|
import { PluginMedia } from "./runner/plugins/media";
|
||||||
|
import { Windows } from "./windows";
|
||||||
|
import { Notifications } from "./scripts/notifications";
|
||||||
|
import AstalNotifd from "gi://AstalNotifd";
|
||||||
|
import { GObject } from "astal";
|
||||||
|
|
||||||
|
|
||||||
let osdTimer: (Time|undefined);
|
let osdTimer: (Time|undefined);
|
||||||
|
let connections = new Map<GObject.Object, (Array<number> | number)>();
|
||||||
|
|
||||||
const runnerPlugins: Array<Runner.Plugin> = [
|
const runnerPlugins: Array<Runner.Plugin> = [
|
||||||
PluginApps,
|
PluginApps,
|
||||||
@@ -27,19 +30,39 @@ const runnerPlugins: Array<Runner.Plugin> = [
|
|||||||
|
|
||||||
App.start({
|
App.start({
|
||||||
instanceName: "astal",
|
instanceName: "astal",
|
||||||
requestHandler(request: string, response: (result: any) => void) {
|
async requestHandler(request: string, response: (result: any) => void) {
|
||||||
// console.log(`[LOG] Arguments received: ${request}`);
|
// console.log(`[LOG] Arguments received: ${request}`);
|
||||||
response(handleArguments(request));
|
response(await handleArguments(request));
|
||||||
},
|
},
|
||||||
main() {
|
main() {
|
||||||
console.log(`[LOG] Initialized astal instance as: ${ App.instanceName || "astal" }`);
|
console.log(`[LOG] Initialized astal instance as: ${ App.instanceName || "astal" }`);
|
||||||
|
App.vfunc_quit = () => {
|
||||||
|
console.log("[LOG] Disconnecting stuff");
|
||||||
|
connections.forEach((v, k) => Array.isArray(v) ?
|
||||||
|
v.map(id => k.disconnect(id))
|
||||||
|
: k.disconnect(v));
|
||||||
|
};
|
||||||
|
|
||||||
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", () =>
|
connections.set(Wireplumber.getDefault(), [
|
||||||
!Windows.isVisible(ControlCenter) && triggerOSD(OSDModes.SINK));
|
Wireplumber.getDefault().getDefaultSink().connect("notify::volume", () =>
|
||||||
|
!Windows.isVisible("osd") && triggerOSD(OSDModes.SINK))
|
||||||
|
]);
|
||||||
|
|
||||||
|
connections.set(Notifications.getDefault(), [
|
||||||
|
Notifications.getDefault().connect("notification-added", (_, _notif: AstalNotifd.Notification) => {
|
||||||
|
Windows.open("floating-notifications");
|
||||||
|
}),
|
||||||
|
Notifications.getDefault().connect("notification-removed", (_: Notifications, _id: number) => {
|
||||||
|
_.notifications.length === 0 && Windows.close("floating-notifications");
|
||||||
|
})
|
||||||
|
]);
|
||||||
|
|
||||||
console.log(`[LOG] Adding runner plugins`);
|
console.log(`[LOG] Adding runner plugins`);
|
||||||
runnerPlugins.map(plugin => Runner.addPlugin(plugin));
|
runnerPlugins.map(plugin => Runner.addPlugin(plugin));
|
||||||
@@ -49,17 +72,18 @@ App.start({
|
|||||||
function triggerOSD(osdModeParam: OSDModes) {
|
function triggerOSD(osdModeParam: OSDModes) {
|
||||||
setOSDMode(osdModeParam);
|
setOSDMode(osdModeParam);
|
||||||
|
|
||||||
Windows.open(OSD);
|
|
||||||
if(!osdTimer) {
|
if(!osdTimer) {
|
||||||
|
Windows.open("osd");
|
||||||
osdTimer = timeout(3000, () => {
|
osdTimer = timeout(3000, () => {
|
||||||
Windows.close(OSD);
|
Windows.close("osd");
|
||||||
osdTimer = undefined;
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
osdTimer.cancel();
|
|
||||||
osdTimer = timeout(3000, () => {
|
|
||||||
Windows.close(OSD);
|
|
||||||
osdTimer = undefined;
|
osdTimer = undefined;
|
||||||
});
|
});
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
osdTimer.cancel();
|
||||||
|
osdTimer = timeout(3000, () => {
|
||||||
|
Windows.close("osd");
|
||||||
|
osdTimer = undefined;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
+19
-14
@@ -3,8 +3,9 @@ import { Gdk, Gtk, Widget } from "astal/gtk3";
|
|||||||
import { PopupWindow, PopupWindowProps } from "../widget/PopupWindow";
|
import { PopupWindow, PopupWindowProps } from "../widget/PopupWindow";
|
||||||
import { updateApps } from "../scripts/apps";
|
import { updateApps } from "../scripts/apps";
|
||||||
import { ResultWidget, ResultWidgetProps } from "../widget/runner/ResultWidget";
|
import { ResultWidget, ResultWidgetProps } from "../widget/runner/ResultWidget";
|
||||||
|
import { Windows } from "../windows";
|
||||||
|
|
||||||
export let runnerInstance: (Widget.Window|null) = null;
|
export let runnerInstance: (Gtk.Window|null) = null;
|
||||||
let onClickTimeout: (AstalIO.Time|undefined);
|
let onClickTimeout: (AstalIO.Time|undefined);
|
||||||
|
|
||||||
export function startRunnerDefault() {
|
export function startRunnerDefault() {
|
||||||
@@ -12,16 +13,21 @@ export function startRunnerDefault() {
|
|||||||
entryPlaceHolder: "Start typing..."
|
entryPlaceHolder: "Start typing..."
|
||||||
} as Runner.RunnerProps,
|
} as Runner.RunnerProps,
|
||||||
() => [
|
() => [
|
||||||
new ResultWidget({
|
|
||||||
icon: "utilities-terminal-symbolic",
|
|
||||||
title: "Run shell commands",
|
|
||||||
description: "Start typing with '!' prefix to run shell commands"
|
|
||||||
} as ResultWidgetProps),
|
|
||||||
new ResultWidget({
|
new ResultWidget({
|
||||||
icon: "application-x-executable-symbolic",
|
icon: "application-x-executable-symbolic",
|
||||||
title: "Run your applications",
|
title: "Run your applications",
|
||||||
description: "Type the name of the application to search"
|
description: "Type the name of the application to search"
|
||||||
} as ResultWidgetProps),
|
} as ResultWidgetProps),
|
||||||
|
new ResultWidget({
|
||||||
|
icon: "media-playback-start-symbolic",
|
||||||
|
title: "Control media",
|
||||||
|
description: "Use prefix ':' to run"
|
||||||
|
} as ResultWidgetProps),
|
||||||
|
new ResultWidget({
|
||||||
|
icon: "utilities-terminal-symbolic",
|
||||||
|
title: "Run shell commands",
|
||||||
|
description: "Start typing with '!' prefix to run shell commands"
|
||||||
|
} as ResultWidgetProps),
|
||||||
new ResultWidget({
|
new ResultWidget({
|
||||||
icon: "applications-internet-symbolic",
|
icon: "applications-internet-symbolic",
|
||||||
title: "Search the Web",
|
title: "Search the Web",
|
||||||
@@ -39,13 +45,11 @@ export namespace Runner {
|
|||||||
entryPlaceHolder?: string;
|
entryPlaceHolder?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function close(gtkWindow?: Widget.Window) {
|
export function close() {
|
||||||
const window = gtkWindow ? gtkWindow : runnerInstance;
|
|
||||||
|
|
||||||
[...plugins.values()].map(plugin =>
|
[...plugins.values()].map(plugin =>
|
||||||
plugin && plugin.onClose && plugin.onClose());
|
plugin && plugin.onClose && plugin.onClose());
|
||||||
|
|
||||||
window?.close();
|
runnerInstance?.close();
|
||||||
runnerInstance = null;
|
runnerInstance = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,7 +87,7 @@ export namespace Runner {
|
|||||||
return plugins.delete(plugin);
|
return plugins.delete(plugin);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function openRunner(props?: RunnerProps, placeholder?: () => Array<ResultWidget>): (Widget.Window|null) {
|
export function openRunner(props?: RunnerProps, placeholder?: () => Array<ResultWidget>): (Gtk.Window|null) {
|
||||||
let subs: Array<() => void> = [];
|
let subs: Array<() => void> = [];
|
||||||
const entryText: Variable<string> = new Variable<string>("");
|
const entryText: Variable<string> = new Variable<string>("");
|
||||||
|
|
||||||
@@ -156,8 +160,9 @@ export namespace Runner {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
if(!runnerInstance)
|
if(!runnerInstance)
|
||||||
runnerInstance = PopupWindow({
|
runnerInstance = Windows.createWindowForFocusedMonitor((mon: number): (Widget.Window) => PopupWindow({
|
||||||
namespace: "runner",
|
namespace: "runner",
|
||||||
|
monitor: mon,
|
||||||
widthRequest: props?.width || 750,
|
widthRequest: props?.width || 750,
|
||||||
heightRequest: props?.height || 0,
|
heightRequest: props?.height || 0,
|
||||||
marginTop: 250,
|
marginTop: 250,
|
||||||
@@ -176,8 +181,8 @@ export namespace Runner {
|
|||||||
|
|
||||||
},
|
},
|
||||||
closeAction: (_) => {
|
closeAction: (_) => {
|
||||||
close(_);
|
|
||||||
subs.map(sub => sub());
|
subs.map(sub => sub());
|
||||||
|
close();
|
||||||
},
|
},
|
||||||
child: new Widget.Box({
|
child: new Widget.Box({
|
||||||
className: "runner main",
|
className: "runner main",
|
||||||
@@ -195,7 +200,7 @@ export namespace Runner {
|
|||||||
})
|
})
|
||||||
]
|
]
|
||||||
} as Widget.BoxProps)
|
} as Widget.BoxProps)
|
||||||
} as PopupWindowProps);
|
} as PopupWindowProps))();
|
||||||
|
|
||||||
return runnerInstance;
|
return runnerInstance;
|
||||||
}
|
}
|
||||||
|
|||||||
+22
-11
@@ -1,5 +1,3 @@
|
|||||||
import { Gtk } from "astal/gtk3";
|
|
||||||
|
|
||||||
import { Wireplumber } from "./volume";
|
import { Wireplumber } from "./volume";
|
||||||
import { Windows } from "../windows";
|
import { Windows } from "../windows";
|
||||||
|
|
||||||
@@ -7,9 +5,10 @@ import { restartInstance } from "./reload-handler";
|
|||||||
import { startRunnerDefault } from "../runner/Runner";
|
import { startRunnerDefault } from "../runner/Runner";
|
||||||
import { showWorkspaceNumbers } from "../widget/bar/Workspaces";
|
import { showWorkspaceNumbers } from "../widget/bar/Workspaces";
|
||||||
import { timeout } from "astal";
|
import { timeout } from "astal";
|
||||||
|
import { App } from "astal/gtk3";
|
||||||
|
|
||||||
|
|
||||||
export function handleArguments(request: string): any {
|
export async function handleArguments(request: string): Promise<any> {
|
||||||
const args: Array<string> = request.split(" ");
|
const args: Array<string> = request.split(" ");
|
||||||
switch(args[0]) {
|
switch(args[0]) {
|
||||||
case "open":
|
case "open":
|
||||||
@@ -27,6 +26,10 @@ export function handleArguments(request: string): any {
|
|||||||
case "reload":
|
case "reload":
|
||||||
restartInstance();
|
restartInstance();
|
||||||
return "Restarting instance..."
|
return "Restarting instance..."
|
||||||
|
|
||||||
|
case "windows":
|
||||||
|
return Object.keys(Windows.windows).map(name =>
|
||||||
|
`${name}: ${Windows.isVisible(name) ? "open" : "closed" }`).join('\n');
|
||||||
|
|
||||||
case "runner":
|
case "runner":
|
||||||
startRunnerDefault();
|
startRunnerDefault();
|
||||||
@@ -39,6 +42,12 @@ export function handleArguments(request: string): any {
|
|||||||
}
|
}
|
||||||
return "Showing numbers";
|
return "Showing numbers";
|
||||||
|
|
||||||
|
case "c":
|
||||||
|
case "code":
|
||||||
|
const input = request.replace(args[0], "").trimStart();
|
||||||
|
console.log(input);
|
||||||
|
return await (App.eval(input).then((v) => v).catch((r) => r));
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return "command not found! try checking help";
|
return "command not found! try checking help";
|
||||||
}
|
}
|
||||||
@@ -46,13 +55,13 @@ 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
|
||||||
function handleWindowArgs(args: Array<string>): string {
|
function handleWindowArgs(args: Array<string>): string {
|
||||||
const specifiedWindow: (Gtk.Window|undefined) = Windows.getWindow(args[1]);
|
if(!args[1])
|
||||||
|
|
||||||
if(!specifiedWindow)
|
|
||||||
return "Window argument not specified!";
|
return "Window argument not specified!";
|
||||||
|
|
||||||
if(!Windows.getList().has(args[1]))
|
const specifiedWindow: string = args[1];
|
||||||
return `Name "${args[1]}" not found windows map! Make sure to add new Windows on the Map!`
|
|
||||||
|
if(!Windows.hasWindow(specifiedWindow))
|
||||||
|
return `Name "${specifiedWindow}" not found windows map! Make sure to add new Windows on the Map!`
|
||||||
|
|
||||||
switch(args[0]) {
|
switch(args[0]) {
|
||||||
case "open":
|
case "open":
|
||||||
@@ -137,11 +146,11 @@ function handleVolumeArgs(args: Array<string>) {
|
|||||||
return `
|
return `
|
||||||
Control speaker and microphone volumes easily!
|
Control speaker and microphone volumes easily!
|
||||||
Options:
|
Options:
|
||||||
sink-set [number]: set sink(speaker) volume with [number], 0 to ${Wireplumber.getDefault().getMaxSinkVolume()}.
|
sink-set [number]: set sink(speaker) volume with [number], 0 to ${Wireplumber.getDefault().getMaxSinkVolume() || 100}.
|
||||||
sink-mute: toggle mute for the sink(speaker) device.
|
sink-mute: toggle mute for the sink(speaker) device.
|
||||||
sink-increase [number]: increases sink(speaker) volume with [number].
|
sink-increase [number]: increases sink(speaker) volume with [number].
|
||||||
sink-decrease [number]: decreases 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-set [number]: set source(microphone) volume with [number], 0 to ${Wireplumber.getDefault().getMaxSourceVolume() || 100}.
|
||||||
source-mute: toggle mute for the source(microphone) device.
|
source-mute: toggle mute for the source(microphone) device.
|
||||||
source-increase [number]: increases source(microphone) volume with [number].
|
source-increase [number]: increases source(microphone) volume with [number].
|
||||||
source-decrease [number]: decreases source(microphone) volume with [number]
|
source-decrease [number]: decreases source(microphone) volume with [number]
|
||||||
@@ -158,11 +167,13 @@ Options:
|
|||||||
open [window_name]: sets specified window's visibility to true.
|
open [window_name]: sets specified window's visibility to true.
|
||||||
close [window_name]: sets specified window's visibility to false.
|
close [window_name]: sets specified window's visibility to false.
|
||||||
toggle [window_name]: toggles visibility of specified window.
|
toggle [window_name]: toggles visibility of specified window.
|
||||||
|
windows: shows available windows to control.
|
||||||
reload: creates a new astal instance and removes this one.
|
reload: creates a new astal instance and removes this one.
|
||||||
volume: wireplumber volume controller, see "volume help".
|
volume: wireplumber volume controller, see "volume help".
|
||||||
runner: open the application runner.
|
runner: open the application runner.
|
||||||
|
c, code [js]: runs provided js in args and returns result.
|
||||||
show-ws-numbers: show or hide workspace numbers in bar.
|
show-ws-numbers: show or hide workspace numbers in bar.
|
||||||
help, -h, --help: shows this help message.
|
h, help: shows this help message.
|
||||||
|
|
||||||
2025 (c) retrozinndev's Hyprland-Dots, licensed under the MIT License.
|
2025 (c) retrozinndev's Hyprland-Dots, licensed under the MIT License.
|
||||||
https://github.com/retrozinndev/Hyprland-Dots
|
https://github.com/retrozinndev/Hyprland-Dots
|
||||||
|
|||||||
+6
-1
@@ -42,7 +42,12 @@ window.ask-popup {
|
|||||||
& button {
|
& button {
|
||||||
background: colors.$bg-primary;
|
background: colors.$bg-primary;
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
padding: 6px;
|
padding: 9px 6px;
|
||||||
|
|
||||||
|
& label {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
margin: {
|
margin: {
|
||||||
left: 4px;
|
left: 4px;
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
@mixin reset-props {
|
@mixin reset-props {
|
||||||
all: unset;
|
all: unset;
|
||||||
transition: 120ms linear;
|
transition: 120ms linear;
|
||||||
font-family: "Cantarell", "Noto Sans",
|
font-family: "Adwaita Sans", "Cantarell", "Noto Sans",
|
||||||
"Noto Sans CJK JP", "Noto Sans CJK KR",
|
"Noto Sans CJK JP", "Noto Sans CJK KR",
|
||||||
"Noto Sans CJK HK", "Noto Sans CJK SC",
|
"Noto Sans CJK HK", "Noto Sans CJK SC",
|
||||||
"Noto Sans CJK TC", sans-serif,
|
"Noto Sans CJK TC", sans-serif,
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { PopupWindow, PopupWindowProps } from "./PopupWindow";
|
|||||||
import { Astal, Gtk, Widget } from "astal/gtk3";
|
import { Astal, Gtk, Widget } from "astal/gtk3";
|
||||||
import { Separator } from "./Separator";
|
import { Separator } from "./Separator";
|
||||||
import { tr } from "../i18n/intl";
|
import { tr } from "../i18n/intl";
|
||||||
|
import { Windows } from "../windows";
|
||||||
|
|
||||||
export type AskPopupProps = {
|
export type AskPopupProps = {
|
||||||
title?: string | Binding<string | undefined>;
|
title?: string | Binding<string | undefined>;
|
||||||
@@ -17,15 +18,16 @@ export type AskPopupProps = {
|
|||||||
* A Popup Widget that asks yes or no to a certain question.
|
* A Popup Widget that asks yes or no to a certain question.
|
||||||
* Runs onAccept() when user accepts or else onDecline() when
|
* Runs onAccept() when user accepts or else onDecline() when
|
||||||
* user doesn't accept or closes window.
|
* user doesn't accept or closes window.
|
||||||
|
* This window isn't registered in this shell windowing stuff.
|
||||||
*/
|
*/
|
||||||
export function AskPopup(props: AskPopupProps) {
|
export function AskPopup(props: AskPopupProps): Gtk.Window {
|
||||||
const buttons = [
|
const buttons = [
|
||||||
new Widget.Button({
|
new Widget.Button({
|
||||||
className: "cancel",
|
className: "cancel",
|
||||||
hexpand: true,
|
hexpand: true,
|
||||||
label: props.cancelText || tr("ask_popup.options.cancel") || "Cancel",
|
label: props.cancelText || tr("ask_popup.options.cancel") || "Cancel",
|
||||||
onClick: (_) => {
|
onClick: (_) => {
|
||||||
window.destroy();
|
window.close();
|
||||||
props.onCancel && props.onCancel();
|
props.onCancel && props.onCancel();
|
||||||
}
|
}
|
||||||
} as Widget.ButtonProps),
|
} as Widget.ButtonProps),
|
||||||
@@ -34,15 +36,14 @@ export function AskPopup(props: AskPopupProps) {
|
|||||||
hexpand: true,
|
hexpand: true,
|
||||||
label: props.acceptText || tr("ask_popup.options.accept") || "Ok",
|
label: props.acceptText || tr("ask_popup.options.accept") || "Ok",
|
||||||
onClick: (_) => {
|
onClick: (_) => {
|
||||||
window.destroy();
|
window.close();
|
||||||
props.onAccept && props.onAccept();
|
props.onAccept && props.onAccept();
|
||||||
}
|
}
|
||||||
} as Widget.ButtonProps)
|
} as Widget.ButtonProps)
|
||||||
];
|
];
|
||||||
|
|
||||||
const window = PopupWindow({
|
const window = Windows.createWindowForFocusedMonitor(PopupWindow({
|
||||||
namespace: "ask-popup",
|
namespace: "ask-popup",
|
||||||
visible: true,
|
|
||||||
className: "ask-popup",
|
className: "ask-popup",
|
||||||
exclusivity: Astal.Exclusivity.IGNORE,
|
exclusivity: Astal.Exclusivity.IGNORE,
|
||||||
widthRequest: 350,
|
widthRequest: 350,
|
||||||
@@ -80,5 +81,7 @@ export function AskPopup(props: AskPopupProps) {
|
|||||||
} as Widget.BoxProps)
|
} as Widget.BoxProps)
|
||||||
]
|
]
|
||||||
} as Widget.BoxProps)
|
} as Widget.BoxProps)
|
||||||
} as PopupWindowProps);
|
} as PopupWindowProps))();
|
||||||
|
|
||||||
|
return window;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { Binding } from "astal";
|
|||||||
import { Astal, Gdk, Gtk, Widget } from "astal/gtk3";
|
import { Astal, Gdk, Gtk, Widget } from "astal/gtk3";
|
||||||
|
|
||||||
|
|
||||||
const { TOP, BOTTOM, LEFT, RIGHT }: typeof Astal.WindowAnchor = Astal.WindowAnchor;
|
const { TOP, BOTTOM, LEFT, RIGHT } = Astal.WindowAnchor;
|
||||||
|
|
||||||
export type PopupWindowProps = Pick<Widget.WindowProps,
|
export type PopupWindowProps = Pick<Widget.WindowProps,
|
||||||
"namespace"
|
"namespace"
|
||||||
@@ -16,27 +16,26 @@ export type PopupWindowProps = Pick<Widget.WindowProps,
|
|||||||
| "layer"
|
| "layer"
|
||||||
| "widthRequest"
|
| "widthRequest"
|
||||||
| "heightRequest"
|
| "heightRequest"
|
||||||
| "child"
|
|
||||||
| "monitor"
|
| "monitor"
|
||||||
| "setup"
|
| "setup"
|
||||||
| "exclusivity"> & {
|
| "exclusivity"> & {
|
||||||
|
child?: (Gtk.Widget | Binding<Gtk.Widget | undefined>);
|
||||||
|
children?: (Array<Gtk.Widget | Binding<Gtk.Widget | undefined>>);
|
||||||
marginTop?: number;
|
marginTop?: number;
|
||||||
marginLeft?: number;
|
marginLeft?: number;
|
||||||
marginBottom?: number;
|
marginBottom?: number;
|
||||||
marginRight?: number;
|
marginRight?: number;
|
||||||
onKeyPressEvent?: (self: Widget.Window, event: Gdk.Event) => void;
|
onKeyPressEvent?: (self: Widget.Window, event: Gdk.Event) => void;
|
||||||
/** Do something else instead of hiding window on close action(clicking outside conent / pressing Escape)
|
/** Do something else instead of closing window on close action(clicking outside conent/pressing Escape)
|
||||||
* Observation: onClose() function will still be ran after close action if defined.
|
* Observation: onClose() function will still be ran after close action if defined.
|
||||||
*/
|
*/
|
||||||
closeAction?: (self: Widget.Window) => void;
|
closeAction?: (self: Widget.Window) => void;
|
||||||
onClose?: (self: Widget.Window) => void;
|
onClose?: (self: Widget.Window) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function PopupWindow(props: PopupWindowProps): Widget.Window {
|
export const PopupWindow = (props: PopupWindowProps): Widget.Window => {
|
||||||
if(!props.closeAction)
|
if(!props.closeAction)
|
||||||
props.closeAction = (window) => {
|
props.closeAction = (window) => window.close();
|
||||||
window.hide();
|
|
||||||
};
|
|
||||||
|
|
||||||
return new Widget.Window({
|
return new Widget.Window({
|
||||||
namespace: props?.namespace || "popup-window",
|
namespace: props?.namespace || "popup-window",
|
||||||
@@ -47,9 +46,8 @@ export function PopupWindow(props: PopupWindowProps): Widget.Window {
|
|||||||
keymode: Astal.Keymode.EXCLUSIVE,
|
keymode: Astal.Keymode.EXCLUSIVE,
|
||||||
layer: props?.layer || Astal.Layer.OVERLAY,
|
layer: props?.layer || Astal.Layer.OVERLAY,
|
||||||
focusOnMap: true,
|
focusOnMap: true,
|
||||||
visible: props?.visible,
|
|
||||||
monitor: props?.monitor || 0,
|
|
||||||
setup: props.setup,
|
setup: props.setup,
|
||||||
|
monitor: props.monitor || 0,
|
||||||
onButtonPressEvent: (_, event: Gdk.Event) => {
|
onButtonPressEvent: (_, event: Gdk.Event) => {
|
||||||
const [, posX, posY] = event.get_coords();
|
const [, posX, posY] = event.get_coords();
|
||||||
const childAllocation = _.get_child()!.get_allocation();
|
const childAllocation = _.get_child()!.get_allocation();
|
||||||
@@ -88,7 +86,8 @@ export function PopupWindow(props: PopupWindowProps): Widget.Window {
|
|||||||
widthRequest: props.widthRequest,
|
widthRequest: props.widthRequest,
|
||||||
heightRequest: props.heightRequest,
|
heightRequest: props.heightRequest,
|
||||||
onButtonPressEvent: () => true,
|
onButtonPressEvent: () => true,
|
||||||
child: props.child
|
...(props.child ? { child: props.child } : {}),
|
||||||
|
...(props.children ? { children: props.children } : {})
|
||||||
} as Widget.BoxProps)
|
} as Widget.BoxProps)
|
||||||
} as Widget.WindowProps);
|
} as Widget.WindowProps);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { Windows } from "../../windows";
|
|||||||
|
|
||||||
export function Apps(): Gtk.Widget {
|
export function Apps(): Gtk.Widget {
|
||||||
return new Widget.EventBox({
|
return new Widget.EventBox({
|
||||||
onClickRelease: () => Windows.getWindow("apps-window")?.show(),
|
onClickRelease: () => Windows.open("apps-window"),
|
||||||
className: "apps",
|
className: "apps",
|
||||||
child: new Widget.Box({
|
child: new Widget.Box({
|
||||||
child: new Widget.Icon({
|
child: new Widget.Icon({
|
||||||
|
|||||||
@@ -2,14 +2,13 @@ import { Gtk, Widget } from "astal/gtk3";
|
|||||||
import { getDateTime } from "../../scripts/time";
|
import { getDateTime } from "../../scripts/time";
|
||||||
import { bind, GLib } from "astal";
|
import { bind, GLib } from "astal";
|
||||||
import { Windows } from "../../windows";
|
import { Windows } from "../../windows";
|
||||||
import { CenterWindow } from "../../window/CenterWindow";
|
|
||||||
|
|
||||||
export function Clock(): Gtk.Widget {
|
export function Clock(): Gtk.Widget {
|
||||||
return new Widget.Box({
|
return new Widget.Box({
|
||||||
className: bind(CenterWindow, "visible").as((visible: boolean) =>
|
className: bind(Windows, "openWindows").as((openWins) =>
|
||||||
visible ? "clock open" : "clock"),
|
Object.hasOwn(openWins, "center-window") ? "open clock" : "clock"),
|
||||||
child: new Widget.Button({
|
child: new Widget.Button({
|
||||||
onClick: () => Windows.toggle(CenterWindow),
|
onClick: () => Windows.toggle("center-window"),
|
||||||
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")
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -71,9 +71,8 @@ export function Media(): Gtk.Widget {
|
|||||||
|
|
||||||
const mediaWidget = new Widget.EventBox({
|
const mediaWidget = new Widget.EventBox({
|
||||||
className: "media-eventbox",
|
className: "media-eventbox",
|
||||||
visible: bind(mpris, "players").as((players: Array<AstalMpris.Player>) => {
|
visible: bind(mpris, "players").as((players: Array<AstalMpris.Player>) =>
|
||||||
return players[0] && players[0].get_available() || CenterWindow.is_visible();
|
players[0] && players[0].get_available()),
|
||||||
}),
|
|
||||||
onDestroy: (_) => {
|
onDestroy: (_) => {
|
||||||
hoverConnectionId !== undefined &&
|
hoverConnectionId !== undefined &&
|
||||||
_.disconnect(hoverConnectionId);
|
_.disconnect(hoverConnectionId);
|
||||||
@@ -81,7 +80,7 @@ export function Media(): Gtk.Widget {
|
|||||||
hoverLostConnectionId !== undefined &&
|
hoverLostConnectionId !== undefined &&
|
||||||
_.disconnect(hoverLostConnectionId);
|
_.disconnect(hoverLostConnectionId);
|
||||||
},
|
},
|
||||||
onClick: () => Windows.toggle(CenterWindow),
|
onClick: () => Windows.toggle("center-window"),
|
||||||
child: new Widget.Box({
|
child: new Widget.Box({
|
||||||
className: "media",
|
className: "media",
|
||||||
children: [
|
children: [
|
||||||
|
|||||||
@@ -5,16 +5,15 @@ import AstalWp from "gi://AstalWp";
|
|||||||
import { bind, Variable } from "astal";
|
import { bind, Variable } from "astal";
|
||||||
import { Gtk, Widget } from "astal/gtk3";
|
import { Gtk, Widget } from "astal/gtk3";
|
||||||
import { Wireplumber } from "../../scripts/volume";
|
import { Wireplumber } from "../../scripts/volume";
|
||||||
import { ControlCenter } from "../../window/ControlCenter";
|
|
||||||
import { Notifications } from "../../scripts/notifications";
|
import { Notifications } from "../../scripts/notifications";
|
||||||
import { Windows } from "../../windows";
|
import { Windows } from "../../windows";
|
||||||
|
|
||||||
|
|
||||||
export function Status(): Gtk.Widget {
|
export function Status(): Gtk.Widget {
|
||||||
return new Widget.EventBox({
|
return new Widget.EventBox({
|
||||||
className: bind(ControlCenter, "visible").as((visible: boolean) =>
|
className: bind(Windows, "openWindows").as((openWins) =>
|
||||||
visible ? "status open" : "status"),
|
Object.hasOwn(openWins, "control-center") ? "open status" : "status"),
|
||||||
onClick: () => Windows.toggle(ControlCenter!),
|
onClick: () => Windows.toggle("control-center"),
|
||||||
child: new Widget.Box({
|
child: new Widget.Box({
|
||||||
children: [
|
children: [
|
||||||
volumeStatusSlider({
|
volumeStatusSlider({
|
||||||
@@ -63,6 +62,7 @@ function volumeStatusSlider(props: { className?: string, endpoint: AstalWp.Endpo
|
|||||||
|
|
||||||
revealer.add(new Widget.Slider({
|
revealer.add(new Widget.Slider({
|
||||||
className: "slider",
|
className: "slider",
|
||||||
|
setup: (slider) => slider.set_value(Math.floor(props.endpoint.get_volume() * 100)),
|
||||||
onDragged: (slider) => props.endpoint.set_volume(slider.value / 100),
|
onDragged: (slider) => props.endpoint.set_volume(slider.value / 100),
|
||||||
value: bind(props.endpoint, "volume").as((volume) =>
|
value: bind(props.endpoint, "volume").as((volume) =>
|
||||||
Math.floor(volume * 100)),
|
Math.floor(volume * 100)),
|
||||||
|
|||||||
@@ -2,9 +2,10 @@ import { AstalIO, bind, Binding, execAsync, GLib, timeout } from "astal";
|
|||||||
import { Gtk, Widget } from "astal/gtk3";
|
import { Gtk, Widget } from "astal/gtk3";
|
||||||
import AstalMpris from "gi://AstalMpris";
|
import AstalMpris from "gi://AstalMpris";
|
||||||
|
|
||||||
let dragTimer: (AstalIO.Time|undefined);
|
|
||||||
|
|
||||||
export function BigMedia(): Gtk.Widget {
|
export function BigMedia(): Gtk.Widget {
|
||||||
|
let dragTimer: (AstalIO.Time|undefined);
|
||||||
|
|
||||||
return new Widget.Box({
|
return new Widget.Box({
|
||||||
className: "big-media",
|
className: "big-media",
|
||||||
orientation: Gtk.Orientation.VERTICAL,
|
orientation: Gtk.Orientation.VERTICAL,
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { HistoryNotification, Notifications } from "../../scripts/notifications"
|
|||||||
import { NotificationWidget } from "../Notification";
|
import { NotificationWidget } from "../Notification";
|
||||||
|
|
||||||
|
|
||||||
export const NotifHistory: Gtk.Widget = new Widget.Box({
|
export const NotifHistory = () => new Widget.Box({
|
||||||
orientation: Gtk.Orientation.VERTICAL,
|
orientation: Gtk.Orientation.VERTICAL,
|
||||||
className: "history",
|
className: "history",
|
||||||
visible: bind(Notifications.getDefault(), "history").as(history => history.length > 0),
|
visible: bind(Notifications.getDefault(), "history").as(history => history.length > 0),
|
||||||
|
|||||||
@@ -2,20 +2,29 @@ import { timeout, Variable } from "astal";
|
|||||||
import { Gtk, Widget } from "astal/gtk3";
|
import { Gtk, Widget } from "astal/gtk3";
|
||||||
import { Page } from "./pages/Page";
|
import { Page } from "./pages/Page";
|
||||||
|
|
||||||
const currentPage = new Variable<Page|undefined>(undefined);
|
const currentPage = new Variable<Page | undefined>(undefined);
|
||||||
|
let pagesInstance: (Widget.Revealer | undefined);
|
||||||
|
|
||||||
export const PagesWidget: Widget.Revealer = new Widget.Revealer({
|
export const PagesWidget = () => {
|
||||||
revealChild: false,
|
const revealer = new Widget.Revealer({
|
||||||
className: "pages",
|
revealChild: false,
|
||||||
transitionType: Gtk.RevealerTransitionType.SLIDE_DOWN,
|
className: "pages",
|
||||||
transitionDuration: 360,
|
transitionType: Gtk.RevealerTransitionType.SLIDE_DOWN,
|
||||||
child: currentPage((page: (Page|undefined)) =>
|
transitionDuration: 360,
|
||||||
!page ? new Widget.Box() : page.getPage())
|
child: currentPage((page: (Page|undefined)) =>
|
||||||
} as Widget.RevealerProps);
|
!page ? new Widget.Box() : page.getPage())
|
||||||
|
} as Widget.RevealerProps);
|
||||||
|
|
||||||
|
pagesInstance = revealer;
|
||||||
|
|
||||||
|
return revealer;
|
||||||
|
}
|
||||||
|
|
||||||
export function showPages(page: Page): void {
|
export function showPages(page: Page): void {
|
||||||
|
if(!pagesInstance) return;
|
||||||
|
|
||||||
currentPage.set(page);
|
currentPage.set(page);
|
||||||
PagesWidget.set_reveal_child(true);
|
pagesInstance.set_reveal_child(true);
|
||||||
page.props.onOpen && page.props.onOpen();
|
page.props.onOpen && page.props.onOpen();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -24,7 +33,9 @@ export function getPage(): (Page|undefined) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function togglePage(page: Page): void {
|
export function togglePage(page: Page): void {
|
||||||
if(!PagesWidget.revealChild) {
|
if(!pagesInstance) return;
|
||||||
|
|
||||||
|
if(!pagesInstance.revealChild) {
|
||||||
showPages(page);
|
showPages(page);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -33,10 +44,12 @@ export function togglePage(page: Page): void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function hidePages() {
|
export function hidePages() {
|
||||||
PagesWidget.set_reveal_child(false);
|
if(!pagesInstance) return;
|
||||||
|
|
||||||
|
pagesInstance.set_reveal_child(false);
|
||||||
if(!currentPage.get()) return;
|
if(!currentPage.get()) return;
|
||||||
|
|
||||||
timeout(500, () => {
|
timeout(pagesInstance.transitionDuration || 500, () => {
|
||||||
if(currentPage.get() && currentPage.get()?.props.onClose)
|
if(currentPage.get() && currentPage.get()?.props.onClose)
|
||||||
currentPage.get()!.props.onClose!();
|
currentPage.get()!.props.onClose!();
|
||||||
|
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ function LogoutButton(): Widget.Button {
|
|||||||
} as Widget.ButtonProps);
|
} as Widget.ButtonProps);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const QuickActions: Widget.Box = new Widget.Box({
|
export const QuickActions = () => new Widget.Box({
|
||||||
className: "quickactions",
|
className: "quickactions",
|
||||||
children: [
|
children: [
|
||||||
new Widget.Box({
|
new Widget.Box({
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { bind } from "astal";
|
|||||||
import { Gtk, Widget } from "astal/gtk3";
|
import { Gtk, Widget } from "astal/gtk3";
|
||||||
import { Wireplumber } from "../../scripts/volume";
|
import { Wireplumber } from "../../scripts/volume";
|
||||||
|
|
||||||
export const Sliders: Gtk.Widget = new Widget.Box({
|
export const Sliders = () => new Widget.Box({
|
||||||
className: "sliders",
|
className: "sliders",
|
||||||
orientation: Gtk.Orientation.VERTICAL,
|
orientation: Gtk.Orientation.VERTICAL,
|
||||||
expand: true,
|
expand: true,
|
||||||
@@ -17,6 +17,7 @@ export const Sliders: Gtk.Widget = new Widget.Box({
|
|||||||
new Widget.Slider({
|
new Widget.Slider({
|
||||||
drawValue: false,
|
drawValue: false,
|
||||||
hexpand: true,
|
hexpand: true,
|
||||||
|
setup: (slider) => slider.set_value(Wireplumber.getDefault().getSinkVolume()),
|
||||||
value: bind(Wireplumber.getDefault().getDefaultSink(), "volume").as((volume: number) =>
|
value: bind(Wireplumber.getDefault().getDefaultSink(), "volume").as((volume: number) =>
|
||||||
Math.floor(volume * 100)),
|
Math.floor(volume * 100)),
|
||||||
max: Wireplumber.getDefault().getMaxSinkVolume(),
|
max: Wireplumber.getDefault().getMaxSinkVolume(),
|
||||||
@@ -34,6 +35,7 @@ export const Sliders: Gtk.Widget = new Widget.Box({
|
|||||||
new Widget.Slider({
|
new Widget.Slider({
|
||||||
drawValue: false,
|
drawValue: false,
|
||||||
hexpand: true,
|
hexpand: true,
|
||||||
|
setup: (slider) => slider.set_value(Wireplumber.getDefault().getSourceVolume()),
|
||||||
value: bind(Wireplumber.getDefault().getDefaultSource(), "volume").as((volume: number) =>
|
value: bind(Wireplumber.getDefault().getDefaultSource(), "volume").as((volume: number) =>
|
||||||
Math.floor(volume * 100)),
|
Math.floor(volume * 100)),
|
||||||
max: Wireplumber.getDefault().getMaxSourceVolume(),
|
max: Wireplumber.getDefault().getMaxSourceVolume(),
|
||||||
|
|||||||
@@ -3,13 +3,13 @@ import { TileNetwork } from "./tiles/Network";
|
|||||||
import { TileBluetooth } from "./tiles/Bluetooth";
|
import { TileBluetooth } from "./tiles/Bluetooth";
|
||||||
import { TileDND } from "./tiles/DoNotDisturb";
|
import { TileDND } from "./tiles/DoNotDisturb";
|
||||||
|
|
||||||
export const tileList: Array<any> = [
|
export const tileList: Array<() => Gtk.Widget> = [
|
||||||
TileNetwork,
|
TileNetwork,
|
||||||
TileBluetooth,
|
TileBluetooth,
|
||||||
TileDND
|
TileDND
|
||||||
];
|
];
|
||||||
|
|
||||||
export function TilesWidget(): Gtk.Widget {
|
export function Tiles(): Gtk.Widget {
|
||||||
const tilesFlowBox: Gtk.FlowBox = new Gtk.FlowBox({
|
const tilesFlowBox: Gtk.FlowBox = new Gtk.FlowBox({
|
||||||
visible: true,
|
visible: true,
|
||||||
orientation: Gtk.Orientation.HORIZONTAL,
|
orientation: Gtk.Orientation.HORIZONTAL,
|
||||||
@@ -21,13 +21,11 @@ export function TilesWidget(): Gtk.Widget {
|
|||||||
homogeneous: true,
|
homogeneous: true,
|
||||||
} as Gtk.FlowBox.ConstructorProps);
|
} as Gtk.FlowBox.ConstructorProps);
|
||||||
|
|
||||||
tileList.map((item: Gtk.Widget) =>
|
tileList.map((item: (() => Gtk.Widget)) =>
|
||||||
tilesFlowBox.insert(item, -1));
|
tilesFlowBox.insert(item(), -1));
|
||||||
|
|
||||||
return new Widget.Box({
|
return new Widget.Box({
|
||||||
className: "tiles-container",
|
className: "tiles-container",
|
||||||
child: tilesFlowBox
|
child: tilesFlowBox
|
||||||
} as Widget.BoxProps);
|
} as Widget.BoxProps);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Tiles: Gtk.Widget = TilesWidget();
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { togglePage } from "../Pages";
|
|||||||
import { PageNetwork } from "../pages/Network";
|
import { PageNetwork } from "../pages/Network";
|
||||||
import { tr } from "../../../i18n/intl";
|
import { tr } from "../../../i18n/intl";
|
||||||
|
|
||||||
export const TileNetwork = new Widget.Box({
|
export const TileNetwork = () => new Widget.Box({
|
||||||
child: Variable.derive([
|
child: Variable.derive([
|
||||||
bind(AstalNetwork.get_default(), "primary"),
|
bind(AstalNetwork.get_default(), "primary"),
|
||||||
bind(AstalNetwork.get_default(), "wired"),
|
bind(AstalNetwork.get_default(), "wired"),
|
||||||
@@ -36,7 +36,7 @@ export const TileNetwork = new Widget.Box({
|
|||||||
icon: "",
|
icon: "",
|
||||||
iconSize: 16,
|
iconSize: 16,
|
||||||
toggleState: bind(wifi, "enabled")
|
toggleState: bind(wifi, "enabled")
|
||||||
} as TileProps);
|
} as TileProps)();
|
||||||
|
|
||||||
} else if(primary === AstalNetwork.Primary.WIRED) {
|
} else if(primary === AstalNetwork.Primary.WIRED) {
|
||||||
return Tile({
|
return Tile({
|
||||||
@@ -69,7 +69,7 @@ export const TileNetwork = new Widget.Box({
|
|||||||
internet === AstalNetwork.Internet.CONNECTING
|
internet === AstalNetwork.Internet.CONNECTING
|
||||||
|| internet === AstalNetwork.Internet.CONNECTED
|
|| internet === AstalNetwork.Internet.CONNECTED
|
||||||
)
|
)
|
||||||
} as TileProps);
|
} as TileProps)();
|
||||||
}
|
}
|
||||||
|
|
||||||
return Tile({
|
return Tile({
|
||||||
@@ -82,6 +82,6 @@ export const TileNetwork = new Widget.Box({
|
|||||||
iconSize: 16,
|
iconSize: 16,
|
||||||
toggleState: bind(wired, "internet").as((internet: AstalNetwork.Internet) =>
|
toggleState: bind(wired, "internet").as((internet: AstalNetwork.Internet) =>
|
||||||
internet === AstalNetwork.Internet.CONNECTING || internet === AstalNetwork.Internet.CONNECTED)
|
internet === AstalNetwork.Internet.CONNECTING || internet === AstalNetwork.Internet.CONNECTED)
|
||||||
} as TileProps);
|
} as TileProps)();
|
||||||
})()
|
})()
|
||||||
} as Widget.BoxProps);
|
} as Widget.BoxProps);
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export type TileProps = {
|
|||||||
onClickMore?: () => void;
|
onClickMore?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Tile(props: TileProps): Widget.EventBox {
|
export function Tile(props: TileProps): (() => Widget.EventBox) {
|
||||||
const toggled = new Variable<boolean>(props.toggleState instanceof Binding ?
|
const toggled = new Variable<boolean>(props.toggleState instanceof Binding ?
|
||||||
(props.toggleState.get() || false) : (props.toggleState || false));
|
(props.toggleState.get() || false) : (props.toggleState || false));
|
||||||
|
|
||||||
@@ -24,7 +24,7 @@ export function Tile(props: TileProps): Widget.EventBox {
|
|||||||
if(props?.toggleState instanceof Binding)
|
if(props?.toggleState instanceof Binding)
|
||||||
subscription = props.toggleState.subscribe(val => toggled.set(val || false));
|
subscription = props.toggleState.subscribe(val => toggled.set(val || false));
|
||||||
|
|
||||||
return new Widget.EventBox({
|
return () => new Widget.EventBox({
|
||||||
className: toggled().as((state: boolean) =>
|
className: toggled().as((state: boolean) =>
|
||||||
state ? "tile-eventbox toggled" : "tile-eventbox"),
|
state ? "tile-eventbox toggled" : "tile-eventbox"),
|
||||||
expand: true,
|
expand: true,
|
||||||
@@ -39,7 +39,7 @@ export function Tile(props: TileProps): Widget.EventBox {
|
|||||||
toggled.set(true);
|
toggled.set(true);
|
||||||
props.onToggledOn && props.onToggledOn();
|
props.onToggledOn && props.onToggledOn();
|
||||||
},
|
},
|
||||||
onDestroy: () => subscription(),
|
onDestroy: () => subscription?.(),
|
||||||
child: new Widget.Box({
|
child: new Widget.Box({
|
||||||
className: (props.className instanceof Binding) ?
|
className: (props.className instanceof Binding) ?
|
||||||
props.className.as((clsName: (string|undefined)) =>
|
props.className.as((clsName: (string|undefined)) =>
|
||||||
|
|||||||
+99
-93
@@ -4,108 +4,114 @@ import { cleanExec, getAstalApps } from "../scripts/apps";
|
|||||||
import AstalApps from "gi://AstalApps";
|
import AstalApps from "gi://AstalApps";
|
||||||
|
|
||||||
const { TOP, LEFT, RIGHT, BOTTOM } = Astal.WindowAnchor;
|
const { TOP, LEFT, RIGHT, BOTTOM } = Astal.WindowAnchor;
|
||||||
const searchString = new Variable<string>("");
|
|
||||||
let searchSubscription: () => void;
|
|
||||||
|
|
||||||
export const AppsWindow = new Widget.Window({
|
export const AppsWindow = (mon: number): (Widget.Window) => {
|
||||||
namespace: "apps-window",
|
const searchString = new Variable<string>("");
|
||||||
layer: Astal.Layer.OVERLAY,
|
let searchSubscription: () => void;
|
||||||
exclusivity: Astal.Exclusivity.IGNORE,
|
|
||||||
anchor: TOP | LEFT | RIGHT | BOTTOM,
|
|
||||||
visible: false,
|
|
||||||
keymode: Astal.Keymode.EXCLUSIVE,
|
|
||||||
onDestroy: () => searchSubscription(),
|
|
||||||
setup: (window) => {
|
|
||||||
const flowbox = new Gtk.FlowBox({
|
|
||||||
rowSpacing: 6,
|
|
||||||
homogeneous: true,
|
|
||||||
columnSpacing: 6,
|
|
||||||
expand: false,
|
|
||||||
orientation: Gtk.Orientation.HORIZONTAL,
|
|
||||||
visible: true
|
|
||||||
} as Gtk.FlowBox.ConstructorProps);
|
|
||||||
|
|
||||||
const entry = new Widget.Entry({
|
return new Widget.Window({
|
||||||
className: "entry",
|
namespace: "apps-window",
|
||||||
halign: Gtk.Align.CENTER,
|
layer: Astal.Layer.OVERLAY,
|
||||||
primary_icon_name: "system-search",
|
exclusivity: Astal.Exclusivity.IGNORE,
|
||||||
onChanged: (entry) => {
|
anchor: TOP | LEFT | RIGHT | BOTTOM,
|
||||||
searchString.set(entry.text);
|
keymode: Astal.Keymode.EXCLUSIVE,
|
||||||
}
|
monitor: mon,
|
||||||
} as Widget.EntryProps);
|
onDestroy: () => {
|
||||||
|
searchString.set("");
|
||||||
|
searchSubscription()
|
||||||
|
},
|
||||||
|
setup: (window) => {
|
||||||
|
const flowbox = new Gtk.FlowBox({
|
||||||
|
rowSpacing: 6,
|
||||||
|
homogeneous: true,
|
||||||
|
columnSpacing: 6,
|
||||||
|
expand: false,
|
||||||
|
orientation: Gtk.Orientation.HORIZONTAL,
|
||||||
|
visible: true
|
||||||
|
} as Gtk.FlowBox.ConstructorProps);
|
||||||
|
|
||||||
searchSubscription = searchString.subscribe((str: string) => {
|
const entry = new Widget.Entry({
|
||||||
const results: Array<AstalApps.Application> = getAstalApps().fuzzy_query(str);
|
className: "entry",
|
||||||
|
halign: Gtk.Align.CENTER,
|
||||||
|
primary_icon_name: "system-search",
|
||||||
|
onChanged: (entry) => {
|
||||||
|
searchString.set(entry.text);
|
||||||
|
}
|
||||||
|
} as Widget.EntryProps);
|
||||||
|
|
||||||
// Destroy is handled by GnomeJS
|
searchSubscription = searchString.subscribe((str: string) => {
|
||||||
flowbox.get_children().map(flowboxChild => flowbox.remove(flowboxChild));
|
const results: Array<AstalApps.Application> = getAstalApps().fuzzy_query(str);
|
||||||
|
|
||||||
results.map(app => {
|
// Destroy is handled by GnomeJS
|
||||||
flowbox.insert(new Widget.Button({
|
flowbox.get_children().map(flowboxChild => flowbox.remove(flowboxChild));
|
||||||
onClick: (_button, event: Astal.ClickEvent) => {
|
|
||||||
if(event.button === Astal.MouseButton.PRIMARY) {
|
|
||||||
searchString.set("");
|
|
||||||
entry.text = "";
|
|
||||||
window.hide();
|
|
||||||
cleanExec(app);
|
|
||||||
|
|
||||||
return;
|
results.map(app => {
|
||||||
}
|
flowbox.insert(new Widget.Button({
|
||||||
|
onClick: (_button, event: Astal.ClickEvent) => {
|
||||||
|
if(event.button === Astal.MouseButton.PRIMARY) {
|
||||||
|
searchString.set("");
|
||||||
|
entry.text = "";
|
||||||
|
window.close();
|
||||||
|
cleanExec(app);
|
||||||
|
|
||||||
// select app launch options TODO
|
return;
|
||||||
},
|
}
|
||||||
child: new Widget.Box({
|
|
||||||
orientation: Gtk.Orientation.VERTICAL,
|
|
||||||
children: [
|
|
||||||
new Widget.Icon({
|
|
||||||
className: "icon",
|
|
||||||
expand: true,
|
|
||||||
icon: app.get_icon_name()
|
|
||||||
} as Widget.IconProps),
|
|
||||||
new Widget.Label({
|
|
||||||
className: "name",
|
|
||||||
truncate: true,
|
|
||||||
label: app.get_name()
|
|
||||||
} as Widget.LabelProps)
|
|
||||||
]
|
|
||||||
} as Widget.BoxProps)
|
|
||||||
} as Widget.ButtonProps), -1);
|
|
||||||
|
|
||||||
const flowboxchild = flowbox.get_children()[flowbox.get_children().length-1];
|
// select app launch options TODO
|
||||||
flowboxchild.set_valign(Gtk.Align.START);
|
},
|
||||||
|
child: new Widget.Box({
|
||||||
|
orientation: Gtk.Orientation.VERTICAL,
|
||||||
|
children: [
|
||||||
|
new Widget.Icon({
|
||||||
|
className: "icon",
|
||||||
|
expand: true,
|
||||||
|
icon: app.get_icon_name()
|
||||||
|
} as Widget.IconProps),
|
||||||
|
new Widget.Label({
|
||||||
|
className: "name",
|
||||||
|
truncate: true,
|
||||||
|
label: app.get_name()
|
||||||
|
} as Widget.LabelProps)
|
||||||
|
]
|
||||||
|
} as Widget.BoxProps)
|
||||||
|
} as Widget.ButtonProps), -1);
|
||||||
|
|
||||||
|
const flowboxchild = flowbox.get_children()[flowbox.get_children().length-1];
|
||||||
|
flowboxchild.set_valign(Gtk.Align.START);
|
||||||
|
});
|
||||||
|
|
||||||
|
const firstChild = flowbox.get_child_at_index(0);
|
||||||
|
firstChild && flowbox.select_child(firstChild);
|
||||||
});
|
});
|
||||||
|
|
||||||
const firstChild = flowbox.get_child_at_index(0);
|
window.add(new Widget.EventBox({
|
||||||
firstChild && flowbox.select_child(firstChild);
|
onClick: () => {
|
||||||
});
|
|
||||||
|
|
||||||
window.add(new Widget.EventBox({
|
|
||||||
onClick: () => {
|
|
||||||
searchString.set("");
|
|
||||||
entry.text = "";
|
|
||||||
window.hide();
|
|
||||||
},
|
|
||||||
onKeyPressEvent: (_, event: Gdk.Event) => {
|
|
||||||
if(event.get_keyval()[1] === Gdk.KEY_Escape) {
|
|
||||||
searchString.set("");
|
searchString.set("");
|
||||||
entry.text = "";
|
entry.text = "";
|
||||||
window.hide();
|
window.close();
|
||||||
}
|
},
|
||||||
},
|
onKeyPressEvent: (_, event: Gdk.Event) => {
|
||||||
child: new Widget.Box({
|
if(event.get_keyval()[1] === Gdk.KEY_Escape) {
|
||||||
className: "apps-window-container",
|
searchString.set("");
|
||||||
expand: true,
|
entry.text = "";
|
||||||
orientation: Gtk.Orientation.VERTICAL,
|
window.close();
|
||||||
children: [
|
}
|
||||||
entry,
|
},
|
||||||
new Widget.Scrollable({
|
child: new Widget.Box({
|
||||||
vscroll: Gtk.PolicyType.AUTOMATIC,
|
className: "apps-window-container",
|
||||||
hscroll: Gtk.PolicyType.NEVER,
|
expand: true,
|
||||||
expand: true,
|
orientation: Gtk.Orientation.VERTICAL,
|
||||||
child: flowbox
|
children: [
|
||||||
} as Widget.ScrollableProps)
|
entry,
|
||||||
]
|
new Widget.Scrollable({
|
||||||
} as Widget.BoxProps)
|
vscroll: Gtk.PolicyType.AUTOMATIC,
|
||||||
} as Widget.EventBoxProps));
|
hscroll: Gtk.PolicyType.NEVER,
|
||||||
}
|
expand: true,
|
||||||
} as Widget.WindowProps);
|
child: flowbox
|
||||||
|
} as Widget.ScrollableProps)
|
||||||
|
]
|
||||||
|
} as Widget.BoxProps)
|
||||||
|
} as Widget.EventBoxProps));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
+5
-5
@@ -1,22 +1,22 @@
|
|||||||
import { Astal, Gtk, Widget } from "astal/gtk3";
|
import { Astal, Gtk, Widget } from "astal/gtk3";
|
||||||
|
|
||||||
import { Clock } from "../widget/bar/Clock";
|
|
||||||
import { Tray } from "../widget/bar/Tray";
|
import { Tray } from "../widget/bar/Tray";
|
||||||
import { Workspaces } from "../widget/bar/Workspaces";
|
import { Workspaces } from "../widget/bar/Workspaces";
|
||||||
import { FocusedClient } from "../widget/bar/FocusedClient";
|
import { FocusedClient } from "../widget/bar/FocusedClient";
|
||||||
import { Media } from "../widget/bar/Media";
|
import { Media } from "../widget/bar/Media";
|
||||||
import { Status } from "../widget/bar/Status";
|
|
||||||
import { Apps } from "../widget/bar/Apps";
|
import { Apps } from "../widget/bar/Apps";
|
||||||
|
import { Clock } from "../widget/bar/Clock";
|
||||||
|
import { Status } from "../widget/bar/Status";
|
||||||
|
|
||||||
export const Bar: Widget.Window = new Widget.Window({
|
export const Bar = (mon: number) => new Widget.Window({
|
||||||
monitor: 0,
|
|
||||||
namespace: "top-bar",
|
namespace: "top-bar",
|
||||||
anchor: Astal.WindowAnchor.TOP | Astal.WindowAnchor.LEFT | Astal.WindowAnchor.RIGHT,
|
anchor: Astal.WindowAnchor.TOP | Astal.WindowAnchor.LEFT | Astal.WindowAnchor.RIGHT,
|
||||||
layer: Astal.Layer.TOP,
|
layer: Astal.Layer.TOP,
|
||||||
exclusivity: Astal.Exclusivity.EXCLUSIVE,
|
exclusivity: Astal.Exclusivity.EXCLUSIVE,
|
||||||
heightRequest: 46,
|
heightRequest: 46,
|
||||||
canFocus: false,
|
monitor: mon,
|
||||||
visible: true,
|
visible: true,
|
||||||
|
canFocus: false,
|
||||||
child: new Widget.Box({
|
child: new Widget.Box({
|
||||||
className: "bar-container",
|
className: "bar-container",
|
||||||
child: new Widget.CenterBox({
|
child: new Widget.CenterBox({
|
||||||
|
|||||||
+55
-62
@@ -1,73 +1,66 @@
|
|||||||
import { Gtk, Widget } from "astal/gtk3";
|
import { Gtk, Widget } from "astal/gtk3";
|
||||||
import { bind, GLib } from "astal";
|
import { GLib } from "astal";
|
||||||
|
|
||||||
import { getDateTime } from "../scripts/time";
|
import { getDateTime } from "../scripts/time";
|
||||||
import { BigMedia } from "../widget/center-window/BigMedia";
|
|
||||||
import { Separator, SeparatorProps } from "../widget/Separator";
|
import { Separator, SeparatorProps } from "../widget/Separator";
|
||||||
import { PopupWindow, PopupWindowProps } from "../widget/PopupWindow";
|
import { PopupWindow, PopupWindowProps } from "../widget/PopupWindow";
|
||||||
|
import { BigMedia } from "../widget/center-window/BigMedia";
|
||||||
|
|
||||||
const BigMediaWidget = BigMedia();
|
export const CenterWindow = (mon: number) => PopupWindow({
|
||||||
|
className: "center-window-container",
|
||||||
export const CenterWindow: Widget.Window = PopupWindow({
|
|
||||||
className: "center-window",
|
|
||||||
namespace: "center-window",
|
namespace: "center-window",
|
||||||
monitor: 0,
|
|
||||||
visible: false,
|
|
||||||
marginTop: 10,
|
marginTop: 10,
|
||||||
valign: Gtk.Align.START,
|
valign: Gtk.Align.START,
|
||||||
halign: Gtk.Align.CENTER,
|
halign: Gtk.Align.CENTER,
|
||||||
child: new Widget.Box({
|
monitor: mon,
|
||||||
className: "center-window-container",
|
children: [
|
||||||
children: [
|
new Widget.Box({
|
||||||
new Widget.Box({
|
className: "vertical left",
|
||||||
className: "vertical left",
|
orientation: Gtk.Orientation.VERTICAL,
|
||||||
orientation: Gtk.Orientation.VERTICAL,
|
children: [
|
||||||
children: [
|
new Widget.Box({
|
||||||
new Widget.Box({
|
className: "top",
|
||||||
className: "top",
|
orientation: Gtk.Orientation.VERTICAL,
|
||||||
orientation: Gtk.Orientation.VERTICAL,
|
valign: Gtk.Align.START,
|
||||||
valign: Gtk.Align.START,
|
children: [
|
||||||
children: [
|
new Widget.Label({
|
||||||
new Widget.Label({
|
className: "time",
|
||||||
className: "time",
|
label: getDateTime().as((dateTime: GLib.DateTime) =>
|
||||||
label: getDateTime().as((dateTime: GLib.DateTime) =>
|
dateTime.format("%H:%M"))
|
||||||
dateTime.format("%H:%M"))
|
} as Widget.LabelProps),
|
||||||
} as Widget.LabelProps),
|
new Widget.Label({
|
||||||
new Widget.Label({
|
className: "date",
|
||||||
className: "date",
|
label: getDateTime().as((dateTime: GLib.DateTime) =>
|
||||||
label: getDateTime().as((dateTime: GLib.DateTime) =>
|
dateTime.format("%A, %B %d"))
|
||||||
dateTime.format("%A, %B %d"))
|
} as Widget.LabelProps)
|
||||||
} as Widget.LabelProps)
|
]
|
||||||
]
|
} as Widget.BoxProps),
|
||||||
} as Widget.BoxProps),
|
new Widget.Box({
|
||||||
new Widget.Box({
|
className: "calendar-box",
|
||||||
className: "calendar-box",
|
vexpand: false,
|
||||||
vexpand: false,
|
hexpand: true,
|
||||||
hexpand: true,
|
valign: Gtk.Align.START,
|
||||||
valign: Gtk.Align.START,
|
child: new Gtk.Calendar({
|
||||||
child: new Gtk.Calendar({
|
visible: true,
|
||||||
visible: true,
|
showHeading: true,
|
||||||
show_heading: true,
|
showDayNames: true,
|
||||||
show_day_names: true,
|
showWeekNumbers: false
|
||||||
show_week_numbers: false
|
} as Gtk.Calendar.ConstructorProps)
|
||||||
} as Gtk.Calendar.ConstructorProps)
|
} as Widget.BoxProps)
|
||||||
} as Widget.BoxProps)
|
]
|
||||||
]
|
} as Widget.BoxProps),
|
||||||
} as Widget.BoxProps),
|
Separator({
|
||||||
Separator({
|
orientation: Gtk.Orientation.HORIZONTAL,
|
||||||
visible: bind(BigMediaWidget, "visible"),
|
alpha: .5,
|
||||||
orientation: Gtk.Orientation.HORIZONTAL,
|
cssColor: "gray",
|
||||||
alpha: .5,
|
size: 1
|
||||||
cssColor: "gray",
|
} as SeparatorProps),
|
||||||
size: 1
|
new Widget.Box({
|
||||||
} as SeparatorProps),
|
className: "vertical right",
|
||||||
new Widget.Box({
|
orientation: Gtk.Orientation.VERTICAL,
|
||||||
className: "vertical right",
|
children: [
|
||||||
orientation: Gtk.Orientation.VERTICAL,
|
BigMedia()
|
||||||
children: [
|
]
|
||||||
BigMediaWidget
|
} as Widget.BoxProps)
|
||||||
]
|
]
|
||||||
} as Widget.BoxProps)
|
|
||||||
]
|
|
||||||
} as Widget.BoxProps)
|
|
||||||
} as PopupWindowProps);
|
} as PopupWindowProps);
|
||||||
|
|||||||
+13
-16
@@ -4,11 +4,11 @@ import { Tiles } from "../widget/control-center/Tiles";
|
|||||||
import { Sliders } from "../widget/control-center/Sliders";
|
import { Sliders } from "../widget/control-center/Sliders";
|
||||||
import { hidePages, PagesWidget } from "../widget/control-center/Pages";
|
import { hidePages, PagesWidget } from "../widget/control-center/Pages";
|
||||||
import { NotifHistory } from "../widget/control-center/NotifHistory";
|
import { NotifHistory } from "../widget/control-center/NotifHistory";
|
||||||
|
import { Windows } from "../windows";
|
||||||
|
|
||||||
const connections: Array<number> = [];
|
|
||||||
const { TOP, LEFT, BOTTOM, RIGHT } = Astal.WindowAnchor;
|
const { TOP, LEFT, BOTTOM, RIGHT } = Astal.WindowAnchor;
|
||||||
|
|
||||||
export const ControlCenter = new Widget.Window({
|
export const ControlCenter = (mon: number) => new Widget.Window({
|
||||||
namespace: "control-center",
|
namespace: "control-center",
|
||||||
className: "control-center",
|
className: "control-center",
|
||||||
anchor: TOP | BOTTOM | LEFT | RIGHT,
|
anchor: TOP | BOTTOM | LEFT | RIGHT,
|
||||||
@@ -16,22 +16,23 @@ export const ControlCenter = new Widget.Window({
|
|||||||
keymode: Astal.Keymode.EXCLUSIVE,
|
keymode: Astal.Keymode.EXCLUSIVE,
|
||||||
layer: Astal.Layer.OVERLAY,
|
layer: Astal.Layer.OVERLAY,
|
||||||
focusOnMap: true,
|
focusOnMap: true,
|
||||||
visible: false,
|
monitor: mon,
|
||||||
monitor: 0,
|
onDestroy: (_) => {
|
||||||
onDestroy: (_) => connections.map(id => _.disconnect(id)),
|
hidePages();
|
||||||
|
},
|
||||||
onButtonPressEvent: (_, event: Gdk.Event) => {
|
onButtonPressEvent: (_, event: Gdk.Event) => {
|
||||||
const [, posX, posY] = event.get_coords();
|
const [, posX, posY] = event.get_coords();
|
||||||
const childAllocation = _.get_child()!.get_allocation();
|
const childAllocation = _.get_child()!.get_allocation();
|
||||||
|
|
||||||
if((posX < childAllocation.x || posX > (childAllocation.x + childAllocation.width)) ||
|
if((posX < childAllocation.x || posX > (childAllocation.x + childAllocation.width)) ||
|
||||||
(posY < childAllocation.y || posY > (childAllocation.y + childAllocation.height))) {
|
(posY < childAllocation.y || posY > (childAllocation.y + childAllocation.height))) {
|
||||||
_.hide();
|
Windows.close("control-center");
|
||||||
hidePages();
|
hidePages();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onKeyPressEvent: (_, event: Gdk.Event) => {
|
onKeyPressEvent: (_, event: Gdk.Event) => {
|
||||||
if(event.get_keyval()[1] === Gdk.KEY_Escape) {
|
if(event.get_keyval()[1] === Gdk.KEY_Escape) {
|
||||||
_.hide();
|
Windows.close("control-center");
|
||||||
hidePages();
|
hidePages();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -54,17 +55,13 @@ export const ControlCenter = new Widget.Window({
|
|||||||
widthRequest: 400,
|
widthRequest: 400,
|
||||||
vexpand: false,
|
vexpand: false,
|
||||||
children: [
|
children: [
|
||||||
QuickActions,
|
QuickActions(),
|
||||||
Sliders,
|
Sliders(),
|
||||||
Tiles,
|
Tiles(),
|
||||||
PagesWidget
|
PagesWidget()
|
||||||
]
|
]
|
||||||
} as Widget.BoxProps),
|
} as Widget.BoxProps),
|
||||||
NotifHistory
|
NotifHistory()
|
||||||
]
|
]
|
||||||
} as Widget.BoxProps)
|
} as Widget.BoxProps)
|
||||||
} as Widget.WindowProps);
|
} as Widget.WindowProps);
|
||||||
|
|
||||||
connections.push(ControlCenter.connect("hide", (_) => {
|
|
||||||
hidePages();
|
|
||||||
}));
|
|
||||||
|
|||||||
@@ -1,42 +1,23 @@
|
|||||||
import { Astal, Gtk, Widget } from "astal/gtk3";
|
import { Astal, Gtk, Widget } from "astal/gtk3";
|
||||||
import AstalNotifd from "gi://AstalNotifd";
|
|
||||||
import { bind } from "astal/binding";
|
import { bind } from "astal/binding";
|
||||||
import { Notifications } from "../scripts/notifications";
|
import { Notifications } from "../scripts/notifications";
|
||||||
import { NotificationWidget } from "../widget/Notification";
|
import { NotificationWidget } from "../widget/Notification";
|
||||||
|
|
||||||
const connections: Array<number> = [];
|
|
||||||
|
|
||||||
export const FloatingNotifications: Widget.Window = new Widget.Window({
|
export const FloatingNotifications = (mon: number) => new Widget.Window({
|
||||||
namespace: "floating-notifications",
|
namespace: "floating-notifications",
|
||||||
canFocus: false,
|
canFocus: false,
|
||||||
anchor: Astal.WindowAnchor.TOP | Astal.WindowAnchor.RIGHT,
|
anchor: Astal.WindowAnchor.TOP | Astal.WindowAnchor.RIGHT,
|
||||||
monitor: 0,
|
monitor: mon,
|
||||||
layer: Astal.Layer.OVERLAY,
|
layer: Astal.Layer.OVERLAY,
|
||||||
visible: false,
|
|
||||||
widthRequest: 450,
|
widthRequest: 450,
|
||||||
exclusivity: Astal.Exclusivity.NORMAL,
|
exclusivity: Astal.Exclusivity.NORMAL,
|
||||||
setup: (window) => {
|
|
||||||
connections.push(
|
|
||||||
Notifications.getDefault().connect("notification-added", (_, _notif: AstalNotifd.Notification) => {
|
|
||||||
!window.is_visible() && window.show();
|
|
||||||
}),
|
|
||||||
Notifications.getDefault().connect("notification-removed", (_: Notifications, _id: number) => {
|
|
||||||
window.is_visible() && _.notifications.length === 0 && window.hide()
|
|
||||||
window.isFocus = false;
|
|
||||||
})
|
|
||||||
);
|
|
||||||
},
|
|
||||||
onDestroy: () => connections.map(id => Notifications.getDefault().disconnect(id)),
|
|
||||||
child: new Widget.Box({
|
child: new Widget.Box({
|
||||||
className: "floating-notifications-container",
|
className: "floating-notifications-container",
|
||||||
orientation: Gtk.Orientation.VERTICAL,
|
orientation: Gtk.Orientation.VERTICAL,
|
||||||
homogeneous: false,
|
homogeneous: false,
|
||||||
visible: bind(Notifications.getDefault(), "notifications").as(notifs => notifs.length > 0),
|
visible: bind(Notifications.getDefault(), "notifications").as(notifs => notifs.length > 0),
|
||||||
children: bind(Notifications.getDefault(), "notifications").as((notifs) =>
|
children: bind(Notifications.getDefault(), "notifications").as((notifs) =>
|
||||||
notifs.map((item) =>
|
notifs.map((item) => NotificationWidget(item, () => Notifications.getDefault().removeNotification(item)))),
|
||||||
NotificationWidget(item,
|
|
||||||
() => Notifications.getDefault().removeNotification(item))
|
|
||||||
)
|
|
||||||
),
|
|
||||||
} as Widget.BoxProps)
|
} as Widget.BoxProps)
|
||||||
} as Widget.WindowProps);
|
} as Widget.WindowProps);
|
||||||
|
|||||||
@@ -6,14 +6,13 @@ import { AskPopup } from "../widget/AskPopup";
|
|||||||
|
|
||||||
const { TOP, LEFT, RIGHT, BOTTOM } = Astal.WindowAnchor;
|
const { TOP, LEFT, RIGHT, BOTTOM } = Astal.WindowAnchor;
|
||||||
|
|
||||||
export const LogoutMenu: Widget.Window = new Widget.Window({
|
export const LogoutMenu = (mon: number) => new Widget.Window({
|
||||||
namespace: "logout-menu",
|
namespace: "logout-menu",
|
||||||
anchor: TOP | LEFT | RIGHT | BOTTOM,
|
anchor: TOP | LEFT | RIGHT | BOTTOM,
|
||||||
layer: Astal.Layer.OVERLAY,
|
layer: Astal.Layer.OVERLAY,
|
||||||
exclusivity: Astal.Exclusivity.IGNORE,
|
exclusivity: Astal.Exclusivity.IGNORE,
|
||||||
keymode: Astal.Keymode.EXCLUSIVE,
|
keymode: Astal.Keymode.EXCLUSIVE,
|
||||||
monitor: 0,
|
monitor: mon,
|
||||||
visible: false,
|
|
||||||
onKeyPressEvent: (_, event: Gdk.Event) => {
|
onKeyPressEvent: (_, event: Gdk.Event) => {
|
||||||
event.get_keyval()[1] === Gdk.KEY_Escape &&
|
event.get_keyval()[1] === Gdk.KEY_Escape &&
|
||||||
_.hide();
|
_.hide();
|
||||||
@@ -57,8 +56,6 @@ export const LogoutMenu: Widget.Window = new Widget.Window({
|
|||||||
onClick: () => AskPopup({
|
onClick: () => AskPopup({
|
||||||
title: "Power Off",
|
title: "Power Off",
|
||||||
text: "Are you sure you want to power off? Unsaved work will be lost.",
|
text: "Are you sure you want to power off? Unsaved work will be lost.",
|
||||||
cancelText: "No! Let me go back",
|
|
||||||
acceptText: "Yes, shutdown",
|
|
||||||
onAccept: () => execAsync("systemctl poweroff")
|
onAccept: () => execAsync("systemctl poweroff")
|
||||||
})
|
})
|
||||||
} as Widget.ButtonProps),
|
} as Widget.ButtonProps),
|
||||||
@@ -68,8 +65,6 @@ export const LogoutMenu: Widget.Window = new Widget.Window({
|
|||||||
onClick: () => AskPopup({
|
onClick: () => AskPopup({
|
||||||
title: "Reboot",
|
title: "Reboot",
|
||||||
text: "Are you sure you want to Reboot? Unsaved work will be lost.",
|
text: "Are you sure you want to Reboot? Unsaved work will be lost.",
|
||||||
cancelText: "No! Let me go back",
|
|
||||||
acceptText: "Yes, reboot",
|
|
||||||
onAccept: () => execAsync("systemctl reboot")
|
onAccept: () => execAsync("systemctl reboot")
|
||||||
})
|
})
|
||||||
} as Widget.ButtonProps),
|
} as Widget.ButtonProps),
|
||||||
@@ -79,8 +74,6 @@ export const LogoutMenu: Widget.Window = new Widget.Window({
|
|||||||
onClick: () => AskPopup({
|
onClick: () => AskPopup({
|
||||||
title: "Suspend",
|
title: "Suspend",
|
||||||
text: "Are you sure you want to Suspend?",
|
text: "Are you sure you want to Suspend?",
|
||||||
cancelText: "No! Let me go back",
|
|
||||||
acceptText: "Yes, suspend",
|
|
||||||
onAccept: () => execAsync("systemctl suspend")
|
onAccept: () => execAsync("systemctl suspend")
|
||||||
})
|
})
|
||||||
} as Widget.ButtonProps),
|
} as Widget.ButtonProps),
|
||||||
@@ -90,8 +83,6 @@ export const LogoutMenu: Widget.Window = new Widget.Window({
|
|||||||
onClick: () => AskPopup({
|
onClick: () => AskPopup({
|
||||||
title: "Log out",
|
title: "Log out",
|
||||||
text: "Are you sure you want to log out? Your session will be ended.",
|
text: "Are you sure you want to log out? Your session will be ended.",
|
||||||
cancelText: "No! Let me go back",
|
|
||||||
acceptText: "Yes, please log out",
|
|
||||||
onAccept: () => execAsync(`sh -c "loginctl terminate-user ${GLib.getenv("USER") || "$USER"}"`)
|
onAccept: () => execAsync(`sh -c "loginctl terminate-user ${GLib.getenv("USER") || "$USER"}"`)
|
||||||
})
|
})
|
||||||
} as Widget.ButtonProps),
|
} as Widget.ButtonProps),
|
||||||
|
|||||||
+4
-4
@@ -20,15 +20,15 @@ export function setOSDMode(newMode: OSDModes): void {
|
|||||||
osdMode.set(newMode);
|
osdMode.set(newMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const OSD: Widget.Window = new Widget.Window({
|
export const OSD = (mon: number) => new Widget.Window({
|
||||||
namespace: "osd",
|
namespace: "osd",
|
||||||
layer: Astal.Layer.OVERLAY,
|
layer: Astal.Layer.OVERLAY,
|
||||||
anchor: Astal.WindowAnchor.BOTTOM,
|
anchor: Astal.WindowAnchor.BOTTOM,
|
||||||
canFocus: false,
|
canFocus: false,
|
||||||
margin_bottom: 80,
|
marginBottom: 80,
|
||||||
monitor: 0,
|
|
||||||
visible: false,
|
|
||||||
focusOnClick: false,
|
focusOnClick: false,
|
||||||
|
clickThrough: true,
|
||||||
|
monitor: mon,
|
||||||
child: new Widget.Box({
|
child: new Widget.Box({
|
||||||
className: "osd",
|
className: "osd",
|
||||||
children: [
|
children: [
|
||||||
|
|||||||
+164
-30
@@ -1,58 +1,192 @@
|
|||||||
import { Gtk } from "astal/gtk3";
|
import { Widget } from "astal/gtk3";
|
||||||
|
|
||||||
import { Bar } from "./window/Bar";
|
import { Bar } from "./window/Bar";
|
||||||
import { OSD } from "./window/OSD";
|
import { OSD } from "./window/OSD";
|
||||||
import { ControlCenter } from "./window/ControlCenter";
|
import { ControlCenter } from "./window/ControlCenter";
|
||||||
import { CenterWindow } from "./window/CenterWindow";
|
import { CenterWindow } from "./window/CenterWindow";
|
||||||
import { FloatingNotifications } from "./window/FloatingNotifications";
|
|
||||||
import { GObject, register } from "astal";
|
|
||||||
import { LogoutMenu } from "./window/LogoutMenu";
|
import { LogoutMenu } from "./window/LogoutMenu";
|
||||||
|
import { FloatingNotifications } from "./window/FloatingNotifications";
|
||||||
import { AppsWindow } from "./window/AppsWindow";
|
import { AppsWindow } from "./window/AppsWindow";
|
||||||
|
import AstalHyprland from "gi://AstalHyprland";
|
||||||
|
import { GObject } from "astal";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get open windows / interact with windows(e.g.: close, open or toggle)
|
* get open windows / interact with windows(e.g.: close, open or toggle)
|
||||||
*/
|
*/
|
||||||
@register({ GTypeName: "Windows" })
|
export const Windows = GObject.registerClass({
|
||||||
class WindowsClass extends GObject.Object {
|
GTypeName: "Windows",
|
||||||
private static windowsMap: Map<string, Gtk.Window> = new Map<string, Gtk.Window>();
|
Signals: {
|
||||||
|
"open": { param_types: [ GObject.TYPE_STRING ] },
|
||||||
|
"close": { param_types: [ GObject.TYPE_STRING ] }
|
||||||
|
},
|
||||||
|
Properties: {
|
||||||
|
"open-windows": GObject.ParamSpec.jsobject(
|
||||||
|
"open-windows",
|
||||||
|
"Open Windows",
|
||||||
|
"A Readonly object that stores open GTKLayerShell Windows",
|
||||||
|
GObject.ParamFlags.READABLE
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}, class Windows extends GObject.Object {
|
||||||
|
#openWindows: Record<string, Widget.Window | Array<Widget.Window>> = {};
|
||||||
|
static #instance: (Windows | null);
|
||||||
|
|
||||||
static {
|
#windows: Record<string, (() => (Widget.Window | Array<Widget.Window>))> = {
|
||||||
this.setWindow("bar", Bar);
|
"bar": this.createWindowForMonitors(Bar),
|
||||||
this.setWindow("osd", OSD);
|
"osd": this.createWindowForFocusedMonitor(OSD),
|
||||||
this.setWindow("control-center", ControlCenter);
|
"control-center": this.createWindowForFocusedMonitor(ControlCenter),
|
||||||
this.setWindow("center-window", CenterWindow);
|
"center-window": this.createWindowForFocusedMonitor(CenterWindow),
|
||||||
this.setWindow("logout-menu", LogoutMenu);
|
"logout-menu": this.createWindowForFocusedMonitor(LogoutMenu),
|
||||||
this.setWindow("floating-notifications", FloatingNotifications);
|
"floating-notifications": this.createWindowForFocusedMonitor(FloatingNotifications),
|
||||||
this.setWindow("apps-window", AppsWindow);
|
"apps-window": this.createWindowForFocusedMonitor(AppsWindow)
|
||||||
|
};
|
||||||
|
|
||||||
|
#windowConnections: Record<string, (Array<number> | Array<Array<number>>)> = {};
|
||||||
|
|
||||||
|
get windows() { return this.#windows; }
|
||||||
|
get openWindows(): Record<string, Widget.Window | Array<Widget.Window>> { return this.#openWindows; };
|
||||||
|
|
||||||
|
vfunc_dispose() {
|
||||||
|
for(const name of Object.keys(this.#windowConnections)) {
|
||||||
|
const window = this.openWindows[name];
|
||||||
|
if(!window) continue;
|
||||||
|
|
||||||
|
this.disconnectWindow(name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static setWindow(name: string, window: Gtk.Window): void {
|
private disconnectWindow(name: keyof typeof this.windows) {
|
||||||
WindowsClass.windowsMap.set(name, window);
|
const window = this.openWindows[name];
|
||||||
|
if(!window) {
|
||||||
|
console.log("couldn't disconnect, window is not open");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.#windowConnections[name].map((id: Array<number> | number) => {
|
||||||
|
if(Array.isArray(window)) {
|
||||||
|
window.map((win, i) => win.disconnect((id as Array<number>)[i]));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
window.disconnect(id as number);
|
||||||
|
});
|
||||||
|
|
||||||
|
delete this.#windowConnections[name];
|
||||||
}
|
}
|
||||||
|
|
||||||
public static getWindow(name: string): (Gtk.Window|undefined) {
|
private connectWindow(name: keyof typeof this.windows) {
|
||||||
return WindowsClass.windowsMap.get(name);
|
if(Object.hasOwn(this.#windowConnections, name)) return;
|
||||||
|
if(!this.openWindows?.[name]) {
|
||||||
|
console.log(`${name} is not open, will not connect`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(Array.isArray(this.openWindows[name])) {
|
||||||
|
this.#windowConnections[name] = this.openWindows[name].map(win => [
|
||||||
|
win.connect("map", (window) => {
|
||||||
|
this.#openWindows[name] = window;
|
||||||
|
}),
|
||||||
|
win.connect("destroy", () => {
|
||||||
|
this.disconnectWindow(name);
|
||||||
|
delete this.#openWindows[name];
|
||||||
|
})
|
||||||
|
]);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.#windowConnections[name] = [
|
||||||
|
this.openWindows[name].connect("map", (window) => {
|
||||||
|
this.#openWindows[name] = window;
|
||||||
|
}),
|
||||||
|
this.openWindows[name].connect("destroy", () => {
|
||||||
|
this.disconnectWindow(name);
|
||||||
|
delete this.#openWindows[name];
|
||||||
|
})
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public static getList(): Map<string, Gtk.Window> {
|
public static getDefault(): Windows {
|
||||||
return WindowsClass.windowsMap;
|
if(!this.#instance)
|
||||||
|
this.#instance = new Windows();
|
||||||
|
|
||||||
|
return this.#instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static open(window: Gtk.Window): void {
|
public createWindowForMonitors(windowFun: (mon: number) => Widget.Window): (() => Array<Widget.Window>) {
|
||||||
!WindowsClass.isVisible(window) && window.show();
|
return () => AstalHyprland.get_default().get_monitors().map(mon =>
|
||||||
|
windowFun(mon.id));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static isVisible(window: Gtk.Window): boolean {
|
public createWindowForFocusedMonitor(windowFun: (mon: number) => Widget.Window): (() => Widget.Window) {
|
||||||
return window.get_visible();
|
return () => windowFun(AstalHyprland.get_default().get_monitors().filter(mon => mon.focused)[0].id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static close(window: Gtk.Window): void {
|
public addWindow(name: string, window: (() => (Widget.Window | Array<Widget.Window>))): void {
|
||||||
WindowsClass.isVisible(window) && window.hide();
|
this.#windows[name] = window;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static toggle(window: Gtk.Window): void {
|
public hasWindow(name: keyof typeof this.windows): boolean {
|
||||||
window.is_visible() ? WindowsClass.close(window) : WindowsClass.open(window);
|
return Boolean(this.windows?.[name as keyof typeof this.windows]);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export { WindowsClass as Windows };
|
public getWindow(name: (keyof typeof this.windows | string)): ((() => (Widget.Window | Array<Widget.Window>)) | undefined) {
|
||||||
|
return this.windows?.[name as keyof typeof this.windows];
|
||||||
|
}
|
||||||
|
|
||||||
|
public getOpenWindow(name: (keyof typeof this.openWindows)): (Widget.Window | Array<Widget.Window> | undefined) {
|
||||||
|
return this.openWindows?.[name as keyof typeof this.openWindows];
|
||||||
|
}
|
||||||
|
|
||||||
|
public getWindows(): Array<(() => (Widget.Window | Array<Widget.Window>))> {
|
||||||
|
return Object.values(this.windows);
|
||||||
|
}
|
||||||
|
|
||||||
|
public isVisible(name: keyof typeof this.windows): boolean {
|
||||||
|
return Object.hasOwn(this.#openWindows, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public open(name: keyof typeof this.windows): void {
|
||||||
|
if(this.isVisible(name)) return;
|
||||||
|
|
||||||
|
let window: (() => (Widget.Window | Array<Widget.Window>)) = this.getWindow(name)!;
|
||||||
|
const openWindows: (Array<Widget.Window> | Widget.Window) = window();
|
||||||
|
this.#openWindows[name] = openWindows;
|
||||||
|
|
||||||
|
this.connectWindow(name);
|
||||||
|
|
||||||
|
this.emit("open", name);
|
||||||
|
this.notify("open-windows");
|
||||||
|
|
||||||
|
if(Array.isArray(openWindows)) {
|
||||||
|
openWindows.map(win => win.show());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
openWindows.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
public close(name: keyof typeof this.windows): void {
|
||||||
|
if(!this.isVisible(name)) return;
|
||||||
|
|
||||||
|
this.disconnectWindow(name);
|
||||||
|
|
||||||
|
const windows = this.#openWindows[name];
|
||||||
|
delete this.#openWindows[name];
|
||||||
|
|
||||||
|
if(Array.isArray(windows)) {
|
||||||
|
windows.map(win => win.close());
|
||||||
|
this.emit("close", name);
|
||||||
|
this.notify("open-windows");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
windows.close();
|
||||||
|
this.emit("close", name);
|
||||||
|
this.notify("open-windows");
|
||||||
|
}
|
||||||
|
|
||||||
|
public toggle(name: keyof typeof this.windows): void {
|
||||||
|
this.isVisible(name) ? this.close(name) : this.open(name);
|
||||||
|
}
|
||||||
|
}).getDefault();
|
||||||
|
|||||||
Reference in New Issue
Block a user