This commit is contained in:
xsghetti 2024-04-11 00:21:35 -04:00
parent 1f8cb3c145
commit 610604e80f
253 changed files with 27055 additions and 44 deletions

View file

@ -0,0 +1,208 @@
const { Gio, GLib } = imports.gi;
import Variable from 'resource:///com/github/Aylur/ags/variable.js';
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
import { ConfigToggle, ConfigMulipleSelection } from '../.commonwidgets/configwidgets.js';
import * as Utils from 'resource:///com/github/Aylur/ags/utils.js';
const { execAsync } = Utils;
import { setupCursorHover } from '../.widgetutils/cursorhover.js';
import { showColorScheme } from '../../variables.js';
import { MaterialIcon } from '../.commonwidgets/materialicon.js';
const ColorBox = ({
name = 'Color',
...rest
}) => Widget.Box({
...rest,
homogeneous: true,
children: [
Widget.Label({
label: `${name}`,
})
]
})
const ColorSchemeSettingsRevealer = () => {
const headerButtonIcon = MaterialIcon('expand_more', 'norm');
const header = Widget.Button({
className: 'osd-settings-btn-arrow',
onClicked: () => {
content.revealChild = !content.revealChild;
headerButtonIcon.label = content.revealChild ? 'expand_less' : 'expand_more';
},
setup: setupCursorHover,
hpack: 'end',
child: headerButtonIcon,
});
const content = Widget.Revealer({
revealChild: false,
transition: 'slide_down',
transitionDuration: 200,
child: ColorSchemeSettings(),
setup: (self) => self.hook(isHoveredColorschemeSettings, (revealer) => {
if (isHoveredColorschemeSettings.value == false) {
setTimeout(() => {
if (isHoveredColorschemeSettings.value == false)
revealer.revealChild = false;
headerButtonIcon.label = 'expand_more';
}, 1500);
}
}),
});
return Widget.EventBox({
onHover: (self) => {
isHoveredColorschemeSettings.setValue(true);
},
onHoverLost: (self) => {
isHoveredColorschemeSettings.setValue(false);
},
child: Widget.Box({
vertical: true,
children: [
header,
content,
]
}),
});
}
function calculateSchemeInitIndex(optionsArr, searchValue = 'vibrant') {
if (searchValue == '')
searchValue = 'vibrant';
const flatArray = optionsArr.flatMap(subArray => subArray);
const result = flatArray.findIndex(element => element.value === searchValue);
const rowIndex = Math.floor(result / optionsArr[0].length);
const columnIndex = result % optionsArr[0].length;
return [rowIndex, columnIndex];
}
const schemeOptionsArr = [
[
{ name: 'Tonal Spot', value: 'tonalspot' },
{ name: 'Fruit Salad', value: 'fruitsalad' },
{ name: 'Fidelity', value: 'fidelity' },
{ name: 'Rainbow', value: 'rainbow' },
],
[
{ name: 'Neutral', value: 'neutral' },
{ name: 'Monochrome', value: 'monochrome' },
{ name: 'Expressive', value: 'expressive' },
{ name: 'Vibrant', value: 'vibrant' },
],
//[
// { name: 'Content', value: 'content' },
//]
];
const initColorMode = Utils.exec('bash -c "sed -n \'1p\' $HOME/.cache/ags/user/colormode.txt"');
const initColorVal = (initColorMode == "dark") ? 1 : 0;
const initTransparency = Utils.exec('bash -c "sed -n \'2p\' $HOME/.cache/ags/user/colormode.txt"');
const initTransparencyVal = (initTransparency == "transparent") ? 1 : 0;
const initScheme = Utils.exec('bash -c "sed -n \'3p\' $HOME/.cache/ags/user/colormode.txt"');
const initSchemeIndex = calculateSchemeInitIndex(schemeOptionsArr, initScheme);
const ColorSchemeSettings = () => Widget.Box({
className: 'osd-colorscheme-settings spacing-v-5',
vertical: true,
vpack: 'center',
children: [
Widget.Box({
children: [
ConfigToggle({
name: 'Dark Mode',
initValue: initColorVal,
onChange: (self, newValue) => {
let lightdark = newValue == 0 ? "light" : "dark";
execAsync([`bash`, `-c`, `mkdir -p ${GLib.get_user_cache_dir()}/ags/user && sed -i "1s/.*/${lightdark}/" ${GLib.get_user_cache_dir()}/ags/user/colormode.txt`])
.then(execAsync(['bash', '-c', `${App.configDir}/scripts/color_generation/switchcolor.sh`]))
.catch(print);
},
}),
ConfigToggle({
name: 'Transparency',
initValue: initTransparencyVal,
onChange: (self, newValue) => {
let transparency = newValue == 0 ? "opaque" : "transparent";
execAsync([`bash`, `-c`, `mkdir -p ${GLib.get_user_cache_dir()}/ags/user && sed -i "2s/.*/${transparency}/" ${GLib.get_user_cache_dir()}/ags/user/colormode.txt`])
.then(execAsync(['bash', '-c', `${App.configDir}/scripts/color_generation/switchcolor.sh`]))
.catch(print);
},
}),
]
}),
ConfigMulipleSelection({
hpack: 'center',
vpack: 'center',
optionsArr: schemeOptionsArr,
initIndex: initSchemeIndex,
onChange: (value, name) => {
execAsync([`bash`, `-c`, `mkdir -p ${GLib.get_user_cache_dir()}/ags/user && sed -i "3s/.*/${value}/" ${GLib.get_user_cache_dir()}/ags/user/colormode.txt`])
.then(execAsync(['bash', '-c', `${App.configDir}/scripts/color_generation/switchcolor.sh`]))
.catch(print);
},
}),
]
});
const ColorschemeContent = () => Widget.Box({
className: 'osd-colorscheme spacing-v-5',
vertical: true,
hpack: 'center',
children: [
Widget.Label({
xalign: 0,
className: 'txt-norm titlefont txt',
label: 'Colorscheme',
hpack: 'center',
}),
Widget.Box({
className: 'spacing-h-5',
hpack: 'center',
children: [
ColorBox({ name: 'P', className: 'osd-color osd-color-primary' }),
ColorBox({ name: 'S', className: 'osd-color osd-color-secondary' }),
ColorBox({ name: 'T', className: 'osd-color osd-color-tertiary' }),
ColorBox({ name: 'Sf', className: 'osd-color osd-color-surface' }),
ColorBox({ name: 'Sf-i', className: 'osd-color osd-color-inverseSurface' }),
ColorBox({ name: 'E', className: 'osd-color osd-color-error' }),
]
}),
Widget.Box({
className: 'spacing-h-5',
hpack: 'center',
children: [
ColorBox({ name: 'P-c', className: 'osd-color osd-color-primaryContainer' }),
ColorBox({ name: 'S-c', className: 'osd-color osd-color-secondaryContainer' }),
ColorBox({ name: 'T-c', className: 'osd-color osd-color-tertiaryContainer' }),
ColorBox({ name: 'Sf-c', className: 'osd-color osd-color-surfaceContainer' }),
ColorBox({ name: 'Sf-v', className: 'osd-color osd-color-surfaceVariant' }),
ColorBox({ name: 'E-c', className: 'osd-color osd-color-errorContainer' }),
]
}),
ColorSchemeSettingsRevealer(),
]
});
const isHoveredColorschemeSettings = Variable(false);
export default () => Widget.Revealer({
transition: 'slide_down',
transitionDuration: userOptions.animations.durationLarge,
child: ColorschemeContent(),
setup: (self) => {
self
.hook(showColorScheme, (revealer) => {
if (showColorScheme.value == true)
revealer.revealChild = true;
else
revealer.revealChild = isHoveredColorschemeSettings.value;
})
.hook(isHoveredColorschemeSettings, (revealer) => {
if (isHoveredColorschemeSettings.value == false) {
setTimeout(() => {
if (isHoveredColorschemeSettings.value == false)
revealer.revealChild = showColorScheme.value;
}, 2000);
}
})
},
})

