nixos-configs/modules/ags/config/ts/notifications/gesture.ts

224 lines
7.1 KiB
TypeScript
Raw Normal View History

const Notifications = await Service.import('notifications');
const { Box, EventBox } = Widget;
const { timeout } = Utils;
import { HasNotifs } from './base.ts';
const { Gdk, Gtk } = imports.gi;
const display = Gdk.Display.get_default();
2024-01-13 11:15:08 -05:00
// Types
2024-01-29 20:56:56 -05:00
import { BoxGeneric } from 'global-types';
2024-01-13 11:15:08 -05:00
const MAX_OFFSET = 200;
const OFFSCREEN = 300;
const ANIM_DURATION = 500;
const SLIDE_MIN_THRESHOLD = 10;
2023-12-20 17:14:07 -05:00
const TRANSITION = 'transition: margin 0.5s ease, opacity 0.5s ease;';
const SQUEEZED = 'margin-bottom: -70px; margin-top: -70px;';
const MAX_LEFT = `
margin-left: -${Number(MAX_OFFSET + OFFSCREEN)}px;
margin-right: ${Number(MAX_OFFSET + OFFSCREEN)}px;
`;
const MAX_RIGHT = `
margin-left: ${Number(MAX_OFFSET + OFFSCREEN)}px;
margin-right: -${Number(MAX_OFFSET + OFFSCREEN)}px;
`;
const slideLeft = `${TRANSITION} ${MAX_LEFT}
margin-top: 0px;
margin-bottom: 0px;
opacity: 0;`;
const squeezeLeft = `${TRANSITION} ${MAX_LEFT} ${SQUEEZED} opacity: 0;`;
const slideRight = `${TRANSITION} ${MAX_RIGHT}
margin-top: 0px;
margin-bottom: 0px;
opacity: 0;`;
const squeezeRight = `${TRANSITION} ${MAX_RIGHT} ${SQUEEZED} opacity: 0;`;
const defaultStyle = `${TRANSITION} margin: unset; opacity: 1;`;
export default ({
id,
slideIn = 'Left',
2023-12-20 17:14:07 -05:00
command = () => {/**/},
...props
}) => {
const widget = EventBox({
...props,
2023-12-22 16:58:12 -05:00
setup: (self) => {
self
// OnClick
.on('button-press-event', () => {
2024-01-22 10:23:32 -05:00
if (!display) {
return;
}
self.window.set_cursor(Gdk.Cursor.new_from_name(
display,
'grabbing',
));
2023-12-22 16:58:12 -05:00
})
// OnRelease
.on('button-release-event', () => {
2024-01-22 10:23:32 -05:00
if (!display) {
return;
}
self.window.set_cursor(Gdk.Cursor.new_from_name(
display,
'grab',
));
})
// OnHover
2023-12-22 16:58:12 -05:00
.on('enter-notify-event', () => {
2024-01-22 10:23:32 -05:00
if (!display) {
return;
}
self.window.set_cursor(Gdk.Cursor.new_from_name(
display,
'grab',
));
self.toggleClassName('hover', true);
2023-12-22 16:58:12 -05:00
if (!self.attribute.hovered) {
self.attribute.hovered = true;
}
})
// OnHoverLost
.on('leave-notify-event', () => {
self.window.set_cursor(null);
self.toggleClassName('hover', false);
if (self.attribute.hovered) {
self.attribute.hovered = false;
}
2023-12-22 16:58:12 -05:00
});
},
2023-12-20 17:14:07 -05:00
attribute: {
dragging: false,
hovered: false,
ready: false,
id,
2024-01-13 11:15:08 -05:00
slideAway: (side: 'Left' | 'Right') => {
2024-01-29 20:56:56 -05:00
(widget.child as BoxGeneric)
2024-01-13 11:15:08 -05:00
.setCss(side === 'Left' ? slideLeft : slideRight);
2023-12-20 17:14:07 -05:00
// Make it uninteractable
widget.sensitive = false;
timeout(ANIM_DURATION - 100, () => {
// Reduce height after sliding away
2024-01-29 20:56:56 -05:00
(widget.child as BoxGeneric)?.setCss(side === 'Left' ?
2023-12-20 17:14:07 -05:00
squeezeLeft :
squeezeRight);
timeout(ANIM_DURATION, () => {
// Kill notif and update HasNotifs after anim is done
command();
HasNotifs.value = Notifications
.notifications.length > 0;
2024-01-29 20:56:56 -05:00
(widget.get_parent() as BoxGeneric)?.remove(widget);
2023-12-20 17:14:07 -05:00
});
});
},
},
});
const gesture = Gtk.GestureDrag.new(widget);
widget.add(Box({
css: squeezeLeft,
setup: (self) => {
self
// When dragging
.hook(gesture, () => {
let offset = gesture.get_offset()[1];
2024-01-22 10:23:32 -05:00
if (!offset || offset === 0) {
return;
}
// Slide right
if (offset > 0) {
self.setCss(`
margin-top: 0px; margin-bottom: 0px;
opacity: 1; transition: none;
margin-left: ${offset}px;
margin-right: -${offset}px;
2023-11-01 15:29:47 -04:00
`);
}
2023-11-01 15:29:47 -04:00
// Slide left
else {
offset = Math.abs(offset);
self.setCss(`
margin-top: 0px; margin-bottom: 0px;
opacity: 1; transition: none;
margin-right: ${offset}px;
margin-left: -${offset}px;
2023-11-01 15:29:47 -04:00
`);
}
// Put a threshold on if a click is actually dragging
2023-12-20 17:14:07 -05:00
widget.attribute.dragging =
Math.abs(offset) > SLIDE_MIN_THRESHOLD;
widget.cursor = 'grabbing';
}, 'drag-update')
// On drag end
.hook(gesture, () => {
// Make it slide in on init
2023-12-20 17:14:07 -05:00
if (!widget.attribute.ready) {
// Reverse of slideAway, so it started at squeeze, then we go to slide
self.setCss(slideIn === 'Left' ?
slideLeft :
slideRight);
timeout(ANIM_DURATION, () => {
// Then we go to center
self.setCss(defaultStyle);
timeout(ANIM_DURATION, () => {
2023-12-20 17:14:07 -05:00
widget.attribute.ready = true;
});
});
return;
}
const offset = gesture.get_offset()[1];
2024-01-22 10:23:32 -05:00
if (!offset) {
return;
}
// If crosses threshold after letting go, slide away
if (Math.abs(offset) > MAX_OFFSET) {
if (offset > 0) {
2023-12-20 17:14:07 -05:00
widget.attribute.slideAway('Right');
}
else {
2023-12-20 17:14:07 -05:00
widget.attribute.slideAway('Left');
}
}
else {
self.setCss(defaultStyle);
widget.cursor = 'grab';
2023-12-20 17:14:07 -05:00
widget.attribute.dragging = false;
}
}, 'drag-end');
},
}));
return widget;
};