ags: finish center-window widget with big-media and calendar

This commit is contained in:
retrozinndev
2025-02-16 19:29:03 -03:00
parent 1e6b3bcbe3
commit 23d3b271b4
19 changed files with 394 additions and 181 deletions
+51 -44
View File
@@ -1,29 +1,30 @@
import AstalNotifd from "gi://AstalNotifd"; import AstalNotifd from "gi://AstalNotifd";
import { timeout } from "astal/time"; import { timeout } from "astal/time";
import { Connectable } from "astal/binding"; import { Subscribable } from "astal/binding";
import { GObject, register, signal } from "astal"; import { GObject, property, register, Variable } from "astal";
import { Windows } from "../windows"; import { Windows } from "../windows";
import { FloatingNotifications } from "../window/FloatingNotifications";
@register({ GTypeName: "Notifications" }) @register({ GTypeName: "Notifications" })
class NotificationsClass extends GObject.Object implements Connectable { class NotificationsClass extends GObject.Object implements Subscribable {
private static instance: NotificationsClass; private static instance: NotificationsClass;
@property(AstalNotifd.Notifd)
private notifd: AstalNotifd.Notifd; private notifd: AstalNotifd.Notifd;
public notifications: Array<AstalNotifd.Notification> = []; @property(Boolean)
private doNotDisturb: boolean = false;
@property()
public notificationHistory: Array<AstalNotifd.Notification> = []; public notificationHistory: Array<AstalNotifd.Notification> = [];
@signal(AstalNotifd.Notification) @property()
declare notificationAdded: (added: AstalNotifd.Notification) => void; public notifications: Variable<Array<AstalNotifd.Notification>> = new Variable<Array<AstalNotifd.Notification>>([]);
@signal(Number)
declare notificationRemoved: (id: number) => void;
public static getDefault(): NotificationsClass { public static getDefault(): NotificationsClass {
if(!NotificationsClass.instance) { if(!NotificationsClass.instance) {
NotificationsClass.instance = new NotificationsClass(); NotificationsClass.instance = new NotificationsClass();
this.instance._init();
} }
return NotificationsClass.instance; return NotificationsClass.instance;
@@ -36,13 +37,28 @@ class NotificationsClass extends GObject.Object implements Connectable {
dontDisturb: false dontDisturb: false
} as AstalNotifd.Notifd.ConstructorProps); } as AstalNotifd.Notifd.ConstructorProps);
this.getNotifd().connect("notified", (_source: AstalNotifd.Notifd, id: number, _replaced: boolean) => { this.getNotifd().connect("notified", (daemon: AstalNotifd.Notifd, id: number) => {
this.addNotification(this.getNotifd().get_notification(id)); const notification: (AstalNotifd.Notification|null) = daemon.get_notification(id);
if(!notification) {
console.log("[LOG] Notification is null, ignoring");
return;
}
if(!this.doNotDisturb) {
this.handleNotification(notification);
return;
}
this.addHistory(notification);
}); });
} }
public addNotification(notification: AstalNotifd.Notification) { public handleNotification(notification: AstalNotifd.Notification): void {
this.prependArray(this.notifications, this.getNotifd().get_notification(notification.id)); Windows.open(FloatingNotifications);
let tmpArray = this.notifications.get().reverse();
tmpArray.push(notification);
this.notifications.set(tmpArray.reverse());
// default timeout if undefined // default timeout if undefined
let notificationTimeout = 4000; let notificationTimeout = 4000;
@@ -56,47 +72,38 @@ class NotificationsClass extends GObject.Object implements Connectable {
break; break;
} }
notification.urgency !== AstalNotifd.Urgency.CRITICAL ? notification.urgency !== AstalNotifd.Urgency.CRITICAL &&
timeout(notificationTimeout, () => { timeout(notificationTimeout, () => {
this.notifications.map((item: AstalNotifd.Notification) => notification.dismiss();
item.id === notification.id && (() => { this.notifications.set(this.notifications.get().filter((item) => item.id !== notification.id));
this.removeNotification(notification.id); this.addHistory(notification);
this.addToNotificationHistory(notification); });
})())
})
: this.addToNotificationHistory(notification);
this.emit("notification-added", notification);
} }
public removeNotification(notificationId: number) { public addHistory(notification: AstalNotifd.Notification): void {
if(this.notifications.length === 1) let tmpArray: Array<AstalNotifd.Notification> = this.notificationHistory.reverse()
Windows.close(Windows.getWindow("floating-notifications")!); .filter((item: AstalNotifd.Notification) => item.id !== notification.id);
tmpArray.push(notification);
this.notifications = this.notifications.filter((notification: AstalNotifd.Notification) => this.notificationHistory = tmpArray.reverse();
notification.id !== notificationId);
this.emit("notification-removed", notificationId);
} }
public addToNotificationHistory(notification: AstalNotifd.Notification) { public removeHistory(notification: AstalNotifd.Notification) {
this.prependArray(this.notificationHistory, notification);
}
public removeFromNotificationHistory(notificationId: number) {
this.notificationHistory = this.notificationHistory.filter((curNotification: AstalNotifd.Notification) => this.notificationHistory = this.notificationHistory.filter((curNotification: AstalNotifd.Notification) =>
curNotification.id !== notificationId); curNotification.id !== notification.id);
}
private prependArray(array: Array<any>, item: any): Array<any> {
let tmpArray = array.reverse();
tmpArray.push(item);
return tmpArray.reverse();
} }
public getNotifd(): AstalNotifd.Notifd { public getNotifd(): AstalNotifd.Notifd {
return this.notifd; return this.notifd;
} }
get() {
return this.notifications.get();
}
subscribe(callback: (list: Array<AstalNotifd.Notification>) => void) {
return this.notifications.subscribe(callback);
}
} }
export const Notifications = new NotificationsClass(); export const Notifications = new NotificationsClass();
View File
View File
+16
View File
@@ -2,11 +2,13 @@
@use "./style/wal"; @use "./style/wal";
@use "./style/mixins"; @use "./style/mixins";
@use "./style/colors";
@use "./style/bar"; @use "./style/bar";
@use "./style/osd"; @use "./style/osd";
@use "./style/control-center"; @use "./style/control-center";
@use "./style/center-window"; @use "./style/center-window";
@use "./style/float-notifications";
* { * {
@@ -16,3 +18,17 @@
window * { window * {
@include mixins.default-styles; @include mixins.default-styles;
} }
tooltip {
padding: 16px;
& > box {
padding: 7px 8px;
border-radius: 10px;
background: rgba(colors.$bg-primary, .98);
font-size: 13.1px;
font-weight: 500;
color: colors.$fg-primary;
box-shadow: 0 1px 4px 1px rgba(colors.$bg-primary, .6);
}
}
+17 -3
View File
@@ -91,6 +91,10 @@
} }
} }
.clock.open > button {
background-color: colors.$bg-primary;
}
.media-eventbox { .media-eventbox {
& > .media { & > .media {
border-radius: 12px; border-radius: 12px;
@@ -98,6 +102,10 @@
padding: 0 8px; padding: 0 8px;
} }
&:hover > .media {
box-shadow: inset 0 0 0 300px rgba(colors.$fg-primary, .2);
}
& .nf { & .nf {
margin-right: 4px; margin-right: 4px;
font-size: 14px; font-size: 14px;
@@ -105,10 +113,9 @@
& .media-controls { & .media-controls {
transition: none; transition: none;
padding-left: 6px; padding: 0 6px;
border-top-right-radius: 12px; border-top-right-radius: 12px;
border-bottom-right-radius: 12px; border-bottom-right-radius: 12px;
background: linear-gradient(to left, colors.$bg-primary 45px, colors.$bg-primary);
& > button { & > button {
margin: 4px 1px; margin: 4px 1px;
@@ -124,6 +131,12 @@
& .media > box { & .media > box {
border-top-right-radius: 0; border-top-right-radius: 0;
border-bottom-right-radius: 0; border-bottom-right-radius: 0;
padding: 0 6px;
}
& .media {
padding-left: 0;
padding-right: 0;
} }
} }
} }
@@ -154,7 +167,8 @@
.audio { .audio {
@include mixins.reset-props; @include mixins.reset-props;
&:hover > box { &:hover > box,
&.open > box {
background: colors.$bg-primary; background: colors.$bg-primary;
} }
+74 -26
View File
@@ -8,24 +8,8 @@
border-radius: 18px; border-radius: 18px;
padding: 12px; padding: 12px;
& .left {
& > .top {
padding-bottom: 10px;
& .time {
font-size: 28px;
font-weight: 800;
}
& .date {
font-size: 14px;
font-weight: 500;
color: colors.$fg-disabled;
}
}
& .big-media { & .big-media {
padding: 6px 16px; padding: 6px;
& > box > .image { & > box > .image {
background-size: cover; background-size: cover;
@@ -51,27 +35,91 @@
} }
} }
& > .controls { & slider {
padding: 8px 0; background: transparent;
min-height: .6em;
}
& trough {
border-radius: 4px;
min-height: .6em;
}
& trough highlight {
border-radius: 4px;
min-height: .6em;
}
& .bottom {
& .controls {
margin-top: 5px;
& button {
padding: 7px;
& label {
font-size: 10px;
} }
} }
} }
& .right { & .elapsed,
& .length {
font-size: 12px;
color: colors.$fg-disabled;
}
}
}
& .left .top {
padding-bottom: 10px;
& .time {
font-size: 28px;
font-weight: 800;
}
& .date {
font-size: 14px;
font-weight: 500;
color: colors.$fg-disabled;
}
}
& .calendar-box { & .calendar-box {
padding: 5px;
& calendar { & calendar {
border-radius: 6px; $border-radius: 10px;
padding: 2px; font-weight: 600;
padding-bottom: 2px;
&.view { &.view {
background: funs.toRGB(color.adjust($color: wal.$color1, $lightness: -35%)); background: colors.$bg-primary;
border-radius: $border-radius;
}
& header { &.header {
background: funs.toRGB(color.adjust($color: wal.$background, $lightness: -20%)); background: colors.$bg-secondary;
border-top-left-radius: $border-radius;
border-top-right-radius: $border-radius;
padding: 4px;
} }
&.button {
transition: 80ms linear;
border-radius: 6px;
&:hover {
background-color: colors.$bg-tertiary;
} }
} }
&:selected {
background: colors.$bg-secondary;
border-radius: 6px;
}
&.highlight {
background: transparent;
box-shadow: 0 2px 0 -1px rgba(colors.$bg-secondary, .5);
}
} }
} }
} }
+41
View File
@@ -0,0 +1,41 @@
@use "./colors";
.floating-notifications-container {
padding: {
right: 6px;
top: 6px;
};
& > .notification {
background: colors.$bg-primary;
border-radius: 16px;
padding: 12px;
margin: 6px 0;
& > .top {
& .app-name {
font-size: 12px;
color: colors.$fg-disabled;
}
}
& .content {
& .image {
$size: 78px;
min-width: $size;
min-height: $size;
background-size: cover;
background-position: center 0;
margin: 6px;
}
}
&:first-child {
margin-top: 0;
}
&:last-child {
margin-bottom: 0;
}
}
}
-10
View File
@@ -69,16 +69,6 @@
} }
} }
& tooltip {
padding: 8px;
border-radius: 14px;
& label {
font-size: 14px;
color: colors.$fg-primary;
}
}
& trough { & trough {
background: funs.toRGB(color.adjust($color: wal.$color1, $lightness: -20%)); background: funs.toRGB(color.adjust($color: wal.$color1, $lightness: -20%));
border-radius: 8px; border-radius: 8px;
+20 -20
View File
@@ -1,26 +1,26 @@
// SCSS Variables // SCSS Variables
// Generated by 'wal' // Generated by 'wal'
$wallpaper: "/home/joaov/wallpapers/Miku, Rin and Luka Chibi.jpg"; $wallpaper: "/home/joaov/wallpapers/Miku Bush.jpg";
// Special // Special
$background: #3d2217; $background: #0f1b06;
$foreground: #cec7c5; $foreground: #c3c6c0;
$cursor: #cec7c5; $cursor: #c3c6c0;
// Colors // Colors
$color0: #3d2217; $color0: #0f1b06;
$color1: #b38678; $color1: #4e7278;
$color2: #a4998a; $color2: #5b7b94;
$color3: #b39e8a; $color3: #71807c;
$color4: #a5a09b; $color4: #7b9882;
$color5: #aea299; $color5: #a3a881;
$color6: #b4aea2; $color6: #778591;
$color7: #a39c99; $color7: #93988d;
$color8: #7f6f68; $color8: #626d59;
$color9: #EFB3A1; $color9: #6898A1;
$color10: #DBCCB9; $color10: #7AA5C6;
$color11: #EFD3B9; $color11: #97ABA6;
$color12: #DDD6CF; $color12: #A5CBAE;
$color13: #E8D8CD; $color13: #DAE0AC;
$color14: #F1E8D9; $color14: #9FB2C2;
$color15: #cec7c5; $color15: #c3c6c0;
+1
View File
@@ -0,0 +1 @@
//TODO
+5 -2
View File
@@ -1,3 +1,4 @@
import { Binding } from "astal";
import { Gtk, Widget } from "astal/gtk3"; import { Gtk, Widget } from "astal/gtk3";
export interface SeparatorProps { export interface SeparatorProps {
@@ -6,21 +7,23 @@ export interface SeparatorProps {
cssColor?: string; cssColor?: string;
orientation?: Gtk.Orientation; orientation?: Gtk.Orientation;
size?: number; size?: number;
visible?: boolean | Binding<boolean | undefined>;
} }
export function Separator(props: SeparatorProps) { export function Separator(props: SeparatorProps) {
return new Widget.Box({ return new Widget.Box({
className: `separator separator-${ props.orientation == Gtk.Orientation.VERTICAL ? "vertical" : "horizontal" } ${ props.class && props.class }`, className: `separator separator-${ props.orientation == Gtk.Orientation.VERTICAL ? "vertical" : "horizontal" } ${ props.class && props.class }`,
visible: props.visible,
css: `.separator { css: `.separator {
background: ${ props.cssColor || "lightgray" }; background: ${ props.cssColor || "lightgray" };
opacity: ${ props.alpha || 1 }; opacity: ${ props.alpha || 1 };
} }
.separator-horizontal { .separator-horizontal {
padding-right: ${props.size || 1 }px; padding-bottom: ${props.size || 1 }px;
margin: 7px 4px; margin: 7px 4px;
} }
.separator-vertical { .separator-vertical {
padding-bottom: ${props.size || 1 }px; padding-right: ${props.size || 1 }px;
margin: 4px 7px; margin: 4px 7px;
}`, }`,
} as Widget.BoxProps); } as Widget.BoxProps);
+3 -1
View File
@@ -2,12 +2,14 @@ import { bind, Process } from "astal";
import { Widget } from "astal/gtk3"; import { Widget } from "astal/gtk3";
import AstalWp from "gi://AstalWp"; import AstalWp from "gi://AstalWp";
import { Wireplumber } from "../../scripts/volume"; import { Wireplumber } from "../../scripts/volume";
import { ControlCenter } from "../../window/ControlCenter";
const wp = AstalWp.get_default(); const wp = AstalWp.get_default();
export function Audio() { export function Audio() {
return wp && new Widget.EventBox({ return wp && new Widget.EventBox({
className: "audio", className: bind(ControlCenter, "visible").as((visible: boolean) =>
visible ? "audio open" : "audio"),
onClick: () => Process.exec_async("astal toggle control-center", () => {}), onClick: () => Process.exec_async("astal toggle control-center", () => {}),
child: new Widget.Box({ child: new Widget.Box({
children: [ children: [
+3 -2
View File
@@ -1,12 +1,13 @@
import { Widget } from "astal/gtk3"; import { Widget } from "astal/gtk3";
import { getDateTime } from "../../scripts/time"; import { getDateTime } from "../../scripts/time";
import { GLib } from "astal"; import { bind, GLib } from "astal";
import { Windows } from "../../windows"; import { Windows } from "../../windows";
import { CenterWindow } from "../../window/CenterWindow"; import { CenterWindow } from "../../window/CenterWindow";
export function Clock(): JSX.Element { export function Clock(): JSX.Element {
return new Widget.Box({ return new Widget.Box({
className: "clock", className: bind(CenterWindow, "visible").as((visible: boolean) =>
visible ? "clock open" : "clock"),
child: new Widget.Button({ child: new Widget.Button({
onClick: () => Windows.toggle(CenterWindow), onClick: () => Windows.toggle(CenterWindow),
label: getDateTime().as((dateTime: GLib.DateTime) => { label: getDateTime().as((dateTime: GLib.DateTime) => {
+7 -1
View File
@@ -2,6 +2,8 @@ import { bind, GLib, Process } from "astal";
import { Gtk, Widget } from "astal/gtk3"; import { Gtk, Widget } from "astal/gtk3";
import AstalMpris from "gi://AstalMpris"; import AstalMpris from "gi://AstalMpris";
import { Separator, SeparatorProps } from "../Separator"; import { Separator, SeparatorProps } from "../Separator";
import { CenterWindow } from "../../window/CenterWindow";
import { Windows } from "../../windows";
const mpris: AstalMpris.Mpris = AstalMpris.get_default(); const mpris: AstalMpris.Mpris = AstalMpris.get_default();
@@ -66,7 +68,10 @@ 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>) => players[0]).as(Boolean), visible: bind(mpris, "players").as((players: Array<AstalMpris.Player>) => {
return players[0] && players[0].get_available() || CenterWindow.is_visible();
}),
onClick: () => Windows.toggle(CenterWindow),
child: new Widget.Box({ child: new Widget.Box({
className: "media", className: "media",
children: [ children: [
@@ -85,6 +90,7 @@ export function Media(): Gtk.Widget {
label: bind(players[0], "title").as((title: string) => title || "No Title") label: bind(players[0], "title").as((title: string) => title || "No Title")
} as Widget.LabelProps), } as Widget.LabelProps),
Separator({ Separator({
orientation: Gtk.Orientation.VERTICAL,
size: 2, size: 2,
cssColor: `rgb(180, 180, 180)`, cssColor: `rgb(180, 180, 180)`,
alpha: 1 alpha: 1
+34 -6
View File
@@ -8,8 +8,11 @@ export const BigMedia: Gtk.Widget = new Widget.Box({
className: "big-media", className: "big-media",
orientation: Gtk.Orientation.VERTICAL, orientation: Gtk.Orientation.VERTICAL,
homogeneous: false, homogeneous: false,
width_request: 250,
visible: bind(AstalMpris.get_default(), "players").as((players: Array<AstalMpris.Player>) =>
players[0] ? true : false),
children: bind(AstalMpris.get_default(), "players").as((players: Array<AstalMpris.Player>) => children: bind(AstalMpris.get_default(), "players").as((players: Array<AstalMpris.Player>) =>
players[0] ? [ players[0] && [
new Widget.Box({ new Widget.Box({
halign: Gtk.Align.CENTER, halign: Gtk.Align.CENTER,
child: new Widget.Box({ child: new Widget.Box({
@@ -72,16 +75,29 @@ export const BigMedia: Gtk.Widget = new Widget.Box({
}) })
] ]
}), }),
new Widget.Box({ new Widget.CenterBox({
className: "controls button-row", className: "bottom",
homogeneous: false,
hexpand: true, hexpand: true,
halign: Gtk.Align.CENTER, startWidget: new Widget.Label({
className: "elapsed",
valign: Gtk.Align.START,
halign: Gtk.Align.START,
label: bind(players[0], "position").as((pos: number) => {
const sec: number = Math.floor(pos % 60);
return pos > 0 && players[0].length > 0 ?
`${Math.floor(pos / 60)}:${sec < 10 ? "0" : ""}${sec}`
: `0:00`;
})
} as Widget.LabelProps),
centerWidget: new Widget.Box({
className: "controls button-row",
children: [ children: [
new Widget.Button({ new Widget.Button({
className: "link nf", className: "link nf",
label: "󰌹", label: "󰌹",
tooltipText: "Copy link to Clipboard", tooltipText: "Copy link to Clipboard",
visible: bind(players[0], "metadata").as((_metadata: GLib.HashTable) => visible: bind(players[0], "metadata").as((_meta: GLib.HashTable) =>
players[0].get_meta("xesam:url") === null), players[0].get_meta("xesam:url") === null),
onClick: () => Process.exec(`wl-copy ${players[0].get_meta("xesam:url")?.get_string()[0]}`) onClick: () => Process.exec(`wl-copy ${players[0].get_meta("xesam:url")?.get_string()[0]}`)
} as Widget.ButtonProps), } as Widget.ButtonProps),
@@ -111,6 +127,18 @@ export const BigMedia: Gtk.Widget = new Widget.Box({
onClick: () => players[0].canGoNext && players[0].next() onClick: () => players[0].canGoNext && players[0].next()
} as Widget.ButtonProps) } as Widget.ButtonProps)
] ]
} as Widget.BoxProps),
endWidget: new Widget.Label({
className: "length",
valign: Gtk.Align.START,
halign: Gtk.Align.END,
label: bind(players[0], "length").as((len/* bananananananana */: number) => {
const sec: number = Math.floor(len % 60);
return len > 0 ?
`${Math.floor(len / 60)}:${sec < 10 ? "0" : ""}${sec}`
: "0:00";
}) })
] : new Widget.Box({ className: "empty no-media" })) } as Widget.LabelProps)
})
])
} as Widget.BoxProps); } as Widget.BoxProps);
+20 -10
View File
@@ -1,8 +1,9 @@
import { Astal, Gtk, Widget } from "astal/gtk3"; import { Astal, Gtk, Widget } from "astal/gtk3";
import { GLib } from "astal"; import { bind, GLib } from "astal";
import { getDateTime } from "../scripts/time"; import { getDateTime } from "../scripts/time";
import { BigMedia } from "../widget/center-window/BigMedia"; import { BigMedia } from "../widget/center-window/BigMedia";
import { Separator, SeparatorProps } from "../widget/Separator";
export const CenterWindow: Widget.Window = new Widget.Window({ export const CenterWindow: Widget.Window = new Widget.Window({
className: "center-window", className: "center-window",
@@ -12,7 +13,6 @@ export const CenterWindow: Widget.Window = new Widget.Window({
layer: Astal.Layer.OVERLAY, layer: Astal.Layer.OVERLAY,
exclusivity: Astal.Exclusivity.NORMAL, exclusivity: Astal.Exclusivity.NORMAL,
visible: false, visible: false,
height_request: 400,
margin_top: 10, margin_top: 10,
anchor: Astal.WindowAnchor.TOP, anchor: Astal.WindowAnchor.TOP,
child: new Widget.Box({ child: new Widget.Box({
@@ -21,7 +21,6 @@ export const CenterWindow: Widget.Window = new Widget.Window({
new Widget.Box({ new Widget.Box({
className: "vertical left", className: "vertical left",
orientation: Gtk.Orientation.VERTICAL, orientation: Gtk.Orientation.VERTICAL,
width_request: 300,
children: [ children: [
new Widget.Box({ new Widget.Box({
className: "top", className: "top",
@@ -36,18 +35,15 @@ export const CenterWindow: Widget.Window = new Widget.Window({
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 %Y")) dateTime.format("%A, %B %d"))
} as Widget.LabelProps) } as Widget.LabelProps)
] ]
} as Widget.BoxProps), } as Widget.BoxProps),
BigMedia
]
} as Widget.BoxProps),
new Widget.Box({
className: "vertical right",
children: [
new Widget.Box({ new Widget.Box({
className: "calendar-box", className: "calendar-box",
vexpand: false,
hexpand: true,
valign: Gtk.Align.START,
child: new Gtk.Calendar({ child: new Gtk.Calendar({
visible: true, visible: true,
show_heading: true, show_heading: true,
@@ -56,6 +52,20 @@ export const CenterWindow: Widget.Window = new Widget.Window({
} as Gtk.Calendar.ConstructorProps) } as Gtk.Calendar.ConstructorProps)
} as Widget.BoxProps) } as Widget.BoxProps)
] ]
} as Widget.BoxProps),
Separator({
visible: bind(BigMedia, "visible"),
orientation: Gtk.Orientation.VERTICAL,
alpha: .5,
cssColor: "gray",
size: 1
} as SeparatorProps),
new Widget.Box({
className: "vertical right",
orientation: Gtk.Orientation.VERTICAL,
children: [
BigMedia
]
} as Widget.BoxProps) } as Widget.BoxProps)
] ]
} as Widget.BoxProps) } as Widget.BoxProps)
+13 -5
View File
@@ -16,14 +16,22 @@ function NotificationWidget(notification: AstalNotifd.Notification): Gtk.Widget
hexpand: true, hexpand: true,
vexpand: false, vexpand: false,
children: [ children: [
new Widget.Icon({
className: "icon",
visible: notification.appIcon !== "",
icon: notification.appIcon || "image-missing",
iconSize: Gtk.IconSize.DND,
css: ".icon { font-size: 24px; }"
}),
new Widget.Label({ new Widget.Label({
className: "app-name", className: "app-name",
halign: Gtk.Align.START, halign: Gtk.Align.START,
label: notification.appName || "Unknown Application" label: notification.appName || "Unknown Application"
} as Widget.LabelProps), } as Widget.LabelProps),
new Widget.Button({ new Widget.Button({
className: "close-button", className: "close nf",
onClick: () => Notifications.removeNotification(notification.id) onClick: () => notification.dismiss(),
label: "󰅖"
} as Widget.ButtonProps) } as Widget.ButtonProps)
] ]
} as Widget.BoxProps), } as Widget.BoxProps),
@@ -71,8 +79,8 @@ export const FloatingNotifications: Widget.Window = new Widget.Window({
className: "floating-notifications-container", className: "floating-notifications-container",
orientation: Gtk.Orientation.VERTICAL, orientation: Gtk.Orientation.VERTICAL,
homogeneous: false, homogeneous: false,
children: bind(Notifications, "notifications").as((notifications: Array<AstalNotifd.Notification>) => children: Notifications.notifications().as((notifications: Array<AstalNotifd.Notification>) =>
notifications.map((notification: AstalNotifd.Notification) => notifications.map((item: AstalNotifd.Notification) =>
NotificationWidget(notification))) NotificationWidget(item)))
} as Widget.BoxProps) } as Widget.BoxProps)
} as Widget.WindowProps); } as Widget.WindowProps);
+37
View File
@@ -0,0 +1,37 @@
import { Variable } from "astal";
import { Astal, Gtk, Widget } from "astal/gtk3";
// TODO
export interface RunnerProps {
anchor?: Astal.WindowAnchor;
width?: number;
height?: number;
entryPlaceHolder?: string;
resultsPlaceholder?: Array<Gtk.Widget>;
}
export function Runner(props?: RunnerProps) {
const entryText: Variable<string> = new Variable<string>("");
const resultsBox: Widget.Box = new Widget.Box({
className: "results",
} as Widget.BoxProps);
return new Widget.Window({
namespace: "runner",
widthRequest: props?.width || 600,
heightRequest: props?.height || 500,
child: new Widget.Box({
className: "main",
children: [
new Widget.Entry({
className: "search",
onChanged: (entry) => entryText.set(entry.text),
} as Widget.EntryProps),
]
} as Widget.BoxProps)
} as Widget.WindowProps);
}
+2 -1
View File
@@ -1,4 +1,4 @@
import { Gtk, Widget } from "astal/gtk3"; import { Gtk } from "astal/gtk3";
import { Bar } from "./window/Bar"; import { Bar } from "./window/Bar";
import { OSD } from "./window/OSD"; import { OSD } from "./window/OSD";
@@ -22,6 +22,7 @@ export const Windows = GObject.registerClass({
WindowsClass.windowsMap.set("control-center", ControlCenter); WindowsClass.windowsMap.set("control-center", ControlCenter);
WindowsClass.windowsMap.set("center-window", CenterWindow); WindowsClass.windowsMap.set("center-window", CenterWindow);
WindowsClass.windowsMap.set("logout-menu", LogoutMenu); WindowsClass.windowsMap.set("logout-menu", LogoutMenu);
WindowsClass.windowsMap.set("floating-notifications", FloatingNotifications);
} }
public _init(...args: any[]) { public _init(...args: any[]) {