forked from Shinonome/dots-hyprland
should add multimonitor support
This commit is contained in:
+28
-20
@@ -1,13 +1,14 @@
|
||||
"use strict";
|
||||
// Import
|
||||
import Gdk from 'gi://Gdk';
|
||||
import App from 'resource:///com/github/Aylur/ags/app.js'
|
||||
import * as Utils from 'resource:///com/github/Aylur/ags/utils.js'
|
||||
// Widgets
|
||||
import Bar from './widgets/bar/main.js';
|
||||
import { Bar, BarCornerTopleft, BarCornerTopright } from './widgets/bar/main.js';
|
||||
import Cheatsheet from './widgets/cheatsheet/main.js';
|
||||
import DesktopBackground from './widgets/desktopbackground/main.js';
|
||||
// import Dock from './widgets/dock/main.js';
|
||||
import { CornerTopleft, CornerTopright, CornerBottomleft, CornerBottomright } from './widgets/screencorners/main.js';
|
||||
import Corner from './widgets/screencorners/main.js';
|
||||
import Indicator from './widgets/indicators/main.js';
|
||||
import Osk from './widgets/onscreenkeyboard/main.js';
|
||||
import Overview from './widgets/overview/main.js';
|
||||
@@ -15,7 +16,11 @@ import Session from './widgets/session/main.js';
|
||||
import SideLeft from './widgets/sideleft/main.js';
|
||||
import SideRight from './widgets/sideright/main.js';
|
||||
|
||||
const CLOSE_ANIM_TIME = 210; // Longer than actual anim time (see styles) to make sure widgets animate fully
|
||||
const range = (length, start = 1) => Array.from({ length }, (_, i) => i + start);
|
||||
function forMonitors(widget) {
|
||||
const n = Gdk.Display.get_default()?.get_n_monitors() || 1;
|
||||
return range(n, 0).map(widget).flat(1);
|
||||
}
|
||||
|
||||
// SCSS compilation
|
||||
Utils.exec(`bash -c 'echo "" > ${App.configDir}/scss/_musicwal.scss'`); // reset music styles
|
||||
@@ -28,7 +33,25 @@ function applyStyle() {
|
||||
}
|
||||
applyStyle();
|
||||
|
||||
// Config object
|
||||
const Windows = () => [
|
||||
DesktopBackground(),
|
||||
// Dock(),
|
||||
Overview(),
|
||||
forMonitors(Indicator),
|
||||
Cheatsheet(),
|
||||
SideLeft(),
|
||||
SideRight(),
|
||||
Osk(),
|
||||
Session(),
|
||||
Bar(),
|
||||
BarCornerTopleft(),
|
||||
BarCornerTopright(),
|
||||
forMonitors((id) => Corner(id, 'top left')),
|
||||
forMonitors((id) => Corner(id, 'top right')),
|
||||
forMonitors((id) => Corner(id, 'bottom left')),
|
||||
forMonitors((id) => Corner(id, 'bottom right')),
|
||||
];
|
||||
const CLOSE_ANIM_TIME = 210; // Longer than actual anim time to make sure widgets animate fully
|
||||
export default {
|
||||
css: `${App.configDir}/style.css`,
|
||||
stackTraceOnError: true,
|
||||
@@ -37,20 +60,5 @@ export default {
|
||||
'sideleft': CLOSE_ANIM_TIME,
|
||||
'osk': CLOSE_ANIM_TIME,
|
||||
},
|
||||
windows: [
|
||||
CornerTopleft(),
|
||||
CornerTopright(),
|
||||
CornerBottomleft(),
|
||||
CornerBottomright(),
|
||||
DesktopBackground(), // If you're going to uncomment these,
|
||||
// Dock(), // Buggy // uncomment the import statement too.
|
||||
Overview(),
|
||||
Indicator(),
|
||||
Cheatsheet(),
|
||||
SideLeft(),
|
||||
SideRight(),
|
||||
Osk(), // On-screen keyboard
|
||||
Session(), // Power menu, if that's what you like to call it
|
||||
Bar(),
|
||||
],
|
||||
windows: Windows().flat(1),
|
||||
};
|
||||
|
||||
@@ -3,20 +3,19 @@ const require = async file => (await import(resource(file))).default;
|
||||
const service = async file => (await require(`service/${file}`));
|
||||
|
||||
export const App = await require('app');
|
||||
export const Widget = await require('widget');
|
||||
export const Service = await require('service');
|
||||
export const Variable = await require('variable');
|
||||
// export const Widget = await require('widget');
|
||||
// export const Service = await require('service');
|
||||
// export const Variable = await require('variable');
|
||||
export const Utils = await import(resource('utils'));
|
||||
|
||||
export const Applications = await service('applications');
|
||||
export const Audio = await service('audio');
|
||||
export const Battery = await service('battery');
|
||||
export const Bluetooth = await service('bluetooth');
|
||||
// export const Applications = await service('applications');
|
||||
// export const Audio = await service('audio');
|
||||
// export const Battery = await service('battery');
|
||||
// export const Bluetooth = await service('bluetooth');
|
||||
// export const Hyprland = await service('hyprland');
|
||||
export const Mpris = await service('mpris');
|
||||
export const Network = await service('network');
|
||||
export const Notifications = await service('notifications');
|
||||
export const SystemTray = await service('systemtray');
|
||||
// export const SystemTray = await service('systemtray');
|
||||
|
||||
globalThis['App'] = App; //////////////////////////////
|
||||
// globalThis['Widget'] = Widget;
|
||||
|
||||
@@ -40,7 +40,6 @@ export const MarginRevealer = ({
|
||||
child.css = `margin-top: -${child.get_allocated_height()}px;`;
|
||||
},
|
||||
'toggle': () => {
|
||||
console.log('toggle');
|
||||
if (widget.attribute.revealChild) widget.attribute.hide();
|
||||
else widget.attribute.show();
|
||||
},
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
||||
const { Gtk } = imports.gi;
|
||||
const Lang = imports.lang;
|
||||
import Cairo from 'gi://cairo?version=1.0';
|
||||
|
||||
export const dummyRegion = new Cairo.Region();
|
||||
export const enableClickthrough = (self) => self.input_shape_combine_region(dummyRegion);
|
||||
|
||||
export const RoundedCorner = (place, props) => Widget.DrawingArea({
|
||||
...props,
|
||||
|
||||
@@ -331,6 +331,11 @@ $bar_subgroup_bg: $surfaceVariant;
|
||||
background-color: $onSurfaceVariant;
|
||||
}
|
||||
|
||||
.bar-corner-spacing {
|
||||
min-width: $rounding_large;
|
||||
min-height: $rounding_large;
|
||||
}
|
||||
|
||||
.corner {
|
||||
background-color: $t_background;
|
||||
@include large-rounding;
|
||||
|
||||
@@ -823,6 +823,10 @@ tooltip {
|
||||
min-height: 0.409rem;
|
||||
background-color: #bfc8ca; }
|
||||
|
||||
.bar-corner-spacing {
|
||||
min-width: 1.705rem;
|
||||
min-height: 1.705rem; }
|
||||
|
||||
.corner {
|
||||
background-color: #0b0f10;
|
||||
border-radius: 1.705rem;
|
||||
@@ -2276,6 +2280,20 @@ tooltip {
|
||||
.notif-action-critical:active {
|
||||
background-color: #566e73; }
|
||||
|
||||
@keyframes flyin-top {
|
||||
from {
|
||||
margin-top: -2.795rem; }
|
||||
to {
|
||||
margin-top: 0rem; } }
|
||||
|
||||
@keyframes flyin-bottom {
|
||||
from {
|
||||
margin-top: 4.841rem;
|
||||
margin-bottom: -4.841rem; }
|
||||
to {
|
||||
margin-bottom: 0rem;
|
||||
margin-top: 0rem; } }
|
||||
|
||||
.osd-music {
|
||||
transition: 300ms cubic-bezier(0.1, 1, 0, 1);
|
||||
box-shadow: 0px 2px 3px rgba(0, 0, 0, 0.45);
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
const { Gtk } = imports.gi;
|
||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
||||
|
||||
import { ModuleLeftSpace } from "./leftspace.js";
|
||||
import { ModuleRightSpace } from "./rightspace.js";
|
||||
import ModuleSpaceLeft from "./spaceleft.js";
|
||||
import ModuleSpaceRight from "./spaceright.js";
|
||||
import { ModuleMusic } from "./music.js";
|
||||
import { ModuleSystem } from "./system.js";
|
||||
import { RoundedCorner, dummyRegion, enableClickthrough } from "../../lib/roundedcorner.js";
|
||||
const OptionalWorkspaces = async () => {
|
||||
try {
|
||||
return (await import('./workspaces_hyprland.js')).default();
|
||||
@@ -13,42 +14,64 @@ const OptionalWorkspaces = async () => {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
const optionalWorkspacesInstance = await OptionalWorkspaces();
|
||||
|
||||
const left = Widget.Box({
|
||||
className: 'bar-sidemodule',
|
||||
children: [ ModuleMusic()],
|
||||
});
|
||||
export const Bar = (monitor = 0) => {
|
||||
const left = Widget.Box({
|
||||
className: 'bar-sidemodule',
|
||||
children: [ModuleMusic()],
|
||||
});
|
||||
|
||||
const center = Widget.Box({
|
||||
children: [await OptionalWorkspaces()],
|
||||
});
|
||||
const center = Widget.Box({
|
||||
children: [optionalWorkspacesInstance],
|
||||
});
|
||||
|
||||
const right = Widget.Box({
|
||||
className: 'bar-sidemodule',
|
||||
children: [ModuleSystem()],
|
||||
});
|
||||
|
||||
export default () => Widget.Window({
|
||||
name: 'bar',
|
||||
anchor: ['top', 'left', 'right'],
|
||||
exclusivity: 'exclusive',
|
||||
visible: true,
|
||||
child: Widget.CenterBox({
|
||||
className: 'bar-bg',
|
||||
startWidget: ModuleLeftSpace(),
|
||||
centerWidget: Widget.Box({
|
||||
className: 'spacing-h-4',
|
||||
children: [
|
||||
left,
|
||||
center,
|
||||
right,
|
||||
]
|
||||
const right = Widget.Box({
|
||||
className: 'bar-sidemodule',
|
||||
children: [ModuleSystem()],
|
||||
});
|
||||
return Widget.Window({
|
||||
monitor,
|
||||
name: `bar${monitor}`,
|
||||
anchor: ['top', 'left', 'right'],
|
||||
exclusivity: 'exclusive',
|
||||
visible: true,
|
||||
child: Widget.CenterBox({
|
||||
className: 'bar-bg',
|
||||
startWidget: ModuleSpaceLeft(),
|
||||
endWidget: ModuleSpaceRight(),
|
||||
centerWidget: Widget.Box({
|
||||
className: 'spacing-h-4',
|
||||
children: [
|
||||
left,
|
||||
center,
|
||||
right,
|
||||
]
|
||||
}),
|
||||
setup: (self) => {
|
||||
const styleContext = self.get_style_context();
|
||||
const minHeight = styleContext.get_property('min-height', Gtk.StateFlags.NORMAL);
|
||||
// execAsync(['bash', '-c', `hyprctl keyword monitor ,addreserved,${minHeight},0,0,0`]).catch(print);
|
||||
}
|
||||
}),
|
||||
endWidget: ModuleRightSpace(),
|
||||
setup: (self) => {
|
||||
const styleContext = self.get_style_context();
|
||||
const minHeight = styleContext.get_property('min-height', Gtk.StateFlags.NORMAL);
|
||||
// execAsync(['bash', '-c', `hyprctl keyword monitor ,addreserved,${minHeight},0,0,0`]).catch(print);
|
||||
}
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
export const BarCornerTopleft = (id = '') => Widget.Window({
|
||||
name: `barcornertl${id}`,
|
||||
layer: 'top',
|
||||
anchor: ['top', 'left'],
|
||||
exclusivity: 'normal',
|
||||
visible: true,
|
||||
child: RoundedCorner('topleft', { className: 'corner', }),
|
||||
setup: enableClickthrough,
|
||||
});
|
||||
export const BarCornerTopright = (id = '') => Widget.Window({
|
||||
name: `barcornertr${id}`,
|
||||
layer: 'top',
|
||||
anchor: ['top', 'right'],
|
||||
exclusivity: 'normal',
|
||||
visible: true,
|
||||
child: RoundedCorner('topright', { className: 'corner', }),
|
||||
setup: enableClickthrough,
|
||||
});
|
||||
@@ -6,10 +6,12 @@ import { AnimatedCircProg } from "../../lib/animatedcircularprogress.js";
|
||||
import { showMusicControls } from '../../variables.js';
|
||||
|
||||
function trimTrackTitle(title) {
|
||||
var cleanedTitle = title;
|
||||
cleanedTitle = cleanedTitle.replace(/【[^】]*】/, ''); // Remove stuff like【C93】 at beginning
|
||||
cleanedTitle = cleanedTitle.replace(/\[FREE DOWNLOAD\]/g, ''); // Remove F-777's [FREE DOWNLOAD]
|
||||
return cleanedTitle.trim();
|
||||
cleanRegexes = [
|
||||
/【[^】]*】/, // Touhou n weeb stuff
|
||||
/\[FREE DOWNLOAD\]/, // F-777
|
||||
];
|
||||
cleanRegexes.forEach((expr) => cleanedTitle.replace(expr, ''));
|
||||
return title;
|
||||
}
|
||||
|
||||
const TrackProgress = () => {
|
||||
@@ -38,61 +40,66 @@ const moveToRelativeWorkspace = async (self, num) => {
|
||||
}
|
||||
}
|
||||
|
||||
export const ModuleMusic = () => Widget.EventBox({ // TODO: use cairo to make button bounce smaller on click
|
||||
onScrollUp: (self) => moveToRelativeWorkspace(self, -1),
|
||||
onScrollDown: (self) => moveToRelativeWorkspace(self, +1),
|
||||
onPrimaryClickRelease: () => showMusicControls.setValue(!showMusicControls.value),
|
||||
onSecondaryClickRelease: () => execAsync(['bash', '-c', 'playerctl next || playerctl position `bc <<< "100 * $(playerctl metadata mpris:length) / 1000000 / 100"` &']),
|
||||
onMiddleClickRelease: () => execAsync('playerctl play-pause').catch(print),
|
||||
child: Widget.Box({
|
||||
className: 'bar-group-margin bar-sides',
|
||||
children: [
|
||||
Widget.Box({
|
||||
className: 'bar-group bar-group-standalone bar-group-pad-music spacing-h-10',
|
||||
children: [
|
||||
Widget.Box({ // Wrap a box cuz overlay can't have margins itself
|
||||
homogeneous: true,
|
||||
children: [Widget.Overlay({
|
||||
child: Widget.Box({
|
||||
vpack: 'center',
|
||||
className: 'bar-music-playstate',
|
||||
homogeneous: true,
|
||||
children: [Widget.Label({
|
||||
vpack: 'center',
|
||||
className: 'bar-music-playstate-txt',
|
||||
justification: 'center',
|
||||
setup: (self) => self.hook(Mpris, label => {
|
||||
const mpris = Mpris.getPlayer('');
|
||||
label.label = `${mpris !== null && mpris.playBackStatus == 'Playing' ? 'pause' : 'play_arrow'}`;
|
||||
}),
|
||||
})],
|
||||
setup: (self) => self.hook(Mpris, label => {
|
||||
const mpris = Mpris.getPlayer('');
|
||||
if (!mpris) return;
|
||||
label.toggleClassName('bar-music-playstate-playing', mpris !== null && mpris.playBackStatus == 'Playing');
|
||||
label.toggleClassName('bar-music-playstate', mpris !== null || mpris.playBackStatus == 'Paused');
|
||||
}),
|
||||
}),
|
||||
overlays: [
|
||||
TrackProgress(),
|
||||
]
|
||||
})]
|
||||
export const ModuleMusic = () => {
|
||||
// TODO: use cairo to make button bounce smaller on click, if that's possible
|
||||
const playingState = Widget.Box({ // Wrap a box cuz overlay can't have margins itself
|
||||
homogeneous: true,
|
||||
children: [Widget.Overlay({
|
||||
child: Widget.Box({
|
||||
vpack: 'center',
|
||||
className: 'bar-music-playstate',
|
||||
homogeneous: true,
|
||||
children: [Widget.Label({
|
||||
vpack: 'center',
|
||||
className: 'bar-music-playstate-txt',
|
||||
justification: 'center',
|
||||
setup: (self) => self.hook(Mpris, label => {
|
||||
const mpris = Mpris.getPlayer('');
|
||||
label.label = `${mpris !== null && mpris.playBackStatus == 'Playing' ? 'pause' : 'play_arrow'}`;
|
||||
}),
|
||||
Widget.Scrollable({
|
||||
hexpand: true,
|
||||
child: Widget.Label({
|
||||
className: 'txt-smallie txt-onSurfaceVariant',
|
||||
setup: (self) => self.hook(Mpris, label => {
|
||||
const mpris = Mpris.getPlayer('');
|
||||
if (mpris)
|
||||
label.label = `${trimTrackTitle(mpris.trackTitle)} • ${mpris.trackArtists.join(', ')}`;
|
||||
else
|
||||
label.label = 'No media';
|
||||
}),
|
||||
})
|
||||
})
|
||||
]
|
||||
})
|
||||
]
|
||||
})],
|
||||
setup: (self) => self.hook(Mpris, label => {
|
||||
const mpris = Mpris.getPlayer('');
|
||||
if (!mpris) return;
|
||||
label.toggleClassName('bar-music-playstate-playing', mpris !== null && mpris.playBackStatus == 'Playing');
|
||||
label.toggleClassName('bar-music-playstate', mpris !== null || mpris.playBackStatus == 'Paused');
|
||||
}),
|
||||
}),
|
||||
overlays: [
|
||||
TrackProgress(),
|
||||
]
|
||||
})]
|
||||
});
|
||||
const trackTitle = Widget.Scrollable({
|
||||
hexpand: true,
|
||||
child: Widget.Label({
|
||||
className: 'txt-smallie txt-onSurfaceVariant',
|
||||
setup: (self) => self.hook(Mpris, label => {
|
||||
const mpris = Mpris.getPlayer('');
|
||||
if (mpris)
|
||||
label.label = `${trimTrackTitle(mpris.trackTitle)} • ${mpris.trackArtists.join(', ')}`;
|
||||
else
|
||||
label.label = 'No media';
|
||||
}),
|
||||
})
|
||||
})
|
||||
});
|
||||
return Widget.EventBox({
|
||||
onScrollUp: (self) => moveToRelativeWorkspace(self, -1),
|
||||
onScrollDown: (self) => moveToRelativeWorkspace(self, +1),
|
||||
onPrimaryClickRelease: () => showMusicControls.setValue(!showMusicControls.value),
|
||||
onSecondaryClickRelease: () => execAsync(['bash', '-c', 'playerctl next || playerctl position `bc <<< "100 * $(playerctl metadata mpris:length) / 1000000 / 100"` &']),
|
||||
onMiddleClickRelease: () => execAsync('playerctl play-pause').catch(print),
|
||||
child: Widget.Box({
|
||||
className: 'bar-group-margin bar-sides',
|
||||
children: [
|
||||
Widget.Box({
|
||||
className: 'bar-group bar-group-standalone bar-group-pad-music spacing-h-10',
|
||||
children: [
|
||||
playingState,
|
||||
trackTitle,
|
||||
]
|
||||
})
|
||||
]
|
||||
})
|
||||
});
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
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 Audio from 'resource:///com/github/Aylur/ags/service/audio.js';
|
||||
import SystemTray from 'resource:///com/github/Aylur/ags/service/systemtray.js';
|
||||
const { execAsync } = Utils;
|
||||
import Indicator from '../../services/indicator.js';
|
||||
import { StatusIcons } from "../../lib/statusicons.js";
|
||||
import { RoundedCorner } from "../../lib/roundedcorner.js";
|
||||
import { Tray } from "./tray.js";
|
||||
|
||||
export const ModuleRightSpace = () => {
|
||||
const barTray = Tray();
|
||||
const barStatusIcons = StatusIcons({
|
||||
className: 'bar-statusicons',
|
||||
setup: (self) => self.hook(App, (self, currentName, visible) => {
|
||||
if (currentName === 'sideright') {
|
||||
self.toggleClassName('bar-statusicons-active', visible);
|
||||
}
|
||||
}),
|
||||
});
|
||||
|
||||
return Widget.EventBox({
|
||||
onScrollUp: () => {
|
||||
if (!Audio.speaker) return;
|
||||
Audio.speaker.volume += 0.03;
|
||||
Indicator.popup(1);
|
||||
},
|
||||
onScrollDown: () => {
|
||||
if (!Audio.speaker) return;
|
||||
Audio.speaker.volume -= 0.03;
|
||||
Indicator.popup(1);
|
||||
},
|
||||
onHover: () => { barStatusIcons.toggleClassName('bar-statusicons-hover', true) },
|
||||
onHoverLost: () => { barStatusIcons.toggleClassName('bar-statusicons-hover', false) },
|
||||
onPrimaryClick: () => App.toggleWindow('sideright'),
|
||||
onSecondaryClickRelease: () => execAsync(['bash', '-c', 'playerctl next || playerctl position `bc <<< "100 * $(playerctl metadata mpris:length) / 1000000 / 100"` &']).catch(print),
|
||||
onMiddleClickRelease: () => execAsync('playerctl play-pause').catch(print),
|
||||
child: Widget.Box({
|
||||
homogeneous: false,
|
||||
children: [
|
||||
Widget.Box({
|
||||
hexpand: true,
|
||||
className: 'spacing-h-5 txt',
|
||||
children: [
|
||||
Widget.Box({
|
||||
hexpand: true,
|
||||
className: 'spacing-h-5 txt',
|
||||
children: [
|
||||
Widget.Box({ hexpand: true, }),
|
||||
barTray,
|
||||
Widget.Revealer({
|
||||
transition: 'slide_left',
|
||||
revealChild: false,
|
||||
attribute: {
|
||||
'count': 0,
|
||||
'update': (self, diff) => {
|
||||
self.attribute.count += diff;
|
||||
self.revealChild = (self.attribute.count > 0);
|
||||
}
|
||||
},
|
||||
child: Widget.Box({
|
||||
vpack: 'center',
|
||||
className: 'separator-circle',
|
||||
}),
|
||||
setup: (self) => self
|
||||
.hook(SystemTray, (self) => self.attribute.update(self, 1), 'added')
|
||||
.hook(SystemTray, (self) => self.attribute.update(self, -1), 'removed')
|
||||
,
|
||||
}),
|
||||
barStatusIcons,
|
||||
],
|
||||
}),
|
||||
]
|
||||
}),
|
||||
RoundedCorner('topright', { className: 'corner-black' })
|
||||
]
|
||||
})
|
||||
});
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
import App from 'resource:///com/github/Aylur/ags/app.js';
|
||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
||||
import { RoundedCorner } from "../../lib/roundedcorner.js";
|
||||
import Brightness from '../../services/brightness.js';
|
||||
import Indicator from '../../services/indicator.js';
|
||||
|
||||
@@ -37,7 +36,7 @@ const WindowTitle = async () => {
|
||||
|
||||
const OptionalWindowTitleInstance = await WindowTitle();
|
||||
|
||||
export const ModuleLeftSpace = () => Widget.EventBox({
|
||||
export default () => Widget.EventBox({
|
||||
onScrollUp: () => {
|
||||
Indicator.popup(1); // Since the brightness and speaker are both on the same window
|
||||
Brightness.screen_value += 0.05;
|
||||
@@ -52,7 +51,7 @@ export const ModuleLeftSpace = () => Widget.EventBox({
|
||||
child: Widget.Box({
|
||||
homogeneous: false,
|
||||
children: [
|
||||
RoundedCorner('topleft', { className: 'corner-black' }),
|
||||
Widget.Box({ className: 'bar-corner-spacing' }),
|
||||
Widget.Overlay({
|
||||
overlays: [
|
||||
Widget.Box({ hexpand: true }),
|
||||
@@ -0,0 +1,82 @@
|
||||
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 Audio from 'resource:///com/github/Aylur/ags/service/audio.js';
|
||||
import SystemTray from 'resource:///com/github/Aylur/ags/service/systemtray.js';
|
||||
const { execAsync } = Utils;
|
||||
import Indicator from '../../services/indicator.js';
|
||||
import { StatusIcons } from "../../lib/statusicons.js";
|
||||
import { Tray } from "./tray.js";
|
||||
|
||||
export default () => {
|
||||
const barTray = Tray();
|
||||
const notifCounter = Widget.Revealer({
|
||||
transition: 'slide_left',
|
||||
revealChild: false,
|
||||
attribute: {
|
||||
'count': 0,
|
||||
'update': (self, diff) => {
|
||||
self.attribute.count += diff;
|
||||
self.revealChild = (self.attribute.count > 0);
|
||||
}
|
||||
},
|
||||
child: Widget.Box({
|
||||
vpack: 'center',
|
||||
className: 'separator-circle',
|
||||
}),
|
||||
setup: (self) => self
|
||||
.hook(SystemTray, (self) => self.attribute.update(self, 1), 'added')
|
||||
.hook(SystemTray, (self) => self.attribute.update(self, -1), 'removed')
|
||||
,
|
||||
});
|
||||
const barStatusIcons = StatusIcons({
|
||||
className: 'bar-statusicons',
|
||||
setup: (self) => self.hook(App, (self, currentName, visible) => {
|
||||
if (currentName === 'sideright') {
|
||||
self.toggleClassName('bar-statusicons-active', visible);
|
||||
}
|
||||
}),
|
||||
});
|
||||
const actualContent = Widget.Box({
|
||||
hexpand: true,
|
||||
className: 'spacing-h-5 txt',
|
||||
children: [
|
||||
Widget.Box({
|
||||
hexpand: true,
|
||||
className: 'spacing-h-5 txt',
|
||||
children: [
|
||||
Widget.Box({ hexpand: true, }),
|
||||
barTray,
|
||||
notifCounter,
|
||||
barStatusIcons,
|
||||
],
|
||||
}),
|
||||
]
|
||||
});
|
||||
|
||||
return Widget.EventBox({
|
||||
onScrollUp: () => {
|
||||
if (!Audio.speaker) return;
|
||||
Audio.speaker.volume += 0.03;
|
||||
Indicator.popup(1);
|
||||
},
|
||||
onScrollDown: () => {
|
||||
if (!Audio.speaker) return;
|
||||
Audio.speaker.volume -= 0.03;
|
||||
Indicator.popup(1);
|
||||
},
|
||||
onHover: () => { barStatusIcons.toggleClassName('bar-statusicons-hover', true) },
|
||||
onHoverLost: () => { barStatusIcons.toggleClassName('bar-statusicons-hover', false) },
|
||||
onPrimaryClick: () => App.toggleWindow('sideright'),
|
||||
onSecondaryClickRelease: () => execAsync(['bash', '-c', 'playerctl next || playerctl position `bc <<< "100 * $(playerctl metadata mpris:length) / 1000000 / 100"` &']).catch(print),
|
||||
onMiddleClickRelease: () => execAsync('playerctl play-pause').catch(print),
|
||||
child: Widget.Box({
|
||||
homogeneous: false,
|
||||
children: [
|
||||
actualContent,
|
||||
Widget.Box({ className: 'bar-corner-spacing' }),
|
||||
]
|
||||
})
|
||||
});
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
const { Gdk, Gtk } = imports.gi;
|
||||
const Lang = imports.lang;
|
||||
import { App, Service, Utils, Widget, SCREEN_HEIGHT, SCREEN_WIDTH } from '../../imports.js';
|
||||
const { execAsync, exec } = Utils;
|
||||
const { Box, Label } = Widget;
|
||||
|
||||
const NUM_OF_VERTICES = 30;
|
||||
const NUM_OF_EDGES = 29;
|
||||
// Vertices
|
||||
var vertices = [];
|
||||
for (var i = 0; i < NUM_OF_VERTICES; i++) {
|
||||
vertices.push([
|
||||
Math.floor(Math.random() * SCREEN_WIDTH),
|
||||
Math.floor(Math.random() * SCREEN_HEIGHT)
|
||||
]);
|
||||
}
|
||||
// Edges
|
||||
function generateRandomEdges(numVertices, numEdges) { // TODO: make sure whole graph is connected
|
||||
var edges = new Set();
|
||||
var vertices = [];
|
||||
|
||||
// Generate vertices
|
||||
for (var i = 0; i < numVertices; i++) {
|
||||
vertices.push(i);
|
||||
}
|
||||
|
||||
// Generate random distinct edges
|
||||
while (edges.size < numEdges) {
|
||||
var randomVertex1 = vertices[Math.floor(Math.random() * numVertices)];
|
||||
var randomVertex2 = vertices[Math.floor(Math.random() * numVertices)];
|
||||
|
||||
// Ensure the two vertices are distinct and the edge doesn't already exist
|
||||
if (randomVertex1 !== randomVertex2) {
|
||||
var edge = [randomVertex1, randomVertex2].sort();
|
||||
edges.add(edge.join(','));
|
||||
}
|
||||
}
|
||||
|
||||
return Array.from(edges).map(edge => edge.split(',').map(Number));
|
||||
}
|
||||
|
||||
var edges = generateRandomEdges(NUM_OF_VERTICES, NUM_OF_EDGES);
|
||||
|
||||
export default () => Box({
|
||||
hpack: 'fill',
|
||||
vpack: 'fill',
|
||||
homogeneous: true,
|
||||
children: [
|
||||
Widget.DrawingArea({
|
||||
className: 'bg-graph',
|
||||
setup: (area) => {
|
||||
area.connect('draw', Lang.bind(area, (area, cr) => {
|
||||
// area.set_size_request(SCREEN_WIDTH, SCREEN_HEIGHT);
|
||||
// console.log('allocated width/height:', area.get_allocated_width(), '/', area.get_allocated_height())
|
||||
const styleContext = area.get_style_context();
|
||||
const color = styleContext.get_property('color', Gtk.StateFlags.NORMAL);
|
||||
const backgroundColor = styleContext.get_property('background-color', Gtk.StateFlags.NORMAL);
|
||||
const radius = area.get_style_context().get_property('border-radius', Gtk.StateFlags.NORMAL);
|
||||
const borderWidth = area.get_style_context().get_border(Gtk.StateFlags.NORMAL).left; // ur going to write border-width: something anyway
|
||||
|
||||
cr.setSourceRGBA(backgroundColor.red, backgroundColor.green, backgroundColor.blue, backgroundColor.alpha);
|
||||
cr.rectangle(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT)
|
||||
cr.fill();
|
||||
cr.setSourceRGBA(color.red, color.green, color.blue, color.alpha);
|
||||
// Draw edges
|
||||
cr.setLineWidth(borderWidth);
|
||||
console.log("line width:", borderWidth);
|
||||
for (var i = 0; i < NUM_OF_EDGES; i++) {
|
||||
console.log(vertices[edges[i][0]][0], vertices[edges[i][0]][1], '->', vertices[edges[i][1]][0], vertices[edges[i][1]][1])
|
||||
cr.moveTo(vertices[edges[i][0]][0], vertices[edges[i][0]][1]);
|
||||
cr.lineTo(vertices[edges[i][1]][0], vertices[edges[i][1]][1]);
|
||||
cr.stroke();
|
||||
}
|
||||
// Draw vertices
|
||||
for (var i = 0; i < NUM_OF_VERTICES; i++) {
|
||||
cr.arc(vertices[i][0], vertices[i][1], radius, 0, 2 * Math.PI)
|
||||
cr.fill()
|
||||
}
|
||||
}))
|
||||
}
|
||||
})
|
||||
]
|
||||
})
|
||||
@@ -1,11 +1,7 @@
|
||||
const { Gdk, Gtk } = imports.gi;
|
||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
||||
import * as Utils from 'resource:///com/github/Aylur/ags/utils.js';
|
||||
const { execAsync, exec } = Utils;
|
||||
|
||||
import TimeAndLaunchesWidget from './timeandlaunches.js'
|
||||
import SystemWidget from './system.js'
|
||||
import GraphWidget from './graph.js'
|
||||
|
||||
export default () => Widget.Window({
|
||||
name: 'desktopbackground',
|
||||
@@ -19,7 +15,6 @@ export default () => Widget.Window({
|
||||
vexpand: true,
|
||||
}),
|
||||
overlays: [
|
||||
// GraphWidget(),
|
||||
TimeAndLaunchesWidget(),
|
||||
SystemWidget(),
|
||||
],
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// 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, Revealer } = Widget;
|
||||
const { Box, Label, ProgressBar } = Widget;
|
||||
import { MarginRevealer } from '../../lib/advancedwidgets.js';
|
||||
import Brightness from '../../services/brightness.js';
|
||||
import Indicator from '../../services/indicator.js';
|
||||
@@ -12,7 +12,7 @@ const OsdValue = (name, labelSetup, progressSetup, props = {}) => {
|
||||
className: 'osd-label',
|
||||
label: `${name}`,
|
||||
});
|
||||
const valueNumber =Label({
|
||||
const valueNumber = Label({
|
||||
hexpand: false, className: 'osd-value-txt',
|
||||
setup: labelSetup,
|
||||
});
|
||||
@@ -44,51 +44,44 @@ const OsdValue = (name, labelSetup, progressSetup, props = {}) => {
|
||||
});
|
||||
}
|
||||
|
||||
const brightnessIndicator = OsdValue('Brightness',
|
||||
(self) => self
|
||||
.hook(Brightness, self => {
|
||||
export default () => {
|
||||
const brightnessIndicator = OsdValue('Brightness',
|
||||
(self) => self.hook(Brightness, self => {
|
||||
self.label = `${Math.round(Brightness.screen_value * 100)}`;
|
||||
}, 'notify::screen-value')
|
||||
,
|
||||
(self) => self
|
||||
.hook(Brightness, (progress) => {
|
||||
}, 'notify::screen-value'),
|
||||
(self) => self.hook(Brightness, (progress) => {
|
||||
const updateValue = Brightness.screen_value;
|
||||
progress.value = updateValue;
|
||||
}, 'notify::screen-value')
|
||||
,
|
||||
)
|
||||
}, 'notify::screen-value'),
|
||||
)
|
||||
|
||||
const volumeIndicator = OsdValue('Volume',
|
||||
(self) => self
|
||||
.hook(Audio, (label) => {
|
||||
const volumeIndicator = OsdValue('Volume',
|
||||
(self) => self.hook(Audio, (label) => {
|
||||
label.label = `${Math.round(Audio.speaker?.volume * 100)}`;
|
||||
})
|
||||
,
|
||||
(self) => self
|
||||
.hook(Audio, (progress) => {
|
||||
}),
|
||||
(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,
|
||||
]
|
||||
})
|
||||
,
|
||||
);
|
||||
|
||||
export default () => 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,
|
||||
]
|
||||
})
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import MusicControls from './musiccontrols.js';
|
||||
import ColorScheme from './colorscheme.js';
|
||||
import NotificationPopups from './notificationpopups.js';
|
||||
|
||||
export default (monitor) => Widget.Window({
|
||||
export default (monitor = 0) => Widget.Window({
|
||||
name: `indicator${monitor}`,
|
||||
monitor,
|
||||
className: 'indicator',
|
||||
|
||||
@@ -32,17 +32,13 @@ function isRealPlayer(player) {
|
||||
);
|
||||
}
|
||||
|
||||
export const getPlayer = (name = PREFERRED_PLAYER) => {
|
||||
return Mpris.getPlayer(name) || Mpris.players[0] || null;
|
||||
}
|
||||
|
||||
export const getPlayer = (name = PREFERRED_PLAYER) => 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 fileExists(filePath) {
|
||||
let file = Gio.File.new_for_path(filePath);
|
||||
return file.query_exists(null);
|
||||
@@ -54,17 +50,11 @@ function detectMediaSource(link) {
|
||||
return ' Firefox'
|
||||
return " File";
|
||||
}
|
||||
// Remove protocol if present
|
||||
let url = link.replace(/(^\w+:|^)\/\//, '');
|
||||
// Extract the domain name
|
||||
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';
|
||||
if (domain == 'ytimg.com') return ' Youtube';
|
||||
if (domain == 'discordapp.net') return ' Discord';
|
||||
if (domain == 'sndcdn.com') return ' SoundCloud';
|
||||
return domain;
|
||||
}
|
||||
|
||||
@@ -72,15 +62,19 @@ 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
|
||||
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) {
|
||||
var cleanedTitle = title;
|
||||
cleanedTitle = cleanedTitle.replace(/【[^】]*】/, ''); // Remove stuff like【C93】 at beginning
|
||||
cleanedTitle = cleanedTitle.replace(/\[FREE DOWNLOAD\]/g, ''); // Remove F-777's [FREE DOWNLOAD]
|
||||
return cleanedTitle.trim();
|
||||
cleanRegexes = [
|
||||
/【[^】]*】/, // Touhou n weeb stuff
|
||||
/\[FREE DOWNLOAD\]/, // F-777
|
||||
];
|
||||
cleanRegexes.forEach((expr) => cleanedTitle.replace(expr, ''));
|
||||
return title;
|
||||
}
|
||||
|
||||
const TrackProgress = ({ player, ...rest }) => {
|
||||
|
||||
@@ -18,7 +18,7 @@ execAsync(`ydotoold`).catch(print); // Start ydotool daemon
|
||||
function releaseAllKeys() {
|
||||
const keycodes = Array.from(Array(249).keys());
|
||||
execAsync([`ydotool`, `key`, ...keycodes.map(keycode => `${keycode}:0`)])
|
||||
.then(console.log('Released all keys'))
|
||||
.then(console.log('[OSK] Released all keys'))
|
||||
.catch(print);
|
||||
}
|
||||
var modsPressed = false;
|
||||
|
||||
@@ -6,7 +6,6 @@ function moveClientToWorkspace(address, workspace) {
|
||||
}
|
||||
|
||||
export function dumpToWorkspace(from, to) {
|
||||
console.log('dump', from, to);
|
||||
if (from == to) return;
|
||||
Hyprland.clients.forEach(client => {
|
||||
if (client.workspace.id == from) {
|
||||
|
||||
@@ -53,7 +53,7 @@ export function launchCustomCommand(command) {
|
||||
execAsync([`bash`, `-c`, `systemctl suspend`]).catch(print);
|
||||
}
|
||||
else if (args[0] == '>logout') { // Log out
|
||||
execAsync([`bash`, `-c`, `killall Hyprland`]).catch(print);
|
||||
execAsync([`bash`, `-c`, `loginctl terminate-user $USER`]).catch(print);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -166,7 +166,7 @@ export const SearchAndWindows = () => {
|
||||
_appSearchResults = Applications.query(text);
|
||||
|
||||
// Calculate
|
||||
if (startsWithNumber(text)) { // Eval on typing is dangerous, this is a small workaround.
|
||||
if (startsWithNumber(text)) { // Eval on typing is dangerous; this is a small workaround.
|
||||
try {
|
||||
const fullResult = eval(text);
|
||||
resultsBox.add(CalculationResultButton({ result: fullResult, text: text }));
|
||||
|
||||
@@ -1,43 +1,16 @@
|
||||
import Cairo from 'gi://cairo?version=1.0';
|
||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
||||
import { RoundedCorner } from "../../lib/roundedcorner.js";
|
||||
import { RoundedCorner, dummyRegion, enableClickthrough } from "../../lib/roundedcorner.js";
|
||||
|
||||
const dummyRegion = new Cairo.Region();
|
||||
const enableClickthrough = (self) => self.input_shape_combine_region(dummyRegion);
|
||||
|
||||
export const CornerTopleft = () => Widget.Window({
|
||||
name: 'cornertl',
|
||||
layer: 'top',
|
||||
anchor: ['top', 'left'],
|
||||
exclusivity: 'normal',
|
||||
visible: true,
|
||||
child: RoundedCorner('topleft', { className: 'corner', }),
|
||||
setup: enableClickthrough,
|
||||
});
|
||||
export const CornerTopright = () => Widget.Window({
|
||||
name: 'cornertr',
|
||||
layer: 'top',
|
||||
anchor: ['top', 'right'],
|
||||
exclusivity: 'normal',
|
||||
visible: true,
|
||||
child: RoundedCorner('topright', { className: 'corner', }),
|
||||
setup: enableClickthrough,
|
||||
});
|
||||
export const CornerBottomleft = () => Widget.Window({
|
||||
name: 'cornerbl',
|
||||
layer: 'top',
|
||||
anchor: ['bottom', 'left'],
|
||||
exclusivity: 'ignore',
|
||||
visible: true,
|
||||
child: RoundedCorner('bottomleft', { className: 'corner-black', }),
|
||||
setup: enableClickthrough,
|
||||
});
|
||||
export const CornerBottomright = () => Widget.Window({
|
||||
name: 'cornerbr',
|
||||
layer: 'top',
|
||||
anchor: ['bottom', 'right'],
|
||||
exclusivity: 'ignore',
|
||||
visible: true,
|
||||
child: RoundedCorner('bottomright', { className: 'corner-black', }),
|
||||
setup: enableClickthrough,
|
||||
});
|
||||
export default (monitor = 0, where = 'bottom left') => {
|
||||
const positionString = where.replace(/\s/, ""); // remove space
|
||||
return Widget.Window({
|
||||
monitor,
|
||||
name: `corner${positionString}${monitor}`,
|
||||
layer: 'overlay',
|
||||
anchor: where.split(' '),
|
||||
exclusivity: 'ignore',
|
||||
visible: true,
|
||||
child: RoundedCorner(positionString, { className: 'corner-black', }),
|
||||
setup: enableClickthrough,
|
||||
});
|
||||
}
|
||||
@@ -63,7 +63,7 @@ export default () => {
|
||||
// lock, logout, sleep
|
||||
// const lockButton = SessionButton('Lock', 'lock', () => { App.closeWindow('session'); execAsync('gtklock') });
|
||||
const lockButton = SessionButton('Lock', 'lock', () => { App.closeWindow('session'); execAsync('swaylock') });
|
||||
const logoutButton = SessionButton('Logout', 'logout', () => { App.closeWindow('session'); execAsync(['bash', '-c', 'killall Hyprland']) });
|
||||
const logoutButton = SessionButton('Logout', 'logout', () => { App.closeWindow('session'); execAsync(['bash', '-c', 'loginctl terminate-user $USER']) });
|
||||
const sleepButton = SessionButton('Sleep', 'sleep', () => { App.closeWindow('session'); execAsync('systemctl suspend') });
|
||||
// hibernate, shutdown, reboot
|
||||
const hibernateButton = SessionButton('Hibernate', 'downloading', () => { App.closeWindow('session'); execAsync('systemctl hibernate') });
|
||||
|
||||
@@ -11,7 +11,6 @@ import { MaterialIcon } from '../../lib/materialicon.js';
|
||||
|
||||
function expandTilde(path) {
|
||||
if (path.startsWith('~')) {
|
||||
console.log(GLib.get_home_dir() + path.slice(1));
|
||||
return GLib.get_home_dir() + path.slice(1);
|
||||
} else {
|
||||
return path;
|
||||
@@ -120,8 +119,6 @@ export const ModuleInvertColors = async (props = {}) => {
|
||||
Hyprland.sendMessage('j/getoption decoration:screen_shader')
|
||||
.then((output) => {
|
||||
const shaderPath = JSON.parse(output)["str"].trim();
|
||||
console.log(output)
|
||||
console.log(shaderPath)
|
||||
if (shaderPath != "[[EMPTY]]" && shaderPath != "") {
|
||||
execAsync(['bash', '-c', `hyprctl keyword decoration:screen_shader '[[EMPTY]]'`]).catch(print);
|
||||
button.toggleClassName('sidebar-button-active', false);
|
||||
|
||||
Reference in New Issue
Block a user