💥 fix(popup-window): now it works as intended

This commit is contained in:
retrozinndev
2025-07-23 20:32:57 -03:00
parent 5465f09f62
commit b0ec47d4a5
2 changed files with 85 additions and 63 deletions
+65 -52
View File
@@ -1,18 +1,18 @@
import { Astal, Gdk, Gtk } from "ags/gtk4";
import { BackgroundWindow } from "./BackgroundWindow";
import { Accessor, CCProps, createComputed } from "ags";
import { Accessor, CCProps, createComputed, createRoot } from "ags";
import { omitObjectKeys, WidgetNodeType } from "../scripts/utils";
import GObject from "ags/gobject";
type PopupWindowSpecificProps = {
$?: (self: Astal.Window) => void;
$?: (self: Astal.Window, container: Gtk.Box) => void;
children?: WidgetNodeType;
onDestroy?: (self: Astal.Window) => void;
/** Stylesheet for the background of the popup-window */
cssBackgroundWindow?: string;
class?: string | Accessor<string>;
onCloseRequest?: (self: Astal.Window) => void|boolean;
actionClickedOutside?: (self: Astal.Window) => void;
actionKeyPressed?: (self: Astal.Window, keyval: number, keycode: number) => void;
};
@@ -29,75 +29,82 @@ export type PopupWindowProps = Pick<Partial<CCProps<Astal.Window, Astal.Window.C
| "canFocus"
| "hasFocus"
| "tooltipMarkup"
| "tooltipText"
| "namespace"
| "visible"
| "widthRequest"
| "heightRequest"
| "halign"
| "valign"
| "anchor"
| "vexpand"
| "hexpand"> & PopupWindowSpecificProps;
const { TOP, LEFT, RIGHT, BOTTOM } = Astal.WindowAnchor;
export function PopupWindow(props: PopupWindowProps): GObject.Object {
props.visible ??= true;
props.layer ??= Astal.Layer.OVERLAY;
props.actionClickedOutside ??= (self: Astal.Window) => self.close();
const bgWindow = props.cssBackgroundWindow ? (<BackgroundWindow
monitor={props.monitor ?? 0}
layer={props.layer}
css={props.cssBackgroundWindow} /> as Astal.Window)
: undefined;
const omittedProps = omitObjectKeys(props, [
"children",
let clickedInside: boolean = false;
return <Astal.Window {...omitObjectKeys(props, [
"actionKeyPressed",
"actionClickedOutside",
"cssBackgroundWindow",
"anchor",
"halign",
"valign",
"namespace",
"marginTop",
"widthRequest",
"heightRequest",
"visible",
"marginLeft",
"marginRight",
"marginBottom"
]);
return <Astal.Window {...omittedProps} visible
namespace={props.namespace ?? "popup-window"} class={
])} namespace={props.namespace ?? "popup-window"} class={
(props.class instanceof Accessor) ?
((props.namespace instanceof Accessor) ?
createComputed([props.class, props.namespace], (clss, namespace) =>
`popup-window ${clss} ${namespace}`)
: props.class.as(clss => `popup-window ${clss} ${props.namespace ?? ""}`))
: `popup-window ${props.class ?? ""} ${props.namespace ?? ""}`
} keymode={Astal.Keymode.EXCLUSIVE} anchor={TOP | LEFT | RIGHT | BOTTOM}
exclusivity={props.exclusivity ?? Astal.Exclusivity.NORMAL}
onDestroy={(self) => {
bgWindow?.close();
props.onDestroy?.(self);
}}
} keymode={Astal.Keymode.EXCLUSIVE} exclusivity={props.exclusivity ?? Astal.Exclusivity.NORMAL}
anchor={TOP | LEFT | BOTTOM | RIGHT} visible={false} onCloseRequest={props.onCloseRequest}
$={(self) => {
props.actionClickedOutside ??= (_: Astal.Window) => self.close();
const conns: Map<GObject.Object, number> = new Map();
const gestureClick = Gtk.GestureClick.new();
const keyController = Gtk.EventControllerKey.new();
const allocation = (self.get_child()! as Gtk.Box).get_first_child()!.get_allocation();
const allocation = self.get_first_child()!.get_first_child()!.get_allocation();
self.add_controller(gestureClick);
self.add_controller(keyController);
props.cssBackgroundWindow && createRoot(() =>
<BackgroundWindow monitor={props.monitor ?? 0}
layer={props.layer} css={props.cssBackgroundWindow}
keymode={Astal.Keymode.NONE} attach={self}
/>
);
props.visible && self.show();
conns.set(gestureClick, gestureClick.connect("released", (_, __, x, y) => {
if((x < allocation.x || x > (allocation.x + allocation.width)) ||
(y < allocation.y || y > (allocation.y + allocation.height))) {
// Disconnect signals because window is being closed
conns.forEach((id, obj) => {
obj.disconnect(id);
});
(y < allocation.y || y > (allocation.y + allocation.height))) {
if(clickedInside) {
clickedInside = false;
return;
}
props.actionClickedOutside!(self);
}
}));
conns.set(keyController, keyController.connect("key-pressed", (_, keyval, keycode) => {
conns.set(keyController, keyController.connect("key-released", (_, keyval, keycode) => {
if(keyval === Gdk.KEY_Escape) {
conns.forEach((id, obj) => {
obj.disconnect(id);
@@ -110,32 +117,38 @@ export function PopupWindow(props: PopupWindowProps): GObject.Object {
props.actionKeyPressed?.(self, keyval, keycode);
}));
conns.set(self, self.connect("destroy", () => conns.forEach((id, obj) =>
conns.set(self, self.connect("close-request", () => conns.forEach((id, obj) =>
obj.disconnect(id))));
props.$?.(self);
props.$?.(self, self.get_first_child()!.get_first_child()!.get_first_child() as Gtk.Box);
}}>
<Gtk.Box halign={props.halign} valign={props.valign} hexpand vexpand css={`box {
margin-left: ${props.marginLeft ?? 0}px;
margin-right: ${props.marginRight ?? 0}px;
margin-top: ${props.marginTop ?? 0}px;
margin-bottom: ${props.marginBottom ?? 0}px;
}`
}>
<Gtk.Box widthRequest={props.widthRequest} heightRequest={props.heightRequest}
$={(self) => {
const gestureClick = Gtk.GestureClick.new();
self.add_controller(gestureClick);
<Gtk.Box hexpand={false} vexpand={false}>
<Gtk.Box class={"popup-window-container"} halign={props.halign}
valign={props.valign} widthRequest={props.widthRequest}
hexpand={props.hexpand} vexpand={props.vexpand}
heightRequest={props.heightRequest} css={`
margin-left: ${props.marginLeft ?? 0}px;
margin-right: ${props.marginRight ?? 0}px;
margin-top: ${props.marginTop ?? 0}px;
margin-bottom: ${props.marginBottom ?? 0}px;
`} $={(self) => {
const conns = new Map<GObject.Object, number>(),
gestureClick = Gtk.GestureClick.new();
const clickConn = gestureClick.connect("released", () => true);
const destroyConn = self.connect("destroy", () => {
gestureClick.disconnect(clickConn);
self.disconnect(destroyConn);
});
gestureClick.set_button(0);
self.add_controller(gestureClick);
conns.set(gestureClick, gestureClick.connect("released", () =>
clickedInside = true
));
conns.set(self, self.connect("destroy", () => conns.forEach((id, obj) =>
obj.disconnect(id))));
}}>
{props.children}
<Gtk.Box>
{props.children}
</Gtk.Box>
</Gtk.Box>
</Gtk.Box>
</Astal.Window>;
</Astal.Window> as Astal.Window;
}