refactor(ags): start type checking
This commit is contained in:
parent
ae545731e7
commit
b85542091d
31 changed files with 774 additions and 639 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,6 +1,7 @@
|
||||||
*.egg-info
|
*.egg-info
|
||||||
*.temp
|
*.temp
|
||||||
*node_modules/
|
*node_modules/
|
||||||
|
*types/
|
||||||
*package-lock.json
|
*package-lock.json
|
||||||
**/ags/style.css
|
**/ags/style.css
|
||||||
result*
|
result*
|
||||||
|
|
|
@ -31,7 +31,6 @@
|
||||||
"curly": ["warn"],
|
"curly": ["warn"],
|
||||||
"default-case-last": ["warn"],
|
"default-case-last": ["warn"],
|
||||||
"default-param-last": ["error"],
|
"default-param-last": ["error"],
|
||||||
"dot-notation": ["warn", { "allowPattern": ".*-|_.*" }],
|
|
||||||
"eqeqeq": ["error", "smart"],
|
"eqeqeq": ["error", "smart"],
|
||||||
"func-names": ["warn", "never"],
|
"func-names": ["warn", "never"],
|
||||||
"func-style": ["warn", "expression"],
|
"func-style": ["warn", "expression"],
|
||||||
|
@ -53,7 +52,6 @@
|
||||||
"ignoreDefaultValues": true
|
"ignoreDefaultValues": true
|
||||||
}],
|
}],
|
||||||
"no-multi-assign": ["error"],
|
"no-multi-assign": ["error"],
|
||||||
"no-negated-condition": ["warn"],
|
|
||||||
"no-new": ["error"],
|
"no-new": ["error"],
|
||||||
"no-new-func": ["error"],
|
"no-new-func": ["error"],
|
||||||
"no-new-wrappers": ["error"],
|
"no-new-wrappers": ["error"],
|
||||||
|
|
|
@ -7,15 +7,24 @@ import { lookUpIcon } from 'resource:///com/github/Aylur/ags/utils.js';
|
||||||
import EventBox from '../misc/cursorbox.js';
|
import EventBox from '../misc/cursorbox.js';
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {import('types/service/applications.js').Application} app
|
||||||
|
*/
|
||||||
export default (app) => {
|
export default (app) => {
|
||||||
const icon = Icon({
|
const icon = Icon({ size: 42 });
|
||||||
icon: lookUpIcon(app.icon_name) ?
|
const iconString = app.app.get_string('Icon');
|
||||||
app.icon_name :
|
|
||||||
app.app.get_string('Icon') === 'nix-snowflake' ?
|
if (app.icon_name) {
|
||||||
'' :
|
if (lookUpIcon(app.icon_name)) {
|
||||||
app.app.get_string('Icon'),
|
icon.icon = app.icon_name;
|
||||||
size: 42,
|
}
|
||||||
});
|
else if (iconString && iconString !== 'nix-snowflake') {
|
||||||
|
icon.icon = iconString;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
icon.icon = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const textBox = Box({
|
const textBox = Box({
|
||||||
vertical: true,
|
vertical: true,
|
||||||
|
@ -46,14 +55,13 @@ export default (app) => {
|
||||||
hexpand: true,
|
hexpand: true,
|
||||||
class_name: 'app',
|
class_name: 'app',
|
||||||
|
|
||||||
setup: (self) => {
|
attribute: { app },
|
||||||
self.app = app;
|
|
||||||
},
|
|
||||||
|
|
||||||
onPrimaryClickRelease: () => {
|
onPrimaryClickRelease: (self) => {
|
||||||
App.closeWindow('applauncher');
|
App.closeWindow('applauncher');
|
||||||
Hyprland.sendMessage(`dispatch exec sh -c ${app.executable}`);
|
Hyprland.sendMessage(`dispatch exec sh -c
|
||||||
++app.frequency;
|
${self.attribute.app.executable}`);
|
||||||
|
++self.attribute.app.frequency;
|
||||||
},
|
},
|
||||||
|
|
||||||
child: Box({
|
child: Box({
|
||||||
|
|
|
@ -14,17 +14,20 @@ const Applauncher = ({ window_name = 'applauncher' } = {}) => {
|
||||||
let fzfResults;
|
let fzfResults;
|
||||||
const list = ListBox();
|
const list = ListBox();
|
||||||
|
|
||||||
|
/** @param {String} text */
|
||||||
const setSort = (text) => {
|
const setSort = (text) => {
|
||||||
const fzf = new Fzf(Applications.list, {
|
const fzf = new Fzf(Applications.list, {
|
||||||
selector: (app) => app.name,
|
selector: (app) => app.name,
|
||||||
tiebreakers: [(a, b) => b._frequency -
|
tiebreakers: [(a, b) => b.frequency -
|
||||||
a._frequency],
|
a.frequency],
|
||||||
});
|
});
|
||||||
|
|
||||||
fzfResults = fzf.find(text);
|
fzfResults = fzf.find(text);
|
||||||
list.set_sort_func((a, b) => {
|
list.set_sort_func((a, b) => {
|
||||||
const row1 = a.get_children()[0]?.app.name;
|
// @ts-expect-error
|
||||||
const row2 = b.get_children()[0]?.app.name;
|
const row1 = a.get_children()[0]?.attribute.app.name;
|
||||||
|
// @ts-expect-error
|
||||||
|
const row2 = b.get_children()[0]?.attribute.app.name;
|
||||||
|
|
||||||
if (!row1 || !row2) {
|
if (!row1 || !row2) {
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -51,9 +54,10 @@ const Applauncher = ({ window_name = 'applauncher' } = {}) => {
|
||||||
|
|
||||||
makeNewChildren();
|
makeNewChildren();
|
||||||
|
|
||||||
|
// FIXME: always visible
|
||||||
const placeholder = Label({
|
const placeholder = Label({
|
||||||
label: " Couldn't find a match",
|
label: " Couldn't find a match",
|
||||||
className: 'placeholder',
|
class_name: 'placeholder',
|
||||||
});
|
});
|
||||||
|
|
||||||
const entry = Entry({
|
const entry = Entry({
|
||||||
|
@ -73,17 +77,23 @@ const Applauncher = ({ window_name = 'applauncher' } = {}) => {
|
||||||
},
|
},
|
||||||
|
|
||||||
on_change: ({ text }) => {
|
on_change: ({ text }) => {
|
||||||
|
if (!text) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
setSort(text);
|
setSort(text);
|
||||||
let visibleApps = 0;
|
let visibleApps = 0;
|
||||||
|
|
||||||
list.get_children().forEach((row) => {
|
list.get_children().forEach((row) => {
|
||||||
|
// @ts-expect-error
|
||||||
row.changed();
|
row.changed();
|
||||||
|
|
||||||
|
// @ts-expect-error
|
||||||
const item = row.get_children()[0];
|
const item = row.get_children()[0];
|
||||||
|
|
||||||
if (item?.app) {
|
if (item?.attribute.app) {
|
||||||
const isMatching = fzfResults.find((r) => {
|
const isMatching = fzfResults.find((r) => {
|
||||||
return r.item.name === item.app.name;
|
return r.item.name === item.attribute.app.name;
|
||||||
});
|
});
|
||||||
|
|
||||||
row.visible = isMatching;
|
row.visible = isMatching;
|
||||||
|
@ -98,7 +108,7 @@ const Applauncher = ({ window_name = 'applauncher' } = {}) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
return Box({
|
return Box({
|
||||||
className: 'applauncher',
|
class_name: 'applauncher',
|
||||||
vertical: true,
|
vertical: true,
|
||||||
|
|
||||||
setup: (self) => {
|
setup: (self) => {
|
||||||
|
@ -120,7 +130,7 @@ const Applauncher = ({ window_name = 'applauncher' } = {}) => {
|
||||||
|
|
||||||
children: [
|
children: [
|
||||||
Box({
|
Box({
|
||||||
className: 'header',
|
class_name: 'header',
|
||||||
children: [
|
children: [
|
||||||
Icon('preferences-system-search-symbolic'),
|
Icon('preferences-system-search-symbolic'),
|
||||||
entry,
|
entry,
|
||||||
|
|
|
@ -5,54 +5,49 @@ import { Label, Box, EventBox, Icon, Revealer } from 'resource:///com/github/Ayl
|
||||||
import { SpeakerIcon } from '../../misc/audio-icons.js';
|
import { SpeakerIcon } from '../../misc/audio-icons.js';
|
||||||
import Separator from '../../misc/separator.js';
|
import Separator from '../../misc/separator.js';
|
||||||
|
|
||||||
|
|
||||||
const SpeakerIndicator = (props) => Icon({
|
|
||||||
...props,
|
|
||||||
binds: [['icon', SpeakerIcon, 'value']],
|
|
||||||
});
|
|
||||||
|
|
||||||
const SpeakerPercentLabel = (props) => Label({
|
|
||||||
...props,
|
|
||||||
setup: (self) => {
|
|
||||||
self.hook(Audio, (label) => {
|
|
||||||
if (Audio.speaker) {
|
|
||||||
label.label = `${Math.round(Audio.speaker.volume * 100)}%`;
|
|
||||||
}
|
|
||||||
}, 'speaker-changed');
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const SPACING = 5;
|
const SPACING = 5;
|
||||||
|
|
||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
const rev = Revealer({
|
const icon = Icon({
|
||||||
|
icon: SpeakerIcon.bind(),
|
||||||
|
});
|
||||||
|
|
||||||
|
const hoverRevLabel = Revealer({
|
||||||
transition: 'slide_right',
|
transition: 'slide_right',
|
||||||
|
|
||||||
child: Box({
|
child: Box({
|
||||||
|
|
||||||
children: [
|
children: [
|
||||||
Separator(SPACING),
|
Separator(SPACING),
|
||||||
SpeakerPercentLabel(),
|
|
||||||
|
Label().hook(Audio, (self) => {
|
||||||
|
if (Audio.speaker?.volume) {
|
||||||
|
self.label =
|
||||||
|
`${Math.round(Audio.speaker?.volume * 100)}%`;
|
||||||
|
}
|
||||||
|
}, 'speaker-changed'),
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
const widget = EventBox({
|
const widget = EventBox({
|
||||||
onHover: () => {
|
on_hover: () => {
|
||||||
rev.revealChild = true;
|
hoverRevLabel.reveal_child = true;
|
||||||
},
|
},
|
||||||
onHoverLost: () => {
|
on_hover_lost: () => {
|
||||||
rev.revealChild = false;
|
hoverRevLabel.reveal_child = false;
|
||||||
},
|
},
|
||||||
child: Box({
|
|
||||||
className: 'audio',
|
|
||||||
children: [
|
|
||||||
SpeakerIndicator(),
|
|
||||||
|
|
||||||
rev,
|
child: Box({
|
||||||
|
class_name: 'audio',
|
||||||
|
|
||||||
|
children: [
|
||||||
|
icon,
|
||||||
|
hoverRevLabel,
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
widget.rev = rev;
|
|
||||||
|
|
||||||
return widget;
|
return widget;
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,41 +5,28 @@ import { Label, Icon, Box } from 'resource:///com/github/Aylur/ags/widget.js';
|
||||||
import Separator from '../../misc/separator.js';
|
import Separator from '../../misc/separator.js';
|
||||||
|
|
||||||
const LOW_BATT = 20;
|
const LOW_BATT = 20;
|
||||||
|
const SPACING = 5;
|
||||||
|
|
||||||
|
|
||||||
const Indicator = () => Icon({
|
export default () => Box({
|
||||||
className: 'battery-indicator',
|
class_name: 'toggle-off battery',
|
||||||
|
|
||||||
binds: [['icon', Battery, 'icon-name']],
|
children: [
|
||||||
|
Icon({
|
||||||
setup: (self) => {
|
class_name: 'battery-indicator',
|
||||||
self.hook(Battery, () => {
|
// @ts-expect-error
|
||||||
|
icon: Battery.bind('icon_name'),
|
||||||
|
}).hook(Battery, (self) => {
|
||||||
self.toggleClassName('charging', Battery.charging);
|
self.toggleClassName('charging', Battery.charging);
|
||||||
self.toggleClassName('charged', Battery.charged);
|
self.toggleClassName('charged', Battery.charged);
|
||||||
self.toggleClassName('low', Battery.percent < LOW_BATT);
|
self.toggleClassName('low', Battery.percent < LOW_BATT);
|
||||||
});
|
}),
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const LevelLabel = (props) => Label({
|
|
||||||
...props,
|
|
||||||
className: 'label',
|
|
||||||
|
|
||||||
setup: (self) => {
|
|
||||||
self.hook(Battery, () => {
|
|
||||||
self.label = `${Battery.percent}%`;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const SPACING = 5;
|
|
||||||
|
|
||||||
export default () => Box({
|
|
||||||
className: 'toggle-off battery',
|
|
||||||
|
|
||||||
children: [
|
|
||||||
Indicator(),
|
|
||||||
Separator(SPACING),
|
Separator(SPACING),
|
||||||
LevelLabel(),
|
|
||||||
|
Label({
|
||||||
|
label: Battery.bind('percent')
|
||||||
|
.transform((v) => `${v}%`),
|
||||||
|
}),
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,14 +1,15 @@
|
||||||
|
// @ts-expect-error
|
||||||
import Bluetooth from 'resource:///com/github/Aylur/ags/service/bluetooth.js';
|
import Bluetooth from 'resource:///com/github/Aylur/ags/service/bluetooth.js';
|
||||||
|
|
||||||
import { Label, Box, EventBox, Icon, Revealer } from 'resource:///com/github/Aylur/ags/widget.js';
|
import { Label, Box, EventBox, Icon, Revealer } from 'resource:///com/github/Aylur/ags/widget.js';
|
||||||
|
|
||||||
import Separator from '../../misc/separator.js';
|
import Separator from '../../misc/separator.js';
|
||||||
|
|
||||||
|
const SPACING = 5;
|
||||||
|
|
||||||
const Indicator = (props) => Icon({
|
|
||||||
...props,
|
export default () => {
|
||||||
setup: (self) => {
|
const icon = Icon().hook(Bluetooth, (self) => {
|
||||||
self.hook(Bluetooth, () => {
|
|
||||||
if (Bluetooth.enabled) {
|
if (Bluetooth.enabled) {
|
||||||
self.icon = Bluetooth.connectedDevices[0] ?
|
self.icon = Bluetooth.connectedDevices[0] ?
|
||||||
Bluetooth.connectedDevices[0].iconName :
|
Bluetooth.connectedDevices[0].iconName :
|
||||||
|
@ -18,51 +19,41 @@ const Indicator = (props) => Icon({
|
||||||
self.icon = 'bluetooth-disabled-symbolic';
|
self.icon = 'bluetooth-disabled-symbolic';
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const ConnectedLabel = (props) => Label({
|
const hoverRevLabel = Revealer({
|
||||||
...props,
|
transition: 'slide_right',
|
||||||
setup: (self) => {
|
|
||||||
self.hook(Bluetooth, () => {
|
child: Box({
|
||||||
|
|
||||||
|
children: [
|
||||||
|
Separator(SPACING),
|
||||||
|
|
||||||
|
Label().hook(Bluetooth, (self) => {
|
||||||
self.label = Bluetooth.connectedDevices[0] ?
|
self.label = Bluetooth.connectedDevices[0] ?
|
||||||
`${Bluetooth.connectedDevices[0]}` :
|
`${Bluetooth.connectedDevices[0]}` :
|
||||||
'Disconnected';
|
'Disconnected';
|
||||||
}, 'notify::connected-devices');
|
}, 'notify::connected-devices'),
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const SPACING = 5;
|
|
||||||
|
|
||||||
export default () => {
|
|
||||||
const rev = Revealer({
|
|
||||||
transition: 'slide_right',
|
|
||||||
child: Box({
|
|
||||||
children: [
|
|
||||||
Separator(SPACING),
|
|
||||||
ConnectedLabel(),
|
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
const widget = EventBox({
|
const widget = EventBox({
|
||||||
onHover: () => {
|
on_hover: () => {
|
||||||
rev.revealChild = true;
|
hoverRevLabel.reveal_child = true;
|
||||||
},
|
},
|
||||||
onHoverLost: () => {
|
on_hover_lost: () => {
|
||||||
rev.revealChild = false;
|
hoverRevLabel.reveal_child = false;
|
||||||
},
|
},
|
||||||
child: Box({
|
|
||||||
className: 'bluetooth',
|
|
||||||
children: [
|
|
||||||
Indicator(),
|
|
||||||
|
|
||||||
rev,
|
child: Box({
|
||||||
|
class_name: 'bluetooth',
|
||||||
|
|
||||||
|
children: [
|
||||||
|
icon,
|
||||||
|
hoverRevLabel,
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
widget.rev = rev;
|
|
||||||
|
|
||||||
return widget;
|
return widget;
|
||||||
};
|
};
|
||||||
|
|
|
@ -6,48 +6,44 @@ import Separator from '../../misc/separator.js';
|
||||||
const SPACING = 5;
|
const SPACING = 5;
|
||||||
|
|
||||||
|
|
||||||
const Indicator = (props) => Icon({
|
|
||||||
...props,
|
|
||||||
binds: [['icon', Brightness, 'screen-icon']],
|
|
||||||
});
|
|
||||||
|
|
||||||
const BrightnessPercentLabel = (props) => Label({
|
|
||||||
...props,
|
|
||||||
setup: (self) => {
|
|
||||||
self.hook(Brightness, () => {
|
|
||||||
self.label = `${Math.round(Brightness.screen * 100)}%`;
|
|
||||||
}, 'screen');
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
const rev = Revealer({
|
const icon = Icon({
|
||||||
|
// @ts-expect-error
|
||||||
|
icon: Brightness.bind('screenIcon'),
|
||||||
|
});
|
||||||
|
|
||||||
|
const hoverRevLabel = Revealer({
|
||||||
transition: 'slide_right',
|
transition: 'slide_right',
|
||||||
|
|
||||||
child: Box({
|
child: Box({
|
||||||
|
|
||||||
children: [
|
children: [
|
||||||
Separator(SPACING),
|
Separator(SPACING),
|
||||||
BrightnessPercentLabel(),
|
|
||||||
|
Label().hook(Brightness, (self) => {
|
||||||
|
self.label = `${Math.round(Brightness.screen * 100)}%`;
|
||||||
|
}, 'screen'),
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
const widget = EventBox({
|
const widget = EventBox({
|
||||||
onHover: () => {
|
on_hover: () => {
|
||||||
rev.revealChild = true;
|
hoverRevLabel.reveal_child = true;
|
||||||
},
|
},
|
||||||
onHoverLost: () => {
|
on_hover_lost: () => {
|
||||||
rev.revealChild = false;
|
hoverRevLabel.reveal_child = false;
|
||||||
},
|
},
|
||||||
|
|
||||||
child: Box({
|
child: Box({
|
||||||
className: 'brightness',
|
class_name: 'brightness',
|
||||||
|
|
||||||
children: [
|
children: [
|
||||||
Indicator(),
|
icon,
|
||||||
rev,
|
hoverRevLabel,
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
widget.rev = rev;
|
|
||||||
|
|
||||||
return widget;
|
return widget;
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,26 +2,11 @@ import App from 'resource:///com/github/Aylur/ags/app.js';
|
||||||
|
|
||||||
import { Label } from 'resource:///com/github/Aylur/ags/widget.js';
|
import { Label } from 'resource:///com/github/Aylur/ags/widget.js';
|
||||||
|
|
||||||
import GLib from 'gi://GLib';
|
const { DateTime } = imports.gi.GLib;
|
||||||
const { DateTime } = GLib;
|
|
||||||
|
|
||||||
import EventBox from '../../misc/cursorbox.js';
|
import EventBox from '../../misc/cursorbox.js';
|
||||||
|
|
||||||
|
|
||||||
const ClockModule = () => Label({
|
|
||||||
className: 'clock',
|
|
||||||
|
|
||||||
setup: (self) => {
|
|
||||||
self.poll(1000, () => {
|
|
||||||
const time = DateTime.new_now_local();
|
|
||||||
|
|
||||||
self.label = time.format('%a. ') +
|
|
||||||
time.get_day_of_month() +
|
|
||||||
time.format(' %b. %H:%M');
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
export default () => EventBox({
|
export default () => EventBox({
|
||||||
className: 'toggle-off',
|
className: 'toggle-off',
|
||||||
|
|
||||||
|
@ -35,5 +20,12 @@ export default () => EventBox({
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
child: ClockModule(),
|
child: Label({ class_name: 'clock' })
|
||||||
|
.poll(1000, (self) => {
|
||||||
|
const time = DateTime.new_now_local();
|
||||||
|
|
||||||
|
self.label = time.format('%a. ') +
|
||||||
|
time.get_day_of_month() +
|
||||||
|
time.format(' %b. %H:%M');
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
|
|
|
@ -11,19 +11,15 @@ export default () => Box({
|
||||||
children: [
|
children: [
|
||||||
Separator(SPACING / 2),
|
Separator(SPACING / 2),
|
||||||
|
|
||||||
Icon({
|
Icon({ size: 30 })
|
||||||
size: 30,
|
.hook(Hyprland.active.client, (self) => {
|
||||||
setup: (self) => {
|
|
||||||
self.hook(Hyprland.active.client, () => {
|
|
||||||
const app = Applications
|
const app = Applications
|
||||||
.query(Hyprland.active.client.class)[0];
|
.query(Hyprland.active.client.class)[0];
|
||||||
|
|
||||||
if (app) {
|
if (app) {
|
||||||
self.icon = app.iconName;
|
self.icon = app.icon_name;
|
||||||
self.visible = Hyprland.active.client.title !== '';
|
self.visible = Hyprland.active.client.title !== '';
|
||||||
}
|
}
|
||||||
});
|
|
||||||
},
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
Separator(SPACING),
|
Separator(SPACING),
|
||||||
|
@ -31,7 +27,7 @@ export default () => Box({
|
||||||
Label({
|
Label({
|
||||||
css: 'color: #CBA6F7; font-size: 18px',
|
css: 'color: #CBA6F7; font-size: 18px',
|
||||||
truncate: 'end',
|
truncate: 'end',
|
||||||
binds: [['label', Hyprland.active.client, 'title']],
|
label: Hyprland.active.client.bind('title'),
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
|
@ -5,7 +5,7 @@ import Variable from 'resource:///com/github/Aylur/ags/variable.js';
|
||||||
import EventBox from '../../misc/cursorbox.js';
|
import EventBox from '../../misc/cursorbox.js';
|
||||||
import Persist from '../../misc/persist.js';
|
import Persist from '../../misc/persist.js';
|
||||||
|
|
||||||
const HeartState = Variable();
|
const HeartState = Variable('');
|
||||||
|
|
||||||
Persist({
|
Persist({
|
||||||
name: 'heart',
|
name: 'heart',
|
||||||
|
@ -22,7 +22,7 @@ export default () => EventBox({
|
||||||
},
|
},
|
||||||
|
|
||||||
child: Label({
|
child: Label({
|
||||||
className: 'heart-toggle',
|
class_name: 'heart-toggle',
|
||||||
binds: [['label', HeartState, 'value']],
|
label: HeartState.bind(),
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
|
@ -8,10 +8,12 @@ const DEFAULT_KB = 'at-translated-set-2-keyboard';
|
||||||
const SPACING = 4;
|
const SPACING = 4;
|
||||||
|
|
||||||
|
|
||||||
const Indicator = () => Label({
|
/**
|
||||||
css: 'font-size: 20px;',
|
* @param {Label} self
|
||||||
setup: (self) => {
|
* @param {string} layout
|
||||||
self.hook(Hyprland, (_, _n, layout) => {
|
* @param {void} _
|
||||||
|
*/
|
||||||
|
const getKbdLayout = (self, _, layout) => {
|
||||||
if (layout) {
|
if (layout) {
|
||||||
if (layout === 'error') {
|
if (layout === 'error') {
|
||||||
return;
|
return;
|
||||||
|
@ -19,58 +21,62 @@ const Indicator = () => Label({
|
||||||
|
|
||||||
const shortName = layout.match(/\(([A-Za-z]+)\)/);
|
const shortName = layout.match(/\(([A-Za-z]+)\)/);
|
||||||
|
|
||||||
|
// @ts-expect-error
|
||||||
self.label = shortName ? shortName[1] : layout;
|
self.label = shortName ? shortName[1] : layout;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// At launch, kb layout is undefined
|
// At launch, kb layout is undefined
|
||||||
Hyprland.sendMessage('j/devices').then((obj) => {
|
Hyprland.sendMessage('j/devices').then((obj) => {
|
||||||
const kb = JSON.parse(obj).keyboards
|
const kb = Array.from(JSON.parse(obj).keyboards)
|
||||||
.find((val) => val.name === DEFAULT_KB);
|
.find((v) => v.name === DEFAULT_KB);
|
||||||
|
|
||||||
layout = kb['active_keymap'];
|
layout = kb['active_keymap'];
|
||||||
|
|
||||||
const shortName = layout
|
const shortName = layout
|
||||||
.match(/\(([A-Za-z]+)\)/);
|
.match(/\(([A-Za-z]+)\)/);
|
||||||
|
|
||||||
|
// @ts-expect-error
|
||||||
self.label = shortName ? shortName[1] : layout;
|
self.label = shortName ? shortName[1] : layout;
|
||||||
}).catch(print);
|
}).catch(print);
|
||||||
}
|
}
|
||||||
}, 'keyboard-layout');
|
};
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
const rev = Revealer({
|
const hoverRevLabel = Revealer({
|
||||||
transition: 'slide_right',
|
transition: 'slide_right',
|
||||||
|
|
||||||
child: Box({
|
child: Box({
|
||||||
|
|
||||||
children: [
|
children: [
|
||||||
Separator(SPACING),
|
Separator(SPACING),
|
||||||
Indicator(),
|
|
||||||
|
Label({ css: 'font-size: 20px;' })
|
||||||
|
.hook(Hyprland, getKbdLayout, 'keyboard-layout'),
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
const widget = EventBox({
|
const widget = EventBox({
|
||||||
onHover: () => {
|
on_hover: () => {
|
||||||
rev.revealChild = true;
|
hoverRevLabel.reveal_child = true;
|
||||||
},
|
},
|
||||||
onHoverLost: () => {
|
on_hover_lost: () => {
|
||||||
rev.revealChild = false;
|
hoverRevLabel.reveal_child = false;
|
||||||
},
|
},
|
||||||
|
|
||||||
child: Box({
|
child: Box({
|
||||||
css: 'padding: 0 10px; margin-right: -10px;',
|
css: 'padding: 0 10px; margin-right: -10px;',
|
||||||
|
|
||||||
children: [
|
children: [
|
||||||
Icon({
|
Icon({
|
||||||
icon: 'input-keyboard-symbolic',
|
icon: 'input-keyboard-symbolic',
|
||||||
size: 20,
|
size: 20,
|
||||||
}),
|
}),
|
||||||
|
|
||||||
rev,
|
hoverRevLabel,
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
widget.rev = rev;
|
|
||||||
|
|
||||||
return widget;
|
return widget;
|
||||||
};
|
};
|
||||||
|
|
|
@ -4,30 +4,25 @@ import { Label, Box, EventBox, Icon, Revealer } from 'resource:///com/github/Ayl
|
||||||
|
|
||||||
import Separator from '../../misc/separator.js';
|
import Separator from '../../misc/separator.js';
|
||||||
|
|
||||||
|
const SPACING = 5;
|
||||||
|
|
||||||
const Indicator = (props) => Icon({
|
|
||||||
...props,
|
export default () => {
|
||||||
setup: (self) => {
|
const indicator = Icon().hook(Network, (self) => {
|
||||||
self.hook(Network, () => {
|
|
||||||
if (Network.wifi.internet === 'connected' ||
|
if (Network.wifi.internet === 'connected' ||
|
||||||
Network.wifi.internet === 'connecting') {
|
Network.wifi.internet === 'connecting') {
|
||||||
self.icon = Network.wifi.iconName;
|
self.icon = Network.wifi.icon_name;
|
||||||
}
|
}
|
||||||
else if (Network.wired.internet === 'connected' ||
|
else if (Network.wired.internet === 'connected' ||
|
||||||
Network.wired.internet === 'connecting') {
|
Network.wired.internet === 'connecting') {
|
||||||
self.icon = Network.wired.iconName;
|
self.icon = Network.wired.icon_name;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
self.icon = Network.wifi.iconName;
|
self.icon = Network.wifi.icon_name;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const APLabel = (props) => Label({
|
const label = Label().hook(Network, (self) => {
|
||||||
...props,
|
|
||||||
setup: (self) => {
|
|
||||||
self.hook(Network, () => {
|
|
||||||
if (Network.wifi.internet === 'connected' ||
|
if (Network.wifi.internet === 'connected' ||
|
||||||
Network.wifi.internet === 'connecting') {
|
Network.wifi.internet === 'connecting') {
|
||||||
self.label = Network.wifi.ssid;
|
self.label = Network.wifi.ssid;
|
||||||
|
@ -40,40 +35,35 @@ const APLabel = (props) => Label({
|
||||||
self.label = 'Disconnected';
|
self.label = 'Disconnected';
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const SPACING = 5;
|
const hoverRevLabel = Revealer({
|
||||||
|
|
||||||
export default () => {
|
|
||||||
const rev = Revealer({
|
|
||||||
transition: 'slide_right',
|
transition: 'slide_right',
|
||||||
child: Box({
|
child: Box({
|
||||||
children: [
|
children: [
|
||||||
Separator(SPACING),
|
Separator(SPACING),
|
||||||
APLabel(),
|
label,
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
const widget = EventBox({
|
const widget = EventBox({
|
||||||
onHover: () => {
|
on_hover: () => {
|
||||||
rev.revealChild = true;
|
hoverRevLabel.reveal_child = true;
|
||||||
},
|
},
|
||||||
onHoverLost: () => {
|
on_hover_lost: () => {
|
||||||
rev.revealChild = false;
|
hoverRevLabel.reveal_child = false;
|
||||||
},
|
},
|
||||||
child: Box({
|
|
||||||
className: 'network',
|
|
||||||
children: [
|
|
||||||
Indicator(),
|
|
||||||
|
|
||||||
rev,
|
child: Box({
|
||||||
|
class_name: 'network',
|
||||||
|
|
||||||
|
children: [
|
||||||
|
indicator,
|
||||||
|
|
||||||
|
hoverRevLabel,
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
widget.rev = rev;
|
|
||||||
|
|
||||||
return widget;
|
return widget;
|
||||||
};
|
};
|
||||||
|
|
|
@ -13,8 +13,11 @@ export default () => EventBox({
|
||||||
className: 'toggle-off',
|
className: 'toggle-off',
|
||||||
|
|
||||||
onPrimaryClickRelease: (self) => {
|
onPrimaryClickRelease: (self) => {
|
||||||
App.getWindow('notification-center')
|
// @ts-expect-error
|
||||||
.setXPos(self.get_allocation(), 'right');
|
App.getWindow('notification-center')?.setXPos(
|
||||||
|
self.get_allocation(),
|
||||||
|
'right',
|
||||||
|
);
|
||||||
|
|
||||||
App.toggleWindow('notification-center');
|
App.toggleWindow('notification-center');
|
||||||
},
|
},
|
||||||
|
@ -28,13 +31,11 @@ export default () => EventBox({
|
||||||
},
|
},
|
||||||
|
|
||||||
child: CenterBox({
|
child: CenterBox({
|
||||||
className: 'notif-panel',
|
class_name: 'notif-panel',
|
||||||
|
|
||||||
center_widget: Box({
|
center_widget: Box({
|
||||||
children: [
|
children: [
|
||||||
Icon({
|
Icon().hook(Notifications, (self) => {
|
||||||
setup: (self) => {
|
|
||||||
self.hook(Notifications, () => {
|
|
||||||
if (Notifications.dnd) {
|
if (Notifications.dnd) {
|
||||||
self.icon = 'notification-disabled-symbolic';
|
self.icon = 'notification-disabled-symbolic';
|
||||||
}
|
}
|
||||||
|
@ -44,15 +45,13 @@ export default () => EventBox({
|
||||||
else {
|
else {
|
||||||
self.icon = 'notification-symbolic';
|
self.icon = 'notification-symbolic';
|
||||||
}
|
}
|
||||||
});
|
|
||||||
},
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
Separator(SPACING),
|
Separator(SPACING),
|
||||||
|
|
||||||
Label({
|
Label({
|
||||||
binds: [['label', Notifications, 'notifications',
|
label: Notifications.bind('notifications')
|
||||||
(n) => String(n.length)]],
|
.transform((n) => String(n.length)),
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Box, Label } from 'resource:///com/github/Aylur/ags/widget.js';
|
import { Label } from 'resource:///com/github/Aylur/ags/widget.js';
|
||||||
|
|
||||||
import Tablet from '../../../services/tablet.js';
|
import Tablet from '../../../services/tablet.js';
|
||||||
import EventBox from '../../misc/cursorbox.js';
|
import EventBox from '../../misc/cursorbox.js';
|
||||||
|
@ -9,14 +9,12 @@ export default () => EventBox({
|
||||||
|
|
||||||
onPrimaryClickRelease: () => Tablet.toggleOsk(),
|
onPrimaryClickRelease: () => Tablet.toggleOsk(),
|
||||||
|
|
||||||
setup: (self) => {
|
child: Label({
|
||||||
self.hook(Tablet, () => {
|
class_name: 'osk-toggle',
|
||||||
|
xalign: 0.6,
|
||||||
|
label: ' ',
|
||||||
|
}),
|
||||||
|
|
||||||
|
}).hook(Tablet, (self) => {
|
||||||
self.toggleClassName('toggle-on', Tablet.oskState);
|
self.toggleClassName('toggle-on', Tablet.oskState);
|
||||||
}, 'osk-toggled');
|
}, 'osk-toggled');
|
||||||
},
|
|
||||||
|
|
||||||
child: Box({
|
|
||||||
className: 'osk-toggle',
|
|
||||||
children: [Label(' ')],
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
|
@ -20,8 +20,11 @@ export default () => EventBox({
|
||||||
onHoverLost: () => { /**/ },
|
onHoverLost: () => { /**/ },
|
||||||
|
|
||||||
onPrimaryClickRelease: (self) => {
|
onPrimaryClickRelease: (self) => {
|
||||||
App.getWindow('notification-center')
|
// @ts-expect-error
|
||||||
.setXPos(self.get_allocation(), 'right');
|
App.getWindow('notification-center').setXPos(
|
||||||
|
self.get_allocation(),
|
||||||
|
'right',
|
||||||
|
);
|
||||||
|
|
||||||
App.toggleWindow('quick-settings');
|
App.toggleWindow('quick-settings');
|
||||||
},
|
},
|
||||||
|
@ -35,7 +38,7 @@ export default () => EventBox({
|
||||||
},
|
},
|
||||||
|
|
||||||
child: Box({
|
child: Box({
|
||||||
className: 'quick-settings-toggle',
|
class_name: 'quick-settings-toggle',
|
||||||
vertical: false,
|
vertical: false,
|
||||||
children: [
|
children: [
|
||||||
Separator(SPACING),
|
Separator(SPACING),
|
||||||
|
|
|
@ -9,35 +9,40 @@ const REVEAL_DURATION = 500;
|
||||||
const SPACING = 12;
|
const SPACING = 12;
|
||||||
|
|
||||||
|
|
||||||
|
/** @param {import('types/service/systemtray').TrayItem} item */
|
||||||
const SysTrayItem = (item) => {
|
const SysTrayItem = (item) => {
|
||||||
if (item.id === 'spotify-client') {
|
if (item.id === 'spotify-client') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
return MenuItem({
|
return MenuItem({
|
||||||
|
// @ts-expect-error
|
||||||
|
submenu: item.menu,
|
||||||
|
tooltip_markup: item.bind('tooltip_markup'),
|
||||||
|
|
||||||
child: Revealer({
|
child: Revealer({
|
||||||
transition: 'slide_right',
|
transition: 'slide_right',
|
||||||
transitionDuration: REVEAL_DURATION,
|
transition_duration: REVEAL_DURATION,
|
||||||
|
|
||||||
child: Icon({
|
child: Icon({
|
||||||
size: 24,
|
size: 24,
|
||||||
binds: [['icon', item, 'icon']],
|
icon: item.bind('icon'),
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
submenu: item.menu,
|
|
||||||
binds: [['tooltipMarkup', item, 'tooltip-markup']],
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const SysTray = () => MenuBar({
|
const SysTray = () => MenuBar({
|
||||||
setup: (self) => {
|
attribute: {
|
||||||
self.items = new Map();
|
items: new Map(),
|
||||||
|
},
|
||||||
|
|
||||||
|
setup: (self) => {
|
||||||
self
|
self
|
||||||
.hook(SystemTray, (_, id) => {
|
.hook(SystemTray, (_, id) => {
|
||||||
const item = SystemTray.getItem(id);
|
const item = SystemTray.getItem(id);
|
||||||
|
|
||||||
if (self.items.has(id) || !item) {
|
if (self.attribute.items.has(id) || !item) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,21 +53,22 @@ const SysTray = () => MenuBar({
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.items.set(id, w);
|
self.attribute.items.set(id, w);
|
||||||
self.add(w);
|
self.child = w;
|
||||||
self.show_all();
|
self.show_all();
|
||||||
w.child.revealChild = true;
|
// @ts-expect-error
|
||||||
|
w.child.reveal_child = true;
|
||||||
}, 'added')
|
}, 'added')
|
||||||
|
|
||||||
.hook(SystemTray, (_, id) => {
|
.hook(SystemTray, (_, id) => {
|
||||||
if (!self.items.has(id)) {
|
if (!self.attribute.items.has(id)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.items.get(id).child.revealChild = false;
|
self.attribute.items.get(id).child.reveal_child = false;
|
||||||
timeout(REVEAL_DURATION, () => {
|
timeout(REVEAL_DURATION, () => {
|
||||||
self.items.get(id).destroy();
|
self.attribute.items.get(id).destroy();
|
||||||
self.items.delete(id);
|
self.attribute.items.delete(id);
|
||||||
});
|
});
|
||||||
}, 'removed');
|
}, 'removed');
|
||||||
},
|
},
|
||||||
|
@ -74,21 +80,17 @@ export default () => {
|
||||||
return Revealer({
|
return Revealer({
|
||||||
transition: 'slide_right',
|
transition: 'slide_right',
|
||||||
|
|
||||||
setup: (self) => {
|
|
||||||
self.hook(SystemTray, () => {
|
|
||||||
self.revealChild = systray.get_children().length > 0;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
child: Box({
|
child: Box({
|
||||||
children: [
|
children: [
|
||||||
Box({
|
Box({
|
||||||
className: 'sys-tray',
|
class_name: 'sys-tray',
|
||||||
children: [systray],
|
children: [systray],
|
||||||
}),
|
}),
|
||||||
|
|
||||||
Separator(SPACING),
|
Separator(SPACING),
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
|
}).hook(SystemTray, (self) => {
|
||||||
|
self.reveal_child = systray.get_children().length > 0;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,19 +5,16 @@ import EventBox from '../../misc/cursorbox.js';
|
||||||
|
|
||||||
|
|
||||||
export default () => EventBox({
|
export default () => EventBox({
|
||||||
className: 'toggle-off',
|
class_name: 'toggle-off',
|
||||||
|
|
||||||
onPrimaryClickRelease: () => Tablet.toggleMode(),
|
onPrimaryClickRelease: () => Tablet.toggleMode(),
|
||||||
|
|
||||||
setup: (self) => {
|
|
||||||
self.hook(Tablet, () => {
|
|
||||||
self.toggleClassName('toggle-on', Tablet.tabletMode);
|
|
||||||
}, 'mode-toggled');
|
|
||||||
},
|
|
||||||
|
|
||||||
child: Box({
|
child: Box({
|
||||||
className: 'tablet-toggle',
|
class_name: 'tablet-toggle',
|
||||||
vertical: false,
|
vertical: false,
|
||||||
children: [Label(' ')],
|
children: [Label(' ')],
|
||||||
}),
|
}),
|
||||||
});
|
|
||||||
|
}).hook(Tablet, (self) => {
|
||||||
|
self.toggleClassName('toggle-on', Tablet.tabletMode);
|
||||||
|
}, 'mode-toggled');
|
||||||
|
|
|
@ -7,36 +7,52 @@ import EventBox from '../../misc/cursorbox.js';
|
||||||
|
|
||||||
const URGENT_DURATION = 1000;
|
const URGENT_DURATION = 1000;
|
||||||
|
|
||||||
|
/** @typedef {import('types/widget.js').Widget} Widget */
|
||||||
|
|
||||||
const Workspace = ({ i } = {}) => {
|
|
||||||
|
/** @property {number} id */
|
||||||
|
const Workspace = ({ id }) => {
|
||||||
return Revealer({
|
return Revealer({
|
||||||
transition: 'slide_right',
|
transition: 'slide_right',
|
||||||
properties: [['id', i]],
|
attribute: { id },
|
||||||
|
|
||||||
child: EventBox({
|
child: EventBox({
|
||||||
tooltipText: `${i}`,
|
tooltipText: `${id}`,
|
||||||
|
|
||||||
onPrimaryClickRelease: () => {
|
onPrimaryClickRelease: () => {
|
||||||
Hyprland.sendMessage(`dispatch workspace ${i}`);
|
Hyprland.sendMessage(`dispatch workspace ${id}`);
|
||||||
},
|
},
|
||||||
|
|
||||||
child: Box({
|
child: Box({
|
||||||
vpack: 'center',
|
vpack: 'center',
|
||||||
className: 'button',
|
class_name: 'button',
|
||||||
|
|
||||||
setup: (self) => {
|
setup: (self) => {
|
||||||
self.update = (addr) => {
|
/**
|
||||||
const occupied = Hyprland.getWorkspace(i)?.windows > 0;
|
* @param {Widget} _
|
||||||
|
* @param {string|undefined} addr
|
||||||
|
*/
|
||||||
|
const update = (_, addr) => {
|
||||||
|
const workspace = Hyprland.getWorkspace(id);
|
||||||
|
const occupied = workspace && workspace['windows'] > 0;
|
||||||
|
|
||||||
self.toggleClassName('occupied', occupied);
|
self.toggleClassName('occupied', occupied);
|
||||||
self.toggleClassName('empty', !occupied);
|
self.toggleClassName('empty', !occupied);
|
||||||
|
|
||||||
|
if (!addr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Deal with urgent windows
|
// Deal with urgent windows
|
||||||
if (Hyprland.getClient(addr)?.workspace.id === i) {
|
const client = Hyprland.getClient(addr);
|
||||||
|
const isThisUrgent = client &&
|
||||||
|
client['workspace']['id'] === id;
|
||||||
|
|
||||||
|
if (isThisUrgent) {
|
||||||
self.toggleClassName('urgent', true);
|
self.toggleClassName('urgent', true);
|
||||||
|
|
||||||
// Only show for a sec when urgent is current workspace
|
// Only show for a sec when urgent is current workspace
|
||||||
if (Hyprland.active.workspace.id === i) {
|
if (Hyprland.active.workspace.id === id) {
|
||||||
timeout(URGENT_DURATION, () => {
|
timeout(URGENT_DURATION, () => {
|
||||||
self.toggleClassName('urgent', false);
|
self.toggleClassName('urgent', false);
|
||||||
});
|
});
|
||||||
|
@ -45,13 +61,13 @@ const Workspace = ({ i } = {}) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
self
|
self
|
||||||
.hook(Hyprland, () => self.update())
|
.hook(Hyprland, update)
|
||||||
|
|
||||||
// Deal with urgent windows
|
// Deal with urgent windows
|
||||||
.hook(Hyprland, (_, a) => {
|
.hook(Hyprland, update, 'urgent-window')
|
||||||
self.update(a);
|
|
||||||
}, 'urgent-window')
|
|
||||||
.hook(Hyprland.active.workspace, () => {
|
.hook(Hyprland.active.workspace, () => {
|
||||||
if (Hyprland.active.workspace.id === i) {
|
if (Hyprland.active.workspace.id === id) {
|
||||||
self.toggleClassName('urgent', false);
|
self.toggleClassName('urgent', false);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -65,75 +81,80 @@ export default () => {
|
||||||
const L_PADDING = 16;
|
const L_PADDING = 16;
|
||||||
const WS_WIDTH = 30;
|
const WS_WIDTH = 30;
|
||||||
|
|
||||||
|
/** @param {Widget} self */
|
||||||
const updateHighlight = (self) => {
|
const updateHighlight = (self) => {
|
||||||
const currentId = Hyprland.active.workspace.id;
|
const currentId = Hyprland.active.workspace.id;
|
||||||
|
// @ts-expect-error
|
||||||
const indicators = self.get_parent().get_children()[0].child.children;
|
const indicators = self.get_parent().get_children()[0].child.children;
|
||||||
const currentIndex = indicators.findIndex((w) => w._id === currentId);
|
const currentIndex = Array.from(indicators)
|
||||||
|
.findIndex((w) => w.attribute.id === currentId);
|
||||||
|
|
||||||
if (currentIndex < 0) {
|
if (currentIndex < 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @ts-expect-error
|
||||||
self.setCss(`margin-left: ${L_PADDING + (currentIndex * WS_WIDTH)}px`);
|
self.setCss(`margin-left: ${L_PADDING + (currentIndex * WS_WIDTH)}px`);
|
||||||
};
|
};
|
||||||
|
|
||||||
const highlight = Box({
|
const highlight = Box({
|
||||||
vpack: 'center',
|
vpack: 'center',
|
||||||
hpack: 'start',
|
hpack: 'start',
|
||||||
className: 'button active',
|
class_name: 'button active',
|
||||||
setup: (self) => {
|
}).hook(Hyprland.active.workspace, updateHighlight);
|
||||||
self.hook(Hyprland.active.workspace, updateHighlight);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const widget = Overlay({
|
const widget = Overlay({
|
||||||
pass_through: true,
|
pass_through: true,
|
||||||
overlays: [highlight],
|
overlays: [highlight],
|
||||||
child: EventBox({
|
child: EventBox({
|
||||||
child: Box({
|
child: Box({
|
||||||
className: 'workspaces',
|
class_name: 'workspaces',
|
||||||
|
|
||||||
properties: [
|
attribute: { workspaces: [] },
|
||||||
['workspaces'],
|
|
||||||
|
|
||||||
['refresh', (self) => {
|
setup: (self) => {
|
||||||
self.children.forEach((rev) => {
|
const refresh = () => {
|
||||||
|
Array.from(self.children).forEach((rev) => {
|
||||||
|
// @ts-expect-error
|
||||||
rev.reveal_child = false;
|
rev.reveal_child = false;
|
||||||
});
|
});
|
||||||
self._workspaces.forEach((ws) => {
|
Array.from(self.attribute.workspaces).forEach((ws) => {
|
||||||
ws.revealChild = true;
|
ws.revealChild = true;
|
||||||
});
|
});
|
||||||
}],
|
};
|
||||||
|
|
||||||
['updateWorkspaces', (self) => {
|
const updateWorkspaces = () => {
|
||||||
Hyprland.workspaces.forEach((ws) => {
|
Hyprland.workspaces.forEach((ws) => {
|
||||||
const currentWs = self.children.find((ch) => {
|
const currentWs = Array.from(self.children)
|
||||||
return ch._id === ws.id;
|
// @ts-expect-error
|
||||||
});
|
.find((ch) => ch.attribute.id === ws['id']);
|
||||||
|
|
||||||
if (!currentWs && ws.id > 0) {
|
if (!currentWs && ws['id'] > 0) {
|
||||||
self.add(Workspace({ i: ws.id }));
|
self.add(Workspace({ id: ws['id'] }));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
self.show_all();
|
self.show_all();
|
||||||
|
|
||||||
// Make sure the order is correct
|
// Make sure the order is correct
|
||||||
self._workspaces.forEach((workspace, i) => {
|
Array.from(self.attribute.workspaces)
|
||||||
workspace.get_parent().reorder_child(workspace, i);
|
.forEach((workspace, i) => {
|
||||||
|
workspace.get_parent()
|
||||||
|
.reorder_child(workspace, i);
|
||||||
});
|
});
|
||||||
}],
|
};
|
||||||
],
|
|
||||||
|
|
||||||
setup: (self) => {
|
|
||||||
self.hook(Hyprland, () => {
|
self.hook(Hyprland, () => {
|
||||||
self._workspaces = self.children.filter((ch) => {
|
self.attribute.workspaces =
|
||||||
|
self.children.filter((ch) => {
|
||||||
return Hyprland.workspaces.find((ws) => {
|
return Hyprland.workspaces.find((ws) => {
|
||||||
return ws.id === ch._id;
|
// @ts-expect-error
|
||||||
|
return ws['id'] === ch.attribute.id;
|
||||||
});
|
});
|
||||||
}).sort((a, b) => a._id - b._id);
|
// @ts-expect-error
|
||||||
|
}).sort((a, b) => a.attribute.id - b.attribute.id);
|
||||||
|
|
||||||
self._updateWorkspaces(self);
|
updateWorkspaces();
|
||||||
self._refresh(self);
|
refresh();
|
||||||
|
|
||||||
// Make sure the highlight doesn't go too far
|
// Make sure the highlight doesn't go too far
|
||||||
const TEMP_TIMEOUT = 10;
|
const TEMP_TIMEOUT = 10;
|
||||||
|
|
|
@ -4,6 +4,7 @@ import Variable from 'resource:///com/github/Aylur/ags/variable.js';
|
||||||
import { Box, EventBox, Revealer, Window } from 'resource:///com/github/Aylur/ags/widget.js';
|
import { Box, EventBox, Revealer, Window } from 'resource:///com/github/Aylur/ags/widget.js';
|
||||||
|
|
||||||
|
|
||||||
|
/** @param {import('types/variable.js').Variable} variable */
|
||||||
const BarCloser = (variable) => Window({
|
const BarCloser = (variable) => Window({
|
||||||
name: 'bar-closer',
|
name: 'bar-closer',
|
||||||
visible: false,
|
visible: false,
|
||||||
|
@ -11,8 +12,9 @@ const BarCloser = (variable) => Window({
|
||||||
layer: 'overlay',
|
layer: 'overlay',
|
||||||
|
|
||||||
child: EventBox({
|
child: EventBox({
|
||||||
onHover: (self) => {
|
on_hover: (self) => {
|
||||||
variable.value = false;
|
variable.value = false;
|
||||||
|
// @ts-expect-error
|
||||||
self.get_parent().visible = false;
|
self.get_parent().visible = false;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -22,6 +24,7 @@ const BarCloser = (variable) => Window({
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/** @param {import('types/widgets/revealer').RevealerProps} props */
|
||||||
export default (props) => {
|
export default (props) => {
|
||||||
const Revealed = Variable(true);
|
const Revealed = Variable(true);
|
||||||
const barCloser = BarCloser(Revealed);
|
const barCloser = BarCloser(Revealed);
|
||||||
|
@ -38,7 +41,9 @@ export default (props) => {
|
||||||
Hyprland.active.workspace.id,
|
Hyprland.active.workspace.id,
|
||||||
);
|
);
|
||||||
|
|
||||||
Revealed.value = !workspace?.hasfullscreen;
|
if (workspace) {
|
||||||
|
Revealed.value = !workspace['hasfullscreen'];
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
.hook(Hyprland, (_, fullscreen) => {
|
.hook(Hyprland, (_, fullscreen) => {
|
||||||
|
@ -50,7 +55,7 @@ export default (props) => {
|
||||||
Revealer({
|
Revealer({
|
||||||
...props,
|
...props,
|
||||||
transition: 'slide_down',
|
transition: 'slide_down',
|
||||||
revealChild: true,
|
reveal_child: true,
|
||||||
|
|
||||||
binds: [['revealChild', Revealed, 'value']],
|
binds: [['revealChild', Revealed, 'value']],
|
||||||
}),
|
}),
|
||||||
|
@ -59,7 +64,7 @@ export default (props) => {
|
||||||
binds: [['revealChild', Revealed, 'value', (v) => !v]],
|
binds: [['revealChild', Revealed, 'value', (v) => !v]],
|
||||||
|
|
||||||
child: EventBox({
|
child: EventBox({
|
||||||
onHover: () => {
|
on_hover: () => {
|
||||||
barCloser.visible = true;
|
barCloser.visible = true;
|
||||||
Revealed.value = true;
|
Revealed.value = true;
|
||||||
},
|
},
|
||||||
|
|
|
@ -27,10 +27,9 @@ export default () => Window({
|
||||||
child: BarReveal({
|
child: BarReveal({
|
||||||
child: CenterBox({
|
child: CenterBox({
|
||||||
css: 'margin: 6px 5px 5px 5px',
|
css: 'margin: 6px 5px 5px 5px',
|
||||||
className: 'bar',
|
class_name: 'bar',
|
||||||
vertical: false,
|
|
||||||
|
|
||||||
startWidget: Box({
|
start_widget: Box({
|
||||||
hpack: 'start',
|
hpack: 'start',
|
||||||
children: [
|
children: [
|
||||||
|
|
||||||
|
@ -53,7 +52,7 @@ export default () => Window({
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
|
|
||||||
centerWidget: Box({
|
center_widget: Box({
|
||||||
children: [
|
children: [
|
||||||
Separator(SPACING),
|
Separator(SPACING),
|
||||||
|
|
||||||
|
@ -63,7 +62,7 @@ export default () => Window({
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
|
|
||||||
endWidget: Box({
|
end_widget: Box({
|
||||||
hpack: 'end',
|
hpack: 'end',
|
||||||
children: [
|
children: [
|
||||||
Heart(),
|
Heart(),
|
||||||
|
|
|
@ -4,7 +4,7 @@ import Gtk from 'gi://Gtk';
|
||||||
const Lang = imports.lang;
|
const Lang = imports.lang;
|
||||||
|
|
||||||
export default (
|
export default (
|
||||||
place,
|
place = 'top left',
|
||||||
css = 'background-color: black;',
|
css = 'background-color: black;',
|
||||||
) => Box({
|
) => Box({
|
||||||
hpack: place.includes('left') ? 'start' : 'end',
|
hpack: place.includes('left') ? 'start' : 'end',
|
||||||
|
@ -27,6 +27,7 @@ export default (
|
||||||
.get_property('border-radius', Gtk.StateFlags.NORMAL);
|
.get_property('border-radius', Gtk.StateFlags.NORMAL);
|
||||||
|
|
||||||
widget.set_size_request(r, r);
|
widget.set_size_request(r, r);
|
||||||
|
// @ts-expect-error
|
||||||
widget.connect('draw', Lang.bind(widget, (_, cr) => {
|
widget.connect('draw', Lang.bind(widget, (_, cr) => {
|
||||||
const c = widget.get_style_context()
|
const c = widget.get_style_context()
|
||||||
.get_property('background-color', Gtk.StateFlags.NORMAL);
|
.get_property('background-color', Gtk.StateFlags.NORMAL);
|
||||||
|
|
|
@ -1,29 +1,28 @@
|
||||||
import { Box, Calendar, Label } from 'resource:///com/github/Aylur/ags/widget.js';
|
import { Box, Calendar, Label } from 'resource:///com/github/Aylur/ags/widget.js';
|
||||||
|
|
||||||
import GLib from 'gi://GLib';
|
const { DateTime } = imports.gi.GLib;
|
||||||
const { DateTime } = GLib;
|
|
||||||
|
|
||||||
import PopupWindow from './misc/popup.js';
|
import PopupWindow from './misc/popup.js';
|
||||||
|
|
||||||
|
|
||||||
const Divider = () => Box({
|
const Divider = () => Box({
|
||||||
className: 'divider',
|
class_name: 'divider',
|
||||||
vertical: true,
|
vertical: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
const Time = () => Box({
|
const Time = () => Box({
|
||||||
className: 'timebox',
|
class_name: 'timebox',
|
||||||
vertical: true,
|
vertical: true,
|
||||||
children: [
|
|
||||||
|
|
||||||
|
children: [
|
||||||
Box({
|
Box({
|
||||||
className: 'time-container',
|
class_name: 'time-container',
|
||||||
hpack: 'center',
|
hpack: 'center',
|
||||||
vpack: 'center',
|
vpack: 'center',
|
||||||
children: [
|
|
||||||
|
|
||||||
|
children: [
|
||||||
Label({
|
Label({
|
||||||
className: 'content',
|
class_name: 'content',
|
||||||
label: 'hour',
|
label: 'hour',
|
||||||
setup: (self) => {
|
setup: (self) => {
|
||||||
self.poll(1000, () => {
|
self.poll(1000, () => {
|
||||||
|
@ -35,7 +34,7 @@ const Time = () => Box({
|
||||||
Divider(),
|
Divider(),
|
||||||
|
|
||||||
Label({
|
Label({
|
||||||
className: 'content',
|
class_name: 'content',
|
||||||
label: 'minute',
|
label: 'minute',
|
||||||
setup: (self) => {
|
setup: (self) => {
|
||||||
self.poll(1000, () => {
|
self.poll(1000, () => {
|
||||||
|
@ -48,11 +47,13 @@ const Time = () => Box({
|
||||||
}),
|
}),
|
||||||
|
|
||||||
Box({
|
Box({
|
||||||
className: 'date-container',
|
class_name: 'date-container',
|
||||||
hpack: 'center',
|
hpack: 'center',
|
||||||
|
|
||||||
child: Label({
|
child: Label({
|
||||||
css: 'font-size: 20px',
|
css: 'font-size: 20px',
|
||||||
label: 'complete date',
|
label: 'complete date',
|
||||||
|
|
||||||
setup: (self) => {
|
setup: (self) => {
|
||||||
self.poll(1000, () => {
|
self.poll(1000, () => {
|
||||||
const time = DateTime.new_now_local();
|
const time = DateTime.new_now_local();
|
||||||
|
@ -69,23 +70,26 @@ const Time = () => Box({
|
||||||
});
|
});
|
||||||
|
|
||||||
const CalendarWidget = () => Box({
|
const CalendarWidget = () => Box({
|
||||||
className: 'cal-box',
|
class_name: 'cal-box',
|
||||||
|
|
||||||
child: Calendar({
|
child: Calendar({
|
||||||
showDayNames: true,
|
show_day_names: true,
|
||||||
showHeading: true,
|
show_heading: true,
|
||||||
className: 'cal',
|
class_name: 'cal',
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
const TOP_MARGIN = 6;
|
const TOP_MARGIN = 6;
|
||||||
|
|
||||||
export default () => PopupWindow({
|
export default () => PopupWindow({
|
||||||
|
name: 'calendar',
|
||||||
anchor: ['top'],
|
anchor: ['top'],
|
||||||
margins: [TOP_MARGIN, 0, 0, 0],
|
margins: [TOP_MARGIN, 0, 0, 0],
|
||||||
name: 'calendar',
|
|
||||||
child: Box({
|
child: Box({
|
||||||
className: 'date',
|
class_name: 'date',
|
||||||
vertical: true,
|
vertical: true,
|
||||||
|
|
||||||
children: [
|
children: [
|
||||||
Time(),
|
Time(),
|
||||||
CalendarWidget(),
|
CalendarWidget(),
|
||||||
|
|
|
@ -11,25 +11,49 @@ const TRANSITION = `transition: margin ${ANIM_DURATION}ms ease,
|
||||||
|
|
||||||
|
|
||||||
export default ({
|
export default ({
|
||||||
properties,
|
attribute = {},
|
||||||
setup = () => {/**/},
|
setup = (self) => {
|
||||||
props,
|
self;
|
||||||
} = {}) => {
|
},
|
||||||
|
...props
|
||||||
|
}) => {
|
||||||
const widget = EventBox();
|
const widget = EventBox();
|
||||||
const gesture = Gtk.GestureDrag.new(widget);
|
const gesture = Gtk.GestureDrag.new(widget);
|
||||||
|
|
||||||
// Have empty PlayerBox to define the size of the widget
|
// Have empty PlayerBox to define the size of the widget
|
||||||
const emptyPlayer = Box({ className: 'player' });
|
const emptyPlayer = Box({
|
||||||
|
class_name: 'player',
|
||||||
// Set this prop to differentiate it easily
|
attribute: { empty: true },
|
||||||
emptyPlayer.empty = true;
|
});
|
||||||
|
|
||||||
const content = Overlay({
|
const content = Overlay({
|
||||||
...props,
|
...props,
|
||||||
properties: [
|
attribute: {
|
||||||
...properties,
|
...attribute,
|
||||||
['dragging', false],
|
dragging: false,
|
||||||
],
|
|
||||||
|
list: () => content.get_children()
|
||||||
|
// @ts-expect-error
|
||||||
|
.filter((ch) => !ch.attribute?.empty),
|
||||||
|
|
||||||
|
includesWidget: (playerW) => {
|
||||||
|
return Array.from(content.attribute.list())
|
||||||
|
.find((w) => w === playerW);
|
||||||
|
},
|
||||||
|
|
||||||
|
showTopOnly: () => Array.from(content.attribute.list())
|
||||||
|
.forEach((over) => {
|
||||||
|
over.visible = over === content.attribute.list().at(-1);
|
||||||
|
}),
|
||||||
|
|
||||||
|
moveToTop: (player) => {
|
||||||
|
player.visible = true;
|
||||||
|
content.reorder_overlay(player, -1);
|
||||||
|
timeout(ANIM_DURATION, () => {
|
||||||
|
content.attribute.showTopOnly();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
child: emptyPlayer,
|
child: emptyPlayer,
|
||||||
|
|
||||||
|
@ -37,32 +61,36 @@ export default ({
|
||||||
setup(self);
|
setup(self);
|
||||||
|
|
||||||
self
|
self
|
||||||
|
// @ts-expect-error
|
||||||
.hook(gesture, (overlay, realGesture) => {
|
.hook(gesture, (overlay, realGesture) => {
|
||||||
if (realGesture) {
|
if (realGesture) {
|
||||||
overlay.list().forEach((over) => {
|
overlay.attribute.list().forEach((over) => {
|
||||||
over.visible = true;
|
over.visible = true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
overlay.showTopOnly();
|
overlay.attribute.showTopOnly();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't allow gesture when only one player
|
// Don't allow gesture when only one player
|
||||||
if (overlay.list().length <= 1) {
|
if (overlay.attribute.list().length <= 1) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
overlay._dragging = true;
|
overlay.attribute.dragging = true;
|
||||||
let offset = gesture.get_offset()[1];
|
let offset = gesture.get_offset()[1];
|
||||||
|
const playerBox = overlay.attribute.list().at(-1);
|
||||||
|
|
||||||
const playerBox = overlay.list().at(-1);
|
if (!offset) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Slide right
|
// Slide right
|
||||||
if (offset >= 0) {
|
if (offset >= 0) {
|
||||||
playerBox.setCss(`
|
playerBox.setCss(`
|
||||||
margin-left: ${offset}px;
|
margin-left: ${offset}px;
|
||||||
margin-right: -${offset}px;
|
margin-right: -${offset}px;
|
||||||
${playerBox._bgStyle}
|
${playerBox.attribute.bgStyle}
|
||||||
`);
|
`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,24 +100,24 @@ export default ({
|
||||||
playerBox.setCss(`
|
playerBox.setCss(`
|
||||||
margin-left: -${offset}px;
|
margin-left: -${offset}px;
|
||||||
margin-right: ${offset}px;
|
margin-right: ${offset}px;
|
||||||
${playerBox._bgStyle}
|
${playerBox.attribute.bgStyle}
|
||||||
`);
|
`);
|
||||||
}
|
}
|
||||||
}, 'drag-update')
|
}, 'drag-update')
|
||||||
|
|
||||||
.hook(gesture, (overlay) => {
|
.hook(gesture, (overlay) => {
|
||||||
// Don't allow gesture when only one player
|
// Don't allow gesture when only one player
|
||||||
if (overlay.list().length <= 1) {
|
if (overlay.attribute.list().length <= 1) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
overlay._dragging = false;
|
overlay.attribute.dragging = false;
|
||||||
const offset = gesture.get_offset()[1];
|
const offset = gesture.get_offset()[1];
|
||||||
|
|
||||||
const playerBox = overlay.list().at(-1);
|
const playerBox = overlay.attribute.list().at(-1);
|
||||||
|
|
||||||
// If crosses threshold after letting go, slide away
|
// If crosses threshold after letting go, slide away
|
||||||
if (Math.abs(offset) > MAX_OFFSET) {
|
if (offset && Math.abs(offset) > MAX_OFFSET) {
|
||||||
// Disable inputs during animation
|
// Disable inputs during animation
|
||||||
widget.sensitive = false;
|
widget.sensitive = false;
|
||||||
|
|
||||||
|
@ -99,7 +127,7 @@ export default ({
|
||||||
${TRANSITION}
|
${TRANSITION}
|
||||||
margin-left: ${OFFSCREEN}px;
|
margin-left: ${OFFSCREEN}px;
|
||||||
margin-right: -${OFFSCREEN}px;
|
margin-right: -${OFFSCREEN}px;
|
||||||
opacity: 0.7; ${playerBox._bgStyle}
|
opacity: 0.7; ${playerBox.attribute.bgStyle}
|
||||||
`);
|
`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,7 +137,7 @@ export default ({
|
||||||
${TRANSITION}
|
${TRANSITION}
|
||||||
margin-left: -${OFFSCREEN}px;
|
margin-left: -${OFFSCREEN}px;
|
||||||
margin-right: ${OFFSCREEN}px;
|
margin-right: ${OFFSCREEN}px;
|
||||||
opacity: 0.7; ${playerBox._bgStyle}
|
opacity: 0.7; ${playerBox.attribute.bgStyle}
|
||||||
`);
|
`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,16 +145,17 @@ export default ({
|
||||||
// Put the player in the back after anim
|
// Put the player in the back after anim
|
||||||
overlay.reorder_overlay(playerBox, 0);
|
overlay.reorder_overlay(playerBox, 0);
|
||||||
// Recenter player
|
// Recenter player
|
||||||
playerBox.setCss(playerBox._bgStyle);
|
playerBox.setCss(playerBox.attribute.bgStyle);
|
||||||
|
|
||||||
widget.sensitive = true;
|
widget.sensitive = true;
|
||||||
|
|
||||||
overlay.showTopOnly();
|
overlay.attribute.showTopOnly();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Recenter with transition for animation
|
// Recenter with transition for animation
|
||||||
playerBox.setCss(`${TRANSITION} ${playerBox._bgStyle}`);
|
playerBox.setCss(`${TRANSITION}
|
||||||
|
${playerBox.attribute.bgStyle}`);
|
||||||
}
|
}
|
||||||
}, 'drag-end');
|
}, 'drag-end');
|
||||||
},
|
},
|
||||||
|
@ -134,27 +163,5 @@ export default ({
|
||||||
|
|
||||||
widget.add(content);
|
widget.add(content);
|
||||||
|
|
||||||
// Overlay methods
|
|
||||||
content.list = () => content.get_children()
|
|
||||||
.filter((ch) => !ch.empty);
|
|
||||||
|
|
||||||
content.includesWidget = (playerW) => {
|
|
||||||
return content.list().find((w) => w === playerW);
|
|
||||||
};
|
|
||||||
|
|
||||||
content.showTopOnly = () => content.list().forEach((over) => {
|
|
||||||
over.visible = over === content.list().at(-1);
|
|
||||||
});
|
|
||||||
|
|
||||||
content.moveToTop = (player) => {
|
|
||||||
player.visible = true;
|
|
||||||
content.reorder_overlay(player, -1);
|
|
||||||
timeout(ANIM_DURATION, () => {
|
|
||||||
content.showTopOnly();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
widget.getOverlay = () => content;
|
|
||||||
|
|
||||||
return widget;
|
return widget;
|
||||||
};
|
};
|
||||||
|
|
|
@ -6,6 +6,12 @@ import { execAsync, lookUpIcon, readFileAsync } from 'resource:///com/github/Ayl
|
||||||
import Separator from '../misc/separator.js';
|
import Separator from '../misc/separator.js';
|
||||||
import EventBox from '../misc/cursorbox.js';
|
import EventBox from '../misc/cursorbox.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {import('types/service/mpris').MprisPlayer} Player
|
||||||
|
* @typedef {import('types/variable').Variable} Variable
|
||||||
|
* @typedef {import('types/widgets/overlay').default} Overlay
|
||||||
|
*/
|
||||||
|
|
||||||
const ICON_SIZE = 32;
|
const ICON_SIZE = 32;
|
||||||
|
|
||||||
const icons = {
|
const icons = {
|
||||||
|
@ -29,59 +35,66 @@ const icons = {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export const CoverArt = (player, props) => CenterBox({
|
/**
|
||||||
|
* @param {Player} player
|
||||||
|
* @param {Variable} colors
|
||||||
|
* @param {import('types/widgets/centerbox').CenterBoxProps=} props
|
||||||
|
*/
|
||||||
|
export const CoverArt = (player, colors, props) => CenterBox({
|
||||||
...props,
|
...props,
|
||||||
|
// @ts-expect-error
|
||||||
vertical: true,
|
vertical: true,
|
||||||
|
|
||||||
properties: [
|
attribute: {
|
||||||
['bgStyle', ''],
|
bgStyle: '',
|
||||||
['player', player],
|
player,
|
||||||
],
|
},
|
||||||
|
|
||||||
setup: (self) => {
|
setup: (self) => {
|
||||||
// Give temp cover art
|
// Give temp cover art
|
||||||
readFileAsync(player.coverPath).catch(() => {
|
readFileAsync(player.cover_path).catch(() => {
|
||||||
if (!player.colors.value && !player.trackCoverUrl) {
|
if (!colors.value && !player.track_cover_url) {
|
||||||
player.colors.value = {
|
colors.value = {
|
||||||
imageAccent: '#6b4fa2',
|
imageAccent: '#6b4fa2',
|
||||||
buttonAccent: '#ecdcff',
|
buttonAccent: '#ecdcff',
|
||||||
buttonText: '#25005a',
|
buttonText: '#25005a',
|
||||||
hoverAccent: '#d4baff',
|
hoverAccent: '#d4baff',
|
||||||
};
|
};
|
||||||
|
|
||||||
self._bgStyle = `
|
self.attribute.bgStyle = `
|
||||||
background: radial-gradient(circle,
|
background: radial-gradient(circle,
|
||||||
rgba(0, 0, 0, 0.4) 30%,
|
rgba(0, 0, 0, 0.4) 30%,
|
||||||
${player.colors.value.imageAccent}),
|
${colors.value.imageAccent}),
|
||||||
rgb(0, 0, 0);
|
rgb(0, 0, 0);
|
||||||
background-size: cover;
|
background-size: cover;
|
||||||
background-position: center;
|
background-position: center;
|
||||||
`;
|
`;
|
||||||
self.setCss(self._bgStyle);
|
self.setCss(self.attribute.bgStyle);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
self.hook(player, () => {
|
self.hook(player, () => {
|
||||||
execAsync(['bash', '-c', `[[ -f "${player.coverPath}" ]] &&
|
execAsync(['bash', '-c', `[[ -f "${player.cover_path}" ]] &&
|
||||||
coloryou "${player.coverPath}" | grep -v Warning`])
|
coloryou "${player.cover_path}" | 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);
|
colors.value = JSON.parse(out);
|
||||||
|
|
||||||
self._bgStyle = `
|
self.attribute.bgStyle = `
|
||||||
background: radial-gradient(circle,
|
background: radial-gradient(circle,
|
||||||
rgba(0, 0, 0, 0.4) 30%,
|
rgba(0, 0, 0, 0.4) 30%,
|
||||||
${player.colors.value.imageAccent}),
|
${colors.value.imageAccent}),
|
||||||
url("${player.coverPath}");
|
url("${player.cover_path}");
|
||||||
background-size: cover;
|
background-size: cover;
|
||||||
background-position: center;
|
background-position: center;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
if (!self.get_parent()._dragging) {
|
// @ts-expect-error
|
||||||
self.setCss(self._bgStyle);
|
if (!self?.get_parent()?.attribute.dragging) {
|
||||||
|
self.setCss(self.attribute.bgStyle);
|
||||||
}
|
}
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
if (err !== '') {
|
if (err !== '') {
|
||||||
|
@ -92,40 +105,48 @@ export const CoverArt = (player, props) => CenterBox({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export const TitleLabel = (player, props) => Label({
|
/** @param {Player} player */
|
||||||
...props,
|
export const TitleLabel = (player) => Label({
|
||||||
xalign: 0,
|
xalign: 0,
|
||||||
maxWidthChars: 40,
|
max_width_chars: 40,
|
||||||
truncate: 'end',
|
truncate: 'end',
|
||||||
justification: 'left',
|
justification: 'left',
|
||||||
className: 'title',
|
class_name: 'title',
|
||||||
|
label: player.bind('track_title'),
|
||||||
binds: [['label', player, 'track-title']],
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export const ArtistLabel = (player, props) => Label({
|
/** @param {Player} player */
|
||||||
...props,
|
export const ArtistLabel = (player) => Label({
|
||||||
xalign: 0,
|
xalign: 0,
|
||||||
maxWidthChars: 40,
|
max_width_chars: 40,
|
||||||
truncate: 'end',
|
truncate: 'end',
|
||||||
justification: 'left',
|
justification: 'left',
|
||||||
className: 'artist',
|
class_name: 'artist',
|
||||||
|
label: player.bind('track_artists')
|
||||||
binds: [['label', player, 'track-artists', (a) => a.join(', ') || '']],
|
.transform((a) => a.join(', ') || ''),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const PlayerIcon = (player, overlay, props) => {
|
|
||||||
|
/**
|
||||||
|
* @param {Player} player
|
||||||
|
* @param {Overlay} overlay
|
||||||
|
*/
|
||||||
|
export const PlayerIcon = (player, overlay) => {
|
||||||
|
/**
|
||||||
|
* @param {Player} p
|
||||||
|
* @param {Overlay=} widget
|
||||||
|
* @param {Overlay=} over
|
||||||
|
*/
|
||||||
const playerIcon = (p, widget, over) => EventBox({
|
const playerIcon = (p, widget, over) => EventBox({
|
||||||
...props,
|
tooltip_text: p.identity || '',
|
||||||
tooltipText: p.identity || '',
|
|
||||||
|
|
||||||
onPrimaryClickRelease: () => {
|
onPrimaryClickRelease: () => {
|
||||||
widget?.moveToTop(over);
|
widget?.attribute.moveToTop(over);
|
||||||
},
|
},
|
||||||
|
|
||||||
child: Icon({
|
child: Icon({
|
||||||
className: widget ? 'position-indicator' : 'player-icon',
|
class_name: widget ? 'position-indicator' : 'player-icon',
|
||||||
size: widget ? '' : ICON_SIZE,
|
size: widget ? 0 : ICON_SIZE,
|
||||||
|
|
||||||
setup: (self) => {
|
setup: (self) => {
|
||||||
self.hook(p, () => {
|
self.hook(p, () => {
|
||||||
|
@ -137,33 +158,34 @@ export const PlayerIcon = (player, overlay, props) => {
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
return Box({
|
return Box().hook(Mpris, (self) => {
|
||||||
setup: (self) => {
|
const thisIndex = overlay.attribute.list()
|
||||||
self.hook(Mpris, () => {
|
.indexOf(self?.get_parent()?.get_parent());
|
||||||
const thisIndex = overlay.list()
|
|
||||||
.indexOf(self.get_parent().get_parent());
|
|
||||||
|
|
||||||
self.children = overlay.list().map((over, i) => {
|
self.children = Array.from(overlay.attribute.list())
|
||||||
|
.map((over, i) => {
|
||||||
self.children.push(Separator(2));
|
self.children.push(Separator(2));
|
||||||
|
|
||||||
return i === thisIndex ?
|
return i === thisIndex ?
|
||||||
playerIcon(player) :
|
playerIcon(player) :
|
||||||
playerIcon(over._player, overlay, over);
|
playerIcon(over.attribute.player, overlay, over);
|
||||||
}).reverse();
|
})
|
||||||
});
|
.reverse();
|
||||||
},
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const PositionSlider = (player, props) => Slider({
|
/**
|
||||||
...props,
|
* @param {Player} player
|
||||||
className: 'position-slider',
|
* @param {Variable} colors
|
||||||
|
*/
|
||||||
|
export const PositionSlider = (player, colors) => Slider({
|
||||||
|
class_name: 'position-slider',
|
||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
vpack: 'center',
|
vpack: 'center',
|
||||||
hexpand: true,
|
hexpand: true,
|
||||||
drawValue: false,
|
draw_value: false,
|
||||||
|
|
||||||
onChange: ({ value }) => {
|
on_change: ({ value }) => {
|
||||||
player.position = player.length * value;
|
player.position = player.length * value;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -180,9 +202,9 @@ export const PositionSlider = (player, props) => Slider({
|
||||||
self
|
self
|
||||||
.poll(1000, () => update())
|
.poll(1000, () => update())
|
||||||
.hook(player, () => update(), 'position')
|
.hook(player, () => update(), 'position')
|
||||||
.hook(player.colors, () => {
|
.hook(colors, () => {
|
||||||
if (player.colors.value) {
|
if (colors.value) {
|
||||||
const c = player.colors.value;
|
const c = colors.value;
|
||||||
|
|
||||||
self.setCss(`
|
self.setCss(`
|
||||||
highlight { background-color: ${c.buttonAccent}; }
|
highlight { background-color: ${c.buttonAccent}; }
|
||||||
|
@ -192,29 +214,29 @@ export const PositionSlider = (player, props) => Slider({
|
||||||
`);
|
`);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.on('button-press-event', (s) => {
|
.on('button-press-event', () => {
|
||||||
s.cursor = 'grabbing';
|
self.cursor = 'grabbing';
|
||||||
})
|
})
|
||||||
.on('button-release-event', (s) => {
|
.on('button-release-event', () => {
|
||||||
s.cursor = 'pointer';
|
self.cursor = 'pointer';
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const PlayerButton = ({ player, items, onClick, prop }) => EventBox({
|
const PlayerButton = ({ player, colors, items, onClick, prop }) => EventBox({
|
||||||
child: Button({
|
child: Button({
|
||||||
properties: [['hovered', false]],
|
attribute: { hovered: false },
|
||||||
child: Stack({ items }),
|
child: Stack({ items }),
|
||||||
|
|
||||||
onPrimaryClickRelease: () => player[onClick](),
|
on_primary_click_release: () => player[onClick](),
|
||||||
|
|
||||||
onHover: (self) => {
|
on_hover: (self) => {
|
||||||
self._hovered = true;
|
self.attribute.hovered = true;
|
||||||
|
|
||||||
if (prop === 'playBackStatus' && player.colors.value) {
|
if (prop === 'playBackStatus' && colors.value) {
|
||||||
const c = player.colors.value;
|
const c = colors.value;
|
||||||
|
|
||||||
items.forEach((item) => {
|
Array.from(items).forEach((item) => {
|
||||||
item[1].setCss(`
|
item[1].setCss(`
|
||||||
background-color: ${c.hoverAccent};
|
background-color: ${c.hoverAccent};
|
||||||
color: ${c.buttonText};
|
color: ${c.buttonText};
|
||||||
|
@ -227,12 +249,12 @@ const PlayerButton = ({ player, items, onClick, prop }) => EventBox({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
onHoverLost: (self) => {
|
on_hover_lost: (self) => {
|
||||||
self._hovered = false;
|
self.attribute.hovered = false;
|
||||||
if (prop === 'playBackStatus' && player.colors.value) {
|
if (prop === 'playBackStatus' && colors.value) {
|
||||||
const c = player.colors.value;
|
const c = colors.value;
|
||||||
|
|
||||||
items.forEach((item) => {
|
Array.from(items).forEach((item) => {
|
||||||
item[1].setCss(`
|
item[1].setCss(`
|
||||||
background-color: ${c.buttonAccent};
|
background-color: ${c.buttonAccent};
|
||||||
color: ${c.buttonText};
|
color: ${c.buttonText};
|
||||||
|
@ -246,19 +268,20 @@ const PlayerButton = ({ player, items, onClick, prop }) => EventBox({
|
||||||
setup: (self) => {
|
setup: (self) => {
|
||||||
self
|
self
|
||||||
.hook(player, () => {
|
.hook(player, () => {
|
||||||
|
// @ts-expect-error
|
||||||
self.child.shown = `${player[prop]}`;
|
self.child.shown = `${player[prop]}`;
|
||||||
})
|
})
|
||||||
.hook(player.colors, () => {
|
.hook(colors, () => {
|
||||||
if (!Mpris.players.find((p) => player === p)) {
|
if (!Mpris.players.find((p) => player === p)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (player.colors.value) {
|
if (colors.value) {
|
||||||
const c = player.colors.value;
|
const c = colors.value;
|
||||||
|
|
||||||
if (prop === 'playBackStatus') {
|
if (prop === 'playBackStatus') {
|
||||||
if (self._hovered) {
|
if (self.attribute.hovered) {
|
||||||
items.forEach((item) => {
|
Array.from(items).forEach((item) => {
|
||||||
item[1].setCss(`
|
item[1].setCss(`
|
||||||
background-color: ${c.hoverAccent};
|
background-color: ${c.hoverAccent};
|
||||||
color: ${c.buttonText};
|
color: ${c.buttonText};
|
||||||
|
@ -270,7 +293,7 @@ const PlayerButton = ({ player, items, onClick, prop }) => EventBox({
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
items.forEach((item) => {
|
Array.from(items).forEach((item) => {
|
||||||
item[1].setCss(`
|
item[1].setCss(`
|
||||||
background-color: ${c.buttonAccent};
|
background-color: ${c.buttonAccent};
|
||||||
color: ${c.buttonText};
|
color: ${c.buttonText};
|
||||||
|
@ -292,15 +315,20 @@ const PlayerButton = ({ player, items, onClick, prop }) => EventBox({
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const ShuffleButton = (player) => PlayerButton({
|
/**
|
||||||
|
* @param {Player} player
|
||||||
|
* @param {Variable} colors
|
||||||
|
*/
|
||||||
|
export const ShuffleButton = (player, colors) => PlayerButton({
|
||||||
player,
|
player,
|
||||||
|
colors,
|
||||||
items: [
|
items: [
|
||||||
['true', Label({
|
['true', Label({
|
||||||
className: 'shuffle enabled',
|
class_name: 'shuffle enabled',
|
||||||
label: icons.mpris.shuffle.enabled,
|
label: icons.mpris.shuffle.enabled,
|
||||||
})],
|
})],
|
||||||
['false', Label({
|
['false', Label({
|
||||||
className: 'shuffle disabled',
|
class_name: 'shuffle disabled',
|
||||||
label: icons.mpris.shuffle.disabled,
|
label: icons.mpris.shuffle.disabled,
|
||||||
})],
|
})],
|
||||||
],
|
],
|
||||||
|
@ -308,19 +336,24 @@ export const ShuffleButton = (player) => PlayerButton({
|
||||||
prop: 'shuffleStatus',
|
prop: 'shuffleStatus',
|
||||||
});
|
});
|
||||||
|
|
||||||
export const LoopButton = (player) => PlayerButton({
|
/**
|
||||||
|
* @param {Player} player
|
||||||
|
* @param {Variable} colors
|
||||||
|
*/
|
||||||
|
export const LoopButton = (player, colors) => PlayerButton({
|
||||||
player,
|
player,
|
||||||
|
colors,
|
||||||
items: [
|
items: [
|
||||||
['None', Label({
|
['None', Label({
|
||||||
className: 'loop none',
|
class_name: 'loop none',
|
||||||
label: icons.mpris.loop.none,
|
label: icons.mpris.loop.none,
|
||||||
})],
|
})],
|
||||||
['Track', Label({
|
['Track', Label({
|
||||||
className: 'loop track',
|
class_name: 'loop track',
|
||||||
label: icons.mpris.loop.track,
|
label: icons.mpris.loop.track,
|
||||||
})],
|
})],
|
||||||
['Playlist', Label({
|
['Playlist', Label({
|
||||||
className: 'loop playlist',
|
class_name: 'loop playlist',
|
||||||
label: icons.mpris.loop.playlist,
|
label: icons.mpris.loop.playlist,
|
||||||
})],
|
})],
|
||||||
],
|
],
|
||||||
|
@ -328,19 +361,24 @@ export const LoopButton = (player) => PlayerButton({
|
||||||
prop: 'loopStatus',
|
prop: 'loopStatus',
|
||||||
});
|
});
|
||||||
|
|
||||||
export const PlayPauseButton = (player) => PlayerButton({
|
/**
|
||||||
|
* @param {Player} player
|
||||||
|
* @param {Variable} colors
|
||||||
|
*/
|
||||||
|
export const PlayPauseButton = (player, colors) => PlayerButton({
|
||||||
player,
|
player,
|
||||||
|
colors,
|
||||||
items: [
|
items: [
|
||||||
['Playing', Label({
|
['Playing', Label({
|
||||||
className: 'pausebutton playing',
|
class_name: 'pausebutton playing',
|
||||||
label: icons.mpris.playing,
|
label: icons.mpris.playing,
|
||||||
})],
|
})],
|
||||||
['Paused', Label({
|
['Paused', Label({
|
||||||
className: 'pausebutton paused',
|
class_name: 'pausebutton paused',
|
||||||
label: icons.mpris.paused,
|
label: icons.mpris.paused,
|
||||||
})],
|
})],
|
||||||
['Stopped', Label({
|
['Stopped', Label({
|
||||||
className: 'pausebutton stopped paused',
|
class_name: 'pausebutton stopped paused',
|
||||||
label: icons.mpris.stopped,
|
label: icons.mpris.stopped,
|
||||||
})],
|
})],
|
||||||
],
|
],
|
||||||
|
@ -348,15 +386,20 @@ export const PlayPauseButton = (player) => PlayerButton({
|
||||||
prop: 'playBackStatus',
|
prop: 'playBackStatus',
|
||||||
});
|
});
|
||||||
|
|
||||||
export const PreviousButton = (player) => PlayerButton({
|
/**
|
||||||
|
* @param {Player} player
|
||||||
|
* @param {Variable} colors
|
||||||
|
*/
|
||||||
|
export const PreviousButton = (player, colors) => PlayerButton({
|
||||||
player,
|
player,
|
||||||
|
colors,
|
||||||
items: [
|
items: [
|
||||||
['true', Label({
|
['true', Label({
|
||||||
className: 'previous',
|
class_name: 'previous',
|
||||||
label: icons.mpris.prev,
|
label: icons.mpris.prev,
|
||||||
})],
|
})],
|
||||||
['false', Label({
|
['false', Label({
|
||||||
className: 'previous',
|
class_name: 'previous',
|
||||||
label: icons.mpris.prev,
|
label: icons.mpris.prev,
|
||||||
})],
|
})],
|
||||||
],
|
],
|
||||||
|
@ -364,15 +407,20 @@ export const PreviousButton = (player) => PlayerButton({
|
||||||
prop: 'canGoPrev',
|
prop: 'canGoPrev',
|
||||||
});
|
});
|
||||||
|
|
||||||
export const NextButton = (player) => PlayerButton({
|
/**
|
||||||
|
* @param {Player} player
|
||||||
|
* @param {Variable} colors
|
||||||
|
*/
|
||||||
|
export const NextButton = (player, colors) => PlayerButton({
|
||||||
player,
|
player,
|
||||||
|
colors,
|
||||||
items: [
|
items: [
|
||||||
['true', Label({
|
['true', Label({
|
||||||
className: 'next',
|
class_name: 'next',
|
||||||
label: icons.mpris.next,
|
label: icons.mpris.next,
|
||||||
})],
|
})],
|
||||||
['false', Label({
|
['false', Label({
|
||||||
className: 'next',
|
class_name: 'next',
|
||||||
label: icons.mpris.next,
|
label: icons.mpris.next,
|
||||||
})],
|
})],
|
||||||
],
|
],
|
||||||
|
|
|
@ -8,10 +8,21 @@ 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 FAVE_PLAYER = 'org.mpris.MediaPlayer2.spotify';
|
||||||
|
const SPACING = 8;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {import('types/service/mpris').MprisPlayer} Player
|
||||||
|
* @typedef {import('types/widgets/overlay').default} Overlay
|
||||||
|
* @typedef {import('types/variable').Variable} Variable
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Player} player
|
||||||
|
* @param {Overlay} overlay
|
||||||
|
*/
|
||||||
const Top = (player, overlay) => Box({
|
const Top = (player, overlay) => Box({
|
||||||
className: 'top',
|
class_name: 'top',
|
||||||
hpack: 'start',
|
hpack: 'start',
|
||||||
vpack: 'start',
|
vpack: 'start',
|
||||||
|
|
||||||
|
@ -20,16 +31,21 @@ const Top = (player, overlay) => Box({
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
const Center = (player) => Box({
|
/**
|
||||||
className: 'center',
|
* @param {Player} player
|
||||||
|
* @param {Variable} colors
|
||||||
|
*/
|
||||||
|
const Center = (player, colors) => Box({
|
||||||
|
class_name: 'center',
|
||||||
|
|
||||||
children: [
|
children: [
|
||||||
CenterBox({
|
CenterBox({
|
||||||
|
// @ts-expect-error
|
||||||
vertical: true,
|
vertical: true,
|
||||||
|
|
||||||
children: [
|
children: [
|
||||||
Box({
|
Box({
|
||||||
className: 'metadata',
|
class_name: 'metadata',
|
||||||
vertical: true,
|
vertical: true,
|
||||||
hpack: 'start',
|
hpack: 'start',
|
||||||
vpack: 'center',
|
vpack: 'center',
|
||||||
|
@ -46,11 +62,12 @@ const Center = (player) => Box({
|
||||||
}),
|
}),
|
||||||
|
|
||||||
CenterBox({
|
CenterBox({
|
||||||
|
// @ts-expect-error
|
||||||
vertical: true,
|
vertical: true,
|
||||||
|
|
||||||
children: [
|
children: [
|
||||||
null,
|
null,
|
||||||
mpris.PlayPauseButton(player),
|
mpris.PlayPauseButton(player, colors),
|
||||||
null,
|
null,
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
|
@ -58,41 +75,43 @@ const Center = (player) => Box({
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
const SPACING = 8;
|
/**
|
||||||
|
* @param {Player} player
|
||||||
const Bottom = (player) => Box({
|
* @param {Variable} colors
|
||||||
className: 'bottom',
|
*/
|
||||||
|
const Bottom = (player, colors) => Box({
|
||||||
|
class_name: 'bottom',
|
||||||
|
|
||||||
children: [
|
children: [
|
||||||
mpris.PreviousButton(player, {
|
mpris.PreviousButton(player, colors),
|
||||||
vpack: 'end',
|
|
||||||
hpack: 'start',
|
|
||||||
}),
|
|
||||||
Separator(SPACING),
|
Separator(SPACING),
|
||||||
|
|
||||||
mpris.PositionSlider(player),
|
mpris.PositionSlider(player, colors),
|
||||||
Separator(SPACING),
|
Separator(SPACING),
|
||||||
|
|
||||||
mpris.NextButton(player),
|
mpris.NextButton(player, colors),
|
||||||
Separator(SPACING),
|
Separator(SPACING),
|
||||||
|
|
||||||
mpris.ShuffleButton(player),
|
mpris.ShuffleButton(player, colors),
|
||||||
Separator(SPACING),
|
Separator(SPACING),
|
||||||
|
|
||||||
mpris.LoopButton(player),
|
mpris.LoopButton(player, colors),
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
const PlayerBox = (player, overlay) => {
|
/**
|
||||||
const widget = mpris.CoverArt(player, {
|
* @param {Player} player
|
||||||
className: `player ${player.name}`,
|
* @param {Variable} colors
|
||||||
|
* @param {Overlay} overlay
|
||||||
|
*/
|
||||||
|
const PlayerBox = (player, colors, overlay) => {
|
||||||
|
const widget = mpris.CoverArt(player, colors, {
|
||||||
|
class_name: `player ${player.name}`,
|
||||||
hexpand: true,
|
hexpand: true,
|
||||||
|
|
||||||
children: [
|
start_widget: Top(player, overlay),
|
||||||
Top(player, overlay),
|
center_widget: Center(player, colors),
|
||||||
Center(player),
|
end_widget: Bottom(player, colors),
|
||||||
Bottom(player),
|
|
||||||
],
|
|
||||||
});
|
});
|
||||||
|
|
||||||
widget.visible = false;
|
widget.visible = false;
|
||||||
|
@ -102,26 +121,28 @@ const PlayerBox = (player, overlay) => {
|
||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
const content = PlayerGesture({
|
const content = PlayerGesture({
|
||||||
properties: [
|
attribute: {
|
||||||
['players', new Map()],
|
players: new Map(),
|
||||||
['setup', false],
|
setup: false,
|
||||||
],
|
},
|
||||||
|
|
||||||
setup: (self) => {
|
setup: (self) => {
|
||||||
self
|
self
|
||||||
.hook(Mpris, (overlay, busName) => {
|
.hook(Mpris, (overlay, bus_name) => {
|
||||||
if (overlay._players.has(busName)) {
|
const players = overlay.attribute.players;
|
||||||
|
|
||||||
|
if (players.has(bus_name)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sometimes the signal doesn't give the busName
|
// Sometimes the signal doesn't give the bus_name
|
||||||
if (!busName) {
|
if (!bus_name) {
|
||||||
const player = Mpris.players.find((p) => {
|
const player = Mpris.players.find((p) => {
|
||||||
return !overlay._players.has(p.busName);
|
return !players.has(p.bus_name);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (player) {
|
if (player) {
|
||||||
busName = player.busName;
|
bus_name = player.bus_name;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return;
|
return;
|
||||||
|
@ -129,54 +150,67 @@ export default () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the one on top so we can move it up later
|
// Get the one on top so we can move it up later
|
||||||
const previousFirst = overlay.list().at(-1);
|
const previousFirst = overlay.attribute.list().at(-1);
|
||||||
|
|
||||||
// Make the new player
|
// Make the new player
|
||||||
const player = Mpris.getPlayer(busName);
|
const player = Mpris.getPlayer(bus_name);
|
||||||
|
const Colors = Variable(null);
|
||||||
|
|
||||||
player.colors = Variable();
|
if (!player) {
|
||||||
overlay._players.set(
|
return;
|
||||||
busName,
|
}
|
||||||
PlayerBox(player, content.getOverlay()),
|
|
||||||
|
players.set(
|
||||||
|
bus_name,
|
||||||
|
// @ts-expect-error
|
||||||
|
PlayerBox(player, Colors, content.child),
|
||||||
);
|
);
|
||||||
overlay.overlays = Array.from(overlay._players.values())
|
overlay.overlays = Array.from(players.values())
|
||||||
.map((widget) => widget);
|
.map((widget) => widget);
|
||||||
|
|
||||||
|
const includes = overlay.attribute
|
||||||
|
.includesWidget(previousFirst);
|
||||||
|
|
||||||
// Select favorite player at startup
|
// Select favorite player at startup
|
||||||
if (!overlay._setup && overlay._players.has(FAVE_PLAYER)) {
|
if (!overlay.attribute.setup && players.has(FAVE_PLAYER)) {
|
||||||
overlay.moveToTop(overlay._players.get(FAVE_PLAYER));
|
overlay.attribute.moveToTop(players.get(FAVE_PLAYER));
|
||||||
overlay._setup = true;
|
overlay.attribute.setup = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Move previousFirst on top again
|
// Move previousFirst on top again
|
||||||
else if (overlay.includesWidget(previousFirst)) {
|
else if (includes) {
|
||||||
overlay.moveToTop(previousFirst);
|
overlay.attribute.moveToTop(previousFirst);
|
||||||
}
|
}
|
||||||
}, 'player-added')
|
}, 'player-added')
|
||||||
|
|
||||||
.hook(Mpris, (overlay, busName) => {
|
.hook(Mpris, (overlay, bus_name) => {
|
||||||
if (!busName || !overlay._players.has(busName)) {
|
const players = overlay.attribute.players;
|
||||||
|
|
||||||
|
if (!bus_name || !players.has(bus_name)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the one on top so we can move it up later
|
// Get the one on top so we can move it up later
|
||||||
const previousFirst = overlay.list().at(-1);
|
const previousFirst = overlay.attribute.list().at(-1);
|
||||||
|
|
||||||
// Remake overlays without deleted one
|
// Remake overlays without deleted one
|
||||||
overlay._players.delete(busName);
|
players.delete(bus_name);
|
||||||
overlay.overlays = Array.from(overlay._players.values())
|
overlay.overlays = Array.from(players.values())
|
||||||
.map((widget) => widget);
|
.map((widget) => widget);
|
||||||
|
|
||||||
// Move previousFirst on top again
|
// Move previousFirst on top again
|
||||||
if (overlay.includesWidget(previousFirst)) {
|
const includes = overlay.attribute
|
||||||
overlay.moveToTop(previousFirst);
|
.includesWidget(previousFirst);
|
||||||
|
|
||||||
|
if (includes) {
|
||||||
|
overlay.attribute.moveToTop(previousFirst);
|
||||||
}
|
}
|
||||||
}, 'player-closed');
|
}, 'player-closed');
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return Box({
|
return Box({
|
||||||
className: 'media',
|
class_name: 'media',
|
||||||
child: content,
|
child: content,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
@ -6,19 +6,39 @@ import Gtk from 'gi://Gtk';
|
||||||
|
|
||||||
|
|
||||||
// TODO: wrap in another EventBox for disabled cursor
|
// TODO: wrap in another EventBox for disabled cursor
|
||||||
|
/**
|
||||||
|
* @typedef {import('types/widget.js').Widget} Widget
|
||||||
|
* @typedef {Widget & Object} CursorProps
|
||||||
|
* @property {boolean=} isButton
|
||||||
|
* @property {function(Widget):void=} onPrimaryClickRelease
|
||||||
|
*
|
||||||
|
* @param {CursorProps} obj
|
||||||
|
*/
|
||||||
export default ({
|
export default ({
|
||||||
isButton = false,
|
isButton = false,
|
||||||
onPrimaryClickRelease = () => { /**/ },
|
|
||||||
|
onPrimaryClickRelease = (self) => {
|
||||||
|
self;
|
||||||
|
},
|
||||||
...props
|
...props
|
||||||
}) => {
|
}) => {
|
||||||
// Make this variable to know if the function should
|
// Make this variable to know if the function should
|
||||||
// be executed depending on where the click is released
|
// be executed depending on where the click is released
|
||||||
const CanRun = Variable(true);
|
const CanRun = Variable(true);
|
||||||
|
|
||||||
const properties = {
|
let widgetFunc;
|
||||||
|
|
||||||
|
if (isButton) {
|
||||||
|
widgetFunc = Button;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
widgetFunc = EventBox;
|
||||||
|
}
|
||||||
|
|
||||||
|
const widget = widgetFunc({
|
||||||
...props,
|
...props,
|
||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
onPrimaryClickRelease: (self) => {
|
on_primary_click_release: (self) => {
|
||||||
// Every click, do a one shot connect to
|
// Every click, do a one shot connect to
|
||||||
// CanRun to wait for location of click
|
// CanRun to wait for location of click
|
||||||
const id = CanRun.connect('changed', () => {
|
const id = CanRun.connect('changed', () => {
|
||||||
|
@ -29,19 +49,11 @@ export default ({
|
||||||
CanRun.disconnect(id);
|
CanRun.disconnect(id);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
};
|
});
|
||||||
|
|
||||||
let widget;
|
|
||||||
|
|
||||||
if (isButton) {
|
|
||||||
widget = Button(properties);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
widget = EventBox(properties);
|
|
||||||
}
|
|
||||||
|
|
||||||
const gesture = Gtk.GestureLongPress.new(widget);
|
const gesture = Gtk.GestureLongPress.new(widget);
|
||||||
|
|
||||||
|
// @ts-expect-error
|
||||||
widget.hook(gesture, () => {
|
widget.hook(gesture, () => {
|
||||||
const pointer = gesture.get_point(null);
|
const pointer = gesture.get_point(null);
|
||||||
const x = pointer[1];
|
const x = pointer[1];
|
||||||
|
|
|
@ -1,6 +1,19 @@
|
||||||
import { execAsync, readFileAsync, timeout } from 'resource:///com/github/Aylur/ags/utils.js';
|
import { execAsync, readFileAsync, timeout } from 'resource:///com/github/Aylur/ags/utils.js';
|
||||||
const { get_home_dir } = imports.gi.GLib;
|
const { get_home_dir } = imports.gi.GLib;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} Persist
|
||||||
|
* @property {string} name
|
||||||
|
* @property {typeof imports.gi.GObject} gobject
|
||||||
|
* @property {string} prop
|
||||||
|
* @property {boolean|string=} condition - if string, compare following props to this
|
||||||
|
* @property {boolean|string=} whenTrue
|
||||||
|
* @property {boolean|string=} whenFalse
|
||||||
|
* @property {string=} signal
|
||||||
|
*
|
||||||
|
* @param {Persist} props
|
||||||
|
*/
|
||||||
export default ({
|
export default ({
|
||||||
name,
|
name,
|
||||||
gobject,
|
gobject,
|
||||||
|
@ -9,7 +22,7 @@ export default ({
|
||||||
whenTrue = condition,
|
whenTrue = condition,
|
||||||
whenFalse = false,
|
whenFalse = false,
|
||||||
signal = 'changed',
|
signal = 'changed',
|
||||||
} = {}) => {
|
}) => {
|
||||||
const cacheFile = `${get_home_dir()}/.cache/ags/.${name}`;
|
const cacheFile = `${get_home_dir()}/.cache/ags/.${name}`;
|
||||||
|
|
||||||
const stateCmd = () => ['bash', '-c',
|
const stateCmd = () => ['bash', '-c',
|
||||||
|
|
|
@ -1,17 +1,19 @@
|
||||||
{
|
{
|
||||||
|
"main": "config.js",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@girs/dbusmenugtk3-0.4": "^0.4.0-3.2.2",
|
"fzf": "^0.5.2"
|
||||||
"@girs/gtk-3.0": "^3.24.39-3.2.2",
|
|
||||||
"@girs/gudev-1.0": "^1.0.0-3.2.2",
|
|
||||||
"@girs/gvc-1.0": "^1.0.0-3.2.2",
|
|
||||||
"@girs/nm-1.0": "^1.45.1-3.2.2",
|
|
||||||
"@typescript-eslint/eslint-plugin": "^6.9.1",
|
|
||||||
"@typescript-eslint/parser": "^6.9.1",
|
|
||||||
"eslint": "^8.52.0",
|
|
||||||
"fzf": "^0.5.2",
|
|
||||||
"stylelint-config-standard-scss": "^11.0.0"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@stylistic/eslint-plugin": "^1.4.0"
|
"eslint": "^8.52.0",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^6.9.1",
|
||||||
|
"@typescript-eslint/parser": "^6.9.1",
|
||||||
|
"stylelint-config-standard-scss": "^11.0.0",
|
||||||
|
"@stylistic/eslint-plugin": "^1.4.0",
|
||||||
|
"@girs/dbusmenugtk3-0.4": "^0.4.0-3.2.0",
|
||||||
|
"@girs/gudev-1.0": "^1.0.0-3.2.2",
|
||||||
|
"@girs/gobject-2.0": "^2.76.1-3.2.3",
|
||||||
|
"@girs/gtk-3.0": "^3.24.39-3.2.2",
|
||||||
|
"@girs/gvc-1.0": "^1.0.0-3.1.0",
|
||||||
|
"@girs/nm-1.0": "^1.43.1-3.1.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -105,7 +105,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.label {
|
label {
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
20
devices/wim/config/ags/tsconfig.json
Normal file
20
devices/wim/config/ags/tsconfig.json
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2022",
|
||||||
|
"module": "ES2022",
|
||||||
|
"lib": [
|
||||||
|
"ES2022"
|
||||||
|
],
|
||||||
|
"allowJs": true,
|
||||||
|
"checkJs": true,
|
||||||
|
"strict": true,
|
||||||
|
"noImplicitAny": false,
|
||||||
|
"baseUrl": ".",
|
||||||
|
"typeRoots": [
|
||||||
|
"./types/ags.d.ts",
|
||||||
|
"./node_modules/@girs"
|
||||||
|
],
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"forceConsistentCasingInFileNames": true
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue