const { timeout } = Utils; const { Box, EventBox, Overlay } = Widget; const { Gtk } = imports.gi; const MAX_OFFSET = 200; const OFFSCREEN = 500; const ANIM_DURATION = 500; const TRANSITION = `transition: margin ${ANIM_DURATION}ms ease, opacity ${ANIM_DURATION}ms ease;`; // Types import { CenterBoxGeneric, Gesture, OverlayGeneric, PlayerBox, } from 'global-types'; export default ({ setup = () => {/**/}, ...props }: Gesture) => { const widget = EventBox(); const gesture = Gtk.GestureDrag.new(widget); // Have empty PlayerBox to define the size of the widget const emptyPlayer = Box({ class_name: 'player', attribute: { empty: true }, }); const content = Overlay({ ...props, attribute: { players: new Map(), setup: false, dragging: false, includesWidget: (playerW: OverlayGeneric) => { return content.overlays.find((w) => w === playerW); }, showTopOnly: () => content.overlays.forEach((over) => { over.visible = over === content.overlays.at(-1); }), moveToTop: (player: CenterBoxGeneric) => { player.visible = true; content.reorder_overlay(player, -1); timeout(ANIM_DURATION, () => { content.attribute.showTopOnly(); }); }, }, child: emptyPlayer, setup: (self) => { setup(self); self .hook(gesture, (_, realGesture) => { if (realGesture) { self.overlays.forEach((over) => { over.visible = true; }); } else { self.attribute.showTopOnly(); } // Don't allow gesture when only one player if (self.overlays.length <= 1) { return; } self.attribute.dragging = true; let offset = gesture.get_offset()[1]; const playerBox = self.overlays.at(-1) as PlayerBox; if (!offset) { return; } // Slide right if (offset >= 0) { playerBox.setCss(` margin-left: ${offset}px; margin-right: -${offset}px; ${playerBox.attribute.bgStyle} `); } // Slide left else { offset = Math.abs(offset); playerBox.setCss(` margin-left: -${offset}px; margin-right: ${offset}px; ${playerBox.attribute.bgStyle} `); } }, 'drag-update') .hook(gesture, () => { // Don't allow gesture when only one player if (self.overlays.length <= 1) { return; } self.attribute.dragging = false; const offset = gesture.get_offset()[1]; const playerBox = self.overlays.at(-1) as PlayerBox; // If crosses threshold after letting go, slide away if (offset && Math.abs(offset) > MAX_OFFSET) { // Disable inputs during animation widget.sensitive = false; // Slide away right if (offset >= 0) { playerBox.setCss(` ${TRANSITION} margin-left: ${OFFSCREEN}px; margin-right: -${OFFSCREEN}px; opacity: 0.7; ${playerBox.attribute.bgStyle} `); } // Slide away left else { playerBox.setCss(` ${TRANSITION} margin-left: -${OFFSCREEN}px; margin-right: ${OFFSCREEN}px; opacity: 0.7; ${playerBox.attribute.bgStyle} `); } timeout(ANIM_DURATION, () => { // Put the player in the back after anim self.reorder_overlay(playerBox, 0); // Recenter player playerBox.setCss(playerBox.attribute.bgStyle); widget.sensitive = true; self.attribute.showTopOnly(); }); } else { // Recenter with transition for animation playerBox.setCss(`${TRANSITION} ${playerBox.attribute.bgStyle}`); } }, 'drag-end'); }, }); widget.add(content); return widget; };