View file

@ -0,0 +1,112 @@
// This file is for brightness/volume indicators
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
import Audio from 'resource:///com/github/Aylur/ags/service/audio.js';
const { Box, Label, ProgressBar } = Widget;
import { MarginRevealer } from '../.widgethacks/advancedrevealers.js';
import Brightness from '../../services/brightness.js';
import Indicator from '../../services/indicator.js';
const OsdValue = ({
name, nameSetup = undefined, labelSetup, progressSetup,
extraClassName = '', extraProgressClassName = '',
...rest
}) => {
const valueName = Label({
xalign: 0, yalign: 0, hexpand: true,
className: 'osd-label',
label: `${name}`,
setup: nameSetup,
});
const valueNumber = Label({
hexpand: false, className: 'osd-value-txt',
setup: labelSetup,
});
return Box({ // Volume
vertical: true,
hexpand: true,
className: `osd-bg osd-value ${extraClassName}`,
attribute: {
'disable': () => {
valueNumber.label = '󰖭';
}
},
children: [
Box({
vexpand: true,
children: [
valueName,
valueNumber,
]
}),
ProgressBar({
className: `osd-progress ${extraProgressClassName}`,
hexpand: true,
vertical: false,
setup: progressSetup,
})
],
...rest,
});
}
export default () => {
const brightnessIndicator = OsdValue({
name: 'Brightness',
extraClassName: 'osd-brightness',
extraProgressClassName: 'osd-brightness-progress',
labelSetup: (self) => self.hook(Brightness, self => {
self.label = `${Math.round(Brightness.screen_value * 100)}`;
}, 'notify::screen-value'),
progressSetup: (self) => self.hook(Brightness, (progress) => {
const updateValue = Brightness.screen_value;
progress.value = updateValue;
}, 'notify::screen-value'),
});
const volumeIndicator = OsdValue({
name: 'Volume',
extraClassName: 'osd-volume',
extraProgressClassName: 'osd-volume-progress',
attribute: { headphones: undefined },
nameSetup: (self) => Utils.timeout(1, () => {
const updateAudioDevice = (self) => {
const usingHeadphones = (Audio.speaker?.stream?.port)?.toLowerCase().includes('headphone');
if (volumeIndicator.attribute.headphones === undefined ||
volumeIndicator.attribute.headphones !== usingHeadphones) {
volumeIndicator.attribute.headphones = usingHeadphones;
self.label = usingHeadphones ? 'Headphones' : 'Speakers';
Indicator.popup(1);
}
}
self.hook(Audio, updateAudioDevice);
Utils.timeout(1000, updateAudioDevice);
}),
labelSetup: (self) => self.hook(Audio, (label) => {
label.label = `${Math.round(Audio.speaker?.volume * 100)}`;
}),
progressSetup: (self) => self.hook(Audio, (progress) => {
const updateValue = Audio.speaker?.volume;
if (!isNaN(updateValue)) progress.value = updateValue;
}),
});
return MarginRevealer({
transition: 'slide_down',
showClass: 'osd-show',
hideClass: 'osd-hide',
extraSetup: (self) => self
.hook(Indicator, (revealer, value) => {
if (value > -1) revealer.attribute.show();
else revealer.attribute.hide();
}, 'popup')
,
child: Box({
hpack: 'center',
vertical: false,
className: 'spacing-h--10',
children: [
brightnessIndicator,
volumeIndicator,
]
})
});
}

