From 04312dcc2cb93b8a99923e3f265c074af99f5016 Mon Sep 17 00:00:00 2001 From: matt1432 Date: Tue, 28 Nov 2023 12:24:58 -0500 Subject: [PATCH] feat(ags player): make indicators clickable and big refactor --- .../wim/config/ags/js/media-player/gesture.js | 42 +++++---- .../wim/config/ags/js/media-player/mpris.js | 85 +++++++++---------- .../wim/config/ags/js/media-player/player.js | 71 ++++++---------- 3 files changed, 93 insertions(+), 105 deletions(-) diff --git a/devices/wim/config/ags/js/media-player/gesture.js b/devices/wim/config/ags/js/media-player/gesture.js index 37df33d..0037070 100644 --- a/devices/wim/config/ags/js/media-player/gesture.js +++ b/devices/wim/config/ags/js/media-player/gesture.js @@ -7,7 +7,7 @@ const MAX_OFFSET = 200; const OFFSCREEN = 500; const ANIM_DURATION = 500; const TRANSITION = `transition: margin ${ANIM_DURATION}ms ease, - opacity 3s ease;`; + opacity ${ANIM_DURATION}ms ease;`; export default ({ @@ -24,20 +24,11 @@ export default ({ // Set this prop to differentiate it easily emptyPlayer.empty = true; - widget.add(Overlay({ + const content = Overlay({ ...props, properties: [ ...properties, ['dragging', false], - ['showTopOnly', (overlay) => overlay.list() - .forEach((over) => { - if (over === overlay.list().at(-1)) { - over.visible = true; - } - else { - over.visible = false; - } - })], ], child: emptyPlayer, @@ -52,7 +43,7 @@ export default ({ }); } else { - overlay._showTopOnly(overlay); + overlay.showTopOnly(); } // Don't allow gesture when only one player @@ -128,7 +119,7 @@ export default ({ widget.sensitive = true; - overlay._showTopOnly(overlay); + overlay.showTopOnly(); }); } else { @@ -137,10 +128,31 @@ export default ({ } }, 'drag-end'], ], - })); + }); - widget.child.list = () => widget.child.get_children() + widget.add(content); + + // Overlay methods + content.list = () => content.get_children() .filter((ch) => !ch.empty); + content.includesWidget = (playerW) => { + return content.list().find((w) => w === playerW); + }; + + content.showTopOnly = () => content.list().forEach((over) => { + over.visible = over === content.list().at(-1); + }); + + content.moveToTop = (player) => { + player.visible = true; + content.reorder_overlay(player, -1); + timeout(ANIM_DURATION, () => { + content.showTopOnly(); + }); + }; + + widget.getOverlay = () => content; + return widget; }; diff --git a/devices/wim/config/ags/js/media-player/mpris.js b/devices/wim/config/ags/js/media-player/mpris.js index 49746d5..7aa12a3 100644 --- a/devices/wim/config/ags/js/media-player/mpris.js +++ b/devices/wim/config/ags/js/media-player/mpris.js @@ -6,6 +6,8 @@ import { execAsync, lookUpIcon, readFileAsync } from 'resource:///com/github/Ayl import Separator from '../misc/separator.js'; import EventBox from '../misc/cursorbox.js'; +const ICON_SIZE = 32; + const icons = { mpris: { fallback: 'audio-x-generic-symbolic', @@ -31,7 +33,10 @@ export const CoverArt = (player, props) => CenterBox({ ...props, vertical: true, - properties: [['bgStyle', '']], + properties: [ + ['bgStyle', ''], + ['player', player], + ], setup: (self) => { // Give temp cover art @@ -109,53 +114,39 @@ export const ArtistLabel = (player, props) => Label({ binds: [['label', player, 'track-artists', (a) => a.join(', ') || '']], }); -export const PlayerIcon = (player, { symbolic = true, ...props } = {}) => { - // Get player's icon - const MainIcon = Icon({ +export const PlayerIcon = (player, overlay, props) => { + const playerIcon = (p, widget, over) => EventBox({ ...props, - className: 'player-icon', - size: 32, - tooltipText: player.identity || '', + tooltipText: p.identity || '', - connections: [[player, (self) => { - const name = `${player.entry}${symbolic ? '-symbolic' : ''}`; + onPrimaryClickRelease: () => { + widget?.moveToTop(over); + }, - lookUpIcon(name) ? - self.icon = name : - self.icon = icons.mpris.fallback; - }]], + child: Icon({ + className: widget ? 'position-indicator' : 'player-icon', + size: widget ? '' : ICON_SIZE, + + connections: [[p, (self) => { + self.icon = lookUpIcon(p.entry) ? + p.entry : + icons.mpris.fallback; + }]], + }), }); - // Multiple player indicators return Box({ - properties: [['overlay']], connections: [[Mpris, (self) => { - if (!self._overlay) { - self._overlay = self.get_parent().get_parent().get_parent(); - } + const thisIndex = overlay.list() + .indexOf(self.get_parent().get_parent()); - const overlays = self._overlay.list(); + self.children = overlay.list().map((over, i) => { + self.children.push(Separator(2)); - const playerWidget = overlays.find((overlay) => { - return overlay === self.get_parent().get_parent(); - }); - - const index = overlays.indexOf(playerWidget); - - const children = []; - - for (let i = 0; i < overlays.length; ++i) { - if (i === index) { - children.push(Separator(2)); - children.push(MainIcon); - } - else { - children.push(Separator(2)); - // TODO: make this clickable to switch - children.push(Box({ className: 'position-indicator' })); - } - } - self.children = children.reverse(); + return i === thisIndex ? + playerIcon(player) : + playerIcon(over._player, overlay, over); + }).reverse(); }]], }); }; @@ -216,11 +207,13 @@ const PlayerButton = ({ player, items, onClick, prop }) => EventBox({ onHover: (self) => { self._hovered = true; - if (prop === 'playBackStatus') { + if (prop === 'playBackStatus' && player.colors.value) { + const c = player.colors.value; + items.forEach((item) => { item[1].setCss(` - background-color: ${player.colors.value.hoverAccent}; - color: ${player.colors.value.buttonText}; + background-color: ${c.hoverAccent}; + color: ${c.buttonText}; min-height: 40px; min-width: 36px; margin-bottom: 1px; @@ -232,11 +225,13 @@ const PlayerButton = ({ player, items, onClick, prop }) => EventBox({ onHoverLost: (self) => { self._hovered = false; - if (prop === 'playBackStatus') { + if (prop === 'playBackStatus' && player.colors.value) { + const c = player.colors.value; + items.forEach((item) => { item[1].setCss(` - background-color: ${player.colors.value.buttonAccent}; - color: ${player.colors.value.buttonText}; + background-color: ${c.buttonAccent}; + color: ${c.buttonText}; min-height: 42px; min-width: 38px; `); diff --git a/devices/wim/config/ags/js/media-player/player.js b/devices/wim/config/ags/js/media-player/player.js index 2097721..3f0f6cd 100644 --- a/devices/wim/config/ags/js/media-player/player.js +++ b/devices/wim/config/ags/js/media-player/player.js @@ -10,15 +10,13 @@ import Separator from '../misc/separator.js'; const FAVE_PLAYER = 'org.mpris.MediaPlayer2.spotify'; -const Top = (player) => Box({ +const Top = (player, overlay) => Box({ className: 'top', hpack: 'start', vpack: 'start', children: [ - mpris.PlayerIcon(player, { - symbolic: false, - }), + mpris.PlayerIcon(player, overlay), ], }); @@ -85,13 +83,13 @@ const Bottom = (player) => Box({ ], }); -const PlayerBox = (player) => { +const PlayerBox = (player, overlay) => { const widget = mpris.CoverArt(player, { className: `player ${player.name}`, hexpand: true, children: [ - Top(player), + Top(player, overlay), Center(player), Bottom(player), ], @@ -102,10 +100,8 @@ const PlayerBox = (player) => { return widget; }; -export default () => Box({ - className: 'media', - - child: PlayerGesture({ +export default () => { + const content = PlayerGesture({ properties: [ ['players', new Map()], ['setup', false], @@ -131,39 +127,29 @@ export default () => Box({ } } - // Get the one on top so it stays there - let previousFirst = overlay.get_children().at(-1); - - for (const [key, value] of overlay._players.entries()) { - if (value === previousFirst) { - previousFirst = key; - break; - } - } + // Get the one on top so we can move it up later + const previousFirst = overlay.list().at(-1); // Make the new player const player = Mpris.getPlayer(busName); player.colors = Variable(); - overlay._players.set(busName, PlayerBox(player)); + overlay._players.set( + busName, + PlayerBox(player, content.getOverlay()), + ); overlay.overlays = Array.from(overlay._players.values()) .map((widget) => widget); // Select favorite player at startup if (!overlay._setup && overlay._players.has(FAVE_PLAYER)) { - overlay.reorder_overlay( - overlay._players.get(FAVE_PLAYER), - -1, - ); + overlay.moveToTop(overlay._players.get(FAVE_PLAYER)); overlay._setup = true; } // Move previousFirst on top again - else if (overlay._players.get(previousFirst)) { - overlay.reorder_overlay( - overlay._players.get(previousFirst), - -1, - ); + else if (overlay.includesWidget(previousFirst)) { + overlay.moveToTop(previousFirst); } }, 'player-added'], @@ -173,15 +159,8 @@ export default () => Box({ return; } - // Get the one on top so it stays there - let previousFirst = overlay.get_children().at(-1); - - for (const [key, value] of overlay._players.entries()) { - if (value === previousFirst) { - previousFirst = key; - break; - } - } + // Get the one on top so we can move it up later + const previousFirst = overlay.list().at(-1); // Remake overlays without deleted one overlay._players.delete(busName); @@ -189,13 +168,15 @@ export default () => Box({ .map((widget) => widget); // Move previousFirst on top again - if (overlay._players.has(previousFirst)) { - overlay.reorder_overlay( - overlay._players.get(previousFirst), - -1, - ); + if (overlay.includesWidget(previousFirst)) { + overlay.moveToTop(previousFirst); } }, 'player-closed'], ], - }), -}); + }); + + return Box({ + className: 'media', + child: content, + }); +};