feat(ags player): make indicators clickable and big refactor

This commit is contained in:
matt1432 2023-11-28 12:24:58 -05:00
parent 5b72e6fa0a
commit 04312dcc2c
3 changed files with 93 additions and 105 deletions

View file

@ -7,7 +7,7 @@ const MAX_OFFSET = 200;
const OFFSCREEN = 500; const OFFSCREEN = 500;
const ANIM_DURATION = 500; const ANIM_DURATION = 500;
const TRANSITION = `transition: margin ${ANIM_DURATION}ms ease, const TRANSITION = `transition: margin ${ANIM_DURATION}ms ease,
opacity 3s ease;`; opacity ${ANIM_DURATION}ms ease;`;
export default ({ export default ({
@ -24,20 +24,11 @@ export default ({
// Set this prop to differentiate it easily // Set this prop to differentiate it easily
emptyPlayer.empty = true; emptyPlayer.empty = true;
widget.add(Overlay({ const content = Overlay({
...props, ...props,
properties: [ properties: [
...properties, ...properties,
['dragging', false], ['dragging', false],
['showTopOnly', (overlay) => overlay.list()
.forEach((over) => {
if (over === overlay.list().at(-1)) {
over.visible = true;
}
else {
over.visible = false;
}
})],
], ],
child: emptyPlayer, child: emptyPlayer,
@ -52,7 +43,7 @@ export default ({
}); });
} }
else { else {
overlay._showTopOnly(overlay); overlay.showTopOnly();
} }
// Don't allow gesture when only one player // Don't allow gesture when only one player
@ -128,7 +119,7 @@ export default ({
widget.sensitive = true; widget.sensitive = true;
overlay._showTopOnly(overlay); overlay.showTopOnly();
}); });
} }
else { else {
@ -137,10 +128,31 @@ export default ({
} }
}, 'drag-end'], }, 'drag-end'],
], ],
})); });
widget.child.list = () => widget.child.get_children() widget.add(content);
// Overlay methods
content.list = () => content.get_children()
.filter((ch) => !ch.empty); .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; return widget;
}; };

View file

