should add multimonitor support

This commit is contained in:
end-4
2024-01-17 18:23:35 +07:00
parent 94a5603cd2
commit 29d109770d
23 changed files with 341 additions and 410 deletions
+28 -20
View File
@@ -1,13 +1,14 @@
"use strict"; "use strict";
// Import // Import
import Gdk from 'gi://Gdk';
import App from 'resource:///com/github/Aylur/ags/app.js' import App from 'resource:///com/github/Aylur/ags/app.js'
import * as Utils from 'resource:///com/github/Aylur/ags/utils.js' import * as Utils from 'resource:///com/github/Aylur/ags/utils.js'
// Widgets // 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 Cheatsheet from './widgets/cheatsheet/main.js';
import DesktopBackground from './widgets/desktopbackground/main.js'; import DesktopBackground from './widgets/desktopbackground/main.js';
// import Dock from './widgets/dock/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 Indicator from './widgets/indicators/main.js';
import Osk from './widgets/onscreenkeyboard/main.js'; import Osk from './widgets/onscreenkeyboard/main.js';
import Overview from './widgets/overview/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 SideLeft from './widgets/sideleft/main.js';
import SideRight from './widgets/sideright/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 // SCSS compilation
Utils.exec(`bash -c 'echo "" > ${App.configDir}/scss/_musicwal.scss'`); // reset music styles Utils.exec(`bash -c 'echo "" > ${App.configDir}/scss/_musicwal.scss'`); // reset music styles
@@ -28,7 +33,25 @@ function applyStyle() {
} }
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 { export default {
css: `${App.configDir}/style.css`, css: `${App.configDir}/style.css`,
stackTraceOnError: true, stackTraceOnError: true,
@@ -37,20 +60,5 @@ export default {
'sideleft': CLOSE_ANIM_TIME, 'sideleft': CLOSE_ANIM_TIME,
'osk': CLOSE_ANIM_TIME, 'osk': CLOSE_ANIM_TIME,
}, },
windows: [ windows: Windows().flat(1),
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(),
],
}; };
+8 -9
View File
@@ -3,20 +3,19 @@ const require = async file => (await import(resource(file))).default;
const service = async file => (await require(`service/${file}`)); const service = async file => (await require(`service/${file}`));
export const App = await require('app'); export const App = await require('app');
export const Widget = await require('widget'); // export const Widget = await require('widget');
export const Service = await require('service'); // export const Service = await require('service');
export const Variable = await require('variable'); // export const Variable = await require('variable');
export const Utils = await import(resource('utils')); export const Utils = await import(resource('utils'));
// export const Applications = await service('applications');
export const Applications = await service('applications'); // export const Audio = await service('audio');
export const Audio = await service('audio'); // export const Battery = await service('battery');
export const Battery = await service('battery'); // export const Bluetooth = await service('bluetooth');
export const Bluetooth = await service('bluetooth');
// export const Hyprland = await service('hyprland'); // export const Hyprland = await service('hyprland');
export const Mpris = await service('mpris'); export const Mpris = await service('mpris');
export const Network = await service('network'); export const Network = await service('network');
export const Notifications = await service('notifications'); export const Notifications = await service('notifications');
export const SystemTray = await service('systemtray'); // export const SystemTray = await service('systemtray');
globalThis['App'] = App; ////////////////////////////// globalThis['App'] = App; //////////////////////////////
// globalThis['Widget'] = Widget; // globalThis['Widget'] = Widget;
-1
View File
@@ -40,7 +40,6 @@ export const MarginRevealer = ({
child.css = `margin-top: -${child.get_allocated_height()}px;`; child.css = `margin-top: -${child.get_allocated_height()}px;`;
}, },
'toggle': () => { 'toggle': () => {
console.log('toggle');
if (widget.attribute.revealChild) widget.attribute.hide(); if (widget.attribute.revealChild) widget.attribute.hide();
else widget.attribute.show(); else widget.attribute.show();
}, },
+4
View File
@@ -1,6 +1,10 @@
import Widget from 'resource:///com/github/Aylur/ags/widget.js'; import Widget from 'resource:///com/github/Aylur/ags/widget.js';
const { Gtk } = imports.gi; const { Gtk } = imports.gi;
const Lang = imports.lang; 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({ export const RoundedCorner = (place, props) => Widget.DrawingArea({
...props, ...props,
+5
View File
@@ -331,6 +331,11 @@ $bar_subgroup_bg: $surfaceVariant;
background-color: $onSurfaceVariant; background-color: $onSurfaceVariant;
} }
.bar-corner-spacing {
min-width: $rounding_large;
min-height: $rounding_large;
}
.corner { .corner {
background-color: $t_background; background-color: $t_background;
@include large-rounding; @include large-rounding;
+18
View File
@@ -823,6 +823,10 @@ tooltip {
min-height: 0.409rem; min-height: 0.409rem;
background-color: #bfc8ca; } background-color: #bfc8ca; }
.bar-corner-spacing {
min-width: 1.705rem;
min-height: 1.705rem; }
.corner { .corner {
background-color: #0b0f10; background-color: #0b0f10;
border-radius: 1.705rem; border-radius: 1.705rem;
@@ -2276,6 +2280,20 @@ tooltip {
.notif-action-critical:active { .notif-action-critical:active {
background-color: #566e73; } 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 { .osd-music {
transition: 300ms cubic-bezier(0.1, 1, 0, 1); transition: 300ms cubic-bezier(0.1, 1, 0, 1);
box-shadow: 0px 2px 3px rgba(0, 0, 0, 0.45); box-shadow: 0px 2px 3px rgba(0, 0, 0, 0.45);
+59 -36
View File
@@ -1,10 +1,11 @@
const { Gtk } = imports.gi; const { Gtk } = imports.gi;
import Widget from 'resource:///com/github/Aylur/ags/widget.js'; import Widget from 'resource:///com/github/Aylur/ags/widget.js';
import { ModuleLeftSpace } from "./leftspace.js"; import ModuleSpaceLeft from "./spaceleft.js";
import { ModuleRightSpace } from "./rightspace.js"; import ModuleSpaceRight from "./spaceright.js";
import { ModuleMusic } from "./music.js"; import { ModuleMusic } from "./music.js";
import { ModuleSystem } from "./system.js"; import { ModuleSystem } from "./system.js";
import { RoundedCorner, dummyRegion, enableClickthrough } from "../../lib/roundedcorner.js";
const OptionalWorkspaces = async () => { const OptionalWorkspaces = async () => {
try { try {
return (await import('./workspaces_hyprland.js')).default(); return (await import('./workspaces_hyprland.js')).default();
@@ -13,42 +14,64 @@ const OptionalWorkspaces = async () => {
return null; return null;
} }
}; };
const optionalWorkspacesInstance = await OptionalWorkspaces();
const left = Widget.Box({ export const Bar = (monitor = 0) => {
className: 'bar-sidemodule', const left = Widget.Box({
children: [ ModuleMusic()], className: 'bar-sidemodule',
}); children: [ModuleMusic()],
});
const center = Widget.Box({ const center = Widget.Box({
children: [await OptionalWorkspaces()], children: [optionalWorkspacesInstance],
}); });
const right = Widget.Box({ const right = Widget.Box({
className: 'bar-sidemodule', className: 'bar-sidemodule',
children: [ModuleSystem()], children: [ModuleSystem()],
}); });
return Widget.Window({
export default () => Widget.Window({ monitor,
name: 'bar', name: `bar${monitor}`,
anchor: ['top', 'left', 'right'], anchor: ['top', 'left', 'right'],
exclusivity: 'exclusive', exclusivity: 'exclusive',
visible: true, visible: true,
child: Widget.CenterBox({ child: Widget.CenterBox({
className: 'bar-bg', className: 'bar-bg',
startWidget: ModuleLeftSpace(), startWidget: ModuleSpaceLeft(),
centerWidget: Widget.Box({ endWidget: ModuleSpaceRight(),
className: 'spacing-h-4', centerWidget: Widget.Box({
children: [ className: 'spacing-h-4',
left, children: [
center, left,
right, 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); export const BarCornerTopleft = (id = '') => Widget.Window({
// execAsync(['bash', '-c', `hyprctl keyword monitor ,addreserved,${minHeight},0,0,0`]).catch(print); 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,
}); });
+67 -60
View File
@@ -6,10 +6,12 @@ import { AnimatedCircProg } from "../../lib/animatedcircularprogress.js";
import { showMusicControls } from '../../variables.js'; import { showMusicControls } from '../../variables.js';
function trimTrackTitle(title) { function trimTrackTitle(title) {
var cleanedTitle = title; cleanRegexes = [
cleanedTitle = cleanedTitle.replace(/【[^】]*】/, ''); // Remove stuff like【C93】 at beginning /【[^】]*】/, // Touhou n weeb stuff
cleanedTitle = cleanedTitle.replace(/\[FREE DOWNLOAD\]/g, ''); // Remove F-777's [FREE DOWNLOAD] /\[FREE DOWNLOAD\]/, // F-777
return cleanedTitle.trim(); ];
cleanRegexes.forEach((expr) => cleanedTitle.replace(expr, ''));
return title;
} }
const TrackProgress = () => { 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 export const ModuleMusic = () => {
onScrollUp: (self) => moveToRelativeWorkspace(self, -1), // TODO: use cairo to make button bounce smaller on click, if that's possible
onScrollDown: (self) => moveToRelativeWorkspace(self, +1), const playingState = Widget.Box({ // Wrap a box cuz overlay can't have margins itself
onPrimaryClickRelease: () => showMusicControls.setValue(!showMusicControls.value), homogeneous: true,
onSecondaryClickRelease: () => execAsync(['bash', '-c', 'playerctl next || playerctl position `bc <<< "100 * $(playerctl metadata mpris:length) / 1000000 / 100"` &']), children: [Widget.Overlay({
onMiddleClickRelease: () => execAsync('playerctl play-pause').catch(print), child: Widget.Box({
child: Widget.Box({ vpack: 'center',
className: 'bar-group-margin bar-sides', className: 'bar-music-playstate',
children: [ homogeneous: true,
Widget.Box({ children: [Widget.Label({
className: 'bar-group bar-group-standalone bar-group-pad-music spacing-h-10', vpack: 'center',
children: [ className: 'bar-music-playstate-txt',
Widget.Box({ // Wrap a box cuz overlay can't have margins itself justification: 'center',
homogeneous: true, setup: (self) => self.hook(Mpris, label => {
children: [Widget.Overlay({ const mpris = Mpris.getPlayer('');
child: Widget.Box({ label.label = `${mpris !== null && mpris.playBackStatus == 'Playing' ? 'pause' : 'play_arrow'}`;
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(),
]
})]
}), }),
Widget.Scrollable({ })],
hexpand: true, setup: (self) => self.hook(Mpris, label => {
child: Widget.Label({ const mpris = Mpris.getPlayer('');
className: 'txt-smallie txt-onSurfaceVariant', if (!mpris) return;
setup: (self) => self.hook(Mpris, label => { label.toggleClassName('bar-music-playstate-playing', mpris !== null && mpris.playBackStatus == 'Playing');
const mpris = Mpris.getPlayer(''); label.toggleClassName('bar-music-playstate', mpris !== null || mpris.playBackStatus == 'Paused');
if (mpris) }),
label.label = `${trimTrackTitle(mpris.trackTitle)}${mpris.trackArtists.join(', ')}`; }),
else overlays: [
label.label = 'No media'; 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,
]
})
]
})
});
}
-81
View File
@@ -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 App from 'resource:///com/github/Aylur/ags/app.js';
import Widget from 'resource:///com/github/Aylur/ags/widget.js'; import Widget from 'resource:///com/github/Aylur/ags/widget.js';
import { RoundedCorner } from "../../lib/roundedcorner.js";
import Brightness from '../../services/brightness.js'; import Brightness from '../../services/brightness.js';
import Indicator from '../../services/indicator.js'; import Indicator from '../../services/indicator.js';
@@ -37,7 +36,7 @@ const WindowTitle = async () => {
const OptionalWindowTitleInstance = await WindowTitle(); const OptionalWindowTitleInstance = await WindowTitle();
export const ModuleLeftSpace = () => Widget.EventBox({ export default () => Widget.EventBox({
onScrollUp: () => { onScrollUp: () => {
Indicator.popup(1); // Since the brightness and speaker are both on the same window Indicator.popup(1); // Since the brightness and speaker are both on the same window
Brightness.screen_value += 0.05; Brightness.screen_value += 0.05;
@@ -52,7 +51,7 @@ export const ModuleLeftSpace = () => Widget.EventBox({
child: Widget.Box({ child: Widget.Box({
homogeneous: false, homogeneous: false,
children: [ children: [
RoundedCorner('topleft', { className: 'corner-black' }), Widget.Box({ className: 'bar-corner-spacing' }),
Widget.Overlay({ Widget.Overlay({
overlays: [ overlays: [
Widget.Box({ hexpand: true }), Widget.Box({ hexpand: true }),
+82
View File
@@ -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 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 TimeAndLaunchesWidget from './timeandlaunches.js'
import SystemWidget from './system.js' import SystemWidget from './system.js'
import GraphWidget from './graph.js'
export default () => Widget.Window({ export default () => Widget.Window({
name: 'desktopbackground', name: 'desktopbackground',
@@ -19,7 +15,6 @@ export default () => Widget.Window({
vexpand: true, vexpand: true,
}), }),
overlays: [ overlays: [
// GraphWidget(),
TimeAndLaunchesWidget(), TimeAndLaunchesWidget(),
SystemWidget(), SystemWidget(),
], ],
@@ -1,7 +1,7 @@
// This file is for brightness/volume indicators // This file is for brightness/volume indicators
import Widget from 'resource:///com/github/Aylur/ags/widget.js'; import Widget from 'resource:///com/github/Aylur/ags/widget.js';
import Audio from 'resource:///com/github/Aylur/ags/service/audio.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 { MarginRevealer } from '../../lib/advancedwidgets.js';
import Brightness from '../../services/brightness.js'; import Brightness from '../../services/brightness.js';
import Indicator from '../../services/indicator.js'; import Indicator from '../../services/indicator.js';
@@ -12,7 +12,7 @@ const OsdValue = (name, labelSetup, progressSetup, props = {}) => {
className: 'osd-label', className: 'osd-label',
label: `${name}`, label: `${name}`,
}); });
const valueNumber =Label({ const valueNumber = Label({
hexpand: false, className: 'osd-value-txt', hexpand: false, className: 'osd-value-txt',
setup: labelSetup, setup: labelSetup,
}); });
@@ -44,51 +44,44 @@ const OsdValue = (name, labelSetup, progressSetup, props = {}) => {
}); });
} }
const brightnessIndicator = OsdValue('Brightness', export default () => {
(self) => self const brightnessIndicator = OsdValue('Brightness',
.hook(Brightness, self => { (self) => self.hook(Brightness, self => {
self.label = `${Math.round(Brightness.screen_value * 100)}`; self.label = `${Math.round(Brightness.screen_value * 100)}`;
}, 'notify::screen-value') }, 'notify::screen-value'),
, (self) => self.hook(Brightness, (progress) => {
(self) => self
.hook(Brightness, (progress) => {
const updateValue = Brightness.screen_value; const updateValue = Brightness.screen_value;
progress.value = updateValue; progress.value = updateValue;
}, 'notify::screen-value') }, 'notify::screen-value'),
, )
)
const volumeIndicator = OsdValue('Volume', const volumeIndicator = OsdValue('Volume',
(self) => self (self) => self.hook(Audio, (label) => {
.hook(Audio, (label) => {
label.label = `${Math.round(Audio.speaker?.volume * 100)}`; label.label = `${Math.round(Audio.speaker?.volume * 100)}`;
}) }),
, (self) => self.hook(Audio, (progress) => {
(self) => self
.hook(Audio, (progress) => {
const updateValue = Audio.speaker?.volume; const updateValue = Audio.speaker?.volume;
if (!isNaN(updateValue)) progress.value = updateValue; 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,
]
})
});
+1 -1
View File
@@ -5,7 +5,7 @@ import MusicControls from './musiccontrols.js';
import ColorScheme from './colorscheme.js'; import ColorScheme from './colorscheme.js';
import NotificationPopups from './notificationpopups.js'; import NotificationPopups from './notificationpopups.js';
export default (monitor) => Widget.Window({ export default (monitor = 0) => Widget.Window({
name: `indicator${monitor}`, name: `indicator${monitor}`,
monitor, monitor,
className: 'indicator', className: 'indicator',
+14 -20
View File
@@ -32,17 +32,13 @@ function isRealPlayer(player) {
); );
} }
export const getPlayer = (name = PREFERRED_PLAYER) => { export const getPlayer = (name = PREFERRED_PLAYER) => Mpris.getPlayer(name) || Mpris.players[0] || null;
return Mpris.getPlayer(name) || Mpris.players[0] || null;
}
function lengthStr(length) { function lengthStr(length) {
const min = Math.floor(length / 60); const min = Math.floor(length / 60);
const sec = Math.floor(length % 60); const sec = Math.floor(length % 60);
const sec0 = sec < 10 ? '0' : ''; const sec0 = sec < 10 ? '0' : '';
return `${min}:${sec0}${sec}`; return `${min}:${sec0}${sec}`;
} }
function fileExists(filePath) { function fileExists(filePath) {
let file = Gio.File.new_for_path(filePath); let file = Gio.File.new_for_path(filePath);
return file.query_exists(null); return file.query_exists(null);
@@ -54,17 +50,11 @@ function detectMediaSource(link) {
return '󰈹 Firefox' return '󰈹 Firefox'
return "󰈣 File"; return "󰈣 File";
} }
// Remove protocol if present
let url = link.replace(/(^\w+:|^)\/\//, ''); let url = link.replace(/(^\w+:|^)\/\//, '');
// Extract the domain name
let domain = url.match(/(?:[a-z]+\.)?([a-z]+\.[a-z]+)/i)[1]; let domain = url.match(/(?:[a-z]+\.)?([a-z]+\.[a-z]+)/i)[1];
if (domain == 'ytimg.com') return '󰗃 Youtube';
if (domain == 'ytimg.com') if (domain == 'discordapp.net') return '󰙯 Discord';
return '󰗃 Youtube'; if (domain == 'sndcdn.com') return '󰓀 SoundCloud';
if (domain == 'discordapp.net')
return '󰙯 Discord';
if (domain == 'sndcdn.com')
return '󰓀 SoundCloud';
return domain; return domain;
} }
@@ -72,15 +62,19 @@ const DEFAULT_MUSIC_FONT = 'Gabarito, sans-serif';
function getTrackfont(player) { function getTrackfont(player) {
const title = player.trackTitle; const title = player.trackTitle;
const artists = player.trackArtists.join(' '); const artists = player.trackArtists.join(' ');
if (artists.includes('TANO*C') || artists.includes('USAO') || artists.includes('Kobaryo')) return 'Chakra Petch'; // Rigid square replacement if (artists.includes('TANO*C') || artists.includes('USAO') || artists.includes('Kobaryo'))
if (title.includes('東方')) return 'Crimson Text, serif'; // Serif for Touhou stuff return 'Chakra Petch'; // Rigid square replacement
if (title.includes('東方'))
return 'Crimson Text, serif'; // Serif for Touhou stuff
return DEFAULT_MUSIC_FONT; return DEFAULT_MUSIC_FONT;
} }
function trimTrackTitle(title) { function trimTrackTitle(title) {
var cleanedTitle = title; cleanRegexes = [
cleanedTitle = cleanedTitle.replace(/【[^】]*】/, ''); // Remove stuff like【C93】 at beginning /【[^】]*】/, // Touhou n weeb stuff
cleanedTitle = cleanedTitle.replace(/\[FREE DOWNLOAD\]/g, ''); // Remove F-777's [FREE DOWNLOAD] /\[FREE DOWNLOAD\]/, // F-777
return cleanedTitle.trim(); ];
cleanRegexes.forEach((expr) => cleanedTitle.replace(expr, ''));
return title;
} }
const TrackProgress = ({ player, ...rest }) => { const TrackProgress = ({ player, ...rest }) => {
@@ -18,7 +18,7 @@ execAsync(`ydotoold`).catch(print); // Start ydotool daemon
function releaseAllKeys() { function releaseAllKeys() {
const keycodes = Array.from(Array(249).keys()); const keycodes = Array.from(Array(249).keys());
execAsync([`ydotool`, `key`, ...keycodes.map(keycode => `${keycode}:0`)]) execAsync([`ydotool`, `key`, ...keycodes.map(keycode => `${keycode}:0`)])
.then(console.log('Released all keys')) .then(console.log('[OSK] Released all keys'))
.catch(print); .catch(print);
} }
var modsPressed = false; var modsPressed = false;
-1
View File
@@ -6,7 +6,6 @@ function moveClientToWorkspace(address, workspace) {
} }
export function dumpToWorkspace(from, to) { export function dumpToWorkspace(from, to) {
console.log('dump', from, to);
if (from == to) return; if (from == to) return;
Hyprland.clients.forEach(client => { Hyprland.clients.forEach(client => {
if (client.workspace.id == from) { if (client.workspace.id == from) {
@@ -53,7 +53,7 @@ export function launchCustomCommand(command) {
execAsync([`bash`, `-c`, `systemctl suspend`]).catch(print); execAsync([`bash`, `-c`, `systemctl suspend`]).catch(print);
} }
else if (args[0] == '>logout') { // Log out 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); _appSearchResults = Applications.query(text);
// Calculate // 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 { try {
const fullResult = eval(text); const fullResult = eval(text);
resultsBox.add(CalculationResultButton({ result: fullResult, text: text })); resultsBox.add(CalculationResultButton({ result: fullResult, text: text }));
+14 -41
View File
@@ -1,43 +1,16 @@
import Cairo from 'gi://cairo?version=1.0';
import Widget from 'resource:///com/github/Aylur/ags/widget.js'; 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(); export default (monitor = 0, where = 'bottom left') => {
const enableClickthrough = (self) => self.input_shape_combine_region(dummyRegion); const positionString = where.replace(/\s/, ""); // remove space
return Widget.Window({
export const CornerTopleft = () => Widget.Window({ monitor,
name: 'cornertl', name: `corner${positionString}${monitor}`,
layer: 'top', layer: 'overlay',
anchor: ['top', 'left'], anchor: where.split(' '),
exclusivity: 'normal', exclusivity: 'ignore',
visible: true, visible: true,
child: RoundedCorner('topleft', { className: 'corner', }), child: RoundedCorner(positionString, { className: 'corner-black', }),
setup: enableClickthrough, 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,
});
+1 -1
View File
@@ -63,7 +63,7 @@ export default () => {
// lock, logout, sleep // lock, logout, sleep
// const lockButton = SessionButton('Lock', 'lock', () => { App.closeWindow('session'); execAsync('gtklock') }); // const lockButton = SessionButton('Lock', 'lock', () => { App.closeWindow('session'); execAsync('gtklock') });
const lockButton = SessionButton('Lock', 'lock', () => { App.closeWindow('session'); execAsync('swaylock') }); 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') }); const sleepButton = SessionButton('Sleep', 'sleep', () => { App.closeWindow('session'); execAsync('systemctl suspend') });
// hibernate, shutdown, reboot // hibernate, shutdown, reboot
const hibernateButton = SessionButton('Hibernate', 'downloading', () => { App.closeWindow('session'); execAsync('systemctl hibernate') }); const hibernateButton = SessionButton('Hibernate', 'downloading', () => { App.closeWindow('session'); execAsync('systemctl hibernate') });
@@ -11,7 +11,6 @@ import { MaterialIcon } from '../../lib/materialicon.js';
function expandTilde(path) { function expandTilde(path) {
if (path.startsWith('~')) { if (path.startsWith('~')) {
console.log(GLib.get_home_dir() + path.slice(1));
return GLib.get_home_dir() + path.slice(1); return GLib.get_home_dir() + path.slice(1);
} else { } else {
return path; return path;
@@ -120,8 +119,6 @@ export const ModuleInvertColors = async (props = {}) => {
Hyprland.sendMessage('j/getoption decoration:screen_shader') Hyprland.sendMessage('j/getoption decoration:screen_shader')
.then((output) => { .then((output) => {
const shaderPath = JSON.parse(output)["str"].trim(); const shaderPath = JSON.parse(output)["str"].trim();
console.log(output)
console.log(shaderPath)
if (shaderPath != "[[EMPTY]]" && shaderPath != "") { if (shaderPath != "[[EMPTY]]" && shaderPath != "") {
execAsync(['bash', '-c', `hyprctl keyword decoration:screen_shader '[[EMPTY]]'`]).catch(print); execAsync(['bash', '-c', `hyprctl keyword decoration:screen_shader '[[EMPTY]]'`]).catch(print);
button.toggleClassName('sidebar-button-active', false); button.toggleClassName('sidebar-button-active', false);