View file

@ -0,0 +1,33 @@
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
import Indicator from '../../services/indicator.js';
import IndicatorValues from './indicatorvalues.js';
import MusicControls from './musiccontrols.js';
import ColorScheme from './colorscheme.js';
import NotificationPopups from './notificationpopups.js';
export default (monitor = 0) => Widget.Window({
name: `indicator${monitor}`,
monitor,
className: 'indicator',
layer: 'overlay',
// exclusivity: 'ignore',
visible: true,
anchor: ['top'],
child: Widget.EventBox({
onHover: () => { //make the widget hide when hovering
Indicator.popup(-1);
},
child: Widget.Box({
vertical: true,
className: 'osd-window',
css: 'min-height: 2px;',
children: [
IndicatorValues(),
MusicControls(),
NotificationPopups(),
ColorScheme(),
]
})
}),
});

View file

@ -0,0 +1,408 @@
const { GLib } = imports.gi;
import App from 'resource:///com/github/Aylur/ags/app.js';
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
import * as Utils from 'resource:///com/github/Aylur/ags/utils.js';
import Mpris from 'resource:///com/github/Aylur/ags/service/mpris.js';
const { exec, execAsync } = Utils;
const { Box, EventBox, Icon, Scrollable, Label, Button, Revealer } = Widget;
import { fileExists } from '../.miscutils/files.js';
import { AnimatedCircProg } from "../.commonwidgets/cairo_circularprogress.js";
import { showMusicControls } from '../../variables.js';
import { darkMode, hasPlasmaIntegration } from '../.miscutils/system.js';
const COMPILED_STYLE_DIR = `${GLib.get_user_cache_dir()}/ags/user/generated`
const LIGHTDARK_FILE_LOCATION = `${GLib.get_user_cache_dir()}/ags/user/colormode.txt`;
const colorMode = Utils.exec('bash -c "sed -n \'1p\' $HOME/.cache/ags/user/colormode.txt"');
const lightDark = (colorMode == "light") ? '-l' : '';
const COVER_COLORSCHEME_SUFFIX = '_colorscheme.css';
var lastCoverPath = '';
function isRealPlayer(player) {
return (
// Remove unecessary native buses from browsers if there's plasma integration
!(hasPlasmaIntegration && player.busName.startsWith('org.mpris.MediaPlayer2.firefox')) &&
!(hasPlasmaIntegration && player.busName.startsWith('org.mpris.MediaPlayer2.chromium')) &&
// playerctld just copies other buses and we don't need duplicates
!player.busName.startsWith('org.mpris.MediaPlayer2.playerctld') &&
// Non-instance mpd bus
!player.busName.endsWith('.mpd')
);
}
export const getPlayer = (name = userOptions.music.preferredPlayer) => Mpris.getPlayer(name) || Mpris.players[0] || null;
function lengthStr(length) {
const min = Math.floor(length / 60);
const sec = Math.floor(length % 60);
const sec0 = sec < 10 ? '0' : '';
return `${min}:${sec0}${sec}`;
}
function detectMediaSource(link) {
if (link.startsWith("file://")) {
if (link.includes('firefox-mpris'))
return '󰈹 Firefox'
return "󰈣 File";
}
let url = link.replace(/(^\w+:|^)\/\//, '');
let domain = url.match(/(?:[a-z]+\.)?([a-z]+\.[a-z]+)/i)[1];
if (domain == 'ytimg.com') return '󰗃 Youtube';
if (domain == 'discordapp.net') return '󰙯 Discord';
if (domain == 'sndcdn.com') return '󰓀 SoundCloud';
return domain;
}
const DEFAULT_MUSIC_FONT = 'Gabarito, sans-serif';
function getTrackfont(player) {
const title = player.trackTitle;
const artists = player.trackArtists.join(' ');
if (artists.includes('TANO*C') || artists.includes('USAO') || artists.includes('Kobaryo'))
return 'Chakra Petch'; // Rigid square replacement
if (title.includes('東方'))
return 'Crimson Text, serif'; // Serif for Touhou stuff
return DEFAULT_MUSIC_FONT;
}
function trimTrackTitle(title) {
if (!title) return '';
const cleanPatterns = [
/【[^】]*】/, // Touhou n weeb stuff
" [FREE DOWNLOAD]", // F-777
];
cleanPatterns.forEach((expr) => title = title.replace(expr, ''));
return title;
}
const TrackProgress = ({ player, ...rest }) => {
const _updateProgress = (circprog) => {
// const player = Mpris.getPlayer();
if (!player) return;
// Set circular progress (see definition of AnimatedCircProg for explanation)
circprog.css = `font-size: ${Math.max(player.position / player.length * 100, 0)}px;`
}
return AnimatedCircProg({
...rest,
className: 'osd-music-circprog',
vpack: 'center',
extraSetup: (self) => self
.hook(Mpris, _updateProgress)
.poll(3000, _updateProgress)
,
})
}
const TrackTitle = ({ player, ...rest }) => Label({
...rest,
label: 'No music playing',
xalign: 0,
truncate: 'end',
// wrap: true,
className: 'osd-music-title',
setup: (self) => self.hook(player, (self) => {
// Player name
self.label = player.trackTitle.length > 0 ? trimTrackTitle(player.trackTitle) : 'No media';
// Font based on track/artist
const fontForThisTrack = getTrackfont(player);
self.css = `font-family: ${fontForThisTrack}, ${DEFAULT_MUSIC_FONT};`;
}, 'notify::track-title'),
});
const TrackArtists = ({ player, ...rest }) => Label({
...rest,
xalign: 0,
className: 'osd-music-artists',
truncate: 'end',
setup: (self) => self.hook(player, (self) => {
self.label = player.trackArtists.length > 0 ? player.trackArtists.join(', ') : '';
}, 'notify::track-artists'),
})
const CoverArt = ({ player, ...rest }) => {
const fallbackCoverArt = Box({ // Fallback
className: 'osd-music-cover-fallback',
homogeneous: true,
children: [Label({
className: 'icon-material txt-gigantic txt-thin',
label: 'music_note',
})]
});
// const coverArtDrawingArea = Widget.DrawingArea({ className: 'osd-music-cover-art' });
// const coverArtDrawingAreaStyleContext = coverArtDrawingArea.get_style_context();
const realCoverArt = Box({
className: 'osd-music-cover-art',
homogeneous: true,
// children: [coverArtDrawingArea],
attribute: {
'pixbuf': null,
// 'showImage': (self, imagePath) => {
// const borderRadius = coverArtDrawingAreaStyleContext.get_property('border-radius', Gtk.StateFlags.NORMAL);
// const frameHeight = coverArtDrawingAreaStyleContext.get_property('min-height', Gtk.StateFlags.NORMAL);
// const frameWidth = coverArtDrawingAreaStyleContext.get_property('min-width', Gtk.StateFlags.NORMAL);
// let imageHeight = frameHeight;
// let imageWidth = frameWidth;
// // Get image dimensions
// execAsync(['identify', '-format', '{"w":%w,"h":%h}', imagePath])
// .then((output) => {
// const imageDimensions = JSON.parse(output);
// const imageAspectRatio = imageDimensions.w / imageDimensions.h;
// const displayedAspectRatio = imageWidth / imageHeight;
// if (imageAspectRatio >= displayedAspectRatio) {
// imageWidth = imageHeight * imageAspectRatio;
// } else {
// imageHeight = imageWidth / imageAspectRatio;
// }
// // Real stuff
// // TODO: fix memory leak(?)
// // if (self.attribute.pixbuf) {
// // self.attribute.pixbuf.unref();
// // self.attribute.pixbuf = null;
// // }
// self.attribute.pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(imagePath, imageWidth, imageHeight);
// coverArtDrawingArea.set_size_request(frameWidth, frameHeight);
// coverArtDrawingArea.connect("draw", (widget, cr) => {
// // Clip a rounded rectangle area
// cr.arc(borderRadius, borderRadius, borderRadius, Math.PI, 1.5 * Math.PI);
// cr.arc(frameWidth - borderRadius, borderRadius, borderRadius, 1.5 * Math.PI, 2 * Math.PI);
// cr.arc(frameWidth - borderRadius, frameHeight - borderRadius, borderRadius, 0, 0.5 * Math.PI);
// cr.arc(borderRadius, frameHeight - borderRadius, borderRadius, 0.5 * Math.PI, Math.PI);
// cr.closePath();
// cr.clip();
// // Paint image as bg, centered
// Gdk.cairo_set_source_pixbuf(cr, self.attribute.pixbuf,
// frameWidth / 2 - imageWidth / 2,
// frameHeight / 2 - imageHeight / 2
// );
// cr.paint();
// });
// }).catch(print)
// },
'updateCover': (self) => {
// const player = Mpris.getPlayer(); // Maybe no need to re-get player.. can't remember why I had this
// Player closed
// Note that cover path still remains, so we're checking title
if (!player || player.trackTitle == "") {
self.css = `background-image: none;`; // CSS image
App.applyCss(`${COMPILED_STYLE_DIR}/style.css`);
return;
}
const coverPath = player.coverPath;
const stylePath = `${player.coverPath}${darkMode.value ? '' : '-l'}${COVER_COLORSCHEME_SUFFIX}`;
if (player.coverPath == lastCoverPath) { // Since 'notify::cover-path' emits on cover download complete
Utils.timeout(200, () => {
// self.attribute.showImage(self, coverPath);
self.css = `background-image: url('${coverPath}');`; // CSS image
});
}
lastCoverPath = player.coverPath;
// If a colorscheme has already been generated, skip generation
if (fileExists(stylePath)) {
// self.attribute.showImage(self, coverPath)
self.css = `background-image: url('${coverPath}');`; // CSS image
App.applyCss(stylePath);
return;
}
// Generate colors
execAsync(['bash', '-c',
`${App.configDir}/scripts/color_generation/generate_colors_material.py --path '${coverPath}' --mode ${darkMode.value ? 'dark' : 'light'} > ${App.configDir}/scss/_musicmaterial.scss`])
.then(() => {
exec(`wal -i "${player.coverPath}" -n -t -s -e -q ${darkMode.value ? '' : '-l'}`)
exec(`cp ${GLib.get_user_cache_dir()}/wal/colors.scss ${App.configDir}/scss/_musicwal.scss`);
exec(`sass ${App.configDir}/scss/_music.scss ${stylePath}`);
Utils.timeout(200, () => {
// self.attribute.showImage(self, coverPath)
self.css = `background-image: url('${coverPath}');`; // CSS image
});
App.applyCss(`${stylePath}`);
})
.catch(print);
},
},
setup: (self) => self
.hook(player, (self) => {
self.attribute.updateCover(self);
}, 'notify::cover-path')
,
});
return Box({
...rest,
className: 'osd-music-cover',
children: [
Widget.Overlay({
child: fallbackCoverArt,
overlays: [realCoverArt],
})
],
})
}
const TrackControls = ({ player, ...rest }) => Widget.Revealer({
revealChild: false,
transition: 'slide_right',
transitionDuration: userOptions.animations.durationLarge,
child: Widget.Box({
...rest,
vpack: 'center',
className: 'osd-music-controls spacing-h-3',
children: [
Button({
className: 'osd-music-controlbtn',
onClicked: () => player.previous(),
child: Label({
className: 'icon-material osd-music-controlbtn-txt',
label: 'skip_previous',
})
}),
Button({
className: 'osd-music-controlbtn',
onClicked: () => player.next(),
child: Label({
className: 'icon-material osd-music-controlbtn-txt',
label: 'skip_next',
})
}),
],
}),
setup: (self) => self.hook(Mpris, (self) => {
// const player = Mpris.getPlayer();
if (!player)
self.revealChild = false;
else
self.revealChild = true;
}, 'notify::play-back-status'),
});
const TrackSource = ({ player, ...rest }) => Widget.Revealer({
revealChild: false,
transition: 'slide_left',
transitionDuration: userOptions.animations.durationLarge,
child: Widget.Box({
...rest,
className: 'osd-music-pill spacing-h-5',
homogeneous: true,
children: [
Label({
hpack: 'fill',
justification: 'center',
className: 'icon-nerd',
setup: (self) => self.hook(player, (self) => {
self.label = detectMediaSource(player.trackCoverUrl);
}, 'notify::cover-path'),
}),
],
}),
setup: (self) => self.hook(Mpris, (self) => {
const mpris = Mpris.getPlayer('');
if (!mpris)
self.revealChild = false;
else
self.revealChild = true;
}),
});
const TrackTime = ({ player, ...rest }) => {
return Widget.Revealer({
revealChild: false,
transition: 'slide_left',
transitionDuration: userOptions.animations.durationLarge,
child: Widget.Box({
...rest,
vpack: 'center',
className: 'osd-music-pill spacing-h-5',
children: [
Label({
setup: (self) => self.poll(1000, (self) => {
// const player = Mpris.getPlayer();
if (!player) return;
self.label = lengthStr(player.position);
}),
}),
Label({ label: '/' }),
Label({
setup: (self) => self.hook(Mpris, (self) => {
// const player = Mpris.getPlayer();
if (!player) return;
self.label = lengthStr(player.length);
}),
}),
],
}),
setup: (self) => self.hook(Mpris, (self) => {
if (!player) self.revealChild = false;
else self.revealChild = true;
}),
})
}
const PlayState = ({ player }) => {
var position = 0;
const trackCircProg = TrackProgress({ player: player });
return Widget.Button({
className: 'osd-music-playstate',
child: Widget.Overlay({
child: trackCircProg,
overlays: [
Widget.Button({
className: 'osd-music-playstate-btn',
onClicked: () => player.playPause(),
child: Widget.Label({
justification: 'center',
hpack: 'fill',
vpack: 'center',
setup: (self) => self.hook(player, (label) => {
label.label = `${player.playBackStatus == 'Playing' ? 'pause' : 'play_arrow'}`;
}, 'notify::play-back-status'),
}),
}),
],
passThrough: true,
})
});
}
const MusicControlsWidget = (player) => Box({
className: 'osd-music spacing-h-20 test',
children: [
CoverArt({ player: player, vpack: 'center' }),
Box({
vertical: true,
className: 'spacing-v-5 osd-music-info',
children: [
Box({
vertical: true,
vpack: 'center',
hexpand: true,
children: [
TrackTitle({ player: player }),
TrackArtists({ player: player }),
]
}),
Box({ vexpand: true }),
Box({
className: 'spacing-h-10',
setup: (box) => {
box.pack_start(TrackControls({ player: player }), false, false, 0);
box.pack_end(PlayState({ player: player }), false, false, 0);
if(hasPlasmaIntegration || player.busName.startsWith('org.mpris.MediaPlayer2.chromium')) box.pack_end(TrackTime({ player: player }), false, false, 0)
// box.pack_end(TrackSource({ vpack: 'center', player: player }), false, false, 0);
}
})
]
})
]
})
export default () => Revealer({
transition: 'slide_down',
transitionDuration: userOptions.animations.durationLarge,
revealChild: false,
child: Box({
children: Mpris.bind("players")
.as(players => players.map((player) => (isRealPlayer(player) ? MusicControlsWidget(player) : null)))
}),
setup: (self) => self.hook(showMusicControls, (revealer) => {
revealer.revealChild = showMusicControls.value;
}),
})

View file

@ -0,0 +1,45 @@
// This file is for popup notifications
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
import Notifications from 'resource:///com/github/Aylur/ags/service/notifications.js';
const { Box } = Widget;
import Notification from '../.commonwidgets/notification.js';
export default () => Box({
vertical: true,
hpack: 'center',
className: 'osd-notifs spacing-v-5-revealer',
attribute: {
'map': new Map(),
'dismiss': (box, id, force = false) => {
if (!id || !box.attribute.map.has(id))
return;
const notifWidget = box.attribute.map.get(id);
if (notifWidget == null || notifWidget.attribute.hovered && !force)
return; // cuz already destroyed
notifWidget.revealChild = false;
notifWidget.attribute.destroyWithAnims();
box.attribute.map.delete(id);
},
'notify': (box, id) => {
if (!id || Notifications.dnd) return;
if (!Notifications.getNotification(id)) return;
box.attribute.map.delete(id);
const notif = Notifications.getNotification(id);
const newNotif = Notification({
notifObject: notif,
isPopup: true,
});
box.attribute.map.set(id, newNotif);
box.pack_end(box.attribute.map.get(id), false, false, 0);
box.show_all();
},
},
setup: (self) => self
.hook(Notifications, (box, id) => box.attribute.notify(box, id), 'notified')
.hook(Notifications, (box, id) => box.attribute.dismiss(box, id), 'dismissed')
.hook(Notifications, (box, id) => box.attribute.dismiss(box, id, true), 'closed')
,
});