✨ chore: make apps-window and floating notifications work better on gtk4
This commit is contained in:
+44
-40
@@ -6,10 +6,14 @@ import AstalApps from "gi://AstalApps";
|
|||||||
import Pango from "gi://Pango?version=1.0";
|
import Pango from "gi://Pango?version=1.0";
|
||||||
import { createState, For } from "ags";
|
import { createState, For } from "ags";
|
||||||
|
|
||||||
|
|
||||||
const ignoredKeys = [
|
const ignoredKeys = [
|
||||||
Gdk.KEY_Right,
|
Gdk.KEY_Right,
|
||||||
Gdk.KEY_Down,
|
Gdk.KEY_Down,
|
||||||
Gdk.KEY_Up,
|
Gdk.KEY_Up,
|
||||||
|
Gdk.KEY_Shift_L,
|
||||||
|
Gdk.KEY_Shift_R,
|
||||||
|
Gdk.KEY_Shift_Lock,
|
||||||
Gdk.KEY_Left,
|
Gdk.KEY_Left,
|
||||||
Gdk.KEY_Return,
|
Gdk.KEY_Return,
|
||||||
Gdk.KEY_space
|
Gdk.KEY_space
|
||||||
@@ -18,55 +22,55 @@ const ignoredKeys = [
|
|||||||
export const AppsWindow = (mon: number) => {
|
export const AppsWindow = (mon: number) => {
|
||||||
const [results, setResults] = createState(getApps() as Array<AstalApps.Application>);
|
const [results, setResults] = createState(getApps() as Array<AstalApps.Application>);
|
||||||
|
|
||||||
const entry: Gtk.SearchEntry = <Gtk.SearchEntry onSearchChanged={(self) => {
|
|
||||||
setResults(getAstalApps().fuzzy_query(self.text.trim()));
|
|
||||||
}}/> as Gtk.SearchEntry;
|
|
||||||
|
|
||||||
return <PopupWindow namespace="apps-window" layer={Astal.Layer.OVERLAY}
|
return <PopupWindow namespace="apps-window" layer={Astal.Layer.OVERLAY}
|
||||||
exclusivity={Astal.Exclusivity.IGNORE} monitor={mon} marginTop={64}
|
exclusivity={Astal.Exclusivity.IGNORE} monitor={mon} marginTop={64}
|
||||||
cssBackgroundWindow="background={rgba(0, 0, 0, .2)"
|
cssBackgroundWindow="background: rgba(0, 0, 0, .2);"
|
||||||
actionKeyPressed={(_, key) => {
|
actionKeyPressed={(self, key) => {
|
||||||
|
const entry = self.get_first_child()!.get_first_child()!
|
||||||
|
.get_first_child()!.get_first_child() as Gtk.SearchEntry;
|
||||||
|
|
||||||
for(const ignoredKey of ignoredKeys)
|
for(const ignoredKey of ignoredKeys)
|
||||||
if(key === ignoredKey) return
|
if(key === ignoredKey) return
|
||||||
|
|
||||||
!entry.is_focus && entry.grab_focus();
|
entry.grab_focus();
|
||||||
}}>
|
}}>
|
||||||
|
|
||||||
<Gtk.Box class={"apps-window-container"} orientation={Gtk.Orientation.VERTICAL}>
|
<Gtk.Box class={"apps-window-container"} orientation={Gtk.Orientation.VERTICAL} hexpand vexpand>
|
||||||
<Gtk.Box class="apps-area">
|
<Gtk.SearchEntry onSearchChanged={(self) => {
|
||||||
<Gtk.ScrolledWindow vscrollbarPolicy={Gtk.PolicyType.AUTOMATIC}
|
setResults(getAstalApps().fuzzy_query(self.text.trim()));
|
||||||
hscrollbarPolicy={Gtk.PolicyType.NEVER} overlayScrolling={true}
|
}} />
|
||||||
propagateNaturalHeight={false}>
|
<Gtk.Box class="apps-area" hexpand vexpand>
|
||||||
|
<Gtk.ScrolledWindow vscrollbarPolicy={Gtk.PolicyType.AUTOMATIC}
|
||||||
|
hscrollbarPolicy={Gtk.PolicyType.NEVER} overlayScrolling={true}
|
||||||
|
propagateNaturalHeight={false} hexpand vexpand>
|
||||||
|
|
||||||
<Gtk.FlowBox rowSpacing={60} columnSpacing={60}
|
<Gtk.FlowBox rowSpacing={60} columnSpacing={60} homogeneous activateOnSingleClick
|
||||||
homogeneous={true} visible={true} minChildrenPerLine={1}
|
minChildrenPerLine={1}>
|
||||||
activateOnSingleClick={true}>
|
|
||||||
|
|
||||||
<For each={results}>
|
|
||||||
{(app) =>
|
|
||||||
<Gtk.Button visible={true} heightRequest={150} tooltipMarkup={
|
|
||||||
`${app.name}${app.description ? `\n<span foreground="#7f7f7f">${
|
|
||||||
app.description}</span>` : ""}`.replace(/\&/g, "&")}
|
|
||||||
|
|
||||||
onActivate={() => {
|
<For each={results}>
|
||||||
execApp(app);
|
{(app) =>
|
||||||
window.close();
|
<Gtk.Button heightRequest={150} tooltipMarkup={`${app.name}${app.description ?
|
||||||
}} onClicked={() => {
|
`\n<span foreground="#7f7f7f">${app.description}</span>`
|
||||||
execApp(app);
|
: ""}`.replace(/\&/g, "&")
|
||||||
window.close();
|
} onActivate={() => {
|
||||||
}}>
|
execApp(app);
|
||||||
|
window.close();
|
||||||
<Gtk.Box orientation={Gtk.Orientation.VERTICAL}>
|
}} onClicked={() => {
|
||||||
<Gtk.Image iconName={getAppIcon(app) ?? "application-x-executable"} />
|
execApp(app);
|
||||||
<Gtk.Label ellipsize={Pango.EllipsizeMode.END} valign={Gtk.Align.START}
|
window.close();
|
||||||
label={app.name || "Unnamed App"} />
|
}}>
|
||||||
</Gtk.Box>
|
<Gtk.Box orientation={Gtk.Orientation.VERTICAL} vexpand>
|
||||||
</Gtk.Button>
|
<Gtk.Image iconName={getAppIcon(app) ?? "application-x-executable"}
|
||||||
}
|
iconSize={Gtk.IconSize.LARGE} vexpand={false} />
|
||||||
</For>
|
<Gtk.Label ellipsize={Pango.EllipsizeMode.END} label={app.name}
|
||||||
</Gtk.FlowBox>
|
valign={Gtk.Align.END} vexpand />
|
||||||
</Gtk.ScrolledWindow>
|
</Gtk.Box>
|
||||||
</Gtk.Box>
|
</Gtk.Button>
|
||||||
|
}
|
||||||
|
</For>
|
||||||
|
</Gtk.FlowBox>
|
||||||
|
</Gtk.ScrolledWindow>
|
||||||
|
</Gtk.Box>
|
||||||
</Gtk.Box>
|
</Gtk.Box>
|
||||||
</PopupWindow>
|
</PopupWindow>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { Apps } from "../widget/bar/Apps";
|
|||||||
import { Clock } from "../widget/bar/Clock";
|
import { Clock } from "../widget/bar/Clock";
|
||||||
import { Status } from "../widget/bar/Status";
|
import { Status } from "../widget/bar/Status";
|
||||||
|
|
||||||
|
|
||||||
export const Bar = (mon: number) => {
|
export const Bar = (mon: number) => {
|
||||||
const widgetSpacing = 4;
|
const widgetSpacing = 4;
|
||||||
return <Astal.Window namespace={"top-bar"} layer={Astal.Layer.TOP}
|
return <Astal.Window namespace={"top-bar"} layer={Astal.Layer.TOP}
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ import { time } from "../scripts/utils";
|
|||||||
import { player } from "../widget/bar/Media";
|
import { player } from "../widget/bar/Media";
|
||||||
|
|
||||||
export const CenterWindow = (mon: number) =>
|
export const CenterWindow = (mon: number) =>
|
||||||
<PopupWindow namespace={"center-window"} marginTop={10} halign={Gtk.Align.CENTER}
|
<PopupWindow namespace={"center-window"} marginTop={10} monitor={mon}
|
||||||
valign={Gtk.Align.START} monitor={mon}>
|
halign={Gtk.Align.CENTER} valign={Gtk.Align.START}>
|
||||||
|
|
||||||
<Gtk.Box class={"center-window-container"} spacing={6}>
|
<Gtk.Box class={"center-window-container"} spacing={6}>
|
||||||
<Gtk.Box class={"left"} orientation={Gtk.Orientation.VERTICAL}>
|
<Gtk.Box class={"left"} orientation={Gtk.Orientation.VERTICAL}>
|
||||||
|
|||||||
@@ -1,22 +1,24 @@
|
|||||||
import { Astal, Gtk } from "ags/gtk4";
|
import { Astal, Gtk } from "ags/gtk4";
|
||||||
import { PopupWindow } from "../widget/PopupWindow";
|
import { PopupWindow } from "../widget/PopupWindow";
|
||||||
import { NotifHistory } from "../widget/control-center/NotifHistory";
|
|
||||||
import { QuickActions } from "../widget/control-center/QuickActions";
|
import { QuickActions } from "../widget/control-center/QuickActions";
|
||||||
|
import { NotifHistory } from "../widget/control-center/NotifHistory";
|
||||||
|
import { Tiles } from "../widget/control-center/Tiles";
|
||||||
|
import { Sliders } from "../widget/control-center/Sliders";
|
||||||
|
|
||||||
|
|
||||||
export const ControlCenter = (mon: number) =>
|
export const ControlCenter = (mon: number) =>
|
||||||
<PopupWindow namespace={"control-center"} class={"control-center"}
|
<PopupWindow namespace={"control-center"} class={"control-center"}
|
||||||
halign={Gtk.Align.END} valign={Gtk.Align.START} layer={Astal.Layer.OVERLAY}
|
halign={Gtk.Align.END} valign={Gtk.Align.START} layer={Astal.Layer.OVERLAY}
|
||||||
marginTop={10} marginRight={10} marginBottom={10} monitor={mon}
|
marginTop={10} marginRight={10} marginBottom={10} monitor={mon}
|
||||||
widthRequest={395}>
|
widthRequest={395}>
|
||||||
|
|
||||||
<Gtk.Box orientation={Gtk.Orientation.VERTICAL}
|
<Gtk.Box orientation={Gtk.Orientation.VERTICAL} spacing={16} vexpand={false}>
|
||||||
spacing={16}>
|
<Gtk.Box class={"control-center-container"} vexpand={false}
|
||||||
|
orientation={Gtk.Orientation.VERTICAL} spacing={12}>
|
||||||
<Gtk.Box class={"control-center-container"}
|
|
||||||
orientation={Gtk.Orientation.VERTICAL} vexpand={false}>
|
|
||||||
|
|
||||||
<QuickActions />
|
<QuickActions />
|
||||||
|
<Tiles />
|
||||||
|
<Sliders />
|
||||||
</Gtk.Box>
|
</Gtk.Box>
|
||||||
<NotifHistory />
|
<NotifHistory />
|
||||||
</Gtk.Box>
|
</Gtk.Box>
|
||||||
|
|||||||
@@ -2,32 +2,40 @@ import { Astal, Gtk } from "ags/gtk4";
|
|||||||
import { createBinding, For } from "ags";
|
import { createBinding, For } from "ags";
|
||||||
import { Notifications } from "../scripts/notifications";
|
import { Notifications } from "../scripts/notifications";
|
||||||
import { NotificationWidget } from "../widget/Notification";
|
import { NotificationWidget } from "../widget/Notification";
|
||||||
import { variableToBoolean } from "../scripts/utils";
|
|
||||||
import AstalNotifd from "gi://AstalNotifd?version=0.1";
|
|
||||||
|
|
||||||
|
import AstalNotifd from "gi://AstalNotifd?version=0.1";
|
||||||
|
import Adw from "gi://Adw?version=1";
|
||||||
|
|
||||||
|
const size = 450;
|
||||||
|
|
||||||
export const FloatingNotifications = (mon: number) =>
|
export const FloatingNotifications = (mon: number) =>
|
||||||
<Astal.Window namespace={"floating-notifications"} monitor={mon} layer={Astal.Layer.OVERLAY}
|
<Astal.Window namespace={"floating-notifications"} monitor={mon} layer={Astal.Layer.OVERLAY}
|
||||||
anchor={Astal.WindowAnchor.TOP | Astal.WindowAnchor.RIGHT} widthRequest={450}
|
anchor={Astal.WindowAnchor.TOP | Astal.WindowAnchor.RIGHT} exclusivity={Astal.Exclusivity.NORMAL}
|
||||||
exclusivity={Astal.Exclusivity.NORMAL}>
|
resizable={false} widthRequest={450}>
|
||||||
|
|
||||||
<Gtk.Box class={"floating-notifications-container"}
|
<Gtk.Box class={"floating-notifications-container"} spacing={12}
|
||||||
orientation={Gtk.Orientation.VERTICAL} spacing={12}
|
orientation={Gtk.Orientation.VERTICAL}>
|
||||||
visible={variableToBoolean(createBinding(Notifications.getDefault(), "notifications"))}>
|
|
||||||
|
|
||||||
<For each={createBinding(Notifications.getDefault(), "notifications")}>
|
<For each={createBinding(Notifications.getDefault(), "notifications")}>
|
||||||
{(notif: AstalNotifd.Notification) =>
|
{(notif: AstalNotifd.Notification) =>
|
||||||
<Gtk.Box class={"float-notification"}>
|
<Adw.Clamp maximumSize={size}>
|
||||||
<NotificationWidget notification={notif} showTime={false}
|
<Gtk.Box class={"float-notification"} widthRequest={size} vexpand={false}>
|
||||||
actionClose={() => Notifications.getDefault().removeNotification(notif)}
|
|
||||||
holdOnHover={true} actionClicked={() => {
|
|
||||||
const viewAction = notif.actions.filter(action =>
|
|
||||||
action.label.toLowerCase() === "view")?.[0];
|
|
||||||
|
|
||||||
viewAction && notif.invoke(viewAction.id);
|
{/*
|
||||||
}}
|
Why is holdOnHover disabled: the shell for some reason crashes
|
||||||
/>
|
when removing the notification on hover-lost 💔
|
||||||
</Gtk.Box>
|
*/}
|
||||||
|
<NotificationWidget notification={notif} showTime={false}
|
||||||
|
actionClose={() => Notifications.getDefault().removeNotification(notif)}
|
||||||
|
holdOnHover={false} actionClicked={() => {
|
||||||
|
const viewAction = notif.actions.filter(action =>
|
||||||
|
action.label.toLowerCase() === "view")?.[0];
|
||||||
|
|
||||||
|
viewAction && notif.invoke(viewAction.id);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Gtk.Box>
|
||||||
|
</Adw.Clamp>
|
||||||
}
|
}
|
||||||
</For>
|
</For>
|
||||||
</Gtk.Box>
|
</Gtk.Box>
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import AstalNotifd from "gi://AstalNotifd";
|
|||||||
import Gio from "gi://Gio?version=2.0";
|
import Gio from "gi://Gio?version=2.0";
|
||||||
import GObject from "gi://GObject?version=2.0";
|
import GObject from "gi://GObject?version=2.0";
|
||||||
import { time } from "../scripts/utils";
|
import { time } from "../scripts/utils";
|
||||||
|
import { onCleanup } from "ags";
|
||||||
|
|
||||||
|
|
||||||
const { TOP, LEFT, RIGHT, BOTTOM } = Astal.WindowAnchor;
|
const { TOP, LEFT, RIGHT, BOTTOM } = Astal.WindowAnchor;
|
||||||
@@ -24,10 +25,10 @@ export const LogoutMenu = (mon: number) =>
|
|||||||
self.add_controller(controllerKey);
|
self.add_controller(controllerKey);
|
||||||
conns.set(controllerKey, controllerKey.connect("key-released", (_, keyval) => {
|
conns.set(controllerKey, controllerKey.connect("key-released", (_, keyval) => {
|
||||||
if(keyval === Gdk.KEY_Escape)
|
if(keyval === Gdk.KEY_Escape)
|
||||||
self.close();
|
self.destroy();
|
||||||
}));
|
}));
|
||||||
conns.set(self, self.connect("destroy", () => conns.forEach((id, obj) =>
|
|
||||||
obj.disconnect(id))));
|
onCleanup(() => conns.forEach((id, obj) => obj.disconnect(id)));
|
||||||
}}>
|
}}>
|
||||||
|
|
||||||
<Gtk.Box class={"logout-menu"} orientation={Gtk.Orientation.VERTICAL}
|
<Gtk.Box class={"logout-menu"} orientation={Gtk.Orientation.VERTICAL}
|
||||||
@@ -43,16 +44,19 @@ export const LogoutMenu = (mon: number) =>
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
onCleanup(() => conns.forEach((id, obj) => obj.disconnect(id)));
|
||||||
}}>
|
}}>
|
||||||
|
|
||||||
<Gtk.Box class={"top"} hexpand={true} vexpand={false}
|
<Gtk.Box class={"top"} hexpand={true} vexpand={false}
|
||||||
orientation={Gtk.Orientation.VERTICAL}>
|
orientation={Gtk.Orientation.VERTICAL} valign={Gtk.Align.START}>
|
||||||
|
|
||||||
<Gtk.Label class={"time"} label={time(t => t.format("%H:%M")!)} />
|
<Gtk.Label class={"time"} label={time(t => t.format("%H:%M")!)} />
|
||||||
<Gtk.Label class={"date"} label={time(d => d.format("%A, %B %d %Y")!)} />
|
<Gtk.Label class={"date"} label={time(d => d.format("%A, %B %d %Y")!)} />
|
||||||
</Gtk.Box>
|
</Gtk.Box>
|
||||||
|
|
||||||
<Gtk.Box class={"button-row"} homogeneous={true} heightRequest={360}>
|
<Gtk.Box class={"button-row"} homogeneous={true} heightRequest={360} valign={Gtk.Align.CENTER}
|
||||||
|
vexpand>
|
||||||
<Gtk.Button class={"poweroff"} iconName={"system-shutdown-symbolic"}
|
<Gtk.Button class={"poweroff"} iconName={"system-shutdown-symbolic"}
|
||||||
onClicked={() => AskPopup(poweroffAsk)} onActivate={() =>
|
onClicked={() => AskPopup(poweroffAsk)} onActivate={() =>
|
||||||
AskPopup(poweroffAsk)}
|
AskPopup(poweroffAsk)}
|
||||||
|
|||||||
Reference in New Issue
Block a user