mirror of
https://github.com/xsghetti/HyprCrux.git
synced 2025-07-03 13:50:38 -04:00
updates
This commit is contained in:
parent
1f8cb3c145
commit
610604e80f
253 changed files with 27055 additions and 44 deletions
208
.config/ags/modules/indicators/colorscheme.js
Normal file
208
.config/ags/modules/indicators/colorscheme.js
Normal 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);
|
||||
}
|
||||
})
|
||||
},
|
||||
})
|
112
.config/ags/modules/indicators/indicatorvalues.js
Normal file
112
.config/ags/modules/indicators/indicatorvalues.js
Normal 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,
|
||||
]
|
||||
})
|
||||
});
|
||||
}
|
33
.config/ags/modules/indicators/main.js
Normal file
33
.config/ags/modules/indicators/main.js
Normal 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(),
|
||||
]
|
||||
})
|
||||
}),
|
||||
});
|
||||
|
408
.config/ags/modules/indicators/musiccontrols.js
Normal file
408
.config/ags/modules/indicators/musiccontrols.js
Normal 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;
|
||||
}),
|
||||
})
|
45
.config/ags/modules/indicators/notificationpopups.js
Normal file
45
.config/ags/modules/indicators/notificationpopups.js
Normal 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')
|
||||
,
|
||||
});
|
Loading…
Add table
Add a link
Reference in a new issue