✨ chore: use util methods, improvements
This commit is contained in:
@@ -23,14 +23,9 @@ export type AskPopupProps = {
|
|||||||
export function AskPopup(props: AskPopupProps): Astal.Window {
|
export function AskPopup(props: AskPopupProps): Astal.Window {
|
||||||
let accepted: boolean = false;
|
let accepted: boolean = false;
|
||||||
|
|
||||||
return <CustomDialog
|
return <CustomDialog namespace={"ask-popup"} widthRequest={400} heightRequest={250}
|
||||||
namespace={"ask-popup"}
|
title={props.title ?? tr("ask_popup.title")} text={props.text}
|
||||||
widthRequest={400}
|
onFinish={() => !accepted && props.onCancel?.()} options={[
|
||||||
heightRequest={250}
|
|
||||||
title={props.title ?? tr("ask_popup.title")}
|
|
||||||
text={props.text}
|
|
||||||
onFinish={() => !accepted && props.onCancel?.()}
|
|
||||||
options={[
|
|
||||||
{ text: props.cancelText ?? tr("cancel") },
|
{ text: props.cancelText ?? tr("cancel") },
|
||||||
{
|
{
|
||||||
text: props.acceptText ?? tr("accept"),
|
text: props.acceptText ?? tr("accept"),
|
||||||
|
|||||||
+17
-20
@@ -4,7 +4,7 @@ import { PopupWindow } from "./PopupWindow";
|
|||||||
import { Separator } from "./Separator";
|
import { Separator } from "./Separator";
|
||||||
import { tr } from "../i18n/intl";
|
import { tr } from "../i18n/intl";
|
||||||
import { Accessor, For, With } from "ags";
|
import { Accessor, For, With } from "ags";
|
||||||
import { variableToBoolean, WidgetNodeType } from "../scripts/utils";
|
import { transformWidget, variableToBoolean, WidgetNodeType } from "../scripts/utils";
|
||||||
|
|
||||||
|
|
||||||
export type CustomDialogProps = {
|
export type CustomDialogProps = {
|
||||||
@@ -28,33 +28,34 @@ export interface CustomDialogOption {
|
|||||||
closeOnClick?: boolean | Accessor<boolean>;
|
closeOnClick?: boolean | Accessor<boolean>;
|
||||||
}
|
}
|
||||||
|
|
||||||
function CustomDialogOption(props: CustomDialogOption & { dialog: Astal.Window }) {
|
function CustomDialogOption({closeOnClick = true, ...props}: CustomDialogOption & { dialog: Astal.Window }) {
|
||||||
function onClicked() {
|
function onClicked() {
|
||||||
props.onClick?.();
|
props.onClick?.();
|
||||||
props.closeOnClick && props.dialog.close();
|
closeOnClick &&
|
||||||
|
props.dialog?.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
return <Gtk.Button class="option" hexpand={true} label={props.text}
|
return <Gtk.Button class="option" hexpand label={props.text}
|
||||||
onClicked={onClicked} onActivate={onClicked}/>
|
onClicked={onClicked} onActivate={onClicked}/>
|
||||||
}
|
}
|
||||||
|
|
||||||
export function CustomDialog({ options = [{ text: tr("accept") }], ...props}: CustomDialogProps) {
|
export function CustomDialog({ options = [{ text: tr("accept") }], ...props}: CustomDialogProps) {
|
||||||
let dialog: Astal.Window;
|
return Windows.getDefault().createWindowForFocusedMonitor((mon) =>
|
||||||
return Windows.getDefault().createWindowForFocusedMonitor((mon: number) =>
|
|
||||||
<PopupWindow namespace={props.namespace ?? "custom-dialog"} monitor={mon}
|
<PopupWindow namespace={props.namespace ?? "custom-dialog"} monitor={mon}
|
||||||
cssBackgroundWindow={props.cssBackground ?? "background: rgba(0, 0, 0, .3);"}
|
cssBackgroundWindow={props.cssBackground ?? "background: rgba(0, 0, 0, .3);"}
|
||||||
exclusivity={Astal.Exclusivity.IGNORE} layer={Astal.Layer.OVERLAY}
|
exclusivity={Astal.Exclusivity.IGNORE} layer={Astal.Layer.OVERLAY}
|
||||||
halign={Gtk.Align.CENTER} valign={Gtk.Align.CENTER}
|
halign={Gtk.Align.CENTER} valign={Gtk.Align.CENTER}
|
||||||
widthRequest={props.widthRequest ?? 400} heightRequest={props.heightRequest ?? 220}
|
widthRequest={props.widthRequest ?? 400} heightRequest={props.heightRequest ?? 220}
|
||||||
onDestroy={props.onFinish} $={(self) => dialog = self}>
|
onDestroy={props.onFinish} $={(self) => self.set_child(
|
||||||
|
|
||||||
<Gtk.Box class={props.className ?? "custom-dialog-container"}
|
<Gtk.Box class={props.className ?? "custom-dialog-container"}
|
||||||
orientation={Gtk.Orientation.VERTICAL}>
|
orientation={Gtk.Orientation.VERTICAL}>
|
||||||
|
|
||||||
<Gtk.Label class={"title"} visible={variableToBoolean(props.title)} label={props.title} />
|
<Gtk.Label class={"title"} visible={variableToBoolean(props.title)} label={props.title} />
|
||||||
<Gtk.Label class={"text"} visible={variableToBoolean(props.text)} label={props.text} />
|
<Gtk.Label class={"text"} visible={variableToBoolean(props.text)} label={props.text}
|
||||||
|
vexpand/>
|
||||||
<Gtk.Box class={"custom-children custom-child"} visible={variableToBoolean(props.children)}
|
<Gtk.Box class={"custom-children custom-child"} visible={variableToBoolean(props.children)}
|
||||||
orientation={props.childOrientation ?? Gtk.Orientation.VERTICAL}>
|
orientation={props.childOrientation ?? Gtk.Orientation.VERTICAL}>
|
||||||
|
|
||||||
{
|
{
|
||||||
(props.children instanceof Accessor) ?
|
(props.children instanceof Accessor) ?
|
||||||
(Array.isArray(props.children) ?
|
(Array.isArray(props.children) ?
|
||||||
@@ -72,17 +73,13 @@ export function CustomDialog({ options = [{ text: tr("accept") }], ...props}: Cu
|
|||||||
<Separator alpha={.2} visible={options && options.length > 0}
|
<Separator alpha={.2} visible={options && options.length > 0}
|
||||||
spacing={8} orientation={Gtk.Orientation.VERTICAL} />
|
spacing={8} orientation={Gtk.Orientation.VERTICAL} />
|
||||||
|
|
||||||
{(<Gtk.Box class={"options"} orientation={props.optionsOrientation ?? Gtk.Orientation.HORIZONTAL}
|
<Gtk.Box class={"options"} orientation={props.optionsOrientation ?? Gtk.Orientation.HORIZONTAL}
|
||||||
hexpand={true} heightRequest={38} homogeneous={true}>
|
hexpand={true} heightRequest={38} homogeneous={true}>
|
||||||
{
|
|
||||||
(options instanceof Accessor) ?
|
{transformWidget(options, (props) => <CustomDialogOption {...props} dialog={self} />)}
|
||||||
<For each={options}>
|
|
||||||
{(option) => <CustomDialogOption {...option} dialog={dialog} />}
|
|
||||||
</For>
|
|
||||||
: options.map(option =>
|
|
||||||
<CustomDialogOption {...option} dialog={dialog} />)
|
|
||||||
}
|
|
||||||
</Gtk.Box>)}
|
|
||||||
</Gtk.Box>
|
</Gtk.Box>
|
||||||
</PopupWindow>)();
|
</Gtk.Box> as Gtk.Box
|
||||||
|
)}
|
||||||
|
/> as Astal.Window
|
||||||
|
)();
|
||||||
}
|
}
|
||||||
|
|||||||
+23
-29
@@ -2,11 +2,14 @@ import { Gdk, Gtk } from "ags/gtk4";
|
|||||||
import { Separator } from "./Separator";
|
import { Separator } from "./Separator";
|
||||||
import { HistoryNotification, Notifications } from "../scripts/notifications";
|
import { HistoryNotification, Notifications } from "../scripts/notifications";
|
||||||
import { getAppIcon, getSymbolicIcon } from "../scripts/apps";
|
import { getAppIcon, getSymbolicIcon } from "../scripts/apps";
|
||||||
|
import { onCleanup } from "ags";
|
||||||
|
|
||||||
import AstalNotifd from "gi://AstalNotifd";
|
import AstalNotifd from "gi://AstalNotifd";
|
||||||
import Pango from "gi://Pango?version=1.0";
|
import Pango from "gi://Pango?version=1.0";
|
||||||
import GLib from "gi://GLib?version=2.0";
|
import GLib from "gi://GLib?version=2.0";
|
||||||
import GObject from "gi://GObject?version=2.0";
|
import GObject from "gi://GObject?version=2.0";
|
||||||
|
import { escapeUnintendedMarkup, pathToURI } from "../scripts/utils";
|
||||||
|
|
||||||
|
|
||||||
function getNotificationImage(notif: AstalNotifd.Notification|HistoryNotification): (string|undefined) {
|
function getNotificationImage(notif: AstalNotifd.Notification|HistoryNotification): (string|undefined) {
|
||||||
const img = notif.image || notif.appIcon;
|
const img = notif.image || notif.appIcon;
|
||||||
@@ -14,16 +17,7 @@ function getNotificationImage(notif: AstalNotifd.Notification|HistoryNotificatio
|
|||||||
if(!img || !img.includes('/'))
|
if(!img || !img.includes('/'))
|
||||||
return undefined;
|
return undefined;
|
||||||
|
|
||||||
switch(true) {
|
return pathToURI(img);
|
||||||
case /^[/]/.test(img):
|
|
||||||
return `file://${img}`;
|
|
||||||
|
|
||||||
case /^[~]/.test(img):
|
|
||||||
case /^file:\/\/[~]/i.test(img):
|
|
||||||
return `file://${GLib.get_home_dir()}/${img.replace(/^(file\:\/\/|[~]|file\:\/\[~])/i, "")}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return img;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function NotificationWidget({ notification, actionClicked, holdOnHover, showTime, actionClose }: {
|
export function NotificationWidget({ notification, actionClicked, holdOnHover, showTime, actionClose }: {
|
||||||
@@ -40,6 +34,9 @@ export function NotificationWidget({ notification, actionClicked, holdOnHover, s
|
|||||||
|
|
||||||
const conns: Map<GObject.Object, Array<number>> = new Map();
|
const conns: Map<GObject.Object, Array<number>> = new Map();
|
||||||
|
|
||||||
|
onCleanup(() =>
|
||||||
|
conns.forEach((ids, obj) => ids.forEach(id => obj.disconnect(id))));
|
||||||
|
|
||||||
return <Gtk.Box hexpand class={`notification ${
|
return <Gtk.Box hexpand class={`notification ${
|
||||||
Notifications.getDefault().getUrgencyString(notification.urgency)
|
Notifications.getDefault().getUrgencyString(notification.urgency)
|
||||||
}`} orientation={Gtk.Orientation.VERTICAL} spacing={5}
|
}`} orientation={Gtk.Orientation.VERTICAL} spacing={5}
|
||||||
@@ -54,7 +51,7 @@ export function NotificationWidget({ notification, actionClicked, holdOnHover, s
|
|||||||
eventControllerMotion.connect("enter", () =>
|
eventControllerMotion.connect("enter", () =>
|
||||||
holdOnHover && Notifications.getDefault().holdNotification(notification.id)),
|
holdOnHover && Notifications.getDefault().holdNotification(notification.id)),
|
||||||
eventControllerMotion.connect("leave", () =>
|
eventControllerMotion.connect("leave", () =>
|
||||||
holdOnHover && Notifications.getDefault().removeNotification(notification.id))
|
holdOnHover && notification && Notifications.getDefault().removeNotification(notification.id))
|
||||||
]);
|
]);
|
||||||
|
|
||||||
conns.set(gestureClick, [
|
conns.set(gestureClick, [
|
||||||
@@ -63,12 +60,10 @@ export function NotificationWidget({ notification, actionClicked, holdOnHover, s
|
|||||||
actionClicked?.(notification);
|
actionClicked?.(notification);
|
||||||
})
|
})
|
||||||
]);
|
]);
|
||||||
}} onDestroy={(_) => {
|
|
||||||
conns.forEach((ids, obj) => ids.forEach(id => obj.disconnect(id)));
|
|
||||||
}}>
|
}}>
|
||||||
|
|
||||||
<Gtk.Box class={"top"} hexpand>
|
<Gtk.Box class={"top"} hexpand>
|
||||||
<Gtk.Image css={"font-size: 16px;"} $={(self) => {
|
<Gtk.Image class="app-icon" $={(self) => {
|
||||||
const icon = getSymbolicIcon(notification.appIcon ?? notification.appName) ??
|
const icon = getSymbolicIcon(notification.appIcon ?? notification.appName) ??
|
||||||
getSymbolicIcon(notification.appName) ?? getAppIcon(notification.appName);
|
getSymbolicIcon(notification.appName) ?? getAppIcon(notification.appName);
|
||||||
|
|
||||||
@@ -86,32 +81,31 @@ export function NotificationWidget({ notification, actionClicked, holdOnHover, s
|
|||||||
label={GLib.DateTime.new_from_unix_local(notification.time).format("%H:%M") ?? ""} />
|
label={GLib.DateTime.new_from_unix_local(notification.time).format("%H:%M") ?? ""} />
|
||||||
|
|
||||||
<Gtk.Button halign={Gtk.Align.END} iconName={"window-close-symbolic"}
|
<Gtk.Button halign={Gtk.Align.END} iconName={"window-close-symbolic"}
|
||||||
class={"close icon"}/>
|
class={"close"} onClicked={() => actionClose?.(notification)}/>
|
||||||
</Gtk.Box>
|
</Gtk.Box>
|
||||||
<Separator alpha={.1} orientation={Gtk.Orientation.VERTICAL} />
|
<Separator alpha={.1} orientation={Gtk.Orientation.VERTICAL} />
|
||||||
<Gtk.Box class={"content"} $={(self) => {
|
<Gtk.Box class={"content"}>
|
||||||
const image = getNotificationImage(notification);
|
{getNotificationImage(notification) &&
|
||||||
|
<Gtk.Box class={"image"} hexpand={false} vexpand={false}
|
||||||
image &&
|
css={`background-image: url("${getNotificationImage(notification)}");`}
|
||||||
self.prepend(Gtk.Picture.new_for_filename(image));
|
/>
|
||||||
}}>
|
}
|
||||||
|
|
||||||
<Gtk.Box class={"text"} orientation={Gtk.Orientation.VERTICAL}
|
<Gtk.Box class={"text"} orientation={Gtk.Orientation.VERTICAL}
|
||||||
vexpand={true}>
|
vexpand={true}>
|
||||||
|
|
||||||
<Gtk.Label class={"summary"} useMarkup={true} hexpand={false} xalign={0}
|
<Gtk.Label class={"summary"} useMarkup={true} hexpand xalign={0}
|
||||||
vexpand ellipsize={Pango.EllipsizeMode.END} label={
|
vexpand={false} ellipsize={Pango.EllipsizeMode.END} label={
|
||||||
notification.summary.replace(/[&]/g, "&")}
|
escapeUnintendedMarkup(notification.summary)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Gtk.Label class={"body"} useMarkup={true} xalign={0} wrap={true} hexpand={false}
|
<Gtk.Label class={"body"} useMarkup={true} xalign={0} wrap={true} hexpand
|
||||||
vexpand wrapMode={Pango.WrapMode.WORD_CHAR} label={
|
vexpand wrapMode={Pango.WrapMode.WORD_CHAR} valign={Gtk.Align.START} label={
|
||||||
notification.body.replace(/[&]/g, "&")}
|
escapeUnintendedMarkup(notification.body)}
|
||||||
/>
|
/>
|
||||||
</Gtk.Box>
|
</Gtk.Box>
|
||||||
</Gtk.Box>
|
</Gtk.Box>
|
||||||
|
|
||||||
<Gtk.Box class={"action button-row"} hexpand={true} visible={
|
<Gtk.Box class={"actions button-row"} hexpand={true} visible={
|
||||||
(notification instanceof AstalNotifd.Notification) &&
|
(notification instanceof AstalNotifd.Notification) &&
|
||||||
(notification.actions.filter(action => action.label.toLowerCase() !== "view").length > 0)
|
(notification.actions.filter(action => action.label.toLowerCase() !== "view").length > 0)
|
||||||
}>
|
}>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { Config } from "../../scripts/config";
|
|||||||
|
|
||||||
export const Clock = () =>
|
export const Clock = () =>
|
||||||
<Gtk.Button class={createBinding(Windows.getDefault(), "openWindows").as((wins) =>
|
<Gtk.Button class={createBinding(Windows.getDefault(), "openWindows").as((wins) =>
|
||||||
`clock ${Object.hasOwn(wins, "center-window") ? "open" : ""}`)}
|
`clock ${wins.includes("center-window") ? "open" : ""}`)}
|
||||||
$={(self) => {
|
$={(self) => {
|
||||||
const conns: Array<number> = [
|
const conns: Array<number> = [
|
||||||
self.connect("clicked", (_) => Windows.getDefault().toggle("center-window")),
|
self.connect("clicked", (_) => Windows.getDefault().toggle("center-window")),
|
||||||
|
|||||||
@@ -1,67 +0,0 @@
|
|||||||
import { Accessor, With } from "ags";
|
|
||||||
import { register } from "ags/gobject";
|
|
||||||
import { Gtk } from "ags/gtk4";
|
|
||||||
import Pango from "gi://Pango?version=1.0";
|
|
||||||
import { variableToBoolean } from "../../scripts/utils";
|
|
||||||
|
|
||||||
export { ResultWidget, ResultWidgetProps };
|
|
||||||
|
|
||||||
type ResultWidgetProps = {
|
|
||||||
icon?: string | Accessor<string> | JSX.Element | Accessor<JSX.Element>;
|
|
||||||
title: string | Accessor<string>;
|
|
||||||
description?: string | Accessor<string>;
|
|
||||||
closeOnClick?: boolean;
|
|
||||||
setup?: () => void;
|
|
||||||
onClick?: () => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
@register({ GTypeName: "ResultWidget" })
|
|
||||||
class ResultWidget extends Gtk.Box {
|
|
||||||
|
|
||||||
public readonly onClick: () => void;
|
|
||||||
public readonly setup?: () => void;
|
|
||||||
public icon?: (string | Accessor<string> | JSX.Element | Accessor<JSX.Element>);
|
|
||||||
public closeOnClick: boolean = true;
|
|
||||||
|
|
||||||
|
|
||||||
constructor(props: ResultWidgetProps) {
|
|
||||||
super({
|
|
||||||
cssClasses: ["result"],
|
|
||||||
hexpand: true
|
|
||||||
});
|
|
||||||
|
|
||||||
this.icon = props.icon;
|
|
||||||
this.setup = props.setup;
|
|
||||||
this.closeOnClick = props.closeOnClick ?? true;
|
|
||||||
this.onClick = () => props.onClick?.();
|
|
||||||
|
|
||||||
if(this.icon !== undefined) {
|
|
||||||
if(this.icon instanceof Accessor) {
|
|
||||||
if(typeof this.icon.get() === "string") {
|
|
||||||
this.prepend(<Gtk.Image iconName={
|
|
||||||
this.icon as Accessor<string>
|
|
||||||
} /> as Gtk.Image);
|
|
||||||
} else {
|
|
||||||
this.prepend(<Gtk.Box>
|
|
||||||
<With value={this.icon as Accessor<Gtk.Widget>}>
|
|
||||||
{(widget) => widget}
|
|
||||||
</With>
|
|
||||||
</Gtk.Box> as Gtk.Box);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if(typeof this.icon === "string")
|
|
||||||
this.prepend(<Gtk.Image iconName={this.icon as string} /> as Gtk.Image);
|
|
||||||
else
|
|
||||||
this.prepend(this.icon as Gtk.Widget);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.append(<Gtk.Box orientation={Gtk.Orientation.VERTICAL} valign={Gtk.Align.CENTER}>
|
|
||||||
<Gtk.Label class={"title"} xalign={0} ellipsize={Pango.EllipsizeMode.END}
|
|
||||||
label={props.title} />
|
|
||||||
|
|
||||||
<Gtk.Label class={"description"} visible={variableToBoolean(props.description)}
|
|
||||||
ellipsize={Pango.EllipsizeMode.END} xalign={0} label={props.description ?? ""} />
|
|
||||||
</Gtk.Box> as Gtk.Box);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user