refactor(ags): start update types and use classes for cbox and popup
All checks were successful
Discord / discord commits (push) Has been skipped

This commit is contained in:
matt1432 2024-01-29 18:54:07 -05:00
parent 49f3a92ce3
commit 9c64b00243
30 changed files with 914 additions and 610 deletions

View file

@ -2,7 +2,7 @@
"env": {
"es2021": true
},
"extends": "eslint:recommended",
"extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
"parser": "@typescript-eslint/parser",
"overrides": [],
"parserOptions": {
@ -19,9 +19,6 @@
"no-unreachable-loop": ["error", { "ignore": [
"ForInStatement", "ForOfStatement"
]}],
"no-use-before-define": ["error", {
"functions": false
}],
"block-scoped-var": ["error"],
@ -79,6 +76,7 @@
"no-unused-vars": "off",
"@typescript-eslint/no-unused-vars": "warn",
"@typescript-eslint/no-unsafe-declaration-merging": "off",
"@stylistic/array-bracket-newline": ["warn", "consistent"],
"@stylistic/array-bracket-spacing": ["warn", "never"],

129
modules/ags/config/global-types.d.ts vendored Normal file
View file

@ -0,0 +1,129 @@
import { Widget } from 'types/@girs/gtk-3.0/gtk-3.0.cjs';
import { Widget as agsWidget } from 'types/widgets/widget';
export type AgsWidget = agsWidget<unknown> & Widget;
// For ./ts/applauncher/main.ts
import { Application } from 'types/service/applications.ts';
import { CursorBoxProps } from 'ts/misc/cursorbox';
export type AgsAppItem = AgsEventBox<Widget, { app: Application }
& CursorBoxProps<Widget, unknown>>;
// For ./ts/bar/hovers/keyboard.ts
export type Keyboard = {
address: string;
name: string;
rules: string;
model: string;
layout: string;
variant: string;
options: string;
active_keymap: string;
main: boolean;
};
// For ./ts/bar/items/workspaces.ts
// TODO: improve type
export type Workspace = AgsRevealer<unknown & Widget, unknown & { id: number }>;
// For ./ts/bar/fullscreen.ts
export type DefaultProps = RevealerProps<CenterBoxGeneric>;
// For ./ts/media-player/gesture.ts
export type Gesture = {
attribute?: object
setup?(self: OverlayGeneric): void
props?: OverlayProps<unknown & Widget, unknown>
};
// For ./ts/media-player/mpris.ts
type PlayerDragProps = unknown & { dragging: boolean };
export type PlayerDrag = AgsCenterBox<
unknown & Widget, unknown & Widget, unknown & Widget, unknown & PlayerDragProps
>;
type Colors = {
imageAccent: string;
buttonAccent: string;
buttonText: string;
hoverAccent: string;
};
// For ./ts/media-player
export type PlayerBoxProps = {
bgStyle: string,
player: MprisPlayer,
};
export type PlayerBox = AgsCenterBox<
unknown & Widget, unknown & Widget, unknown & Widget, PlayerBoxProps
>;
export type PlayerOverlay = AgsOverlay<AgsWidget, {
players: Map;
setup: boolean;
dragging: boolean;
includesWidget(playerW: PlayerBox): PlayerBox;
showTopOnly(): void;
moveToTop(player: PlayerBox): void;
}>;
export type PlayerButtonType = {
player: MprisPlayer
colors: Var<Colors>
items: Array<[name: string, widget: AgsWidget]>
onClick: string
prop: string
};
// For CursorBox
import { CursorBox, CursorBoxProps } from 'ts/misc/cursorbox';
export type CursorBox = CursorBox;
export type CursorBoxProps = CursorBoxProps;
// For PopupWindow
export type PopupChild = Binding<
Var<Widget>,
'is_listening' | 'is_polling' | 'value',
Widget[]
>;
export type CloseType = 'none' | 'stay' | 'released' | 'clicked';
export type PopupWindowProps<Child extends Widget, Attr = unknown> =
WindowProps<Child> & {
transition?: RevealerProps<Widget>['transition']
transition_duration?: number
bezier?: string
on_open?(self: AgsWindow<Widget, unknown>): void
on_close?(self: AgsWindow<Widget, unknown>): void
blur?: boolean
close_on_unfocus?: CloseType
attribute?: Attr;
content?: Widget
anchor?: Array<'top' | 'bottom' | 'right' | 'left'>;
};
import { PopupWindow } from 'ts/misc/popup';
export type PopupWindow = PopupWindow;
// Generic widgets
import AgsBox from 'types/widgets/box.ts';
export type BoxGeneric = AgsBox<unknown & Widget, unknown>;
import AgsCenterBox, { CenterBoxProps } from 'types/widgets/centerbox';
export type CenterBoxGeneric = AgsCenterBox<
unknown & Widget, unknown & Widget, unknown & Widget, unknown
>;
export type CenterBoxPropsGeneric = CenterBoxProps<
unknown & Widget, unknown & Widget, unknown & Widget, unknown
>;
import AgsEventBox from 'types/widgets/eventbox.ts';
export type EventBoxGeneric = AgsEventBox<unknown & Widget, unknown>;
import AgsLabel from 'types/widgets/label.ts';
export type LabelGeneric = AgsLabel<unknown>;
import AgsOverlay, { OverlayProps } from 'types/widgets/overlay.ts';
export type OverlayGeneric = AgsOverlay<unknown & Widget, unknown>;
import AgsRevealer, { RevealerProps } from 'types/widgets/revealer.ts';
export type RevealerGeneric = AgsRevealer<unknown & Widget, unknown>;
import AgsStack from 'types/widgets/stack.ts';
export type StackGeneric = AgsStack<unknown & Widget, unknown>;

View file

@ -27,7 +27,7 @@
padding: 4.5px 9px;
}
&.hover box {
&:hover box {
box-shadow: inset 0 0 0 1px rgba(238, 238, 238, 0.03);
background-color: rgba(238, 238, 238, 0.154);
color: #f1f1f1;

View file

@ -31,7 +31,7 @@
padding: 4.5px 9px;
}
&.hover box {
&:hover box {
box-shadow: inset 0 0 0 1px rgba(238, 238, 238, 0.03);
background-color: rgba(238, 238, 238, 0.154);
color: #f1f1f1;

View file

@ -48,7 +48,7 @@
border 0.5s ease-in-out;
}
.toggle-on.hover, .toggle-off.hover {
.toggle-on:hover, .toggle-off:hover {
background-color: rgba(127, 132, 156, 0.4);
transition: background-color 0.5s ease-in-out,
border 0.5s ease-in-out;

View file

@ -15,7 +15,7 @@ const ON_CLICK_TRIGGERS = [
];
// Types
import AgsWindow from 'types/widgets/window';
import { PopupWindow } from 'global-types';
import { Subprocess } from 'types/@girs/gio-2.0/gio-2.0.cjs';
type Layer = {
address: string;
@ -54,7 +54,7 @@ class Pointers extends Service {
#process = null as Subprocess | null;
#lastLine = '';
#pointers = [] as Array<String>;
#pointers = [] as Array<string>;
get process() {
return this.#process;
@ -114,11 +114,11 @@ class Pointers extends Service {
#initAppConnection() {
App.connect('window-toggled', () => {
const anyVisibleAndClosable =
(Array.from(App.windows) as Array<[string, AgsWindow]>)
(Array.from(App.windows) as Array<[string, PopupWindow]>)
.some((w) => {
const closable = w[1].attribute?.close_on_unfocus &&
!(w[1].attribute.close_on_unfocus === 'none' ||
w[1].attribute.close_on_unfocus === 'stay');
const closable = w[1].close_on_unfocus &&
!(w[1].close_on_unfocus === 'none' ||
w[1].close_on_unfocus === 'stay');
return w[1].visible && closable;
});
@ -134,10 +134,10 @@ class Pointers extends Service {
}
static detectClickedOutside(clickStage: string) {
const toClose = (Array.from(App.windows) as Array<[string, AgsWindow]>)
const toClose = (Array.from(App.windows) as Array<[string, PopupWindow]>)
.some((w) => {
const closable = (w[1].attribute?.close_on_unfocus &&
w[1].attribute.close_on_unfocus === clickStage);
const closable = (w[1].close_on_unfocus &&
w[1].close_on_unfocus === clickStage);
return w[1].visible && closable;
});
@ -190,11 +190,11 @@ class Pointers extends Service {
const widgets = overlayLayer.filter((n) => {
const window =
(App.getWindow(n.namespace) as AgsWindow);
(App.getWindow(n.namespace) as PopupWindow);
return window &&
window.attribute?.close_on_unfocus &&
window.attribute?.close_on_unfocus ===
window.close_on_unfocus &&
window.close_on_unfocus ===
clickStage;
});

View file

@ -1,8 +1,7 @@
import App from 'resource:///com/github/Aylur/ags/app.js';
import Applications from 'resource:///com/github/Aylur/ags/service/applications.js';
// FIXME: find cleaner way to import this
// @ts-expect-error
import { Fzf } from 'file:///home/matt/.nix/modules/ags/config/node_modules/fzf/dist/fzf.es.js';
// @ts-expect-error find cleaner way to import this
import { Fzf, FzfResultItem } from 'file:///home/matt/.nix/modules/ags/config/node_modules/fzf/dist/fzf.es.js';
import { Box, Entry, Icon, Label, ListBox, Revealer, Scrollable } from 'resource:///com/github/Aylur/ags/widget.js';
@ -10,14 +9,13 @@ import PopupWindow from '../misc/popup.ts';
import AppItem from './app-item.ts';
// Types
import { Application } from 'types/service/applications.ts';
import { ListBoxRow } from 'types/@girs/gtk-3.0/gtk-3.0.cjs';
import AgsEventBox from 'types/widgets/eventbox';
import { Application } from 'types/service/applications.ts';
import { AgsAppItem } from 'global-types';
const Applauncher = (window_name = 'applauncher') => {
let fzfResults: Array<any>;
// @ts-expect-error
let fzfResults: FzfResultItem<Application>[];
const list = ListBox();
const setSort = (text: string) => {
@ -31,25 +29,21 @@ const Applauncher = (window_name = 'applauncher') => {
});
fzfResults = fzf.find(text);
list.set_sort_func(
(a: ListBoxRow, b: ListBoxRow) => {
const row1 = (a.get_children()[0] as AgsEventBox)
?.attribute.app.name;
const row2 = (b.get_children()[0] as AgsEventBox)
?.attribute.app.name;
list.set_sort_func((a, b) => {
const row1 = (a.get_children()[0] as AgsAppItem).attribute.app.name;
const row2 = (b.get_children()[0] as AgsAppItem).attribute.app.name;
if (!row1 || !row2) {
return 0;
}
if (!row1 || !row2) {
return 0;
}
return fzfResults.indexOf(row1) -
return fzfResults.indexOf(row1) -
fzfResults.indexOf(row1) || 0;
},
);
});
};
const makeNewChildren = () => {
const rows = list.get_children() as Array<ListBoxRow>;
const rows = list.get_children() as ListBoxRow[];
rows.forEach((ch) => {
ch.destroy();
@ -94,14 +88,14 @@ const Applauncher = (window_name = 'applauncher') => {
setSort(text);
let visibleApps = 0;
const rows = list.get_children() as Array<ListBoxRow>;
const rows = list.get_children() as ListBoxRow[];
rows.forEach((row) => {
row.changed();
const item = (row.get_children()[0] as AgsEventBox);
const item = (row.get_children()[0] as AgsAppItem);
if (item?.attribute.app) {
if (item.attribute.app) {
const isMatching = fzfResults.find((r) => {
return r.item.name === item.attribute.app.name;
});
@ -162,5 +156,5 @@ const Applauncher = (window_name = 'applauncher') => {
export default () => PopupWindow({
name: 'applauncher',
keymode: 'on-demand',
child: Applauncher(),
content: Applauncher(),
});

View file

@ -5,8 +5,8 @@ import { Box, EventBox, Revealer, Window } from 'resource:///com/github/Aylur/ag
// Types
import { Variable as Var } from 'types/variable';
import AgsBox from 'types/widgets/box';
import { RevealerProps } from 'types/widgets/revealer';
import { BoxGeneric, DefaultProps } from 'global-types';
const BarCloser = (variable: Var<boolean>) => Window({
name: 'bar-closer',
@ -30,7 +30,7 @@ const BarCloser = (variable: Var<boolean>) => Window({
}),
});
export default (props: RevealerProps) => {
export default (props?: DefaultProps) => {
const Revealed = Variable(true);
const barCloser = BarCloser(Revealed);
@ -50,7 +50,7 @@ export default (props: RevealerProps) => {
}
};
const checkGlobalFsState = (_: AgsBox, fullscreen: boolean) => {
const checkGlobalFsState = (_: BoxGeneric, fullscreen: boolean) => {
Revealed.value = !fullscreen;
};

View file

@ -6,22 +6,11 @@ import HoverRevealer from './hover-revealer.ts';
const DEFAULT_KB = 'at-translated-set-2-keyboard';
import AgsLabel from 'types/widgets/label.ts';
type Keyboard = {
address: string;
name: string;
rules: string;
model: string;
layout: string;
variant: string;
options: string;
active_keymap: string;
main: boolean;
};
// Types
import { Keyboard, LabelGeneric } from 'global-types';
const getKbdLayout = (self: AgsLabel, _: string, layout: string) => {
const getKbdLayout = (self: LabelGeneric, _: string, layout: string) => {
if (layout) {
if (layout === 'error') {
return;
@ -35,7 +24,7 @@ const getKbdLayout = (self: AgsLabel, _: string, layout: string) => {
// At launch, kb layout is undefined
Hyprland.sendMessage('j/devices').then((obj) => {
const keyboards = Array.from(JSON.parse(obj)
.keyboards) as Array<Keyboard>;
.keyboards) as Keyboard[];
const kb = keyboards.find((v) => v.name === DEFAULT_KB);
if (kb) {

View file

@ -9,15 +9,15 @@ import Separator from '../../misc/separator.ts';
const SPACING = 4;
// Types
import AgsWindow from 'types/widgets/window.ts';
import { PopupWindow } from 'global-types';
export default () => CursorBox({
class_name: 'toggle-off',
on_primary_click_release: (self) => {
(App.getWindow('notification-center') as AgsWindow)
?.attribute.set_x_pos(
(App.getWindow('notification-center') as PopupWindow)
.set_x_pos(
self.get_allocation(),
'right',
);

View file

@ -14,9 +14,7 @@ import Separator from '../../misc/separator.ts';
const SPACING = 4;
// Types
import AgsRevealer from 'types/widgets/revealer.ts';
import AgsBox from 'types/widgets/box.ts';
import AgsWindow from 'types/widgets/window.ts';
import { PopupWindow } from 'global-types';
export default () => {
@ -36,8 +34,8 @@ export default () => {
class_name: 'toggle-off',
on_primary_click_release: (self) => {
(App.getWindow('quick-settings') as AgsWindow)
?.attribute.set_x_pos(
(App.getWindow('quick-settings') as PopupWindow)
.set_x_pos(
self.get_allocation(),
'right',
);
@ -55,17 +53,15 @@ export default () => {
attribute: {
hoverRevealers: hoverRevealers.map((rev) => {
const box = rev.child as AgsBox;
const box = rev.child;
return box.children[1];
}),
},
on_hover_lost: (self) => {
self.attribute.hoverRevealers.forEach(
(rev: AgsRevealer) => {
rev.reveal_child = false;
},
);
self.attribute.hoverRevealers.forEach((rev) => {
rev.reveal_child = false;
});
},
child: Box({

View file

@ -10,7 +10,6 @@ const SPACING = 12;
// Types
import { TrayItem } from 'types/service/systemtray.ts';
import AgsRevealer from 'types/widgets/revealer.ts';
const SysTrayItem = (item: TrayItem) => {
@ -54,7 +53,7 @@ const SysTray = () => MenuBar({
self.child = w;
self.show_all();
(<AgsRevealer> w.child).reveal_child = true;
w.child.reveal_child = true;
}, 'added')
.hook(SystemTray, (_, id) => {

View file

@ -8,10 +8,13 @@ import CursorBox from '../../misc/cursorbox.ts';
const URGENT_DURATION = 1000;
// Types
import AgsBox from 'types/widgets/box.ts';
import AgsRevealer from 'types/widgets/revealer.ts';
import AgsOverlay from 'types/widgets/overlay.ts';
import AgsEventBox from 'types/widgets/eventbox.ts';
import {
BoxGeneric,
EventBoxGeneric,
OverlayGeneric,
RevealerGeneric,
Workspace,
} from 'global-types';
const Workspace = ({ id }: { id: number }) => {
@ -31,7 +34,10 @@ const Workspace = ({ id }: { id: number }) => {
class_name: 'button',
setup: (self) => {
const update = (_: AgsBox, addr: string | undefined) => {
const update = (
_: BoxGeneric,
addr: string | undefined,
) => {
const workspace = Hyprland.getWorkspace(id);
const occupied = workspace && workspace.windows > 0;
@ -79,13 +85,13 @@ export default () => {
const L_PADDING = 16;
const WS_WIDTH = 30;
const updateHighlight = (self: AgsBox) => {
const updateHighlight = (self: BoxGeneric) => {
const currentId = Hyprland.active.workspace.id;
const indicators = (((self.get_parent() as AgsOverlay)
.child as AgsEventBox)
.child as AgsBox)
.children as Array<AgsRevealer>;
const indicators = (((self.get_parent() as OverlayGeneric)
.child as EventBoxGeneric)
.child as BoxGeneric)
.children as Workspace[];
const currentIndex = indicators
.findIndex((w) => w.attribute.id === currentId);
@ -111,26 +117,26 @@ export default () => {
child: Box({
class_name: 'workspaces',
attribute: { workspaces: [] },
attribute: { workspaces: [] as Workspace[] },
setup: (self) => {
const workspaces = (): Array<AgsRevealer> =>
self.attribute.workspaces;
const refresh = () => {
(self.children as Array<AgsRevealer>).forEach((rev) => {
rev.reveal_child = false;
});
(self.children as RevealerGeneric[])
.forEach((rev) => {
rev.reveal_child = false;
});
workspaces().forEach((ws) => {
ws.reveal_child = true;
});
self.attribute.workspaces
.forEach((ws) => {
ws.reveal_child = true;
});
};
const updateWorkspaces = () => {
Hyprland.workspaces.forEach((ws) => {
const currentWs = (self.children as Array<AgsBox>)
.find((ch) => ch.attribute.id === ws.id);
const currentWs =
(self.children as Workspace[])
.find((ch) => ch.attribute.id === ws.id);
if (!currentWs && ws.id > 0) {
self.add(Workspace({ id: ws.id }));
@ -139,21 +145,22 @@ export default () => {
self.show_all();
// Make sure the order is correct
workspaces().forEach((workspace, i) => {
(<AgsBox> workspace.get_parent()).reorder_child(
workspace,
i,
);
self.attribute.workspaces.forEach((workspace, i) => {
(workspace.get_parent() as BoxGeneric)
.reorder_child(workspace, i);
});
};
self.hook(Hyprland, () => {
self.attribute.workspaces =
(self.children as Array<AgsBox>).filter((ch) => {
return Hyprland.workspaces.find((ws) => {
return ws.id === ch.attribute.id;
});
}).sort((a, b) => a.attribute.id - b.attribute.id);
(self.children as Workspace[])
.filter((ch) => {
return Hyprland.workspaces.find((ws) => {
return ws.id === ch.attribute.id;
});
})
.sort((a, b) =>
a.attribute.id - b.attribute.id);
updateWorkspaces();
refresh();

View file

@ -26,10 +26,7 @@ export default (
.get_property('border-radius', Gtk.StateFlags.NORMAL);
widget.set_size_request(r, r);
widget.connect('draw', (_, context) => {
// FIXME: get proper Context type
const cr = context as any;
widget.connect('draw', (_, cr) => {
const c = widget.get_style_context()
.get_property('background-color', Gtk.StateFlags.NORMAL);

View file

@ -90,7 +90,7 @@ export default () => PopupWindow({
anchor: ['top'],
margins: [TOP_MARGIN, 0, 0, 0],
child: Box({
content: Box({
class_name: 'date',
vertical: true,

View file

@ -10,20 +10,15 @@ const TRANSITION = `transition: margin ${ANIM_DURATION}ms ease,
opacity ${ANIM_DURATION}ms ease;`;
// Types
import AgsOverlay from 'types/widgets/overlay';
import OverlayProps from 'types/widgets/overlay';
import AgsBox from 'types/widgets/box';
import AgsCenterBox from 'types/widgets/centerbox';
import { Connectable } from 'types/widgets/widget';
type Gesture = {
attribute?: Object
setup?(self: Connectable<AgsOverlay> & AgsOverlay): void
props?: OverlayProps
};
import {
CenterBoxGeneric,
Gesture,
OverlayGeneric,
PlayerBox,
} from 'global-types';
export default ({
attribute = {},
setup = () => {/**/},
...props
}: Gesture) => {
@ -39,10 +34,11 @@ export default ({
const content = Overlay({
...props,
attribute: {
...attribute,
players: new Map(),
setup: false,
dragging: false,
includesWidget: (playerW: AgsOverlay) => {
includesWidget: (playerW: OverlayGeneric) => {
return content.overlays.find((w) => w === playerW);
},
@ -50,7 +46,7 @@ export default ({
over.visible = over === content.overlays.at(-1);
}),
moveToTop: (player: AgsCenterBox) => {
moveToTop: (player: CenterBoxGeneric) => {
player.visible = true;
content.reorder_overlay(player, -1);
timeout(ANIM_DURATION, () => {
@ -82,7 +78,7 @@ export default ({
self.attribute.dragging = true;
let offset = gesture.get_offset()[1];
const playerBox = self.overlays.at(-1) as AgsBox;
const playerBox = self.overlays.at(-1) as PlayerBox;
if (!offset) {
return;
@ -118,7 +114,7 @@ export default ({
self.attribute.dragging = false;
const offset = gesture.get_offset()[1];
const playerBox = self.overlays.at(-1) as AgsBox;
const playerBox = self.overlays.at(-1) as PlayerBox;
// If crosses threshold after letting go, slide away
if (offset && Math.abs(offset) > MAX_OFFSET) {

View file

@ -31,17 +31,23 @@ const icons = {
// Types
import { MprisPlayer } from 'types/service/mpris.ts';
import { Variable as Var } from 'types/variable';
import AgsOverlay from 'types/widgets/overlay.ts';
import AgsCenterBox, { CenterBoxProps } from 'types/widgets/centerbox.ts';
import AgsLabel from 'types/widgets/label.ts';
import AgsIcon from 'types/widgets/icon.ts';
import AgsStack from 'types/widgets/stack.ts';
import {
AgsWidget,
CenterBoxPropsGeneric,
Colors,
PlayerBox,
PlayerButtonType,
PlayerDrag,
PlayerOverlay,
StackGeneric,
} from 'global-types';
export const CoverArt = (
player: MprisPlayer,
colors: Var<any>,
props: CenterBoxProps,
colors: Var<Colors>,
props: CenterBoxPropsGeneric,
) => CenterBox({
...props,
vertical: true,
@ -51,7 +57,7 @@ export const CoverArt = (
player,
},
setup: (self) => {
setup: (self: PlayerBox) => {
// Give temp cover art
readFileAsync(player.cover_path).catch(() => {
if (!colors.value && !player.track_cover_url) {
@ -93,7 +99,7 @@ export const CoverArt = (
background-position: center;
`;
if (!(self.get_parent() as AgsCenterBox)
if (!(self.get_parent() as PlayerDrag)
.attribute.dragging) {
self.setCss(self.attribute.bgStyle);
}
@ -126,16 +132,18 @@ export const ArtistLabel = (player: MprisPlayer) => Label({
});
export const PlayerIcon = (player: MprisPlayer, overlay: AgsOverlay) => {
export const PlayerIcon = (player: MprisPlayer, overlay: PlayerOverlay) => {
const playerIcon = (
p: MprisPlayer,
widget?: AgsOverlay,
over?: AgsOverlay,
widget?: PlayerOverlay,
playerBox?: PlayerBox,
) => CursorBox({
tooltip_text: p.identity || '',
on_primary_click_release: () => {
widget?.attribute.moveToTop(over);
if (widget && playerBox) {
widget.attribute.moveToTop(playerBox);
}
},
child: Icon({
@ -153,7 +161,7 @@ export const PlayerIcon = (player: MprisPlayer, overlay: AgsOverlay) => {
});
return Box().hook(Mpris, (self) => {
const grandPa = self.get_parent()?.get_parent();
const grandPa = self.get_parent()?.get_parent() as AgsWidget;
if (!grandPa) {
return;
@ -162,13 +170,13 @@ export const PlayerIcon = (player: MprisPlayer, overlay: AgsOverlay) => {
const thisIndex = overlay.overlays
.indexOf(grandPa);
self.children = (overlay.overlays as Array<AgsOverlay>)
.map((over, i) => {
self.children = (overlay.overlays as PlayerBox[])
.map((playerBox, i) => {
self.children.push(Separator(2));
return i === thisIndex ?
playerIcon(player) :
playerIcon(over.attribute.player, overlay, over);
playerIcon(playerBox.attribute.player, overlay, playerBox);
})
.reverse();
});
@ -179,7 +187,7 @@ const display = Gdk.Display.get_default();
export const PositionSlider = (
player: MprisPlayer,
colors: Var<any>,
colors: Var<Colors>,
) => Slider({
class_name: 'position-slider',
vpack: 'center',
@ -258,13 +266,6 @@ export const PositionSlider = (
},
});
type PlayerButtonType = {
player: MprisPlayer
colors: Var<any>
items: Array<[name: string, widget: AgsLabel | AgsIcon]>
onClick: string
prop: string
};
const PlayerButton = ({
player,
colors,
@ -316,7 +317,7 @@ const PlayerButton = ({
setup: (self) => {
self
.hook(player, () => {
(self.child as AgsStack).shown = `${player[prop]}`;
(self.child as StackGeneric).shown = `${player[prop]}`;
})
.hook(colors, () => {
if (!Mpris.players.find((p) => player === p)) {
@ -364,7 +365,7 @@ const PlayerButton = ({
export const ShuffleButton = (
player: MprisPlayer,
colors: Var<any>,
colors: Var<Colors>,
) => PlayerButton({
player,
colors,
@ -384,7 +385,7 @@ export const ShuffleButton = (
export const LoopButton = (
player: MprisPlayer,
colors: Var<any>,
colors: Var<Colors>,
) => PlayerButton({
player,
colors,
@ -408,7 +409,7 @@ export const LoopButton = (
export const PlayPauseButton = (
player: MprisPlayer,
colors: Var<any>,
colors: Var<Colors>,
) => PlayerButton({
player,
colors,
@ -432,7 +433,7 @@ export const PlayPauseButton = (
export const PreviousButton = (
player: MprisPlayer,
colors: Var<any>,
colors: Var<Colors>,
) => PlayerButton({
player,
colors,
@ -452,7 +453,7 @@ export const PreviousButton = (
export const NextButton = (
player: MprisPlayer,
colors: Var<any>,
colors: Var<Colors>,
) => PlayerButton({
player,
colors,

View file

@ -12,14 +12,13 @@ const SPACING = 8;
// Types
import { MprisPlayer } from 'types/service/mpris.ts';
import AgsOverlay from 'types/widgets/overlay.ts';
import { Variable as Var } from 'types/variable';
import AgsBox from 'types/widgets/box.ts';
import { Colors, PlayerBox, PlayerOverlay } from 'global-types';
const Top = (
player: MprisPlayer,
overlay: AgsOverlay,
overlay: PlayerOverlay,
) => Box({
class_name: 'top',
hpack: 'start',
@ -32,7 +31,7 @@ const Top = (
const Center = (
player: MprisPlayer,
colors: Var<any>,
colors: Var<Colors>,
) => Box({
class_name: 'center',
@ -65,7 +64,7 @@ const Center = (
const Bottom = (
player: MprisPlayer,
colors: Var<any>,
colors: Var<Colors>,
) => Box({
class_name: 'bottom',
@ -88,8 +87,8 @@ const Bottom = (
const PlayerBox = (
player: MprisPlayer,
colors: Var<any>,
overlay: AgsOverlay,
colors: Var<Colors>,
overlay: PlayerOverlay,
) => {
const widget = mpris.CoverArt(player, colors, {
class_name: `player ${player.name}`,
@ -107,14 +106,9 @@ const PlayerBox = (
export default () => {
const content = PlayerGesture({
attribute: {
players: new Map(),
setup: false,
},
setup: (self) => {
setup: (self: PlayerOverlay) => {
self
.hook(Mpris, (_: AgsOverlay, bus_name: string) => {
.hook(Mpris, (_, bus_name) => {
const players = self.attribute.players;
if (players.has(bus_name)) {
@ -136,11 +130,16 @@ export default () => {
}
// Get the one on top so we can move it up later
const previousFirst = self.overlays.at(-1);
const previousFirst = self.overlays.at(-1) as PlayerBox;
// Make the new player
const player = Mpris.getPlayer(bus_name);
const Colors = Variable(null);
const colorsVar = Variable({
imageAccent: '#6b4fa2',
buttonAccent: '#ecdcff',
buttonText: '#25005a',
hoverAccent: '#d4baff',
});
if (!player) {
return;
@ -148,19 +147,15 @@ export default () => {
players.set(
bus_name,
PlayerBox(
player,
Colors,
content.get_children()[0] as AgsOverlay,
),
PlayerBox(player, colorsVar, self),
);
self.overlays = Array.from(players.values())
.map((widget) => widget) as Array<AgsBox>;
.map((widget) => widget) as PlayerBox[];
const includes = self.attribute
.includesWidget(previousFirst);
// Select favorite player at startup
// Select favorite player at startup
const attrs = self.attribute;
if (!attrs.setup && players.has(FAVE_PLAYER)) {
@ -168,28 +163,28 @@ export default () => {
attrs.setup = true;
}
// Move previousFirst on top again
// Move previousFirst on top again
else if (includes) {
attrs.moveToTop(previousFirst);
}
}, 'player-added')
.hook(Mpris, (_: AgsOverlay, bus_name: string) => {
.hook(Mpris, (_, bus_name) => {
const players = self.attribute.players;
if (!bus_name || !players.has(bus_name)) {
return;
}
// Get the one on top so we can move it up later
const previousFirst = self.overlays.at(-1);
// Get the one on top so we can move it up later
const previousFirst = self.overlays.at(-1) as PlayerBox;
// Remake overlays without deleted one
// Remake overlays without deleted one
players.delete(bus_name);
self.overlays = Array.from(players.values())
.map((widget) => widget) as Array<AgsBox>;
.map((widget) => widget) as PlayerBox[];
// Move previousFirst on top again
// Move previousFirst on top again
const includes = self.attribute
.includesWidget(previousFirst);

View file

@ -23,7 +23,7 @@ Audio.connect('speaker-changed', () => {
return;
}
if (Audio.speaker.stream.is_muted) {
if (Audio.speaker.stream?.is_muted) {
SpeakerIcon.value = speakerIcons[0];
}
else {
@ -43,7 +43,7 @@ Audio.connect('microphone-changed', () => {
return;
}
if (Audio.microphone.stream.is_muted) {
if (Audio.microphone.stream?.is_muted) {
MicIcon.value = micIcons[0];
}
else {

View file

@ -1,13 +1,14 @@
import App from 'resource:///com/github/Aylur/ags/app.js';
// Types
import AgsWindow from 'types/widgets/window';
import { PopupWindow } from 'global-types';
export default () => {
(Array.from(App.windows) as Array<[string, AgsWindow]>)
.filter((w) => w[1].attribute?.close_on_unfocus &&
w[1].attribute?.close_on_unfocus !== 'stay')
(Array.from(App.windows) as Array<[string, PopupWindow]>)
.filter((w) =>
w[1].attribute.close_on_unfocus &&
w[1].attribute.close_on_unfocus !== 'stay')
.forEach((w) => {
App.closeWindow(w[0]);
});

View file

@ -1,93 +1,268 @@
import Variable from 'resource:///com/github/Aylur/ags/variable.js';
import { register } from 'resource:///com/github/Aylur/ags/widget.js';
import Gtk from 'gi://Gtk?version=3.0';
import Gdk from 'gi://Gdk?version=3.0';
import { EventBox } from 'resource:///com/github/Aylur/ags/widget.js';
// Types
import { BaseProps, Widget } from 'types/widgets/widget';
type EventHandler<Self> = (self: Self, event: Gdk.Event) => boolean | unknown;
const { Gtk, Gdk } = imports.gi;
const display = Gdk.Display.get_default();
export type CursorBoxProps<
Child extends Gtk.Widget,
Attr = unknown,
Self = CursorBox<Child, Attr>,
> = BaseProps<Self, Gtk.EventBox.ConstructorProperties & {
child?: Child
on_hover?: EventHandler<Self>
on_hover_lost?: EventHandler<Self>
import * as EventBoxTypes from 'types/widgets/eventbox';
type CursorBox = EventBoxTypes.EventBoxProps & {
on_primary_click_release?(self: EventBoxTypes.default): void;
on_hover?(self: EventBoxTypes.default): void;
on_hover_lost?(self: EventBoxTypes.default): void;
};
on_scroll_up?: EventHandler<Self>
on_scroll_down?: EventHandler<Self>
on_primary_click?: EventHandler<Self>
on_middle_click?: EventHandler<Self>
on_secondary_click?: EventHandler<Self>
on_primary_click_release?: EventHandler<Self>
on_middle_click_release?: EventHandler<Self>
on_secondary_click_release?: EventHandler<Self>
}, Attr>;
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export interface CursorBox<Child, Attr> extends Widget<Attr> { }
export default ({
on_primary_click_release = () => {/**/},
on_hover = () => {/**/},
on_hover_lost = () => {/**/},
attribute,
...props
}: CursorBox) => {
export class CursorBox<Child extends Gtk.Widget, Attr> extends Gtk.EventBox {
static {
register(this, {
properties: {
'on-clicked': ['jsobject', 'rw'],
'on-hover': ['jsobject', 'rw'],
'on-hover-lost': ['jsobject', 'rw'],
'on-scroll-up': ['jsobject', 'rw'],
'on-scroll-down': ['jsobject', 'rw'],
'on-primary-click': ['jsobject', 'rw'],
'on-secondary-click': ['jsobject', 'rw'],
'on-middle-click': ['jsobject', 'rw'],
'on-primary-click-release': ['jsobject', 'rw'],
'on-secondary-click-release': ['jsobject', 'rw'],
'on-middle-click-release': ['jsobject', 'rw'],
},
});
}
constructor(props: CursorBoxProps<Child, Attr> = {}) {
super(props as Gtk.EventBox.ConstructorProperties);
this.add_events(Gdk.EventMask.SCROLL_MASK);
this.add_events(Gdk.EventMask.SMOOTH_SCROLL_MASK);
// Gesture stuff
const gesture = Gtk.GestureLongPress.new(this);
this.hook(gesture, () => {
const pointer = gesture.get_point(null);
const x = pointer[1];
const y = pointer[2];
if ((!x || !y) || (x === 0 && y === 0)) {
return;
}
this.#canRun.value = !(
x > this.get_allocated_width() ||
y > this.get_allocated_height()
);
}, 'end');
this.connect('enter-notify-event', (_, event: Gdk.Event) => {
this.set_state_flags(Gtk.StateFlags.PRELIGHT, false);
if (!this.#display) {
return;
}
this.window.set_cursor(Gdk.Cursor.new_from_name(
this.#display,
this.#disabled.value ?
'not-allowed' :
'pointer',
));
return this.on_hover?.(this, event);
});
this.connect('leave-notify-event', (_, event: Gdk.Event) => {
this.unset_state_flags(Gtk.StateFlags.PRELIGHT);
this.window.set_cursor(null);
return this.on_hover_lost?.(this, event);
});
this.connect('button-press-event', (_, event: Gdk.Event) => {
this.set_state_flags(Gtk.StateFlags.ACTIVE, false);
if (this.#disabled.value) {
return;
}
if (event.get_button()[1] === Gdk.BUTTON_PRIMARY) {
return this.on_primary_click?.(this, event);
}
else if (event.get_button()[1] === Gdk.BUTTON_MIDDLE) {
return this.on_middle_click?.(this, event);
}
else if (event.get_button()[1] === Gdk.BUTTON_SECONDARY) {
return this.on_secondary_click?.(this, event);
}
});
this.connect('button-release-event', (_, event: Gdk.Event) => {
this.unset_state_flags(Gtk.StateFlags.ACTIVE);
if (this.#disabled.value) {
return;
}
if (event.get_button()[1] === Gdk.BUTTON_PRIMARY) {
// Every click, do a one shot connect to
// CanRun to wait for location of click
const id = this.#canRun.connect('changed', () => {
if (this.#canRun.value) {
this.on_primary_click_release?.(this, event);
}
this.#canRun.disconnect(id);
});
}
else if (event.get_button()[1] === Gdk.BUTTON_MIDDLE) {
return this.on_middle_click_release?.(this, event);
}
else if (event.get_button()[1] === Gdk.BUTTON_SECONDARY) {
return this.on_secondary_click_release?.(this, event);
}
});
this.connect('scroll-event', (_, event: Gdk.Event) => {
if (event.get_scroll_deltas()[2] < 0) {
return this.on_scroll_up?.(this, event);
}
else if (event.get_scroll_deltas()[2] > 0) {
return this.on_scroll_down?.(this, event);
}
});
}
#display = Gdk.Display.get_default();
// Make this variable to know if the function should
// be executed depending on where the click is released
const CanRun = Variable(true);
const Disabled = Variable(false);
#canRun = Variable(true);
#disabled = Variable(false);
const cursorBox = EventBox({
...props,
get disabled() {
return this.#disabled.value;
}
attribute: {
...attribute,
disabled: Disabled,
},
set disabled(value: boolean) {
this.#disabled.value = value;
}
on_primary_click_release: (self) => {
// Every click, do a one shot connect to
// CanRun to wait for location of click
const id = CanRun.connect('changed', () => {
if (CanRun.value && !Disabled.value) {
on_primary_click_release(self);
}
get child() {
return super.child as Child;
}
CanRun.disconnect(id);
});
},
set child(child: Child) {
super.child = child;
}
// OnHover
}).on('enter-notify-event', (self) => {
on_hover(self);
if (!display) {
return;
}
self.window.set_cursor(Gdk.Cursor.new_from_name(
display,
Disabled.value ?
'not-allowed' :
'pointer',
));
self.toggleClassName('hover', true);
get on_hover() {
return this._get('on-hover');
}
// OnHoverLost
}).on('leave-notify-event', (self) => {
on_hover_lost(self);
set on_hover(callback: EventHandler<this>) {
this._set('on-hover', callback);
}
self.window.set_cursor(null);
self.toggleClassName('hover', false);
get on_hover_lost() {
return this._get('on-hover-lost');
}
// Disabled class
}).hook(Disabled, (self) => {
self.toggleClassName('disabled', Disabled.value);
});
set on_hover_lost(callback: EventHandler<this>) {
this._set('on-hover-lost', callback);
}
const gesture = Gtk.GestureLongPress.new(cursorBox);
get on_scroll_up() {
return this._get('on-scroll-up');
}
cursorBox.hook(gesture, () => {
const pointer = gesture.get_point(null);
const x = pointer[1];
const y = pointer[2];
set on_scroll_up(callback: EventHandler<this>) {
this._set('on-scroll-up', callback);
}
if ((!x || !y) || (x === 0 && y === 0)) {
return;
}
get on_scroll_down() {
return this._get('on-scroll-down');
}
CanRun.value = !(
x > cursorBox.get_allocated_width() ||
y > cursorBox.get_allocated_height()
);
}, 'end');
set on_scroll_down(callback: EventHandler<this>) {
this._set('on-scroll-down', callback);
}
return cursorBox;
};
get on_primary_click() {
return this._get('on-primary-click');
}
set on_primary_click(callback: EventHandler<this>) {
this._set('on-primary-click', callback);
}
get on_middle_click() {
return this._get('on-middle-click');
}
set on_middle_click(callback: EventHandler<this>) {
this._set('on-middle-click', callback);
}
get on_secondary_click() {
return this._get('on-secondary-click');
}
set on_secondary_click(callback: EventHandler<this>) {
this._set('on-secondary-click', callback);
}
get on_primary_click_release() {
return this._get('on-primary-click-release');
}
set on_primary_click_release(callback: EventHandler<this>) {
this._set('on-primary-click-release', callback);
}
get on_middle_click_release() {
return this._get('on-middle-click-release');
}
set on_middle_click_release(callback: EventHandler<this>) {
this._set('on-middle-click-release', callback);
}
get on_secondary_click_release() {
return this._get('on-secondary-click-release');
}
set on_secondary_click_release(callback: EventHandler<this>) {
this._set('on-secondary-click-release', callback);
}
}
export default <Child extends Gtk.Widget, Attr>(
props?: CursorBoxProps<Child, Attr>,
) => new CursorBox(props ?? {});

View file

@ -2,381 +2,391 @@ import App from 'resource:///com/github/Aylur/ags/app.js';
import Hyprland from 'resource:///com/github/Aylur/ags/service/hyprland.js';
import Variable from 'resource:///com/github/Aylur/ags/variable.js';
import { Box, Overlay, Window } from 'resource:///com/github/Aylur/ags/widget.js';
import { Box, Overlay, register } from 'resource:///com/github/Aylur/ags/widget.js';
import { timeout } from 'resource:///com/github/Aylur/ags/utils.js';
// Types
import { Window } from 'resource:///com/github/Aylur/ags/widgets/window.js';
import { Allocation, Widget } from 'types/@girs/gtk-3.0/gtk-3.0.cjs';
import { RevealerProps } from 'types/widgets/revealer';
import { WindowProps } from 'types/widgets/window';
import AgsWindow from 'types/widgets/window';
import AgsBox from 'types/widgets/box';
import AgsOverlay from 'types/widgets/overlay';
import { Binding } from 'types/service';
type PopupWindow = WindowProps & {
transition?: RevealerProps['transition']
transition_duration?: number
bezier?: string
on_open?(self: AgsWindow): void
on_close?(self: AgsWindow): void
blur?: boolean
close_on_unfocus?: 'none' | 'stay' | 'released' | 'clicked'
anchor?: Array<string>
name: string
};
import { Variable as Var } from 'types/variable';
import {
CloseType,
BoxGeneric,
OverlayGeneric,
PopupChild,
PopupWindowProps,
} from 'global-types';
// FIXME: deal with overlay children?
// TODO: make this a new class to be able to edit props
// TODO: make props changes affect the widget
export default ({
transition = 'slide_down',
transition_duration = 800,
bezier = 'cubic-bezier(0.68, -0.4, 0.32, 1.4)',
on_open = () => {/**/},
on_close = () => {/**/},
// Window props
name,
child = Box(),
visible = false,
anchor = [],
layer = 'overlay',
blur = false,
close_on_unfocus = 'released',
...props
}: PopupWindow) => {
const Child = Variable(child);
const AntiClip = Variable(false);
const needsAnticlipping = bezier.match(/-[0-9]/) !== null &&
transition !== 'crossfade';
const attribute = {
set_x_pos: (
alloc: Allocation,
side = 'right' as 'left' | 'right',
) => {
const window = App.getWindow(name) as AgsWindow;
if (!window) {
return;
}
const width = window.get_display()
.get_monitor_at_point(alloc.x, alloc.y)
.get_geometry().width;
window.margins = [
window.margins[0],
side === 'right' ?
(width - alloc.x - alloc.width) :
window.margins[1],
window.margins[2],
side === 'right' ?
window.margins[3] :
(alloc.x - alloc.width),
];
},
get_child: () => Child.value,
set_child: (new_child: Widget) => {
Child.value = new_child;
App.getWindow(name)?.child.show_all();
},
// This is for my custom pointers.ts
close_on_unfocus,
};
if (transition === 'none') {
return Window({
name,
layer,
anchor,
visible: false,
...props,
attribute,
child: Child.bind(),
export class PopupWindow<
Child extends Widget,
Attr,
> extends Window<Child, Attr> {
static {
register(this, {
properties: {
content: ['widget', 'rw'],
},
});
}
const window = Window({
#content: Var<Widget>;
#antiClip: Var<boolean>;
#needsAnticlipping: boolean;
#close_on_unfocus: CloseType;
get content() {
return this.#content.value;
}
set content(value: Widget) {
this.#content.value = value;
this.child.show_all();
}
get close_on_unfocus() {
return this.#close_on_unfocus;
}
set close_on_unfocus(value: 'none' | 'stay' | 'released' | 'clicked') {
this.#close_on_unfocus = value;
}
constructor({
transition = 'slide_down',
transition_duration = 800,
bezier = 'cubic-bezier(0.68, -0.4, 0.32, 1.4)',
on_open = () => {/**/},
on_close = () => {/**/},
// Window props
name,
layer,
anchor,
visible: false,
...props,
visible = false,
anchor = [],
layer = 'overlay',
attribute,
content = Box(),
blur = false,
close_on_unfocus = 'released',
...rest
}: PopupWindowProps<Child, Attr>) {
const needsAnticlipping = bezier.match(/-[0-9]/) !== null &&
transition !== 'crossfade';
const contentVar = Variable(Box() as Widget);
const antiClip = Variable(false);
setup: () => {
// Add way to make window open on startup
const id = App.connect('config-parsed', () => {
if (visible) {
App.openWindow(`${name}`);
if (content) {
contentVar.value = content;
}
super({
...rest,
name,
visible,
anchor,
layer,
attribute,
setup: () => {
// Add way to make window open on startup
const id = App.connect('config-parsed', () => {
if (visible) {
App.openWindow(`${name}`);
}
App.disconnect(id);
});
if (blur) {
Hyprland.sendMessage('[[BATCH]] ' +
`keyword layerrule ignorealpha[0.97],${name}; ` +
`keyword layerrule blur,${name}`);
}
App.disconnect(id);
});
},
child: Overlay({
overlays: [Box({
css: `
min-height: 1px;
min-width: 1px;
padding: 1px;
`,
setup: (self) => {
// Make sure child doesn't
// get bigger than it should
const MAX_ANCHORS = 4;
if (blur) {
Hyprland.sendMessage('[[BATCH]] ' +
`keyword layerrule ignorealpha[0.97],${name}; ` +
`keyword layerrule blur,${name}`);
}
},
child: Overlay({
overlays: [Box({
css: `
min-height: 1px;
min-width: 1px;
padding: 1px;
`,
setup: (self) => {
// Make sure child doesn't
// get bigger than it should
const MAX_ANCHORS = 4;
self.hpack = 'center';
self.vpack = 'center';
if (anchor.includes('top') &&
anchor.includes('bottom')) {
self.vpack = 'center';
}
else if (anchor.includes('top')) {
self.vpack = 'start';
}
else if (anchor.includes('bottom')) {
self.vpack = 'end';
}
if (anchor.includes('left') &&
anchor.includes('right')) {
self.hpack = 'center';
}
else if (anchor.includes('left')) {
self.hpack = 'start';
}
else if (anchor.includes('right')) {
self.hpack = 'end';
}
if (anchor.length === MAX_ANCHORS) {
self.hpack = 'center';
self.vpack = 'center';
}
if (needsAnticlipping) {
const reorder_child = (position: number) => {
// If unanchored, we have another anticlip widget
// so we can't change the order
if (anchor.length !== 0) {
for (const ch of self.children) {
if (ch !== Child.value) {
self.reorder_child(ch, position);
if (anchor.includes('top') &&
anchor.includes('bottom')) {
self.vpack = 'center';
}
else if (anchor.includes('top')) {
self.vpack = 'start';
}
else if (anchor.includes('bottom')) {
self.vpack = 'end';
}
return;
if (anchor.includes('left') &&
anchor.includes('right')) {
self.hpack = 'center';
}
else if (anchor.includes('left')) {
self.hpack = 'start';
}
else if (anchor.includes('right')) {
self.hpack = 'end';
}
if (anchor.length === MAX_ANCHORS) {
self.hpack = 'center';
self.vpack = 'center';
}
if (needsAnticlipping) {
const reorder_child = (position: number) => {
// If unanchored, we have another anticlip widget
// so we can't change the order
if (anchor.length !== 0) {
for (const ch of self.children) {
if (ch !== contentVar.value) {
self.reorder_child(ch, position);
return;
}
}
}
}
};
};
self.hook(AntiClip, () => {
if (transition === 'slide_down') {
self.vertical = true;
reorder_child(-1);
}
else if (transition === 'slide_up') {
self.vertical = true;
reorder_child(0);
}
else if (transition === 'slide_right') {
self.vertical = false;
reorder_child(-1);
}
else if (transition === 'slide_left') {
self.vertical = false;
reorder_child(0);
}
});
}
},
self.hook(antiClip, () => {
if (transition === 'slide_down') {
self.vertical = true;
reorder_child(-1);
}
else if (transition === 'slide_up') {
self.vertical = true;
reorder_child(0);
}
else if (transition === 'slide_right') {
self.vertical = false;
reorder_child(-1);
}
else if (transition === 'slide_left') {
self.vertical = false;
reorder_child(0);
}
});
}
},
children: Child.bind().transform((v) => {
if (needsAnticlipping) {
return [
// Add an anticlip widget when unanchored
// to not have a weird animation
anchor.length === 0 && Box({
css: `
min-height: 100px;
min-width: 100px;
padding: 2px;
`,
visible: AntiClip.bind(),
}),
v,
Box({
css: `
min-height: 100px;
min-width: 100px;
padding: 2px;
`,
visible: AntiClip.bind(),
}),
];
}
else {
return [v];
}
}) as Binding<any, any, Widget[]>,
})],
children: contentVar.bind().transform((v) => {
if (needsAnticlipping) {
return [
// Add an anticlip widget when unanchored
// to not have a weird animation
anchor.length === 0 && Box({
css: `
min-height: 100px;
min-width: 100px;
padding: 2px;
`,
visible: antiClip.bind(),
}),
v,
Box({
css: `
min-height: 100px;
min-width: 100px;
padding: 2px;
`,
visible: antiClip.bind(),
}),
];
}
else {
return [v];
}
}) as PopupChild,
})],
setup: (self) => {
self.on('get-child-position', (_, ch) => {
const overlay = (Child.value as Widget)
.get_parent() as AgsOverlay;
setup: (self) => {
self.on('get-child-position', (_, ch) => {
const overlay = contentVar.value
.get_parent() as OverlayGeneric;
if (ch === overlay) {
const alloc = overlay.get_allocation();
if (ch === overlay) {
const alloc = overlay.get_allocation();
(self.child as AgsBox).css = `
(self.child as BoxGeneric).css = `
min-height: ${alloc.height}px;
min-width: ${alloc.width}px;
`;
}
});
},
}
});
},
child: Box({
css: `
min-height: 1px;
min-width: 1px;
padding: 1px;
`,
child: Box({
css: `
min-height: 1px;
min-width: 1px;
padding: 1px;
`,
setup: (self) => {
let currentTimeout: number;
setup: (self) => {
let currentTimeout: number;
self.hook(App, (_, currentName, isOpen) => {
if (currentName === name) {
const overlay = (Child.value as Widget)
.get_parent() as AgsOverlay;
self.hook(App, (_, currentName, isOpen) => {
if (currentName === name) {
const overlay = contentVar.value
.get_parent() as OverlayGeneric;
const alloc = overlay.get_allocation();
const height = needsAnticlipping ?
alloc.height + 100 + 10 :
alloc.height + 10;
const alloc = overlay.get_allocation();
const height = antiClip ?
alloc.height + 100 + 10 :
alloc.height + 10;
if (needsAnticlipping) {
AntiClip.value = true;
if (needsAnticlipping) {
antiClip.value = true;
const thisTimeout = timeout(
transition_duration,
() => {
// Only run the timeout if there isn't a newer timeout
if (thisTimeout === currentTimeout) {
AntiClip.value = false;
}
},
);
const thisTimeout = timeout(
transition_duration,
() => {
// Only run the timeout if there isn't a newer timeout
if (thisTimeout ===
currentTimeout) {
antiClip.value = false;
}
},
);
currentTimeout = thisTimeout;
}
currentTimeout = thisTimeout;
}
let css = '';
let css = '';
/* Margin: top | right | bottom | left */
switch (transition) {
case 'slide_down':
css = `margin:
-${height}px
0
${height}px
0
;`;
break;
/* Margin: top | right | bottom | left */
switch (transition) {
case 'slide_down':
css = `margin:
-${height}px
0
${height}px
0
;`;
break;
case 'slide_up':
css = `margin:
${height}px
0
-${height}px
0
;`;
break;
case 'slide_up':
css = `margin:
${height}px
0
-${height}px
0
;`;
break;
case 'slide_left':
css = `margin:
0
-${height}px
0
${height}px
;`;
break;
case 'slide_left':
css = `margin:
0
-${height}px
0
${height}px
;`;
break;
case 'slide_right':
css = `margin:
0
${height}px
0
-${height}px
;`;
break;
case 'slide_right':
css = `margin:
0
${height}px
0
-${height}px
;`;
break;
case 'crossfade':
css = `
opacity: 0;
min-height: 1px;
min-width: 1px;
`;
break;
case 'crossfade':
css = `
opacity: 0;
min-height: 1px;
min-width: 1px;
`;
break;
default:
break;
}
default:
break;
}
if (isOpen) {
on_open(window);
if (isOpen) {
on_open(this);
// To get the animation, we need to set the css
// to hide the widget and then timeout to have
// the animation
overlay.css = css;
timeout(10, () => {
overlay.css = `
// To get the animation, we need to set the css
// to hide the widget and then timeout to have
// the animation
overlay.css = css;
timeout(10, () => {
overlay.css = `
transition: margin
${transition_duration}ms
${bezier},
opacity
${transition_duration}ms
${bezier};
`;
});
}
else {
timeout(transition_duration, () => {
on_close(this);
});
overlay.css = `${css}
transition: margin
${transition_duration}ms ${bezier},
opacity
${transition_duration}ms ${bezier};
`;
});
}
}
else {
timeout(transition_duration, () => {
on_close(window);
});
overlay.css = `${css}
transition: margin
${transition_duration}ms ${bezier},
opacity
${transition_duration}ms ${bezier};
`;
}
}
});
},
});
},
}),
}),
}),
});
});
return window;
};
this.#content = contentVar;
this.#close_on_unfocus = close_on_unfocus;
this.#needsAnticlipping = needsAnticlipping;
this.#antiClip = antiClip;
}
set_x_pos(
alloc: Allocation,
side = 'right' as 'left' | 'right',
) {
const width = this.get_display()
.get_monitor_at_point(alloc.x, alloc.y)
.get_geometry().width;
this.margins = [
this.margins[0],
side === 'right' ?
(width - alloc.x - alloc.width) :
this.margins[1],
this.margins[2],
side === 'right' ?
this.margins[3] :
(alloc.x - alloc.width),
];
}
}
export default <Child extends Widget, Attr>(
props: PopupWindowProps<Child, Attr>,
) => new PopupWindow(props);

View file

@ -13,13 +13,19 @@ import CursorBox from '../misc/cursorbox.ts';
// Types
import { Notification as NotifObj } from 'types/service/notifications.ts';
import AgsEventBox from 'types/widgets/eventbox.ts';
import AgsEventBox from 'types/widgets/eventbox';
import { Widget } from 'types/@girs/gtk-3.0/gtk-3.0.cjs';
import { Client } from 'types/service/hyprland.ts';
type NotificationWidget = {
notif: NotifObj
slideIn?: 'Left' | 'Right'
command?(): void
};
import {
EventBoxGeneric,
CursorBox as CBox,
} from 'global-types';
const setTime = (time: number) => {
return GLib.DateTime
@ -27,13 +33,17 @@ const setTime = (time: number) => {
.format('%H:%M');
};
const getDragState = (box: AgsEventBox) => (box.get_parent()?.get_parent()
?.get_parent()?.get_parent()?.get_parent() as AgsEventBox)
const getDragState = (box: EventBoxGeneric) => (box
.get_parent()
?.get_parent()
?.get_parent()
?.get_parent()
?.get_parent() as AgsEventBox<Widget, { dragging: boolean }>)
?.attribute.dragging;
const NotificationIcon = (notif: NotifObj) => {
let iconCmd = (box: AgsEventBox):void => {
let iconCmd = (box: CBox):void => {
console.log(box);
};
@ -89,7 +99,9 @@ const NotificationIcon = (notif: NotifObj) => {
if (notif.image) {
return CursorBox({
on_primary_click_release: iconCmd,
on_primary_click_release: (self) => {
iconCmd(self);
},
child: Box({
vpack: 'start',
@ -120,7 +132,9 @@ const NotificationIcon = (notif: NotifObj) => {
return CursorBox({
on_primary_click_release: iconCmd,
on_primary_click_release: (self) => {
iconCmd(self);
},
child: Box({
vpack: 'start',
@ -175,7 +189,7 @@ export const Notification = ({
});
// Add body to notif
(notifWidget.child as AgsEventBox).add(Box({
(notifWidget.child as EventBoxGeneric).add(Box({
class_name: `notification ${notif.urgency}`,
vexpand: false,

View file

@ -21,5 +21,5 @@ export const NotifCenter = () => PopupWindow({
transition: 'slide_up',
monitor: 1,
child: NotifCenterWidget(),
content: NotifCenterWidget(),
});

View file

@ -8,11 +8,11 @@ import { Notification, HasNotifs } from './base.ts';
import CursorBox from '../misc/cursorbox.ts';
// Types
import AgsBox from 'types/widgets/box.ts';
import { Notification as NotifObj } from 'resource:///com/github/Aylur/ags/service/notifications.js';
import { BoxGeneric } from 'global-types';
const addNotif = (box: AgsBox, notif: NotifObj) => {
const addNotif = (box: BoxGeneric, notif: NotifObj) => {
if (notif) {
const NewNotif = Notification({
notif,
@ -53,7 +53,7 @@ const NotificationList = () => Box({
}, 'notified')
.hook(Notifications, (box, id) => {
const notif = (box.children as Array<AgsBox>)
const notif = (box.children as BoxGeneric[])
.find((ch) => ch.attribute.id === id);
if (notif?.sensitive) {
@ -73,7 +73,7 @@ const ClearButton = () => CursorBox({
setup: (self) => {
self.hook(HasNotifs, () => {
self.attribute.disabled?.setValue(!HasNotifs.value);
self.disabled = !HasNotifs.value;
});
},

View file

@ -18,5 +18,5 @@ export const NotifCenter = () => PopupWindow({
anchor: ['top', 'right'],
margins: [TOP_MARGIN, 0, 0, 0],
child: NotifCenterWidget(),
content: NotifCenterWidget(),
});

View file

@ -61,5 +61,5 @@ export default () => PopupWindow({
transition: 'slide_up',
transition_duration,
bezier: 'ease',
child: OSDs(),
content: OSDs(),
});

View file

@ -44,5 +44,5 @@ const PowermenuWidget = () => CenterBox({
export default () => PopupWindow({
name: 'powermenu',
child: PowermenuWidget(),
content: PowermenuWidget(),
});

View file

@ -54,5 +54,5 @@ export default () => PopupWindow({
name: 'quick-settings',
anchor: ['top', 'right'],
margins: [TOP_MARGIN, 0, 0, 0],
child: QuickSettingsWidget(),
content: QuickSettingsWidget(),
});

View file

@ -10,7 +10,10 @@
"strict": true,
"noImplicitAny": false,
"baseUrl": ".",
"typeRoots": ["./types"],
"typeRoots": [
"./types",
"./global-types.d.ts"
],
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
}