refactor(ags): improve typecheck and fix bugs

This commit is contained in:
matt1432 2023-12-19 12:28:29 -05:00
parent da30e6f05f
commit a126d97bbf
12 changed files with 201 additions and 157 deletions

View file

@ -1,15 +1,12 @@
import { Label } from 'resource:///com/github/Aylur/ags/widget.js'; import { Label } from 'resource:///com/github/Aylur/ags/widget.js';
export default () => Label({ export default () => Label({ className: 'clock' })
className: 'clock', .poll(1000, (self) => {
connections: [[1000, (self) => {
const time = imports.gi.GLib const time = imports.gi.GLib
.DateTime.new_now_local(); .DateTime.new_now_local();
self.label = time.format('%a. ') + self.label = time.format('%a. ') +
time.get_day_of_month() + time.get_day_of_month() +
time.format(' %b. %H:%M'); time.format(' %b. %H:%M');
}]], });
})

View file

@ -9,7 +9,7 @@ export const NotifPopups = () => PopupWindow({
anchor: ['bottom', 'left'], anchor: ['bottom', 'left'],
visible: true, visible: true,
transition: 'none', transition: 'none',
closeOnUnfocus: 'stay', close_on_unfocus: 'stay',
monitor: 1, monitor: 1,
child: PopUpsWidget(), child: PopUpsWidget(),

View file

@ -11,7 +11,7 @@ const SPACING = 4;
/** /**
* @param {Label} self * @param {Label} self
* @param {string} layout * @param {string} layout
* @param {void} _ * @param {string} _
*/ */
const getKbdLayout = (self, _, layout) => { const getKbdLayout = (self, _, layout) => {
if (layout) { if (layout) {

View file

@ -9,12 +9,16 @@ export default () => CursorBox({
on_primary_click_release: () => Tablet.toggleOsk(), on_primary_click_release: () => Tablet.toggleOsk(),
setup: (self) => {
self.hook(Tablet, () => {
self.toggleClassName('toggle-on', Tablet.oskState);
}, 'osk-toggled');
},
child: Label({ child: Label({
class_name: 'osk-toggle', class_name: 'osk-toggle',
xalign: 0.6, xalign: 0.6,
label: '󰌌 ', label: '󰌌 ',
}), }),
}).hook(Tablet, (self) => { });
self.toggleClassName('toggle-on', Tablet.oskState);
}, 'osk-toggled');

View file

@ -38,39 +38,50 @@ const SysTray = () => MenuBar({
}, },
setup: (self) => { setup: (self) => {
/**
* @param {import('types/widget').default} _
* @param {string} id
*/
const addTray = (_, id) => {
const item = SystemTray.getItem(id);
if (self.attribute.items.has(id) || !item) {
return;
}
const w = SysTrayItem(item);
// Early return if item is in blocklist
if (!w) {
return;
}
self.attribute.items.set(id, w);
self.child = w;
self.show_all();
// @ts-expect-error
w.child.reveal_child = true;
};
/**
* @param {import('types/widget').default} _
* @param {string} id
*/
const removeTray = (_, id) => {
if (!self.attribute.items.has(id)) {
return;
}
self.attribute.items.get(id).child.reveal_child = false;
timeout(REVEAL_DURATION, () => {
self.attribute.items.get(id).destroy();
self.attribute.items.delete(id);
});
};
self self
.hook(SystemTray, (_, id) => { .hook(SystemTray, addTray, 'added')
const item = SystemTray.getItem(id); .hook(SystemTray, removeTray, 'removed');
if (self.attribute.items.has(id) || !item) {
return;
}
const w = SysTrayItem(item);
// Early return if item is in blocklist
if (!w) {
return;
}
self.attribute.items.set(id, w);
self.child = w;
self.show_all();
// @ts-expect-error
w.child.reveal_child = true;
}, 'added')
.hook(SystemTray, (_, id) => {
if (!self.attribute.items.has(id)) {
return;
}
self.attribute.items.get(id).child.reveal_child = false;
timeout(REVEAL_DURATION, () => {
self.attribute.items.get(id).destroy();
self.attribute.items.delete(id);
});
}, 'removed');
}, },
}); });

View file

