refactor(ags): make all widgets functions, use export default and some formatting

This commit is contained in:
matt1432 2023-10-17 13:47:02 -04:00
parent f6160e3e0b
commit 9b4e7fac44
39 changed files with 797 additions and 694 deletions

View file

@ -1,8 +1,8 @@
import { App, Applications, Utils, Widget } from '../../imports.js'; import { App, Applications, Utils, Widget } from '../../imports.js';
const { Label, Box, Icon, Button, Scrollable, Entry } = Widget; const { Label, Box, Icon, Button, Scrollable, Entry } = Widget;
import { Separator } from '../misc/separator.js'; import Separator from '../misc/separator.js';
import { PopupWindow } from '../misc/popup.js'; import PopupWindow from '../misc/popup.js';
const icons = { const icons = {
apps: { apps: {

View file

@ -1,8 +1,8 @@
import { Audio, Widget } from '../../imports.js'; import { Audio, Widget } from '../../imports.js';
const { Label, Box, Icon } = Widget; const { Label, Box, Icon } = Widget;
import { Separator } from '../misc/separator.js'; import Separator from '../misc/separator.js';
import { EventBox } from '../misc/cursorbox.js'; import EventBox from '../misc/cursorbox.js';
const items = { const items = {
101: 'audio-volume-overamplified-symbolic', 101: 'audio-volume-overamplified-symbolic',
@ -13,36 +13,36 @@ const items = {
}; };
const SpeakerIndicator = params => Icon({ const SpeakerIndicator = props => Icon({
...params, ...props,
icon: '', icon: '',
connections: [[Audio, icon => { connections: [[Audio, self => {
if (Audio.speaker) { if (!Audio.speaker)
if (Audio.speaker.stream.isMuted) { return;
icon.icon = items[0];
} if (Audio.speaker.stream.isMuted) {
else { self.icon = items[0];
const vol = Audio.speaker.volume * 100; }
for (const threshold of [-1, 0, 33, 66, 100]) { else {
if (vol > threshold + 1) { const vol = Audio.speaker.volume * 100;
icon.icon = items[threshold + 1];
} for (const threshold of [-1, 0, 33, 66, 100]) {
} if (vol > threshold + 1)
self.icon = items[threshold + 1];
} }
} }
}, 'speaker-changed']], }, 'speaker-changed']],
}); });
const SpeakerPercentLabel = params => Label({ const SpeakerPercentLabel = props => Label({
...params, ...props,
connections: [[Audio, label => { connections: [[Audio, label => {
if (Audio.speaker) { if (Audio.speaker)
label.label = Math.round(Audio.speaker.volume * 100) + '%'; label.label = Math.round(Audio.speaker.volume * 100) + '%';
}
}, 'speaker-changed']], }, 'speaker-changed']],
}); });
export const AudioIndicator = EventBox({ export default () => EventBox({
onPrimaryClickRelease: 'pavucontrol', onPrimaryClickRelease: 'pavucontrol',
className: 'toggle-off', className: 'toggle-off',
child: Box({ child: Box({

View file

@ -1,7 +1,7 @@
import { Battery, Widget } from '../../imports.js'; import { Battery, Widget } from '../../imports.js';
const { Label, Icon, Stack, Box } = Widget; const { Label, Icon, Stack, Box } = Widget;
import { Separator } from '../misc/separator.js'; import Separator from '../misc/separator.js';
const icons = charging => ([ const icons = charging => ([
...Array.from({ length: 10 }, (_, i) => i * 10).map(i => ([ ...Array.from({ length: 10 }, (_, i) => i * 10).map(i => ([
@ -27,9 +27,9 @@ const Indicators = charging => Stack({
const Indicator = ({ const Indicator = ({
charging = Indicators(true), charging = Indicators(true),
discharging = Indicators(false), discharging = Indicators(false),
...params ...props
} = {}) => Stack({ } = {}) => Stack({
...params, ...props,
className: 'battery-indicator', className: 'battery-indicator',
items: [ items: [
['true', charging], ['true', charging],
@ -43,13 +43,13 @@ const Indicator = ({
}]], }]],
}); });
const LevelLabel = params => Label({ const LevelLabel = props => Label({
...params, ...props,
className: 'label', className: 'label',
connections: [[Battery, label => label.label = `${Battery.percent}%`]], connections: [[Battery, self => self.label = `${Battery.percent}%`]],
}); });
export const BatteryIndicator = Box({ export default () => Box({
className: 'toggle-off battery', className: 'toggle-off battery',
children: [ children: [
Indicator(), Indicator(),

View file

@ -1,25 +1,23 @@
import { Utils, Widget } from '../../imports.js'; import { Utils, Widget } from '../../imports.js';
const { ProgressBar, Overlay, Box } = Widget; const { ProgressBar, Overlay, Box } = Widget;
import { Separator } from '../misc/separator.js'; import Separator from '../misc/separator.js';
import { Heart } from './heart.js'; import Heart from './heart.js';
export const Brightness = Overlay({ export default () => Overlay({
setup: widget => { tooltipText: 'Brightness',
widget.set_tooltip_text('Brightness');
},
child: ProgressBar({ child: ProgressBar({
className: 'toggle-off brightness', className: 'toggle-off brightness',
connections: [ connections: [
[200, progress => { [200, self => {
Utils.execAsync('brightnessctl get').then(out => { Utils.execAsync('brightnessctl get').then(out => {
let br = out / 255; let br = out / 255;
if (br > 0.33) { if (br > 0.33) {
progress.value = br; self.value = br;
} }
else { else {
progress.value = 0.33; self.value = 0.33;
} }
}).catch(print); }).catch(print);
}], }],
@ -30,7 +28,7 @@ export const Brightness = Overlay({
style: 'color: #CBA6F7;', style: 'color: #CBA6F7;',
children: [ children: [
Separator(25), Separator(25),
Heart, Heart(),
], ],
}), }),
], ],

View file

@ -4,30 +4,32 @@ const { Box, Label } = Widget;
import GLib from 'gi://GLib'; import GLib from 'gi://GLib';
const { DateTime } = GLib; const { DateTime } = GLib;
import { EventBox } from '../misc/cursorbox.js'; import EventBox from '../misc/cursorbox.js';
const ClockModule = ({ const ClockModule = ({
interval = 1000, interval = 1000,
...params ...props
}) => Label({ }) => Label({
...params, ...props,
className: 'clock', className: 'clock',
connections: [ connections: [
[interval, label => { [interval, self => {
var time = DateTime.new_now_local(); var time = DateTime.new_now_local();
label.label = time.format('%a. ') + time.get_day_of_month() + time.format(' %b. %H:%M'); self.label = time.format('%a. ') +
time.get_day_of_month() +
time.format(' %b. %H:%M');
}], }],
], ],
}); });
export const Clock = EventBox({ export default () => EventBox({
className: 'toggle-off', className: 'toggle-off',
onPrimaryClickRelease: () => App.toggleWindow('calendar'), onPrimaryClickRelease: () => App.toggleWindow('calendar'),
connections: [ connections: [
[App, (box, windowName, visible) => { [App, (self, windowName, visible) => {
if (windowName == 'calendar') { if (windowName == 'calendar') {
box.toggleClassName('toggle-on', visible); self.toggleClassName('toggle-on', visible);
} }
}], }],
], ],

View file

@ -2,12 +2,8 @@ import { Widget, Hyprland } from '../../imports.js';
const { Label } = Widget; const { Label } = Widget;
export const CurrentWindow = Label({ export default () => Label({
style: 'color: #CBA6F7; font-size: 18px', style: 'color: #CBA6F7; font-size: 18px',
truncate: 'end', truncate: 'end',
connections: [ binds: [['label', Hyprland.active.client, 'title']],
[Hyprland, label => {
label.label = Hyprland.active.client.title;
}, 'changed'],
],
}); });

View file

@ -4,19 +4,14 @@ const { Box, EventBox, Overlay } = Widget;
const Revealed = Variable(true); const Revealed = Variable(true);
const Hovering = Variable(false); const Hovering = Variable(false);
import { Gesture } from './gesture.js'; import { RoundedCorner } from '../screen-corners.js';
import { RoundedCorner } from '../screen-corners.js'; import Gesture from './gesture.js';
export const Revealer = params => Overlay({ export default (props) => Overlay({
overlays: [ overlays: [
RoundedCorner('topleft', { RoundedCorner('topleft', { className: 'corner' }),
className: 'corner', RoundedCorner('topright', { className: 'corner' }),
}),
RoundedCorner('topright', {
className: 'corner',
}),
], ],
child: Box({ child: Box({
@ -27,27 +22,26 @@ export const Revealer = params => Overlay({
Widget.Revealer({ Widget.Revealer({
transition: 'slide_down', transition: 'slide_down',
setup: self => self.revealChild = true, setup: self => self.revealChild = true,
properties: [
['timeouts', []],
],
connections: [[Hyprland, self => {
Utils.execAsync('hyprctl activewindow -j')
.then(result => {
let client = JSON.parse(result);
if (client.fullscreen !== Revealed.value) {
Revealed.value = client.fullscreen;
if (Revealed.value) { properties: [['timeouts', []]],
setTimeout(() => { connections: [[Hyprland, self => {
if (Revealed.value) Utils.execAsync('hyprctl activewindow -j').then(out => {
self.revealChild = false let client = JSON.parse(out);
}, 2000); if (client.fullscreen === Revealed.value)
} return;
else {
self.revealChild = true; Revealed.value = client.fullscreen;
}
} if (Revealed.value) {
}).catch(print); setTimeout(() => {
if (Revealed.value)
self.revealChild = false
}, 2000);
}
else {
self.revealChild = true;
}
}).catch(print);
}]], }]],
child: Gesture({ child: Gesture({
@ -63,7 +57,7 @@ export const Revealer = params => Overlay({
}, 2000); }, 2000);
} }
}, },
...params, ...props,
}), }),
}), }),

View file

@ -4,30 +4,25 @@ const { CenterBox, EventBox } = Widget;
import Gtk from 'gi://Gtk'; import Gtk from 'gi://Gtk';
export const Gesture = ({ export default ({
child, child,
...params ...props
}) => { }) => {
let w = EventBox({ let widget = EventBox({
...params, ...props,
}); });
let gesture = Gtk.GestureSwipe.new(w); let gesture = Gtk.GestureSwipe.new(widget);
w.child = CenterBox({ widget.child = CenterBox({
children: [ children: [ child ],
child, connections: [[gesture, () => {
], const velocity = gesture.get_velocity()[1];
connections: [ if (velocity < -100)
App.openWindow('quick-settings');
[gesture, _ => { }, 'update']],
const velocity = gesture.get_velocity()[1];
if (velocity < -100)
App.openWindow('quick-settings');
}, 'update'],
],
}); });
return w; return widget;
}; };

View file

@ -2,26 +2,28 @@ import { Utils, Widget } from '../../imports.js';
const { Box, Label } = Widget; const { Box, Label } = Widget;
const { subprocess, execAsync } = Utils; const { subprocess, execAsync } = Utils;
import { EventBox } from '../misc/cursorbox.js'; import EventBox from '../misc/cursorbox.js';
subprocess( export default () => EventBox({
['bash', '-c', 'tail -f /home/matt/.config/.heart'],
(output) => {
Heart.child.children[0].label = ' ' + output;
},
);
export const Heart = EventBox({
halign: 'center', halign: 'center',
onPrimaryClickRelease: () => { onPrimaryClickRelease: () => {
execAsync(['bash', '-c', '$AGS_PATH/heart.sh toggle']).catch(print); execAsync(['bash', '-c', '$AGS_PATH/heart.sh toggle']).catch(print);
}, },
child: Box({ child: Box({
className: 'heart-toggle', className: 'heart-toggle',
vertical: false, vertical: false,
child: Label({ child: Label({
label: '', label: '',
setup: self => {
subprocess(
['bash', '-c', 'tail -f /home/matt/.config/.heart'],
(output) => self.label = ' ' + output,
);
},
}), }),
}), }),
}); });

View file

@ -3,7 +3,7 @@ const { Label, Box, Icon } = Widget;
const DEFAULT_KB = "at-translated-set-2-keyboard"; const DEFAULT_KB = "at-translated-set-2-keyboard";
export default Box({ export default () => Box({
className: 'toggle-off', className: 'toggle-off',
children: [ children: [
Icon({ Icon({

View file

@ -1,20 +1,20 @@
import { Widget } from '../../imports.js'; import { Widget } from '../../imports.js';
const { Window, CenterBox, Box } = Widget; const { Window, CenterBox, Box } = Widget;
import { Separator } from '../misc/separator.js'; import Separator from '../misc/separator.js';
import { CurrentWindow } from './current-window.js'; import CurrentWindow from './current-window.js';
import { Workspaces } from './workspaces.js'; import Workspaces from './workspaces.js';
import { OskToggle } from './osk-toggle.js'; import OskToggle from './osk-toggle.js';
import { TabletToggle } from './tablet-toggle.js'; import TabletToggle from './tablet-toggle.js';
import { QsToggle } from './quick-settings.js'; import QsToggle from './quick-settings.js';
import { NotifButton } from './notif-button.js'; import NotifButton from './notif-button.js';
import { Clock } from './clock.js'; import Clock from './clock.js';
import { SysTray } from './systray.js'; import SysTray from './systray.js';
import { BatteryIndicator } from './battery.js'; import Battery from './battery.js';
import { Brightness } from './brightness.js'; import Brightness from './brightness.js';
import { AudioIndicator } from './audio.js'; import Audio from './audio.js';
import { Revealer } from './fullscreen.js'; import Revealer from './fullscreen.js';
//import KeyboardLayout from './keyboard-layout.js'; //import KeyboardLayout from './keyboard-layout.js';
export const Bar = () => Window({ export const Bar = () => Window({
@ -31,55 +31,55 @@ export const Bar = () => Window({
halign: 'start', halign: 'start',
children: [ children: [
OskToggle, OskToggle(),
Separator(12), Separator(12),
TabletToggle, TabletToggle(),
Separator(12), Separator(12),
SysTray, SysTray(),
AudioIndicator, Audio(),
Separator(12), Separator(12),
Brightness, Brightness(),
Separator(12), Separator(12),
Workspaces, Workspaces(),
], ],
}), }),
centerWidget: Box({ centerWidget: Box({
children: [ children: [
CurrentWindow, CurrentWindow(),
], ],
}), }),
endWidget: Box({ endWidget: Box({
halign: 'end', halign: 'end',
children: [ children: [
BatteryIndicator, Battery(),
Separator(12), Separator(12),
//KeyboardLayout, //KeyboardLayout(),
//Separator(12), //Separator(12),
Clock, Clock(),
Separator(12), Separator(12),
NotifButton, NotifButton(),
Separator(12), Separator(12),
QsToggle, QsToggle(),
], ],
}), }),
}), }),

View file

@ -1,20 +1,17 @@
import { App, Notifications, Widget } from '../../imports.js'; import { App, Notifications, Widget } from '../../imports.js';
const { Box, Label, Icon } = Widget; const { Box, Label, Icon } = Widget;
import { Separator } from '../misc/separator.js'; import Separator from '../misc/separator.js';
import { EventBox } from '../misc/cursorbox.js'; import EventBox from '../misc/cursorbox.js';
export const NotifButton = EventBox({ export default () => EventBox({
className: 'toggle-off', className: 'toggle-off',
onPrimaryClickRelease: () => App.toggleWindow('notification-center'), onPrimaryClickRelease: () => App.toggleWindow('notification-center'),
connections: [ connections: [[App, (self, windowName, visible) => {
[App, (box, windowName, visible) => { if (windowName == 'notification-center')
if (windowName == 'notification-center') { self.toggleClassName('toggle-on', visible);
box.toggleClassName('toggle-on', visible); }]],
}
}],
],
child: Box({ child: Box({
className: 'notif-panel', className: 'notif-panel',
vertical: false, vertical: false,
@ -22,30 +19,26 @@ export const NotifButton = EventBox({
Separator(28), Separator(28),
Icon({ Icon({
connections: [ connections: [[Notifications, self => {
[Notifications, icon => { if (Notifications.dnd) {
if (Notifications.dnd) { self.icon = 'notification-disabled-symbolic'
icon.icon = 'notification-disabled-symbolic' }
else {
if (Notifications.notifications.length > 0) {
self.icon = 'notification-new-symbolic'
} }
else { else {
if (Notifications.notifications.length > 0) { self.icon = 'notification-symbolic'
icon.icon = 'notification-new-symbolic'
}
else {
icon.icon = 'notification-symbolic'
}
} }
}], }
], }]],
}), }),
Separator(8), Separator(8),
Label({ Label({
connections: [ binds: [
[Notifications, label => { ['label', Notifications, 'notifications', n => String(n.length)],
label.label = String(Notifications.notifications.length);
}],
], ],
}), }),

View file

@ -2,33 +2,25 @@ import { Utils, Widget } from '../../imports.js';
const { Box, Label } = Widget; const { Box, Label } = Widget;
const { subprocess } = Utils; const { subprocess } = Utils;
import { EventBox } from '../misc/cursorbox.js'; import EventBox from '../misc/cursorbox.js';
subprocess( export default () => EventBox({
['bash', '-c', '$AGS_PATH/osk-toggle.sh getState'],
(output) => {
if (output == 'Running') {
OskToggle.toggleClassName('toggle-on', true);
} else {
OskToggle.toggleClassName('toggle-on', false);
}
},
);
export const OskToggle = EventBox({
className: 'toggle-off', className: 'toggle-off',
onPrimaryClickRelease: function() { setup: self => {
subprocess( subprocess(
['bash', '-c', '$AGS_PATH/osk-toggle.sh toggle'], ['bash', '-c', '$AGS_PATH/osk-toggle.sh getState'],
(output) => { (output) => self.toggleClassName('toggle-on', output === 'Running'),
if (output == 'Running') {
OskToggle.toggleClassName('toggle-on', false);
} else {
OskToggle.toggleClassName('toggle-on', true);
}
},
); );
}, },
onPrimaryClickRelease: self => {
subprocess(
['bash', '-c', '$AGS_PATH/osk-toggle.sh toggle'],
(output) => self.toggleClassName('toggle-on', output !== 'Running'),
);
},
child: Box({ child: Box({
className: 'osk-toggle', className: 'osk-toggle',
vertical: false, vertical: false,

View file

@ -1,19 +1,16 @@
import { Widget, App } from '../../imports.js'; import { Widget, App } from '../../imports.js';
const { Box, Label } = Widget; const { Box, Label } = Widget;
import { EventBox } from '../misc/cursorbox.js'; import EventBox from '../misc/cursorbox.js';
export const QsToggle = EventBox({ export default () => EventBox({
className: 'toggle-off', className: 'toggle-off',
onPrimaryClickRelease: () => App.toggleWindow('quick-settings'), onPrimaryClickRelease: () => App.toggleWindow('quick-settings'),
connections: [ connections: [[App, (self, windowName, visible) => {
[App, (box, windowName, visible) => { if (windowName == 'quick-settings')
if (windowName == 'quick-settings') { self.toggleClassName('toggle-on', visible);
box.toggleClassName('toggle-on', visible); }]],
}
}],
],
child: Box({ child: Box({
className: 'quick-settings-toggle', className: 'quick-settings-toggle',
vertical: false, vertical: false,

View file

@ -3,7 +3,7 @@ const { Box, Revealer, Icon, MenuItem } = Widget;
import Gtk from 'gi://Gtk'; import Gtk from 'gi://Gtk';
import { Separator } from '../misc/separator.js'; import Separator from '../misc/separator.js';
const SysTrayItem = item => MenuItem({ const SysTrayItem = item => MenuItem({
@ -21,7 +21,7 @@ const SysTrayItem = item => MenuItem({
}]] }]]
}); });
export const SysTray = Revealer({ export default () => Revealer({
transition: 'slide_right', transition: 'slide_right',
connections: [[SystemTray, rev => { connections: [[SystemTray, rev => {
rev.revealChild = rev.child.children[0].get_children().length > 0; rev.revealChild = rev.child.children[0].get_children().length > 0;

View file

@ -2,22 +2,15 @@ import { Utils, Widget } from '../../imports.js';
const { Box, Label } = Widget; const { Box, Label } = Widget;
const { subprocess } = Utils; const { subprocess } = Utils;
import { EventBox } from '../misc/cursorbox.js'; import EventBox from '../misc/cursorbox.js';
export const TabletToggle = EventBox({ export default () => EventBox({
className: 'toggle-off', className: 'toggle-off',
onPrimaryClickRelease: function() { onPrimaryClickRelease: self => {
subprocess( subprocess(
['bash', '-c', '$AGS_PATH/tablet-toggle.sh toggle'], ['bash', '-c', '$AGS_PATH/tablet-toggle.sh toggle'],
(output) => { (output) => self.toggleClassName('toggle-on', output == 'Tablet'),
print(output)
if (output == 'Tablet') {
TabletToggle.toggleClassName('toggle-on', true);
} else {
TabletToggle.toggleClassName('toggle-on', false);
}
},
); );
}, },
child: Box({ child: Box({

View file

@ -1,69 +1,69 @@
import { Hyprland, Utils, Widget } from '../../imports.js'; import { Hyprland, Utils, Widget } from '../../imports.js';
const { Box, Label, Revealer } = Widget; const { Box, Revealer } = Widget;
const { execAsync } = Utils; const { execAsync } = Utils;
import { EventBox } from '../misc/cursorbox.js'; import EventBox from '../misc/cursorbox.js';
const Workspace = ({ i } = {}) => const Workspace = ({ i } = {}) =>
Revealer({ Revealer({
transition: "slide_right", transition: "slide_right",
properties: [ properties: [['id', i]],
['id', i],
],
child: EventBox({ child: EventBox({
tooltipText: `${i}`, tooltipText: `${i}`,
onPrimaryClickRelease: () => execAsync(`hyprctl dispatch workspace ${i}`).catch(print), onPrimaryClickRelease: () => {
execAsync(`hyprctl dispatch workspace ${i}`)
.catch(print);
},
child: Box({ child: Box({
className: 'button', className: 'button',
connections: [ connections: [[Hyprland, self => {
[Hyprland, btn => { const occupied = Hyprland.getWorkspace(i)?.windows > 0;
const occupied = Hyprland.getWorkspace(i)?.windows > 0; self.toggleClassName('active', Hyprland.active.workspace.id === i);
btn.toggleClassName('active', Hyprland.active.workspace.id === i); self.toggleClassName('occupied', occupied);
btn.toggleClassName('occupied', occupied); self.toggleClassName('empty', !occupied);
btn.toggleClassName('empty', !occupied); }]],
}]
],
}), }),
}), }),
}); });
export const Workspaces = Box({ export default () => Box({
className: 'workspaces', className: 'workspaces',
children: [EventBox({ children: [EventBox({
child: Box({ child: Box({
properties: [ properties: [
['workspaces'], ['workspaces'],
['refresh', box => { ['refresh', self => {
box.children.forEach(rev => rev.reveal_child = false); self.children.forEach(rev => rev.reveal_child = false);
box._workspaces.forEach(ws => { self._workspaces.forEach(ws => {
ws.revealChild = true; ws.revealChild = true;
}); });
}], }],
['updateWs', box => { ['updateWs', self => {
Hyprland.workspaces.forEach(ws => { Hyprland.workspaces.forEach(ws => {
let currentWs = box.children.find(ch => ch._id == ws.id); let currentWs = self.children.find(ch => ch._id == ws.id);
if (!currentWs && ws.id > 0) { if (!currentWs && ws.id > 0) {
box.add(Workspace({ i: ws.id})); self.add(Workspace({ i: ws.id}));
} }
}); });
box.show_all(); self.show_all();
// Make sure the order is correct // Make sure the order is correct
box._workspaces.forEach((workspace, i) => { self._workspaces.forEach((workspace, i) => {
workspace.get_parent().reorder_child(workspace, i); workspace.get_parent().reorder_child(workspace, i);
}); });
}], }],
], ],
connections: [[Hyprland, box => { connections: [[Hyprland, self => {
box._workspaces = box.children.filter(ch => { self._workspaces = self.children.filter(ch => {
return Hyprland.workspaces.find(ws => ws.id == ch._id) return Hyprland.workspaces.find(ws => ws.id == ch._id)
}).sort((a, b) => a._id - b._id); }).sort((a, b) => a._id - b._id);
box._updateWs(box); self._updateWs(self);
box._refresh(box); self._refresh(self);
}]], }]],
}), }),
})], })],

View file

@ -5,7 +5,7 @@ import Gtk from 'gi://Gtk';
import GLib from 'gi://GLib'; import GLib from 'gi://GLib';
const { DateTime } = GLib; const { DateTime } = GLib;
import { PopupWindow } from './misc/popup.js'; import PopupWindow from './misc/popup.js';
const Divider = () => Box({ const Divider = () => Box({
@ -27,8 +27,8 @@ const Time = () => Box({
Label({ Label({
className: 'content', className: 'content',
label: 'hour', label: 'hour',
connections: [[1000, label => { connections: [[1000, self => {
label.label = DateTime.new_now_local().format('%H'); self.label = DateTime.new_now_local().format('%H');
}]], }]],
}), }),
@ -37,8 +37,8 @@ const Time = () => Box({
Label({ Label({
className: 'content', className: 'content',
label: 'minute', label: 'minute',
connections: [[1000, label => { connections: [[1000, self => {
label.label = DateTime.new_now_local().format('%M'); self.label = DateTime.new_now_local().format('%M');
}]], }]],
}), }),
@ -51,9 +51,11 @@ const Time = () => Box({
child: Label({ child: Label({
style: 'font-size: 20px', style: 'font-size: 20px',
label: 'complete date', label: 'complete date',
connections: [[1000, label => { connections: [[1000, self => {
var time = DateTime.new_now_local(); var time = DateTime.new_now_local();
label.label = time.format("%A, %B ") + time.get_day_of_month() + time.format(", %Y"); self.label = time.format("%A, %B ") +
time.get_day_of_month() +
time.format(", %Y");
}]], }]],
}), }),
}), }),
@ -68,7 +70,6 @@ const CalendarWidget = () => Box({
showDayNames: true, showDayNames: true,
showHeading: true, showHeading: true,
className: 'cal', className: 'cal',
connections: [/* */]
}), }),
}); });

View file

@ -8,12 +8,12 @@ const OFFSCREEN = 500;
const TRANSITION = 'transition: margin 0.5s ease, opacity 3s ease;'; const TRANSITION = 'transition: margin 0.5s ease, opacity 3s ease;';
export default ({ properties, connections, params } = {}) => { export default ({ properties, connections, props } = {}) => {
let widget = EventBox(); let widget = EventBox();
let gesture = Gtk.GestureDrag.new(widget) let gesture = Gtk.GestureDrag.new(widget)
widget.child = Overlay({ widget.child = Overlay({
...params, ...props,
properties: [ properties: [
...properties, ...properties,
['dragging', false], ['dragging', false],

View file

@ -5,8 +5,8 @@ const { execAsync, lookUpIcon } = Utils;
import Gdk from 'gi://Gdk'; import Gdk from 'gi://Gdk';
const display = Gdk.Display.get_default(); const display = Gdk.Display.get_default();
import { EventBox } from '../misc/cursorbox.js'; import Separator from '../misc/separator.js';
import { Separator } from '../misc/separator.js'; import EventBox from '../misc/cursorbox.js';
const icons = { const icons = {
mpris: { mpris: {
@ -29,34 +29,35 @@ const icons = {
} }
export const CoverArt = (player, params) => CenterBox({ export const CoverArt = (player, props) => CenterBox({
...params, ...props,
vertical: true, vertical: true,
properties: [['bgStyle', '']], properties: [['bgStyle', '']],
connections: [ connections: [[player, self => {
[player, box => { execAsync(['bash', '-c', `[[ -f "${player.coverPath}" ]] &&
execAsync(['bash', '-c', `[[ -f "${player.coverPath}" ]] && coloryou "${player.coverPath}" | grep -v Warning`]) coloryou "${player.coverPath}" | grep -v Warning`])
.then(out => { .then(out => {
if (!Mpris.players.find(p => player === p)) if (!Mpris.players.find(p => player === p))
return; return;
player.colors.value = JSON.parse(out); player.colors.value = JSON.parse(out);
box._bgStyle = `background: radial-gradient(circle, self._bgStyle = `background: radial-gradient(circle,
rgba(0, 0, 0, 0.4) 30%, rgba(0, 0, 0, 0.4) 30%,
${player.colors.value.imageAccent}), ${player.colors.value.imageAccent}),
url("${player.coverPath}"); url("${player.coverPath}");
background-size: cover; background-size: cover;
background-position: center;`; background-position: center;`;
if (!box.get_parent()._dragging)
box.setStyle(box._bgStyle); if (!self.get_parent()._dragging)
}).catch(err => { if (err !== "") print(err) }); self.setStyle(self._bgStyle);
}],
], }).catch(err => {if (err !== "") print(err)});
}]],
}); });
export const TitleLabel = (player, params) => Label({ export const TitleLabel = (player, props) => Label({
...params, ...props,
xalign: 0, xalign: 0,
maxWidthChars: 40, maxWidthChars: 40,
truncate: 'end', truncate: 'end',
@ -65,8 +66,8 @@ export const TitleLabel = (player, params) => Label({
binds: [['label', player, 'track-title']], binds: [['label', player, 'track-title']],
}); });
export const ArtistLabel = (player, params) => Label({ export const ArtistLabel = (player, props) => Label({
...params, ...props,
xalign: 0, xalign: 0,
maxWidthChars: 40, maxWidthChars: 40,
truncate: 'end', truncate: 'end',
@ -75,80 +76,83 @@ export const ArtistLabel = (player, params) => Label({
binds: [['label', player, 'track-artists', a => a.join(', ') || '']], binds: [['label', player, 'track-artists', a => a.join(', ') || '']],
}); });
export const PlayerIcon = (player, { symbolic = true, ...params } = {}) => { export const PlayerIcon = (player, { symbolic = true, ...props } = {}) => {
let MainIcon = Icon({ let MainIcon = Icon({
...params, ...props,
className: 'player-icon', className: 'player-icon',
size: 32, size: 32,
tooltipText: player.identity || '', tooltipText: player.identity || '',
connections: [ connections: [[player, self => {
[player, icon => { const name = `${player.entry}${symbolic ? '-symbolic' : ''}`;
const name = `${player.entry}${symbolic ? '-symbolic' : ''}`; lookUpIcon(name) ? self.icon = name
lookUpIcon(name) ? icon.icon = name : self.icon = icons.mpris.fallback;
: icon.icon = icons.mpris.fallback; }]],
}],
],
}); });
return Box({ return Box({
connections: [ connections: [[Mpris, self => {
[Mpris, box => { let overlays = self.get_parent().get_parent()
let overlays = box.get_parent().get_parent().get_parent().list(); .get_parent().list();
let player = overlays.find(overlay => overlay === box.get_parent().get_parent());
let index = overlays.indexOf(player)
let children = []; let player = overlays.find(overlay => {
for (let i = 0; i < overlays.length; ++i) { overlay === self.get_parent().get_parent();
if (i === index) { });
children.push(MainIcon);
children.push(Separator(2)); let index = overlays.indexOf(player);
}
else { let children = [];
children.push(Box({ className: 'position-indicator' })); for (let i = 0; i < overlays.length; ++i) {
children.push(Separator(2)); if (i === index) {
} children.push(MainIcon);
children.push(Separator(2));
} }
box.children = children; else {
}], children.push(Box({className: 'position-indicator'}));
], children.push(Separator(2));
}
}
self.children = children;
}]],
}); });
} }
export const PositionSlider = (player, params) => EventBox({ // FIXME: get the cursors right or just don't display when disabled
export const PositionSlider = (player, props) => EventBox({
child: Slider({ child: Slider({
...params, ...props,
className: 'position-slider', className: 'position-slider',
hexpand: true, hexpand: true,
drawValue: false, drawValue: false,
onChange: ({ value }) => { onChange: ({ value }) => {
player.position = player.length * value; player.position = player.length * value;
}, },
properties: [ properties: [['update', slider => {
['update', slider => { if (slider.dragging) {
if (slider.dragging) { slider.get_parent().window.set_cursor(Gdk.Cursor
slider.get_parent().window.set_cursor(Gdk.Cursor.new_from_name(display, 'grabbing')); .new_from_name(display, 'grabbing'));
}
else {
if (slider.get_parent() && slider.get_parent().window) {
slider.get_parent().window.set_cursor(Gdk.Cursor
.new_from_name(display, 'pointer'));
} }
else {
if (slider.get_parent() && slider.get_parent().window) {
slider.get_parent().window.set_cursor(Gdk.Cursor.new_from_name(display, 'pointer'));
}
slider.sensitive = player.length > 0; slider.sensitive = player.length > 0;
if (player.length > 0) { if (player.length > 0) {
slider.value = player.position / player.length; slider.value = player.position / player.length;
}
} }
}], }
], }]],
connections: [ connections: [
[player, s => s._update(s), 'position'], [player, s => s._update(s), 'position'],
[1000, s => s._update(s)], [1000, s => s._update(s)],
[player.colors, s => { [player.colors, s => {
if (player.colors.value) let c = player.colors.value;
s.setCss(`highlight { background-color: ${player.colors.value.buttonAccent}; } if (c)
slider { background-color: ${player.colors.value.buttonAccent}; } s.setCss(`highlight { background-color: ${c.buttonAccent}; }
slider:hover { background-color: ${player.colors.value.hoverAccent}; } slider { background-color: ${c.buttonAccent}; }
trough { background-color: ${player.colors.value.buttonText}; }`); slider:hover { background-color: ${c.hoverAccent}; }
trough { background-color: ${c.buttonText}; }`);
}], }],
], ],
}), }),
@ -162,9 +166,9 @@ function lengthStr(length) {
} }
export const PositionLabel = player => Label({ export const PositionLabel = player => Label({
properties: [['update', label => { properties: [['update', self => {
player.length > 0 ? label.label = lengthStr(player.position) player.length > 0 ? self.label = lengthStr(player.position)
: label.visible = !!player; : self.visible = !!player;
}]], }]],
connections: [ connections: [
[player, l => l._update(l), 'position'], [player, l => l._update(l), 'position'],
@ -173,16 +177,16 @@ export const PositionLabel = player => Label({
}); });
export const LengthLabel = player => Label({ export const LengthLabel = player => Label({
connections: [[player, label => { connections: [[player, self => {
player.length > 0 ? label.label = lengthStr(player.length) player.length > 0 ? self.label = lengthStr(player.length)
: label.visible = !!player; : self.visible = !!player;
}]], }]],
}); });
export const Slash = player => Label({ export const Slash = player => Label({
label: '/', label: '/',
connections: [[player, label => { connections: [[player, self => {
label.visible = player.length > 0; self.visible = player.length > 0;
}]], }]],
}); });
@ -191,13 +195,13 @@ const PlayerButton = ({ player, items, onClick, prop }) => Button({
child: Stack({ items }), child: Stack({ items }),
onPrimaryClickRelease: () => player[onClick](), onPrimaryClickRelease: () => player[onClick](),
properties: [['hovered', false]], properties: [['hovered', false]],
onHover: box => { onHover: self => {
box._hovered = true; self._hovered = true;
if (! box.child.sensitive || ! box.sensitive) { if (! self.child.sensitive || ! self.sensitive) {
box.window.set_cursor(Gdk.Cursor.new_from_name(display, 'not-allowed')); self.window.set_cursor(Gdk.Cursor.new_from_name(display, 'not-allowed'));
} }
else { else {
box.window.set_cursor(Gdk.Cursor.new_from_name(display, 'pointer')); self.window.set_cursor(Gdk.Cursor.new_from_name(display, 'pointer'));
} }
if (prop == 'playBackStatus') { if (prop == 'playBackStatus') {
@ -209,9 +213,9 @@ const PlayerButton = ({ player, items, onClick, prop }) => Button({
}); });
} }
}, },
onHoverLost: box => { onHoverLost: self => {
box._hovered = false; self._hovered = false;
box.window.set_cursor(null); self.window.set_cursor(null);
if (prop == 'playBackStatus') { if (prop == 'playBackStatus') {
items.forEach(item => { items.forEach(item => {
item[1].setStyle(`background-color: ${player.colors.value.buttonAccent}; item[1].setStyle(`background-color: ${player.colors.value.buttonAccent};

View file

@ -1,9 +1,11 @@
import { Mpris, Variable, Widget } from '../../imports.js'; import { Mpris, Variable, Widget } from '../../imports.js';
const { Box, CenterBox, Label } = Widget; const { Box, CenterBox, Label } = Widget;
import * as mpris from './mpris.js'; import * as mpris from './mpris.js';
import PlayerGesture from './gesture.js'; import PlayerGesture from './gesture.js';
import { Separator } from '../misc/separator.js'; import Separator from '../misc/separator.js';
const FAVE_PLAYER = 'org.mpris.MediaPlayer2.spotify';
const Top = player => Box({ const Top = player => Box({
@ -56,20 +58,22 @@ const Center = player => Box({
const Bottom = player => Box({ const Bottom = player => Box({
className: 'bottom', className: 'bottom',
children: [ children: [
mpris.PreviousButton(player, { mpris.PreviousButton(player, {
valign: 'end', valign: 'end',
halign: 'start', halign: 'start',
}), }),
Separator(8), Separator(8),
mpris.PositionSlider(player), mpris.PositionSlider(player),
Separator(8), Separator(8),
mpris.NextButton(player), mpris.NextButton(player),
Separator(8), Separator(8),
mpris.ShuffleButton(player), mpris.ShuffleButton(player),
Separator(8), Separator(8),
mpris.LoopButton(player),
mpris.LoopButton(player),
], ],
}); });
@ -98,10 +102,12 @@ export default () => Box({
if (!busName) { if (!busName) {
let player = Mpris.players.find(p => !overlay._players.has(p.busName)); let player = Mpris.players.find(p => !overlay._players.has(p.busName));
if (player) if (player) {
busName = player.busName; busName = player.busName;
else }
else {
return; return;
}
} }
const player = Mpris.getPlayer(busName); const player = Mpris.getPlayer(busName);
@ -115,18 +121,20 @@ export default () => Box({
overlay.overlays = result; overlay.overlays = result;
// Favor spotify // Select favorite player at startup
if (!overlay._setup) { if (!overlay._setup) {
if (overlay._players.has('org.mpris.MediaPlayer2.spotify')) { if (overlay._players.has(FAVE_PLAYER)) {
overlay._selected = overlay._players.get('org.mpris.MediaPlayer2.spotify'); overlay._selected = overlay._players.get(FAVE_PLAYER);
} }
overlay._setup = true; overlay._setup = true;
} }
if (overlay._selected) if (overlay._selected)
overlay.reorder_overlay(overlay._selected, -1); overlay.reorder_overlay(overlay._selected, -1);
}, 'player-added'], }, 'player-added'],
[Mpris, (overlay, busName) => { [Mpris, (overlay, busName) => {
if (!busName || !overlay._players.has(busName)) if (!busName || !overlay._players.has(busName))
return; return;
@ -141,6 +149,7 @@ export default () => Box({
overlay.overlays = result; overlay.overlays = result;
if (overlay._selected) if (overlay._selected)
overlay.reorder_overlay(overlay._selected, -1); overlay.reorder_overlay(overlay._selected, -1);
}, 'player-closed'], }, 'player-closed'],
], ],
}), }),

View file

@ -21,7 +21,7 @@ const closeAll = () => {
}); });
App.closeWindow('closer'); App.closeWindow('closer');
}; };
globalThis.closeAll = () => closeAll(); globalThis.closeAll = closeAll;
Pointers.connect('new-line', (_, out) => { Pointers.connect('new-line', (_, out) => {
if (out) { if (out) {

View file

@ -4,34 +4,43 @@ import Gdk from 'gi://Gdk';
const display = Gdk.Display.get_default(); const display = Gdk.Display.get_default();
export const EventBox = ({ reset = true, ...params }) => Widget.EventBox({ export default ({
...params, type = "EventBox",
onHover: box => { reset = true,
if (! box.child.sensitive || ! box.sensitive) { ...props
box.window.set_cursor(Gdk.Cursor.new_from_name(display, 'not-allowed')); }) => {
} if (type === "EventBox") {
else { return Widget.EventBox({
box.window.set_cursor(Gdk.Cursor.new_from_name(display, 'pointer')); ...props,
} onHover: self => {
}, if (!self.child.sensitive || !self.sensitive) {
onHoverLost: box => { self.window.set_cursor(Gdk.Cursor.new_from_name(display, 'not-allowed'));
if (reset) }
box.window.set_cursor(null); else {
}, self.window.set_cursor(Gdk.Cursor.new_from_name(display, 'pointer'));
}); }
},
export const Button = ({ reset = true, ...params }) => Widget.Button({ onHoverLost: self => {
...params, if (reset)
onHover: box => { self.window.set_cursor(null);
if (! box.child.sensitive || ! box.sensitive) { },
box.window.set_cursor(Gdk.Cursor.new_from_name(display, 'not-allowed')); });
} }
else { else {
box.window.set_cursor(Gdk.Cursor.new_from_name(display, 'pointer')); return Widget.Button({
} ...props,
}, onHover: self => {
onHoverLost: box => { if (!self.child.sensitive || !self.sensitive) {
if (reset) self.window.set_cursor(Gdk.Cursor.new_from_name(display, 'not-allowed'));
box.window.set_cursor(null); }
}, else {
}); self.window.set_cursor(Gdk.Cursor.new_from_name(display, 'pointer'));
}
},
onHoverLost: self => {
if (reset)
self.window.set_cursor(null);
},
});
}
}

View file

@ -2,32 +2,38 @@ import { App, Widget } from '../../imports.js';
const { Revealer, Box, Window } = Widget; const { Revealer, Box, Window } = Widget;
export const PopupWindow = ({ export default ({
name, name,
child, child,
transition = 'slide_down', transition = 'slide_down',
...params onOpen = rev => {},
}) => Window({ ...props
name, }) => {
popup: true, let window = Window({
visible: false, name,
layer: 'overlay', popup: true,
...params, visible: false,
layer: 'overlay',
...props,
child: Box({ child: Box({
style: 'min-height:1px; min-width:1px', style: 'min-height:1px; min-width:1px',
child: Revealer({ child: Revealer({
transition, transition,
transitionDuration: 500, transitionDuration: 500,
connections: [[App, (revealer, currentName, visible) => { connections: [[App, (rev, currentName, visible) => {
if (currentName === name) { if (currentName === name) {
revealer.reveal_child = visible; rev.reveal_child = visible;
onOpen(child);
if (visible && name !== 'overview') if (visible && name !== 'overview')
App.openWindow('closer'); App.openWindow('closer');
} }
}]], }]],
child: child, child: child,
}),
}), }),
}), });
}); window.getChild = () => child;
return window;
}

View file

@ -2,6 +2,15 @@ import { Widget } from '../../imports.js';
const { Box } = Widget; const { Box } = Widget;
export const Separator = width => Box({ export default (size, { vertical = false } = {}) => {
style: `min-width: ${width}px;`, if (vertical) {
}); return Box({
style: `min-height: ${size}px;`,
});
}
else {
return Box({
style: `min-width: ${size}px;`,
});
}
}

View file

@ -4,8 +4,17 @@ const { Box, Icon, Label, Button } = Widget;
import GLib from 'gi://GLib'; import GLib from 'gi://GLib';
const setTime = time => {
return GLib.DateTime
.new_from_unix_local(time)
.format('%H:%M');
};
const getDragState = box => box.get_parent().get_parent()
.get_parent().get_parent().get_parent()._dragging;
import Gesture from './gesture.js'; import Gesture from './gesture.js';
import { EventBox } from '../misc/cursorbox.js' import EventBox from '../misc/cursorbox.js'
const NotificationIcon = notif => { const NotificationIcon = notif => {
@ -14,19 +23,19 @@ const NotificationIcon = notif => {
if (Applications.query(notif.appEntry).length > 0) { if (Applications.query(notif.appEntry).length > 0) {
let app = Applications.query(notif.appEntry)[0]; let app = Applications.query(notif.appEntry)[0];
if (app.app.get_string('StartupWMClass') != null) { let wmClass = app.app.get_string('StartupWMClass');
if (app.app.get_filename().includes('discord'))
wmClass = 'discord';
if (wmClass != null) {
iconCmd = box => { iconCmd = box => {
if (!box.get_parent().get_parent().get_parent().get_parent().get_parent()._dragging) { if (!getDragState(box)) {
execAsync(['bash', '-c', `$AGS_PATH/launch-app.sh ${app.app.get_string('StartupWMClass')} ${app.app.get_string('Exec')}`]).catch(print); execAsync(['bash', '-c',
globalThis.closeAll(); `$AGS_PATH/launch-app.sh
} ${wmClass}
} ${app.app.get_string('Exec')}`
} ]).catch(print);
else if (app.app.get_filename().includes('discord')) {
iconCmd = box => {
if (!box.get_parent().get_parent().get_parent().get_parent().get_parent()._dragging) {
execAsync(['bash', '-c', `$AGS_PATH/launch-app.sh discord ${app.app.get_string('Exec')}`])
.catch(print);
globalThis.closeAll(); globalThis.closeAll();
} }
} }
@ -82,7 +91,7 @@ const NotificationIcon = notif => {
}); });
}; };
export default ({ notif, command = () => {}} = {}) => { export default ({ notif, command = () => {}, } = {}) => {
const BlockedApps = [ const BlockedApps = [
'Spotify', 'Spotify',
]; ];
@ -142,7 +151,7 @@ export default ({ notif, command = () => {}} = {}) => {
Label({ Label({
className: 'time', className: 'time',
valign: 'start', valign: 'start',
label: GLib.DateTime.new_from_unix_local(notif.time).format('%H:%M'), label: setTime(notif.time),
}), }),
EventBox({ EventBox({
reset: false, reset: false,

View file

@ -3,23 +3,30 @@ const { Button, Label, Box, Icon, Scrollable, Revealer } = Widget;
const { timeout } = Utils; const { timeout } = Utils;
import Notification from './base.js'; import Notification from './base.js';
import { EventBox } from '../misc/cursorbox.js'; import PopupWindow from '../misc/popup.js';
import { PopupWindow } from '../misc/popup.js'; import EventBox from '../misc/cursorbox.js';
const ClearButton = () => EventBox({ const ClearButton = () => EventBox({
child: Button({ child: Button({
onPrimaryClickRelease: button => { onPrimaryClickRelease: button => {
button._popups.children.forEach(ch => ch.child.setStyle(ch.child._leftAnim1)); button._popups.children.forEach(ch => {
ch.child.setStyle(ch.child._leftAnim1);
});
button._notifList.children.forEach(ch => { button._notifList.children.forEach(ch => {
ch.child.setStyle(ch.child._rightAnim1); if (ch.child)
ch.child.setStyle(ch.child._rightAnim1);
timeout(500, () => { timeout(500, () => {
button._notifList.remove(ch); button._notifList.remove(ch);
Notifications.notifications.forEach(n => n.close()); Notifications.clear();
}); });
}); });
}, },
properties: [['notifList'], ['popups']], properties: [
['notifList'],
['popups'],
],
connections: [[Notifications, button => { connections: [[Notifications, button => {
if (!button._notifList) if (!button._notifList)
button._notifList = NotificationList; button._notifList = NotificationList;
@ -33,8 +40,8 @@ const ClearButton = () => EventBox({
children: [ children: [
Label('Clear '), Label('Clear '),
Icon({ Icon({
connections: [[Notifications, icon => { connections: [[Notifications, self => {
icon.icon = Notifications.notifications.length > 0 self.icon = Notifications.notifications.length > 0
? 'user-trash-full-symbolic' : 'user-trash-symbolic'; ? 'user-trash-full-symbolic' : 'user-trash-symbolic';
}]], }]],
}), }),
@ -46,7 +53,11 @@ const ClearButton = () => EventBox({
const Header = () => Box({ const Header = () => Box({
className: 'header', className: 'header',
children: [ children: [
Label({ label: 'Notifications', hexpand: true, xalign: 0 }), Label({
label: 'Notifications',
hexpand: true,
xalign: 0,
}),
ClearButton(), ClearButton(),
], ],
}); });
@ -110,26 +121,28 @@ const Placeholder = () => Revealer({
}), }),
}); });
const NotificationCenterWidget = Box({ const NotificationCenterWidget = () => Box({
className: 'notification-center', className: 'notification-center',
vertical: true, vertical: true,
children: [ children: [
Header(), Header(),
Box({ Box({
className: 'notification-wallpaper-box', className: 'notification-wallpaper-box',
children: [Scrollable({ children: [
className: 'notification-list-box', Scrollable({
hscroll: 'never', className: 'notification-list-box',
vscroll: 'automatic', hscroll: 'never',
child: Box({ vscroll: 'automatic',
className: 'notification-list', child: Box({
vertical: true, className: 'notification-list',
children: [ vertical: true,
NotificationList, children: [
Placeholder(), NotificationList,
], Placeholder(),
}), ],
})], }),
})
],
}), }),
], ],
}); });
@ -138,5 +151,5 @@ export default () => PopupWindow({
name: 'notification-center', name: 'notification-center',
anchor: [ 'top', 'right' ], anchor: [ 'top', 'right' ],
margin: [ 8, 60, 0, 0 ], margin: [ 8, 60, 0, 0 ],
child: NotificationCenterWidget, child: NotificationCenterWidget(),
}); });

View file

@ -16,25 +16,25 @@ export default ({
child = '', child = '',
children = [], children = [],
properties = [[]], properties = [[]],
...params ...props
}) => { }) => {
let w = EventBox({ let widget = EventBox({
...params, ...props,
properties: [ properties: [
['dragging', false], ['dragging', false],
...properties, ...properties,
], ],
onHover: box => { onHover: self => {
box.window.set_cursor(Gdk.Cursor.new_from_name(display, 'grab')); self.window.set_cursor(Gdk.Cursor.new_from_name(display, 'grab'));
onHover(box); onHover(self);
}, },
onHoverLost: box => { onHoverLost: self => {
box.window.set_cursor(null); self.window.set_cursor(null);
onHoverLost(box); onHoverLost(self);
}, },
}); });
let gesture = Gtk.GestureDrag.new(w); let gesture = Gtk.GestureDrag.new(widget);
let leftAnim1 = `transition: margin 0.5s ease, opacity 0.5s ease; let leftAnim1 = `transition: margin 0.5s ease, opacity 0.5s ease;
margin-left: -${Number(maxOffset + endMargin)}px; margin-left: -${Number(maxOffset + endMargin)}px;
@ -56,10 +56,10 @@ export default ({
margin-right: -${Number(maxOffset + endMargin)}px; margin-right: -${Number(maxOffset + endMargin)}px;
margin-bottom: -70px; margin-top: -70px; opacity: 0;`; margin-bottom: -70px; margin-top: -70px; opacity: 0;`;
w.child = Box({ widget.child = Box({
properties: [ properties: [
['leftAnim1', leftAnim1], ['leftAnim1', leftAnim1],
['leftAnim2', leftAnim2], ['leftAnim2', leftAnim2],
['rightAnim1', rightAnim1], ['rightAnim1', rightAnim1],
['rightAnim2', rightAnim2], ['rightAnim2', rightAnim2],
['ready', false] ['ready', false]
@ -71,39 +71,39 @@ export default ({
style: leftAnim2, style: leftAnim2,
connections: [ connections: [
[gesture, box => { [gesture, self => {
var offset = gesture.get_offset()[1]; var offset = gesture.get_offset()[1];
if (offset >= 0) { if (offset >= 0) {
box.setStyle('margin-left: ' + Number(offset + startMargin) + 'px; ' + self.setStyle(`margin-left: ${Number(offset + startMargin)}px;
'margin-right: -' + Number(offset + startMargin) + 'px;'); margin-right: -${Number(offset + startMargin)}px;`);
} }
else { else {
offset = Math.abs(offset); offset = Math.abs(offset);
box.setStyle('margin-right: ' + Number(offset + startMargin) + 'px; ' + self.setStyle(`margin-right: ${Number(offset + startMargin)}px;
'margin-left: -' + Number(offset + startMargin) + 'px;'); margin-left: -${Number(offset + startMargin)}px;`);
} }
box.get_parent()._dragging = Math.abs(offset) > 10; self.get_parent()._dragging = Math.abs(offset) > 10;
if (w.window) if (widget.window)
w.window.set_cursor(Gdk.Cursor.new_from_name(display, 'grabbing')); widget.window.set_cursor(Gdk.Cursor.new_from_name(display, 'grabbing'));
}, 'drag-update'], }, 'drag-update'],
[gesture, box => { [gesture, self => {
if (!box._ready) { if (!self._ready) {
box.setStyle(`transition: margin 0.5s ease, opacity 0.5s ease; self.setStyle(`transition: margin 0.5s ease, opacity 0.5s ease;
margin-left: -${Number(maxOffset + endMargin)}px; margin-left: -${Number(maxOffset + endMargin)}px;
margin-right: ${Number(maxOffset + endMargin)}px; margin-right: ${Number(maxOffset + endMargin)}px;
margin-bottom: 0px; margin-top: 0px; opacity: 0;`); margin-bottom: 0px; margin-top: 0px; opacity: 0;`);
setTimeout(() => { setTimeout(() => {
box.setStyle('transition: margin 0.5s ease, opacity 0.5s ease; ' + self.setStyle(`transition: margin 0.5s ease, opacity 0.5s ease;
'margin-left: ' + startMargin + 'px; ' + margin-left: ${startMargin}px;
'margin-right: ' + startMargin + 'px; ' + margin-right: ${startMargin}px;
'margin-bottom: unset; margin-top: unset; opacity: 1;'); margin-bottom: unset; margin-top: unset; opacity: 1;`);
}, 500); }, 500);
setTimeout(() => box._ready = true, 1000); setTimeout(() => self._ready = true, 1000);
return; return;
} }
@ -111,32 +111,32 @@ export default ({
if (Math.abs(offset) > maxOffset) { if (Math.abs(offset) > maxOffset) {
if (offset > 0) { if (offset > 0) {
box.setStyle(rightAnim1); self.setStyle(rightAnim1);
setTimeout(() => box.setStyle(rightAnim2), 500); setTimeout(() => self.setStyle(rightAnim2), 500);
} }
else { else {
box.setStyle(leftAnim1); self.setStyle(leftAnim1);
setTimeout(() => box.setStyle(leftAnim2), 500); setTimeout(() => self.setStyle(leftAnim2), 500);
} }
setTimeout(() => { setTimeout(() => {
command(); command();
box.destroy(); self.destroy();
}, 1000); }, 1000);
} }
else { else {
box.setStyle('transition: margin 0.5s ease, opacity 0.5s ease; ' + self.setStyle(`transition: margin 0.5s ease, opacity 0.5s ease;
'margin-left: ' + startMargin + 'px; ' + margin-left: ${startMargin}px;
'margin-right: ' + startMargin + 'px; ' + margin-right: ${startMargin}px;
'margin-bottom: unset; margin-top: unset; opacity: 1;'); margin-bottom: unset; margin-top: unset; opacity: 1;`);
if (w.window) if (widget.window)
w.window.set_cursor(Gdk.Cursor.new_from_name(display, 'grab')); widget.window.set_cursor(Gdk.Cursor.new_from_name(display, 'grab'));
box.get_parent()._dragging = false; self.get_parent()._dragging = false;
} }
}, 'drag-end'], }, 'drag-end'],
], ],
}); });
return w; return widget;
}; };

View file

@ -8,12 +8,13 @@ const Popups = () => Box({
vertical: true, vertical: true,
properties: [ properties: [
['map', new Map()], ['map', new Map()],
['dismiss', (box, id, force = false) => {
if (!id || !box._map.has(id))
return;
if (box._map.get(id)._hovered && !force) ['dismiss', (box, id, force = false) => {
if (!id || !box._map.has(id) ||
box._map.get(id)._hovered && !force) {
return; return;
}
if (box._map.size - 1 === 0) if (box._map.size - 1 === 0)
box.get_parent().reveal_child = false; box.get_parent().reveal_child = false;
@ -27,6 +28,7 @@ const Popups = () => Box({
box._map.delete(id); box._map.delete(id);
}, 200); }, 200);
}], }],
['notify', (box, id) => { ['notify', (box, id) => {
if (!id || Notifications.dnd) if (!id || Notifications.dnd)
return; return;
@ -43,11 +45,12 @@ const Popups = () => Box({
})); }));
box.children = Array.from(box._map.values()).reverse(); box.children = Array.from(box._map.values()).reverse();
setTimeout(() => { setTimeout(() => {
box.get_parent().revealChild = true; box.get_parent().revealChild = true;
}, 10); }, 10);
box._map.get(id).interval = setInterval(() => { box._map.get(id).interval = setInterval(() => {
print('interval')
if (!box._map.get(id)._hovered) { if (!box._map.get(id)._hovered) {
box._map.get(id).child.setStyle(box._map.get(id).child._leftAnim1); box._map.get(id).child.setStyle(box._map.get(id).child._leftAnim1);

View file

@ -5,10 +5,12 @@ const { execAsync } = Utils;
import { WindowButton } from './dragndrop.js'; import { WindowButton } from './dragndrop.js';
import * as VARS from './variables.js'; import * as VARS from './variables.js';
// Has to be a traditional function for 'this' scope
Array.prototype.remove = function (el) { this.splice(this.indexOf(el), 1) }; Array.prototype.remove = function (el) { this.splice(this.indexOf(el), 1) };
const scale = size => size * VARS.SCALE - VARS.MARGIN; const scale = size => size * VARS.SCALE - VARS.MARGIN;
const getFontSize = client => Math.min(scale(client.size[0]), scale(client.size[1])) * VARS.ICON_SCALE; const getFontSize = client => Math.min(scale(client.size[0]),
scale(client.size[1])) * VARS.ICON_SCALE;
const IconStyle = client => `transition: font-size 0.2s linear; const IconStyle = client => `transition: font-size 0.2s linear;
min-width: ${scale(client.size[0])}px; min-width: ${scale(client.size[0])}px;
@ -31,14 +33,22 @@ const Client = (client, active, clients) => {
], ],
child: WindowButton({ child: WindowButton({
address: client.address, address: client.address,
onSecondaryClickRelease: () => execAsync(`hyprctl dispatch closewindow ${addr}`).catch(print), onSecondaryClickRelease: () => {
execAsync(`hyprctl dispatch closewindow ${addr}`)
.catch(print);
},
onPrimaryClickRelease: () => { onPrimaryClickRelease: () => {
if (wsId < 0) { if (wsId < 0) {
if (client.workspace.name === 'special') { if (client.workspace.name === 'special') {
execAsync(`hyprctl dispatch movetoworkspacesilent special:${wsId},${addr}`).then( execAsync(`hyprctl dispatch
movetoworkspacesilent special:${wsId},${addr}`)
.then(
execAsync(`hyprctl dispatch togglespecialworkspace ${wsId}`).then( execAsync(`hyprctl dispatch togglespecialworkspace ${wsId}`).then(
() => App.closeWindow('overview') () => App.closeWindow('overview')
).catch(print) ).catch(print)
).catch(print); ).catch(print);
} }
else { else {
@ -53,13 +63,15 @@ const Client = (client, active, clients) => {
let currentActive = clients.find(c => c.address === activeAddress) let currentActive = clients.find(c => c.address === activeAddress)
if (currentActive && currentActive.workspace.id < 0) { if (currentActive && currentActive.workspace.id < 0) {
execAsync(`hyprctl dispatch togglespecialworkspace ${wsName}`).catch(print); execAsync(`hyprctl dispatch togglespecialworkspace ${wsName}`)
.catch(print);
} }
execAsync(`hyprctl dispatch focuswindow ${addr}`).then( execAsync(`hyprctl dispatch focuswindow ${addr}`).then(
() => App.closeWindow('overview') () => App.closeWindow('overview')
).catch(print); ).catch(print);
} }
}, },
child: Icon({ child: Icon({
className: `window ${active}`, className: `window ${active}`,
style: IconStyle(client) + 'font-size: 10px;', style: IconStyle(client) + 'font-size: 10px;',
@ -70,15 +82,15 @@ const Client = (client, active, clients) => {
}; };
export function updateClients(box) { export function updateClients(box) {
execAsync('hyprctl clients -j').then( execAsync('hyprctl clients -j').then(out => {
result => { let clients = JSON.parse(out).filter(client => client.class)
let clients = JSON.parse(result).filter(client => client.class)
box._workspaces.forEach(workspace => { box._workspaces.forEach(workspace => {
let fixed = workspace.child.child.get_children()[2].children[0]; let fixed = workspace.getFixed();
let toRemove = fixed.get_children(); let toRemove = fixed.get_children();
clients.filter(client => client.workspace.id == workspace._id).forEach(client => { clients.filter(client => client.workspace.id == workspace._id)
.forEach(client => {
let active = ''; let active = '';
if (client.address == Hyprland.active.client.address) { if (client.address == Hyprland.active.client.address) {
active = 'active'; active = 'active';

View file

@ -6,7 +6,7 @@ import Gtk from 'gi://Gtk';
import Gdk from 'gi://Gdk'; import Gdk from 'gi://Gdk';
import Cairo from 'cairo'; import Cairo from 'cairo';
import { Button } from '../misc/cursorbox.js'; import Button from '../misc/cursorbox.js';
import { updateClients } from './clients.js'; import { updateClients } from './clients.js';
const TARGET = [Gtk.TargetEntry.new('text/plain', Gtk.TargetFlags.SAME_APP, 0)]; const TARGET = [Gtk.TargetEntry.new('text/plain', Gtk.TargetFlags.SAME_APP, 0)];
@ -29,13 +29,13 @@ function createSurfaceFromWidget(widget) {
}; };
let hidden = 0; let hidden = 0;
export const WorkspaceDrop = params => EventBox({ export const WorkspaceDrop = props => EventBox({
...params, ...props,
connections: [['drag-data-received', (eventbox, _c, _x, _y, data) => { connections: [['drag-data-received', (self, _c, _x, _y, data) => {
let id = eventbox.get_parent()._id; let id = self.get_parent()._id;
if (id < -1) { if (id < -1) {
id = eventbox.get_parent()._name; id = self.get_parent()._name;
} }
else if (id === -1) { else if (id === -1) {
id = `special:${++hidden}`; id = `special:${++hidden}`;
@ -46,26 +46,27 @@ export const WorkspaceDrop = params => EventBox({
execAsync(`hyprctl dispatch movetoworkspacesilent ${id},address:${data.get_text()}`) execAsync(`hyprctl dispatch movetoworkspacesilent ${id},address:${data.get_text()}`)
.catch(print); .catch(print);
}]], }]],
setup: eventbox => { setup: self => {
eventbox.drag_dest_set(Gtk.DestDefaults.ALL, TARGET, Gdk.DragAction.COPY); self.drag_dest_set(Gtk.DestDefaults.ALL, TARGET, Gdk.DragAction.COPY);
}, },
}); });
export const WindowButton = ({address, ...params} = {}) => Button({ export const WindowButton = ({address, ...props} = {}) => Button({
...params, type: "Button",
setup: button => { ...props,
button.drag_source_set(Gdk.ModifierType.BUTTON1_MASK, TARGET, Gdk.DragAction.COPY); setup: self => {
button.connect('drag-data-get', (_w, _c, data) => { self.drag_source_set(Gdk.ModifierType.BUTTON1_MASK, TARGET, Gdk.DragAction.COPY);
self.connect('drag-data-get', (_w, _c, data) => {
data.set_text(address, address.length); data.set_text(address, address.length);
}); });
button.connect('drag-begin', (_, context) => { self.connect('drag-begin', (_, context) => {
Gtk.drag_set_icon_surface(context, createSurfaceFromWidget(button)); Gtk.drag_set_icon_surface(context, createSurfaceFromWidget(self));
button.get_parent().revealChild = false; self.get_parent().revealChild = false;
}); });
button.connect('drag-end', () => { self.connect('drag-end', () => {
button.get_parent().destroy(); self.get_parent().destroy();
let mainBox = App.getWindow('overview').child.children[0].child; let mainBox = App.getWindow('overview').getChild();
updateClients(mainBox); updateClients(mainBox);
}); });
}, },

View file

@ -1,10 +1,11 @@
import { App, Hyprland, Widget } from '../../imports.js'; import { App, Hyprland, Widget } from '../../imports.js';
const { Box } = Widget; const { Box } = Widget;
import { PopupWindow } from '../misc/popup.js'; import PopupWindow from '../misc/popup.js';
import { WorkspaceRow, getWorkspaces, updateWorkspaces } from './workspaces.js'; import { WorkspaceRow, getWorkspaces, updateWorkspaces } from './workspaces.js';
import { updateClients } from './clients.js'; import { updateClients } from './clients.js';
function update(box) { function update(box) {
getWorkspaces(box); getWorkspaces(box);
updateWorkspaces(box); updateWorkspaces(box);
@ -14,6 +15,7 @@ function update(box) {
export default () => PopupWindow({ export default () => PopupWindow({
name: 'overview', name: 'overview',
transition: 'crossfade', transition: 'crossfade',
onOpen: child => update(child),
child: Box({ child: Box({
className: 'overview', className: 'overview',
@ -32,17 +34,12 @@ export default () => PopupWindow({
], ],
}), }),
], ],
// The timeout is because the list of clients is async connections: [[Hyprland, self => {
setup: box => setTimeout(() => update(box), 100), if (!App.getWindow('overview').visible)
connections: [ return;
[Hyprland, box => {
if (!App.getWindow('overview').visible)
return;
print('running overview'); update(self);
update(box); }]],
}],
],
properties: [ properties: [
['workspaces'], ['workspaces'],
], ],

View file

@ -6,7 +6,7 @@ import Gtk from 'gi://Gtk';
import { WorkspaceDrop } from './dragndrop.js'; import { WorkspaceDrop } from './dragndrop.js';
import * as VARS from './variables.js'; import * as VARS from './variables.js';
const DEFAULT_STYLE = `min-width: ${VARS.SCREEN.X * VARS.SCALE}px; const DEFAULT_STYLE = `min-width: ${VARS.SCREEN.X * VARS.SCALE}px;
min-height: ${VARS.SCREEN.Y * VARS.SCALE}px;`; min-height: ${VARS.SCREEN.Y * VARS.SCALE}px;`;
@ -48,126 +48,140 @@ export const WorkspaceRow = (className, i) => Revealer({
child: Box({ child: Box({
className: className, className: className,
children: [ children: [
Revealer({ Workspace(className === 'special' ? -1 : 1000,
transition: 'slide_right', className === 'special' ? 'special' : '',
properties: [ true),
['id', className === 'special' ? -1 : 1000],
['name', className === 'special' ? 'special' : ''],
],
child: WorkspaceDrop({
child: Overlay({
child: Box({
className: 'workspace',
style: DEFAULT_STYLE,
}),
overlays: [
Box({
className: 'workspace active',
style: `${DEFAULT_STYLE} opacity: 0;`,
}),
Box({
style: DEFAULT_STYLE,
children: [
Widget({
type: Gtk.Fixed,
}),
Label({
label: ' +',
style: 'font-size: 40px;',
}),
],
})
],
}),
}),
}),
], ],
}), }),
}), null], }), null],
}), }),
}); });
const Workspace = (id, name) => Revealer({ // TODO: please make this readable for the love of god
transition: 'slide_right', const Workspace = (id, name, extra = false) => {
transitionDuration: 500, let workspace;
properties: [ let fixed = Widget({
['id', id], type: Gtk.Fixed,
['name', name], });
['timeouts', []],
['wasActive', false],
],
connections: [[Hyprland, box => {
box._timeouts.forEach(timer => timer.destroy());
let activeId = Hyprland.active.workspace.id; if (!extra) {
let active = activeId === box._id; workspace = Revealer({
transition: 'slide_right',
transitionDuration: 500,
properties: [
['id', id],
['name', name],
['timeouts', []],
['wasActive', false],
],
connections: [[Hyprland, box => {
box._timeouts.forEach(timer => timer.destroy());
let rev = box.child.child.get_children()[1]; let activeId = Hyprland.active.workspace.id;
let n = activeId > box._id; let active = activeId === box._id;
if (Hyprland.getWorkspace(box._id)?.windows > 0 || active) { let rev = box.child.child.get_children()[1];
rev.setStyle(DEFAULT_STYLE); let n = activeId > box._id;
box._timeouts.push(setTimeout(() => {
box.revealChild = true;
}, 100));
} if (Hyprland.getWorkspace(box._id)?.windows > 0 || active) {
else if (!Hyprland.getWorkspace(box._id)?.windows > 0) { rev.setStyle(DEFAULT_STYLE);
rev.setStyle(DEFAULT_STYLE); box._timeouts.push(setTimeout(() => {
box._timeouts.push(setTimeout(() => { box.revealChild = true;
box.revealChild = false; }, 100));
}, 100));
return;
}
if (active) { }
rev.setStyle(`${DEFAULT_STYLE} else if (!Hyprland.getWorkspace(box._id)?.windows > 0) {
rev.setStyle(DEFAULT_STYLE);
box._timeouts.push(setTimeout(() => {
box.revealChild = false;
}, 100));
return;
}
if (active) {
rev.setStyle(`${DEFAULT_STYLE}
transition: margin 0.5s ease-in-out; transition: margin 0.5s ease-in-out;
opacity: 1;`); opacity: 1;`);
box._wasActive = true; box._wasActive = true;
} }
else if (box._wasActive) { else if (box._wasActive) {
box._timeouts.push(setTimeout(() => { box._timeouts.push(setTimeout(() => {
rev.setStyle(`${DEFAULT_STYLE} rev.setStyle(`${DEFAULT_STYLE}
transition: margin 0.5s ease-in-out; transition: margin 0.5s ease-in-out;
opacity: 1; margin-left: ${n ? '' : '-'}300px; opacity: 1; margin-left: ${n ? '' : '-'}300px;
margin-right: ${n ? '-' : ''}300px;`); margin-right: ${n ? '-' : ''}300px;`);
box._wasActive = false; box._wasActive = false;
}, 120)); }, 120));
box._timeouts.push(setTimeout(() => { box._timeouts.push(setTimeout(() => {
rev.setStyle(`${DEFAULT_STYLE} opacity: 0; rev.setStyle(`${DEFAULT_STYLE} opacity: 0;
margin-left: ${n ? '' : '-'}300px; margin-left: ${n ? '' : '-'}300px;
margin-right: ${n ? '-' : ''}300px;`); margin-right: ${n ? '-' : ''}300px;`);
}, 500)); }, 500));
} }
else { else {
rev.setStyle(`${DEFAULT_STYLE} opacity: 0; rev.setStyle(`${DEFAULT_STYLE} opacity: 0;
margin-left: ${n ? '' : '-'}300px; margin-left: ${n ? '' : '-'}300px;
margin-right: ${n ? '-' : ''}300px;`); margin-right: ${n ? '-' : ''}300px;`);
} }
}]], }]],
child: WorkspaceDrop({ child: WorkspaceDrop({
child: Overlay({ child: Overlay({
child: Box({ child: Box({
className: 'workspace active', className: 'workspace active',
style: `${DEFAULT_STYLE} opacity: 0;`, style: `${DEFAULT_STYLE} opacity: 0;`,
}),
overlays: [
Box({
className: 'workspace active',
style: `${DEFAULT_STYLE} opacity: 0;`,
}),
Box({
className: 'workspace',
style: DEFAULT_STYLE,
child: Widget({
type: Gtk.Fixed,
}), }),
}) overlays: [
Box({
className: 'workspace active',
style: `${DEFAULT_STYLE} opacity: 0;`,
}),
Box({
className: 'workspace',
style: DEFAULT_STYLE,
child: fixed,
})
],
}),
}),
});
}
else {
workspace = Revealer({
transition: 'slide_right',
properties: [
['id', id],
['name', name],
], ],
}), child: WorkspaceDrop({
}), child: Overlay({
}); child: Box({
className: 'workspace',
style: DEFAULT_STYLE,
}),
overlays: [
Box({
className: 'workspace active',
style: `${DEFAULT_STYLE} opacity: 0;`,
}),
Box({
style: DEFAULT_STYLE,
children: [
fixed,
Label({
label: ' +',
style: 'font-size: 40px;',
}),
],
})
],
}),
}),
});
}
workspace.getFixed = () => fixed;
return workspace;
};
export function updateWorkspaces(box) { export function updateWorkspaces(box) {
Hyprland.workspaces.forEach(ws => { Hyprland.workspaces.forEach(ws => {

View file

@ -1,15 +1,16 @@
import { Widget } from '../imports.js'; import { Widget } from '../imports.js';
const { CenterBox, Label } = Widget; const { CenterBox, Label } = Widget;
import { PopupWindow } from './misc/popup.js'; import PopupWindow from './misc/popup.js';
import { Button } from './misc/cursorbox.js' import Button from './misc/cursorbox.js'
const PowermenuWidget = CenterBox({ const PowermenuWidget = () => CenterBox({
className: 'powermenu', className: 'powermenu',
vertical: false, vertical: false,
startWidget: Button({ startWidget: Button({
type: "Button",
className: 'shutdown', className: 'shutdown',
onPrimaryClickRelease: 'systemctl poweroff', onPrimaryClickRelease: 'systemctl poweroff',
@ -19,6 +20,7 @@ const PowermenuWidget = CenterBox({
}), }),
centerWidget: Button({ centerWidget: Button({
type: "Button",
className: 'reboot', className: 'reboot',
onPrimaryClickRelease: 'systemctl reboot', onPrimaryClickRelease: 'systemctl reboot',
@ -28,6 +30,7 @@ const PowermenuWidget = CenterBox({
}), }),
endWidget: Button({ endWidget: Button({
type: "Button",
className: 'logout', className: 'logout',
onPrimaryClickRelease: 'hyprctl dispatch exit', onPrimaryClickRelease: 'hyprctl dispatch exit',
@ -40,5 +43,5 @@ const PowermenuWidget = CenterBox({
export default () => PopupWindow({ export default () => PopupWindow({
name: 'powermenu', name: 'powermenu',
transition: 'crossfade', transition: 'crossfade',
child: PowermenuWidget, child: PowermenuWidget(),
}); });

View file

@ -2,10 +2,14 @@ import { Network, Bluetooth, Audio, App, Utils, Widget } from '../../imports.js'
const { Box, CenterBox, Label, Icon } = Widget; const { Box, CenterBox, Label, Icon } = Widget;
const { execAsync } = Utils; const { execAsync } = Utils;
import { EventBox } from '../misc/cursorbox.js'; import EventBox from '../misc/cursorbox.js';
const GridButton = ({ command = () => {}, secondaryCommand = () => {}, icon } = {}) => Box({ const GridButton = ({
command = () => {},
secondaryCommand = () => {},
icon
} = {}) => Box({
className: 'grid-button', className: 'grid-button',
children: [ children: [
@ -27,7 +31,7 @@ const GridButton = ({ command = () => {}, secondaryCommand = () => {}, icon } =
], ],
}); });
const FirstRow = Box({ const FirstRow = () => Box({
className: 'button-row', className: 'button-row',
halign: 'center', halign: 'center',
style: 'margin-top: 15px; margin-bottom: 7px;', style: 'margin-top: 15px; margin-bottom: 7px;',
@ -35,7 +39,10 @@ const FirstRow = Box({
GridButton({ GridButton({
command: () => Network.toggleWifi(), command: () => Network.toggleWifi(),
secondaryCommand: () => execAsync(['bash', '-c', 'nm-connection-editor']).catch(print), secondaryCommand: () => {
execAsync(['bash', '-c', 'nm-connection-editor'])
.catch(print);
},
icon: Icon({ icon: Icon({
className: 'grid-label', className: 'grid-label',
connections: [[Network, icon => { connections: [[Network, icon => {
@ -50,35 +57,47 @@ const FirstRow = Box({
}), }),
GridButton({ GridButton({
command: () => execAsync(['bash', '-c', '$AGS_PATH/qs-toggles.sh blue-toggle']).catch(print), command: () => {
secondaryCommand: () => execAsync(['bash', '-c', 'blueberry']).catch(print), execAsync(['bash', '-c', '$AGS_PATH/qs-toggles.sh blue-toggle'])
.catch(print);
},
secondaryCommand: () => {
execAsync(['bash', '-c', 'blueberry'])
.catch(print)
},
icon: Icon({ icon: Icon({
className: 'grid-label', className: 'grid-label',
connections: [[Bluetooth, icon => { connections: [[Bluetooth, self => {
if (Bluetooth.enabled) { if (Bluetooth.enabled) {
icon.icon = 'bluetooth-active-symbolic'; self.icon = 'bluetooth-active-symbolic';
execAsync(['bash', '-c', 'echo 󰂯 > $HOME/.config/.bluetooth']).catch(print); execAsync(['bash', '-c', 'echo 󰂯 > $HOME/.config/.bluetooth'])
.catch(print);
} }
else { else {
icon.icon = 'bluetooth-disabled-symbolic'; self.icon = 'bluetooth-disabled-symbolic';
execAsync(['bash', '-c', 'echo 󰂲 > $HOME/.config/.bluetooth']).catch(print); execAsync(['bash', '-c', 'echo 󰂲 > $HOME/.config/.bluetooth'])
.catch(print);
} }
}, 'changed']], }, 'changed']],
}) })
}), }),
GridButton({ GridButton({
command: () => execAsync(['bash', '-c', '$AGS_PATH/qs-toggles.sh toggle-radio']).catch(print), command: () => {
secondaryCommand: () => execAsync(['notify-send', 'set this up moron']).catch(print), execAsync(['bash', '-c', '$AGS_PATH/qs-toggles.sh toggle-radio'])
.catch(print);
},
secondaryCommand: () => {
execAsync(['notify-send', 'set this up moron'])
.catch(print);
},
icon: Icon({ icon: Icon({
className: 'grid-label', className: 'grid-label',
connections: [[Network, icon => { connections: [[Network, self => {
if (Network.wifi.enabled) { if (Network.wifi.enabled)
icon.icon = 'airplane-mode-disabled-symbolic'; self.icon = 'airplane-mode-disabled-symbolic';
} else
else { self.icon = 'airplane-mode-symbolic';
icon.icon = 'airplane-mode-symbolic';
}
}, 'changed']], }, 'changed']],
}), }),
}), }),
@ -86,7 +105,7 @@ const FirstRow = Box({
], ],
}); });
const SubRow = CenterBox({ const SubRow = () => CenterBox({
halign: 'start', halign: 'start',
children: [ children: [
@ -94,8 +113,9 @@ const SubRow = CenterBox({
className: 'sub-label', className: 'sub-label',
truncate: 'end', truncate: 'end',
maxWidthChars: 12, maxWidthChars: 12,
connections: [[Network, label => { connections: [[Network, self => {
label.label = Network.wifi.ssid; // TODO: handle ethernet too
self.label = Network.wifi.ssid;
}, 'changed']], }, 'changed']],
}), }),
@ -103,20 +123,15 @@ const SubRow = CenterBox({
className: 'sub-label', className: 'sub-label',
truncate: 'end', truncate: 'end',
maxWidthChars: 12, maxWidthChars: 12,
connections: [[Bluetooth, label => { connections: [[Bluetooth, self => {
label.label = Bluetooth.connectedDevices[0] ? String(Bluetooth.connectedDevices[0]) : if (Bluetooth.connectedDevices[0])
'Disconnected'; self.label = String(Bluetooth.connectedDevices[0])
else
self.label = 'Disconnected';
}, 'changed']], }, 'changed']],
}), }),
Label({ null,
className: '',
truncate: 'end',
maxWidthChars: 12,
/*connections: [[Network, label => {
label.label = Network.wifi.ssid;
}, 'changed']],*/
}),
], ],
}); });
@ -135,15 +150,23 @@ const itemsMic = {
0: 'audio-input-microphone-muted-symbolic', 0: 'audio-input-microphone-muted-symbolic',
}; };
const SecondRow = Box({ const SecondRow = () => Box({
className: 'button-row', className: 'button-row',
halign: 'center', halign: 'center',
style: 'margin-top: 7px; margin-bottom: 15px;', style: 'margin-top: 7px; margin-bottom: 15px;',
children: [ children: [
GridButton({ GridButton({
command: () => execAsync(['swayosd-client', '--output-volume', 'mute-toggle']).catch(print), command: () => {
secondaryCommand: () => execAsync(['bash', '-c', 'pavucontrol']).catch(print), execAsync(['swayosd-client', '--output-volume', 'mute-toggle'])
.catch(print);
},
secondaryCommand: () => {
execAsync(['bash', '-c', 'pavucontrol'])
.catch(print);
},
icon: Icon({ icon: Icon({
className: 'grid-label', className: 'grid-label',
connections: [[Audio, icon => { connections: [[Audio, icon => {
@ -165,8 +188,16 @@ const SecondRow = Box({
}), }),
GridButton({ GridButton({
command: () => execAsync(['swayosd-client', '--input-volume', 'mute-toggle']).catch(print), command: () => {
secondaryCommand: () => execAsync(['bash', '-c', 'pavucontrol']).catch(print), execAsync(['swayosd-client', '--input-volume', 'mute-toggle'])
.catch(print);
},
secondaryCommand: () => {
execAsync(['bash', '-c', 'pavucontrol'])
.catch(print);
},
icon: Icon({ icon: Icon({
className: 'grid-label', className: 'grid-label',
connections: [[Audio, icon => { connections: [[Audio, icon => {
@ -188,7 +219,10 @@ const SecondRow = Box({
}), }),
GridButton({ GridButton({
command: () => execAsync(['bash', '-c', '$LOCK_PATH/lock.sh']).catch(print), command: () => {
execAsync(['bash', '-c', '$LOCK_PATH/lock.sh'])
.catch(print);
},
secondaryCommand: () => App.openWindow('powermenu'), secondaryCommand: () => App.openWindow('powermenu'),
icon: Label({ icon: Label({
className: 'grid-label', className: 'grid-label',
@ -199,14 +233,13 @@ const SecondRow = Box({
], ],
}); });
export default () => Box({
export const ButtonGrid = Box({
className: 'button-grid', className: 'button-grid',
vertical: true, vertical: true,
halign: 'center', halign: 'center',
children: [ children: [
FirstRow, FirstRow(),
SubRow, SubRow(),
SecondRow, SecondRow(),
], ],
}); });

View file

@ -3,14 +3,14 @@ const { Box, Label, Revealer, Icon } = Widget;
import Gtk from 'gi://Gtk'; import Gtk from 'gi://Gtk';
import { ButtonGrid } from './button-grid.js'; import ButtonGrid from './button-grid.js';
import { SliderBox } from './slider-box.js'; import SliderBox from './slider-box.js';
import Player from '../media-player/player.js'; import Player from '../media-player/player.js';
import { EventBox } from '../misc/cursorbox.js'; import EventBox from '../misc/cursorbox.js';
import { PopupWindow } from '../misc/popup.js'; import PopupWindow from '../misc/popup.js';
const QuickSettingsWidget = Box({ const QuickSettingsWidget = () => Box({
className: 'qs-container', className: 'qs-container',
vertical: true, vertical: true,
children: [ children: [
@ -27,9 +27,9 @@ const QuickSettingsWidget = Box({
style: 'margin-left: 20px' style: 'margin-left: 20px'
}), }),
ButtonGrid, ButtonGrid(),
SliderBox, SliderBox(),
EventBox({ EventBox({
child: Widget({ child: Widget({
@ -41,13 +41,15 @@ const QuickSettingsWidget = Box({
}); });
}, },
connections: [['toggled', button => { connections: [['toggled', button => {
let rev = button.get_parent().get_parent().get_parent().children[1];
if (button.get_active()) { if (button.get_active()) {
button.child.setStyle("-gtk-icon-transform: rotate(0deg);"); button.child.setStyle("-gtk-icon-transform: rotate(0deg);");
button.get_parent().get_parent().get_parent().children[1].revealChild = true; rev.revealChild = true;
} }
else { else {
button.child.setStyle('-gtk-icon-transform: rotate(180deg);'); button.child.setStyle('-gtk-icon-transform: rotate(180deg);');
button.get_parent().get_parent().get_parent().children[1].revealChild = false; rev.revealChild = false;
} }
}]], }]],
child: Icon({ child: Icon({
@ -73,5 +75,5 @@ export default () => PopupWindow({
name: 'quick-settings', name: 'quick-settings',
anchor: [ 'top', 'right' ], anchor: [ 'top', 'right' ],
margin: [ 8, 5, 0, ], margin: [ 8, 5, 0, ],
child: QuickSettingsWidget, child: QuickSettingsWidget(),
}); });

View file

@ -11,7 +11,7 @@ const items = {
}; };
export const SliderBox = Box({ export default () => Box({
className: 'slider-box', className: 'slider-box',
vertical: true, vertical: true,
halign: 'center', halign: 'center',
@ -73,11 +73,14 @@ export const SliderBox = Box({
['canChange', true], ['canChange', true],
], ],
onChange: ({ value }) => { onChange: ({ value }) => {
execAsync(`brightnessctl set ${value}`).catch(print); execAsync(`brightnessctl set ${value}`)
.catch(print);
}, },
connections: [[1000, slider => { connections: [[1000, slider => {
if (slider._canChange) { if (slider._canChange) {
execAsync('brightnessctl get').then(out => slider.value = out).catch(print); execAsync('brightnessctl get')
.then(out => slider.value = out)
.catch(print);
} }
}]], }]],
min: 0, min: 0,

View file

@ -8,13 +8,23 @@ export const RoundedCorner = (place, props) => Widget({
halign: place.includes('left') ? 'start' : 'end', halign: place.includes('left') ? 'start' : 'end',
valign: place.includes('top') ? 'start' : 'end', valign: place.includes('top') ? 'start' : 'end',
setup: widget => { setup: widget => {
const r = widget.get_style_context().get_property('border-radius', Gtk.StateFlags.NORMAL); const r = widget.get_style_context()
.get_property('border-radius', Gtk.StateFlags.NORMAL);
widget.set_size_request(r, r); widget.set_size_request(r, r);
widget.connect('draw', Lang.bind(widget, (widget, cr) => { widget.connect('draw', Lang.bind(widget, (widget, cr) => {
const c = widget.get_style_context().get_property('background-color', Gtk.StateFlags.NORMAL); const c = widget.get_style_context()
const r = widget.get_style_context().get_property('border-radius', Gtk.StateFlags.NORMAL); .get_property('background-color', Gtk.StateFlags.NORMAL);
const borderColor = widget.get_style_context().get_property('color', Gtk.StateFlags.NORMAL);
const borderWidth = widget.get_style_context().get_border(Gtk.StateFlags.NORMAL).left; // ur going to write border-width: something anyway const r = widget.get_style_context()
.get_property('border-radius', Gtk.StateFlags.NORMAL);
const borderColor = widget.get_style_context()
.get_property('color', Gtk.StateFlags.NORMAL);
// ur going to write border-width: something anyway
const borderWidth = widget.get_style_context()
.get_border(Gtk.StateFlags.NORMAL).left;
widget.set_size_request(r, r); widget.set_size_request(r, r);
switch (place) { switch (place) {
@ -43,7 +53,10 @@ export const RoundedCorner = (place, props) => Widget({
cr.setSourceRGBA(c.red, c.green, c.blue, c.alpha); cr.setSourceRGBA(c.red, c.green, c.blue, c.alpha);
cr.fill(); cr.fill();
cr.setLineWidth(borderWidth); cr.setLineWidth(borderWidth);
cr.setSourceRGBA(borderColor.red, borderColor.green, borderColor.blue, borderColor.alpha); cr.setSourceRGBA(borderColor.red,
borderColor.green,
borderColor.blue,
borderColor.alpha);
cr.stroke(); cr.stroke();
})); }));
}, },

View file

@ -28,7 +28,7 @@ exec-once = ags
exec-once = gnome-keyring-daemon --start --components=secrets exec-once = gnome-keyring-daemon --start --components=secrets
exec-once = squeekboard exec-once = squeekboard
exec-once = bash -c "sleep 1; ags -t applauncher" exec-once = bash -c "sleep 3; ags -t applauncher"
exec-once = hyprpaper exec-once = hyprpaper
exec-once = wl-paste --watch cliphist store exec-once = wl-paste --watch cliphist store