@ -6,6 +6,8 @@ import { execAsync, lookUpIcon, readFileAsync } from 'resource:///com/github/Ayl
import Separator from '../misc/separator.js'; import Separator from '../misc/separator.js';
import EventBox from '../misc/cursorbox.js'; import EventBox from '../misc/cursorbox.js';
const ICON_SIZE = 32;
const icons = { const icons = {
mpris: { mpris: {
fallback: 'audio-x-generic-symbolic', fallback: 'audio-x-generic-symbolic',
@ -31,7 +33,10 @@ export const CoverArt = (player, props) => CenterBox({
...props, ...props,
vertical: true, vertical: true,
properties: [['bgStyle', '']], properties: [
['bgStyle', ''],
['player', player],
],
setup: (self) => { setup: (self) => {
// Give temp cover art // Give temp cover art
@ -109,53 +114,39 @@ export const ArtistLabel = (player, props) => Label({
binds: [['label', player, 'track-artists', (a) => a.join(', ') || '']], binds: [['label', player, 'track-artists', (a) => a.join(', ') || '']],
}); });
export const PlayerIcon = (player, { symbolic = true, ...props } = {}) => { export const PlayerIcon = (player, overlay, props) => {
// Get player's icon const playerIcon = (p, widget, over) => EventBox({
const MainIcon = Icon({
...props, ...props,
className: 'player-icon', tooltipText: p.identity || '',
size: 32,
tooltipText: player.identity || '',
connections: [[player, (self) => { onPrimaryClickRelease: () => {
const name = `${player.entry}${symbolic ? '-symbolic' : ''}`; widget?.moveToTop(over);
},
lookUpIcon(name) ? child: Icon({
self.icon = name : className: widget ? 'position-indicator' : 'player-icon',
self.icon = icons.mpris.fallback; size: widget ? '' : ICON_SIZE,
connections: [[p, (self) => {
self.icon = lookUpIcon(p.entry) ?
p.entry :
icons.mpris.fallback;
}]], }]],
}),
}); });
// Multiple player indicators
return Box({ return Box({
properties: [['overlay']],
connections: [[Mpris, (self) => { connections: [[Mpris, (self) => {
if (!self._overlay) { const thisIndex = overlay.list()
self._overlay = self.get_parent().get_parent().get_parent(); .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 i === thisIndex ?
return overlay === self.get_parent().get_parent(); playerIcon(player) :
}); playerIcon(over._player, overlay, over);
}).reverse();
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();
}]], }]],
}); });
}; };
@ -216,11 +207,13 @@ const PlayerButton = ({ player, items, onClick, prop }) => EventBox({
onHover: (self) => { onHover: (self) => {
self._hovered = true; self._hovered = true;
if (prop === 'playBackStatus') { if (prop === 'playBackStatus' && player.colors.value) {
const c = player.colors.value;
items.forEach((item) => { items.forEach((item) => {
item[1].setCss(` item[1].setCss(`
background-color: ${player.colors.value.hoverAccent}; background-color: ${c.hoverAccent};
color: ${player.colors.value.buttonText}; color: ${c.buttonText};
min-height: 40px; min-height: 40px;
min-width: 36px; min-width: 36px;
margin-bottom: 1px; margin-bottom: 1px;
@ -232,11 +225,13 @@ const PlayerButton = ({ player, items, onClick, prop }) => EventBox({
onHoverLost: (self) => { onHoverLost: (self) => {
self._hovered = false; self._hovered = false;
if (prop === 'playBackStatus') { if (prop === 'playBackStatus' && player.colors.value) {
const c = player.colors.value;
items.forEach((item) => { items.forEach((item) => {
item[1].setCss(` item[1].setCss(`
background-color: ${player.colors.value.buttonAccent}; background-color: ${c.buttonAccent};
color: ${player.colors.value.buttonText}; color: ${c.buttonText};
min-height: 42px; min-height: 42px;
min-width: 38px; min-width: 38px;
`); `);

View file

@ -10,15 +10,13 @@ import Separator from '../misc/separator.js';
const FAVE_PLAYER = 'org.mpris.MediaPlayer2.spotify'; const FAVE_PLAYER = 'org.mpris.MediaPlayer2.spotify';
const Top = (player) => Box({ const Top = (player, overlay) => Box({
className: 'top', className: 'top',
hpack: 'start', hpack: 'start',
vpack: 'start', vpack: 'start',
children: [ children: [
mpris.PlayerIcon(player, { mpris.PlayerIcon(player, overlay),
symbolic: false,
}),
], ],
}); });
@ -85,13 +83,13 @@ const Bottom = (player) => Box({
], ],
}); });
const PlayerBox = (player) => { const PlayerBox = (player, overlay) => {
const widget = mpris.CoverArt(player, { const widget = mpris.CoverArt(player, {
className: `player ${player.name}`, className: `player ${player.name}`,
hexpand: true, hexpand: true,
children: [ children: [
Top(player), Top(player, overlay),
Center(player), Center(player),
Bottom(player), Bottom(player),
], ],
@ -102,10 +100,8 @@ const PlayerBox = (player) => {
return widget; return widget;
}; };
export default () => Box({ export default () => {
className: 'media', const content = PlayerGesture({
child: PlayerGesture({
properties: [ properties: [
['players', new Map()], ['players', new Map()],
['setup', false], ['setup', false],
@ -131,39 +127,29 @@ export default () => Box({
} }
} }
// Get the one on top so it stays there // Get the one on top so we can move it up later
let previousFirst = overlay.get_children().at(-1); const previousFirst = overlay.list().at(-1);
for (const [key, value] of overlay._players.entries()) {
if (value === previousFirst) {
previousFirst = key;
break;
}
}
// Make the new player // Make the new player
const player = Mpris.getPlayer(busName); const player = Mpris.getPlayer(busName);
player.colors = Variable(); player.colors = Variable();
overlay._players.set(busName, PlayerBox(player)); overlay._players.set(
busName,
PlayerBox(player, content.getOverlay()),
);
overlay.overlays = Array.from(overlay._players.values()) overlay.overlays = Array.from(overlay._players.values())
.map((widget) => widget); .map((widget) => widget);
// Select favorite player at startup // Select favorite player at startup
if (!overlay._setup && overlay._players.has(FAVE_PLAYER)) { if (!overlay._setup && overlay._players.has(FAVE_PLAYER)) {
overlay.reorder_overlay( overlay.moveToTop(overlay._players.get(FAVE_PLAYER));
overlay._players.get(FAVE_PLAYER),
-1,
);
overlay._setup = true; overlay._setup = true;
} }
// Move previousFirst on top again // Move previousFirst on top again
else if (overlay._players.get(previousFirst)) { else if (overlay.includesWidget(previousFirst)) {
overlay.reorder_overlay( overlay.moveToTop(previousFirst);
overlay._players.get(previousFirst),
-1,
);
} }
}, 'player-added'], }, 'player-added'],
@ -173,15 +159,8 @@ export default () => Box({
return; return;
} }
// Get the one on top so it stays there // Get the one on top so we can move it up later
let previousFirst = overlay.get_children().at(-1); const previousFirst = overlay.list().at(-1);
for (const [key, value] of overlay._players.entries()) {
if (value === previousFirst) {
previousFirst = key;
break;
}
}
// Remake overlays without deleted one // Remake overlays without deleted one
overlay._players.delete(busName); overlay._players.delete(busName);
@ -189,13 +168,15 @@ export default () => Box({
.map((widget) => widget); .map((widget) => widget);
// Move previousFirst on top again // Move previousFirst on top again
if (overlay._players.has(previousFirst)) { if (overlay.includesWidget(previousFirst)) {
overlay.reorder_overlay( overlay.moveToTop(previousFirst);
overlay._players.get(previousFirst),
-1,
);
} }
}, 'player-closed'], }, 'player-closed'],
], ],
}), });
});
return Box({
className: 'media',
child: content,
});
};