@ -9,12 +9,16 @@ export default () => CursorBox({
on_primary_click_release: () => Tablet.toggleMode(), on_primary_click_release: () => Tablet.toggleMode(),
setup: (self) => {
self.hook(Tablet, () => {
self.toggleClassName('toggle-on', Tablet.tabletMode);
}, 'mode-toggled');
},
child: Box({ child: Box({
class_name: 'tablet-toggle', class_name: 'tablet-toggle',
vertical: false, vertical: false,
children: [Label(' 󰦧 ')], children: [Label(' 󰦧 ')],
}), }),
}).hook(Tablet, (self) => { });
self.toggleClassName('toggle-on', Tablet.tabletMode);
}, 'mode-toggled');

View file

@ -7,8 +7,6 @@ import CursorBox from '../../misc/cursorbox.js';
const URGENT_DURATION = 1000; const URGENT_DURATION = 1000;
/** @typedef {import('types/widget.js').Widget} Widget */
/** @property {number} id */ /** @property {number} id */
const Workspace = ({ id }) => { const Workspace = ({ id }) => {
@ -17,7 +15,7 @@ const Workspace = ({ id }) => {
attribute: { id }, attribute: { id },
child: CursorBox({ child: CursorBox({
tooltipText: `${id}`, tooltip_text: `${id}`,
on_primary_click_release: () => { on_primary_click_release: () => {
Hyprland.sendMessage(`dispatch workspace ${id}`); Hyprland.sendMessage(`dispatch workspace ${id}`);
@ -29,7 +27,7 @@ const Workspace = ({ id }) => {
setup: (self) => { setup: (self) => {
/** /**
* @param {Widget} _ * @param {import('types/widgets/box').default} _
* @param {string|undefined} addr * @param {string|undefined} addr
*/ */
const update = (_, addr) => { const update = (_, addr) => {
@ -80,11 +78,11 @@ export default () => {
const L_PADDING = 16; const L_PADDING = 16;
const WS_WIDTH = 30; const WS_WIDTH = 30;
/** @param {Widget} self */ /** @param {import('types/widgets/box').default} self */
const updateHighlight = (self) => { const updateHighlight = (self) => {
const currentId = Hyprland.active.workspace.id; const currentId = Hyprland.active.workspace.id;
// @ts-expect-error // @ts-expect-error
const indicators = self.get_parent().get_children()[0].child.child.children; const indicators = self?.get_parent()?.child.child.child.children;
const currentIndex = Array.from(indicators) const currentIndex = Array.from(indicators)
.findIndex((w) => w.attribute.id === currentId); .findIndex((w) => w.attribute.id === currentId);
@ -92,7 +90,6 @@ export default () => {
return; return;
} }
// @ts-expect-error
self.setCss(`margin-left: ${L_PADDING + (currentIndex * WS_WIDTH)}px`); self.setCss(`margin-left: ${L_PADDING + (currentIndex * WS_WIDTH)}px`);
}; };
@ -100,6 +97,7 @@ export default () => {
vpack: 'center', vpack: 'center',
hpack: 'start', hpack: 'start',
class_name: 'button active', class_name: 'button active',
}).hook(Hyprland.active.workspace, updateHighlight); }).hook(Hyprland.active.workspace, updateHighlight);
const widget = Overlay({ const widget = Overlay({
@ -118,7 +116,7 @@ export default () => {
rev.reveal_child = false; rev.reveal_child = false;
}); });
Array.from(self.attribute.workspaces).forEach((ws) => { Array.from(self.attribute.workspaces).forEach((ws) => {
ws.revealChild = true; ws.reveal_child = true;
}); });
}; };

View file

@ -35,20 +35,27 @@ export default (props) => {
vertical: true, vertical: true,
setup: (self) => { setup: (self) => {
const checkCurrentWsFsState = () => {
const workspace = Hyprland.getWorkspace(
Hyprland.active.workspace.id,
);
if (workspace) {
Revealed.value = !workspace['hasfullscreen'];
}
};
/**
* @param {import('types/widgets/box').default} _
* @param {boolean} fullscreen
*/
const checkGlobalFsState = (_, fullscreen) => {
Revealed.value = !fullscreen;
};
self self
.hook(Hyprland.active, () => { .hook(Hyprland.active, checkCurrentWsFsState)
const workspace = Hyprland.getWorkspace( .hook(Hyprland, checkGlobalFsState, 'fullscreen');
Hyprland.active.workspace.id,
);
if (workspace) {
Revealed.value = !workspace['hasfullscreen'];
}
})
.hook(Hyprland, (_, fullscreen) => {
Revealed.value = !fullscreen;
}, 'fullscreen');
}, },
children: [ children: [

View file

@ -10,11 +10,17 @@ const TRANSITION = `transition: margin ${ANIM_DURATION}ms ease,
opacity ${ANIM_DURATION}ms ease;`; opacity ${ANIM_DURATION}ms ease;`;
/**
* @typedef {import('types/widgets/overlay').OverlayProps} OverlayProps
* @typedef {import('types/widgets/overlay').default} Overlay
*
* @param {OverlayProps & {
* setup?: function(Overlay):void
* }} o
*/
export default ({ export default ({
attribute = {}, attribute = {},
setup = (self) => { setup = () => {/**/},
self;
},
...props ...props
}) => { }) => {
const widget = EventBox(); const widget = EventBox();
@ -36,6 +42,7 @@ export default ({
// @ts-expect-error // @ts-expect-error
.filter((ch) => !ch.attribute?.empty), .filter((ch) => !ch.attribute?.empty),
/** @param {Overlay} playerW */
includesWidget: (playerW) => { includesWidget: (playerW) => {
return Array.from(content.attribute.list()) return Array.from(content.attribute.list())
.find((w) => w === playerW); .find((w) => w === playerW);
@ -46,6 +53,7 @@ export default ({
over.visible = over === content.attribute.list().at(-1); over.visible = over === content.attribute.list().at(-1);
}), }),
/** @param {import('types/widgets/centerbox').default} player */
moveToTop: (player) => { moveToTop: (player) => {
player.visible = true; player.visible = true;
content.reorder_overlay(player, -1); content.reorder_overlay(player, -1);
@ -61,102 +69,113 @@ export default ({
setup(self); setup(self);
self self
.hook(gesture, (overlay, realGesture) => { .hook(gesture,
if (realGesture) { /**
Array.from(overlay.attribute.list()).forEach((over) => { * @param {Overlay} overlay
over.visible = true; * @param {number} realGesture
}); */
} (overlay, realGesture) => {
else { if (realGesture) {
overlay.attribute.showTopOnly(); Array.from(overlay.attribute.list())
} .forEach((over) => {
over.visible = true;
});
}
else {
overlay.attribute.showTopOnly();
}
// Don't allow gesture when only one player // Don't allow gesture when only one player
if (overlay.attribute.list().length <= 1) { if (overlay.attribute.list().length <= 1) {
return; return;
} }
overlay.attribute.dragging = true; overlay.attribute.dragging = true;
let offset = gesture.get_offset()[1]; let offset = gesture.get_offset()[1];
const playerBox = overlay.attribute.list().at(-1); const playerBox = overlay.attribute.list().at(-1);
if (!offset) { if (!offset) {
return; return;
} }
// Slide right // Slide right
if (offset >= 0) { if (offset >= 0) {
playerBox.setCss(` playerBox.setCss(`
margin-left: ${offset}px; margin-left: ${offset}px;
margin-right: -${offset}px; margin-right: -${offset}px;
${playerBox.attribute.bgStyle} ${playerBox.attribute.bgStyle}
`); `);
} }
// Slide left // Slide left
else { else {
offset = Math.abs(offset); offset = Math.abs(offset);
playerBox.setCss(` playerBox.setCss(`
margin-left: -${offset}px; margin-left: -${offset}px;
margin-right: ${offset}px; margin-right: ${offset}px;
${playerBox.attribute.bgStyle} ${playerBox.attribute.bgStyle}
`); `);
} }
}, 'drag-update') },
'drag-update')
.hook(gesture, (overlay) => {
// Don't allow gesture when only one player
if (overlay.attribute.list().length <= 1) {
return;
}
overlay.attribute.dragging = false; .hook(gesture,
const offset = gesture.get_offset()[1]; /** @param {Overlay} overlay */
(overlay) => {
// Don't allow gesture when only one player
if (overlay.attribute.list().length <= 1) {
return;
}
const playerBox = overlay.attribute.list().at(-1); overlay.attribute.dragging = false;
const offset = gesture.get_offset()[1];
// If crosses threshold after letting go, slide away const playerBox = overlay.attribute.list().at(-1);
if (offset && Math.abs(offset) > MAX_OFFSET) {
// Disable inputs during animation
widget.sensitive = false;
// Slide away right // If crosses threshold after letting go, slide away
if (offset >= 0) { if (offset && Math.abs(offset) > MAX_OFFSET) {
playerBox.setCss(` // Disable inputs during animation
widget.sensitive = false;
// Slide away right
if (offset >= 0) {
playerBox.setCss(`
${TRANSITION} ${TRANSITION}
margin-left: ${OFFSCREEN}px; margin-left: ${OFFSCREEN}px;
margin-right: -${OFFSCREEN}px; margin-right: -${OFFSCREEN}px;
opacity: 0.7; ${playerBox.attribute.bgStyle} opacity: 0.7; ${playerBox.attribute.bgStyle}
`); `);
} }
// Slide away left // Slide away left
else { else {
playerBox.setCss(` playerBox.setCss(`
${TRANSITION} ${TRANSITION}
margin-left: -${OFFSCREEN}px; margin-left: -${OFFSCREEN}px;
margin-right: ${OFFSCREEN}px; margin-right: ${OFFSCREEN}px;
opacity: 0.7; ${playerBox.attribute.bgStyle} opacity: 0.7; ${playerBox.attribute.bgStyle}
`); `);
}
timeout(ANIM_DURATION, () => {
// Put the player in the back after anim
overlay.reorder_overlay(playerBox, 0);
// Recenter player
playerBox.setCss(playerBox.attribute.bgStyle);
widget.sensitive = true;
overlay.attribute.showTopOnly();
});
} }
else {
timeout(ANIM_DURATION, () => { // Recenter with transition for animation
// Put the player in the back after anim playerBox.setCss(`${TRANSITION}
overlay.reorder_overlay(playerBox, 0);
// Recenter player
playerBox.setCss(playerBox.attribute.bgStyle);
widget.sensitive = true;
overlay.attribute.showTopOnly();
});
}
else {
// Recenter with transition for animation
playerBox.setCss(`${TRANSITION}
${playerBox.attribute.bgStyle}`); ${playerBox.attribute.bgStyle}`);
} }
}, 'drag-end'); },
'drag-end');
}, },
}); });

View file

@ -5,11 +5,11 @@ import { EventBox } from 'resource:///com/github/Aylur/ags/widget.js';
const { Gtk } = imports.gi; const { Gtk } = imports.gi;
// TODO: wrap in another EventBox for disabled cursor
/** /**
* @typedef {import('types/widgets/eventbox').EventBoxProps} EventBox * @typedef {import('types/widgets/eventbox').EventBoxProps} EventBoxProps
* @typedef {import('types/widgets/eventbox').default} EventBox
* *
* @param {EventBox & { * @param {EventBoxProps & {
* on_primary_click_release?: function(EventBox):void * on_primary_click_release?: function(EventBox):void
* }} o * }} o
*/ */
@ -23,22 +23,7 @@ export default ({
const CanRun = Variable(true); const CanRun = Variable(true);
const Disabled = Variable(false); const Disabled = Variable(false);
const widget = EventBox({ let widget; // eslint-disable-line
...props,
sensitive: Disabled.bind().transform((v) => !v),
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) {
on_primary_click_release(self);
}
CanRun.disconnect(id);
});
},
});
const wrapper = EventBox({ const wrapper = EventBox({
cursor: 'pointer', cursor: 'pointer',
@ -55,14 +40,31 @@ export default ({
}, },
}, },
child: widget,
}).hook(Disabled, (self) => { }).hook(Disabled, (self) => {
self.cursor = Disabled.value ? self.cursor = Disabled.value ?
'not-allowed' : 'not-allowed' :
'pointer'; 'pointer';
}); });
widget = EventBox({
...props,
sensitive: Disabled.bind().transform((v) => !v),
on_primary_click_release: () => {
// Every click, do a one shot connect to
// CanRun to wait for location of click
const id = CanRun.connect('changed', () => {
if (CanRun.value) {
on_primary_click_release(wrapper);
}
CanRun.disconnect(id);
});
},
});
wrapper.child = widget;
const gesture = Gtk.GestureLongPress.new(widget); const gesture = Gtk.GestureLongPress.new(widget);
widget.hook(gesture, () => { widget.hook(gesture, () => {

View file

@ -16,6 +16,7 @@ export const NotifPopups = () => PopupWindow({
const TOP_MARGIN = 6; const TOP_MARGIN = 6;
// FIXME: opens at wrong place
export const NotifCenter = () => PopupWindow({ export const NotifCenter = () => PopupWindow({
name: 'notification-center', name: 'notification-center',
anchor: ['top', 'right'], anchor: ['top', 'right'],

View file

@ -7,6 +7,7 @@ import PopupWindow from './misc/popup.js';
import CursorBox from './misc/cursorbox.js'; import CursorBox from './misc/cursorbox.js';
// FIXME: eventboxes are the wrong size
const PowermenuWidget = () => CenterBox({ const PowermenuWidget = () => CenterBox({
class_name: 'powermenu', class_name: 'powermenu',
// @ts-expect-error // @ts-expect-error