refactor(ags): typecheck more stuff
This commit is contained in:
parent
e8197651d9
commit
894fdd92f1
17 changed files with 596 additions and 404 deletions
|
@ -1,6 +1,5 @@
|
||||||
import App from 'resource:///com/github/Aylur/ags/app.js';
|
import App from 'resource:///com/github/Aylur/ags/app.js';
|
||||||
import Applications from 'resource:///com/github/Aylur/ags/service/applications.js';
|
import Applications from 'resource:///com/github/Aylur/ags/service/applications.js';
|
||||||
import Hyprland from 'resource:///com/github/Aylur/ags/service/hyprland.js';
|
|
||||||
// TODO: find cleaner way to import this
|
// TODO: find cleaner way to import this
|
||||||
import { Fzf } from '../../node_modules/fzf/dist/fzf.es.js';
|
import { Fzf } from '../../node_modules/fzf/dist/fzf.es.js';
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
// @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';
|
||||||
|
@ -11,8 +10,8 @@ const SPACING = 5;
|
||||||
export default () => {
|
export default () => {
|
||||||
const icon = Icon().hook(Bluetooth, (self) => {
|
const icon = Icon().hook(Bluetooth, (self) => {
|
||||||
if (Bluetooth.enabled) {
|
if (Bluetooth.enabled) {
|
||||||
self.icon = Bluetooth.connectedDevices[0] ?
|
self.icon = Bluetooth.connected_devices[0] ?
|
||||||
Bluetooth.connectedDevices[0].iconName :
|
Bluetooth.connected_devices[0].icon_name :
|
||||||
'bluetooth-active-symbolic';
|
'bluetooth-active-symbolic';
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -29,8 +28,8 @@ export default () => {
|
||||||
Separator(SPACING),
|
Separator(SPACING),
|
||||||
|
|
||||||
Label().hook(Bluetooth, (self) => {
|
Label().hook(Bluetooth, (self) => {
|
||||||
self.label = Bluetooth.connectedDevices[0] ?
|
self.label = Bluetooth.connected_devices[0] ?
|
||||||
`${Bluetooth.connectedDevices[0]}` :
|
`${Bluetooth.connected_devices[0]}` :
|
||||||
'Disconnected';
|
'Disconnected';
|
||||||
}, 'notify::connected-devices'),
|
}, 'notify::connected-devices'),
|
||||||
],
|
],
|
||||||
|
|
|
@ -70,113 +70,104 @@ export default ({
|
||||||
setup(self);
|
setup(self);
|
||||||
|
|
||||||
self
|
self
|
||||||
.hook(gesture,
|
.hook(gesture, (_, realGesture) => {
|
||||||
/**
|
if (realGesture) {
|
||||||
* @param {Overlay} overlay
|
Array.from(self.attribute.list())
|
||||||
* @param {number} realGesture
|
.forEach((over) => {
|
||||||
*/
|
over.visible = true;
|
||||||
(overlay, realGesture) => {
|
});
|
||||||
if (realGesture) {
|
}
|
||||||
Array.from(overlay.attribute.list())
|
else {
|
||||||
.forEach((over) => {
|
self.attribute.showTopOnly();
|
||||||
over.visible = true;
|
}
|
||||||
});
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
overlay.attribute.showTopOnly();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Don't allow gesture when only one player
|
// Don't allow gesture when only one player
|
||||||
if (overlay.attribute.list().length <= 1) {
|
if (self.attribute.list().length <= 1) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
overlay.attribute.dragging = true;
|
self.attribute.dragging = true;
|
||||||
let offset = gesture.get_offset()[1];
|
let offset = gesture.get_offset()[1];
|
||||||
const playerBox = overlay.attribute.list().at(-1);
|
const playerBox = self.attribute.list().at(-1);
|
||||||
|
|
||||||
if (!offset) {
|
if (!offset) {
|
||||||
return;
|
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.attribute.bgStyle}
|
${playerBox.attribute.bgStyle}
|
||||||
`);
|
`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Slide left
|
// Slide left
|
||||||
else {
|
else {
|
||||||
offset = Math.abs(offset);
|
offset = Math.abs(offset);
|
||||||
playerBox.setCss(`
|
playerBox.setCss(`
|
||||||
margin-left: -${offset}px;
|
margin-left: -${offset}px;
|
||||||
margin-right: ${offset}px;
|
margin-right: ${offset}px;
|
||||||
${playerBox.attribute.bgStyle}
|
${playerBox.attribute.bgStyle}
|
||||||
`);
|
`);
|
||||||
}
|
}
|
||||||
},
|
}, 'drag-update')
|
||||||
'drag-update')
|
|
||||||
|
|
||||||
|
|
||||||
.hook(gesture,
|
.hook(gesture, () => {
|
||||||
/** @param {Overlay} overlay */
|
// Don't allow gesture when only one player
|
||||||
(overlay) => {
|
if (self.attribute.list().length <= 1) {
|
||||||
// Don't allow gesture when only one player
|
return;
|
||||||
if (overlay.attribute.list().length <= 1) {
|
}
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
overlay.attribute.dragging = false;
|
self.attribute.dragging = false;
|
||||||
const offset = gesture.get_offset()[1];
|
const offset = gesture.get_offset()[1];
|
||||||
|
|
||||||
const playerBox = overlay.attribute.list().at(-1);
|
const playerBox = self.attribute.list().at(-1);
|
||||||
|
|
||||||
// If crosses threshold after letting go, slide away
|
// If crosses threshold after letting go, slide away
|
||||||
if (offset && 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;
|
||||||
|
|
||||||
// Slide away right
|
// Slide away right
|
||||||
if (offset >= 0) {
|
if (offset >= 0) {
|
||||||
playerBox.setCss(`
|
playerBox.setCss(`
|
||||||
${TRANSITION}
|
${TRANSITION}
|
||||||
margin-left: ${OFFSCREEN}px;
|
margin-left: ${OFFSCREEN}px;
|
||||||
margin-right: -${OFFSCREEN}px;
|
margin-right: -${OFFSCREEN}px;
|
||||||
opacity: 0.7; ${playerBox.attribute.bgStyle}
|
opacity: 0.7; ${playerBox.attribute.bgStyle}
|
||||||
`);
|
`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Slide away left
|
// Slide away left
|
||||||
else {
|
else {
|
||||||
playerBox.setCss(`
|
playerBox.setCss(`
|
||||||
${TRANSITION}
|
${TRANSITION}
|
||||||
margin-left: -${OFFSCREEN}px;
|
margin-left: -${OFFSCREEN}px;
|
||||||
margin-right: ${OFFSCREEN}px;
|
margin-right: ${OFFSCREEN}px;
|
||||||
opacity: 0.7; ${playerBox.attribute.bgStyle}
|
opacity: 0.7; ${playerBox.attribute.bgStyle}
|
||||||
`);
|
`);
|
||||||
}
|
|
||||||
|
|
||||||
timeout(ANIM_DURATION, () => {
|
|
||||||
// Put the player in the back after anim
|
|
||||||
overlay.reorder_overlay(playerBox, 0);
|
|
||||||
// Recenter player
|
|
||||||
playerBox.setCss(playerBox.attribute.bgStyle);
|
|
||||||
|
|
||||||
widget.sensitive = true;
|
|
||||||
|
|
||||||
overlay.attribute.showTopOnly();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
// Recenter with transition for animation
|
timeout(ANIM_DURATION, () => {
|
||||||
playerBox.setCss(`${TRANSITION}
|
// Put the player in the back after anim
|
||||||
|
self.reorder_overlay(playerBox, 0);
|
||||||
|
// Recenter player
|
||||||
|
playerBox.setCss(playerBox.attribute.bgStyle);
|
||||||
|
|
||||||
|
widget.sensitive = true;
|
||||||
|
|
||||||
|
self.attribute.showTopOnly();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Recenter with transition for animation
|
||||||
|
playerBox.setCss(`${TRANSITION}
|
||||||
${playerBox.attribute.bgStyle}`);
|
${playerBox.attribute.bgStyle}`);
|
||||||
}
|
}
|
||||||
},
|
}, 'drag-end');
|
||||||
'drag-end');
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@ const OSDs = () => {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Send reference of stack to all items
|
||||||
stack.items = OSDList.map((osd, i) => [`${i}`, osd(stack)]);
|
stack.items = OSDList.map((osd, i) => [`${i}`, osd(stack)]);
|
||||||
|
|
||||||
// Delay popup method so it
|
// Delay popup method so it
|
||||||
|
|
|
@ -7,7 +7,16 @@ import { timeout } from 'resource:///com/github/Aylur/ags/utils.js';
|
||||||
import { WindowButton } from './dragndrop.js';
|
import { WindowButton } from './dragndrop.js';
|
||||||
import * as VARS from './variables.js';
|
import * as VARS from './variables.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {import('types/service/hyprland.js').Client} Client
|
||||||
|
* @typedef {import('types/widgets/revealer').default} Revealer
|
||||||
|
* @typedef {import('types/widgets/box').default} Box
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @param {number} size */
|
||||||
const scale = (size) => (size * VARS.SCALE) - VARS.MARGIN;
|
const scale = (size) => (size * VARS.SCALE) - VARS.MARGIN;
|
||||||
|
|
||||||
|
/** @param {Client} client */
|
||||||
const getFontSize = (client) => {
|
const getFontSize = (client) => {
|
||||||
const valX = scale(client.size[0]) * VARS.ICON_SCALE;
|
const valX = scale(client.size[0]) * VARS.ICON_SCALE;
|
||||||
const valY = scale(client.size[1]) * VARS.ICON_SCALE;
|
const valY = scale(client.size[1]) * VARS.ICON_SCALE;
|
||||||
|
@ -17,6 +26,7 @@ const getFontSize = (client) => {
|
||||||
return size <= 0 ? 0.1 : size;
|
return size <= 0 ? 0.1 : size;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** @param {Client} client */
|
||||||
const IconStyle = (client) => `
|
const IconStyle = (client) => `
|
||||||
min-width: ${scale(client.size[0])}px;
|
min-width: ${scale(client.size[0])}px;
|
||||||
min-height: ${scale(client.size[1])}px;
|
min-height: ${scale(client.size[1])}px;
|
||||||
|
@ -24,6 +34,12 @@ const IconStyle = (client) => `
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Client} client
|
||||||
|
* @param {boolean} active
|
||||||
|
* @param {Array<Client>} clients
|
||||||
|
* @param {Box} box
|
||||||
|
*/
|
||||||
const Client = (client, active, clients, box) => {
|
const Client = (client, active, clients, box) => {
|
||||||
const wsName = String(client.workspace.name).replace('special:', '');
|
const wsName = String(client.workspace.name).replace('special:', '');
|
||||||
const wsId = client.workspace.id;
|
const wsId = client.workspace.id;
|
||||||
|
@ -31,42 +47,40 @@ const Client = (client, active, clients, box) => {
|
||||||
|
|
||||||
return Revealer({
|
return Revealer({
|
||||||
transition: 'crossfade',
|
transition: 'crossfade',
|
||||||
|
reveal_child: true,
|
||||||
|
|
||||||
setup: (rev) => {
|
attribute: {
|
||||||
rev.revealChild = true;
|
address: client.address,
|
||||||
|
to_destroy: false,
|
||||||
},
|
},
|
||||||
|
|
||||||
properties: [
|
|
||||||
['address', client.address],
|
|
||||||
['toDestroy', false],
|
|
||||||
],
|
|
||||||
|
|
||||||
child: WindowButton({
|
child: WindowButton({
|
||||||
mainBox: box,
|
mainBox: box,
|
||||||
address: client.address,
|
address: client.address,
|
||||||
|
|
||||||
onSecondaryClickRelease: () => {
|
on_secondary_click_release: () => {
|
||||||
Hyprland.sendMessage(`dispatch closewindow ${addr}`);
|
Hyprland.sendMessage(`dispatch closewindow ${addr}`);
|
||||||
},
|
},
|
||||||
|
|
||||||
onPrimaryClickRelease: () => {
|
on_primary_click_release: () => {
|
||||||
if (wsId < 0) {
|
if (wsId < 0) {
|
||||||
if (client.workspace.name === 'special') {
|
if (client.workspace.name === 'special') {
|
||||||
Hyprland.sendMessage('dispatch ' +
|
Hyprland.sendMessage('dispatch ' +
|
||||||
`movetoworkspacesilent special:${wsId},${addr}`)
|
`movetoworkspacesilent special:${wsId},${addr}`)
|
||||||
.then(
|
.then(() => {
|
||||||
Hyprland.sendMessage('dispatch ' +
|
Hyprland.sendMessage('dispatch ' +
|
||||||
`togglespecialworkspace ${wsId}`)
|
`togglespecialworkspace ${wsId}`)
|
||||||
.then(
|
.then(() => {
|
||||||
() => App.closeWindow('overview'),
|
App.closeWindow('overview');
|
||||||
).catch(print),
|
}).catch(print);
|
||||||
).catch(print);
|
}).catch(print);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Hyprland.sendMessage('dispatch ' +
|
Hyprland.sendMessage('dispatch ' +
|
||||||
`togglespecialworkspace ${wsName}`).then(
|
`togglespecialworkspace ${wsName}`)
|
||||||
() => App.closeWindow('overview'),
|
.then(() => {
|
||||||
).catch(print);
|
App.closeWindow('overview');
|
||||||
|
}).catch(print);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -86,14 +100,15 @@ const Client = (client, active, clients, box) => {
|
||||||
.catch(print);
|
.catch(print);
|
||||||
}
|
}
|
||||||
|
|
||||||
Hyprland.sendMessage(`dispatch focuswindow ${addr}`).then(
|
Hyprland.sendMessage(`dispatch focuswindow ${addr}`)
|
||||||
() => App.closeWindow('overview'),
|
.then(() => {
|
||||||
).catch(print);
|
App.closeWindow('overview');
|
||||||
|
}).catch(print);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
child: Icon({
|
child: Icon({
|
||||||
className: `window ${active}`,
|
class_name: `window ${active ? 'active' : ''}`,
|
||||||
css: `${IconStyle(client)} font-size: 10px;`,
|
css: `${IconStyle(client)} font-size: 10px;`,
|
||||||
icon: client.class,
|
icon: client.class,
|
||||||
}),
|
}),
|
||||||
|
@ -101,69 +116,79 @@ const Client = (client, active, clients, box) => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** @param {Box} box */
|
||||||
export const updateClients = (box) => {
|
export const updateClients = (box) => {
|
||||||
Hyprland.sendMessage('j/clients').then((out) => {
|
Hyprland.sendMessage('j/clients').then((out) => {
|
||||||
const clients = JSON.parse(out).filter((client) => client.class);
|
/** @type Array<Client> */
|
||||||
|
let clients = JSON.parse(out);
|
||||||
|
|
||||||
box._workspaces.forEach((workspace) => {
|
clients = clients.filter((client) => client.class);
|
||||||
const fixed = workspace.getFixed();
|
|
||||||
const toRemove = fixed.get_children();
|
|
||||||
|
|
||||||
clients.filter((client) => client.workspace.id === workspace._id)
|
box.attribute.workspaces.forEach(
|
||||||
.forEach((client) => {
|
/** @param {Revealer} workspace */
|
||||||
let active = '';
|
(workspace) => {
|
||||||
|
const fixed = workspace.attribute.get_fixed();
|
||||||
|
/** @type Array<Revealer> */
|
||||||
|
const toRemove = fixed.get_children();
|
||||||
|
|
||||||
if (client.address === Hyprland.active.client.address) {
|
clients.filter((client) =>
|
||||||
active = 'active';
|
client.workspace.id === workspace.attribute.id)
|
||||||
}
|
.forEach((client) => {
|
||||||
|
const active =
|
||||||
|
client.address === Hyprland.active.client.address;
|
||||||
|
|
||||||
// TODO: fix multi monitor issue. this is just a temp fix
|
// TODO: fix multi monitor issue. this is just a temp fix
|
||||||
client.at[1] -= 2920;
|
client.at[1] -= 2920;
|
||||||
|
|
||||||
// Special workspaces that haven't been opened yet
|
// Special workspaces that haven't been opened yet
|
||||||
// return a size of 0. We need to set them to default
|
// return a size of 0. We need to set them to default
|
||||||
// values to show the workspace properly
|
// values to show the workspace properly
|
||||||
if (client.size[0] === 0) {
|
if (client.size[0] === 0) {
|
||||||
client.size[0] = VARS.DEFAULT_SPECIAL.SIZE_X;
|
client.size[0] = VARS.DEFAULT_SPECIAL.SIZE_X;
|
||||||
client.size[1] = VARS.DEFAULT_SPECIAL.SIZE_Y;
|
client.size[1] = VARS.DEFAULT_SPECIAL.SIZE_Y;
|
||||||
client.at[0] = VARS.DEFAULT_SPECIAL.POS_X;
|
client.at[0] = VARS.DEFAULT_SPECIAL.POS_X;
|
||||||
client.at[1] = VARS.DEFAULT_SPECIAL.POS_Y;
|
client.at[1] = VARS.DEFAULT_SPECIAL.POS_Y;
|
||||||
}
|
}
|
||||||
|
|
||||||
const newClient = [
|
const newClient = [
|
||||||
fixed.get_children()
|
fixed.get_children().find(
|
||||||
.find((ch) => ch._address === client.address),
|
/** @param {typeof WindowButton} ch */
|
||||||
client.at[0] * VARS.SCALE,
|
// @ts-expect-error
|
||||||
client.at[1] * VARS.SCALE,
|
(ch) => ch.attribute.address === client.address,
|
||||||
];
|
),
|
||||||
|
client.at[0] * VARS.SCALE,
|
||||||
|
client.at[1] * VARS.SCALE,
|
||||||
|
];
|
||||||
|
|
||||||
// If it exists already
|
// If it exists already
|
||||||
if (newClient[0]) {
|
if (newClient[0]) {
|
||||||
toRemove.splice(toRemove.indexOf(newClient[0]), 1);
|
toRemove.splice(toRemove.indexOf(newClient[0]), 1);
|
||||||
fixed.move(...newClient);
|
fixed.move(...newClient);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
newClient[0] = Client(client, active, clients, box);
|
||||||
|
fixed.put(...newClient);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set a timeout here to have an animation when the icon first appears
|
||||||
|
timeout(1, () => {
|
||||||
|
newClient[0].child.child.className =
|
||||||
|
`window ${active}`;
|
||||||
|
newClient[0].child.child.setCss(IconStyle(client));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
fixed.show_all();
|
||||||
|
toRemove.forEach((ch) => {
|
||||||
|
if (ch.attribute.to_destroy) {
|
||||||
|
ch.destroy();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
newClient[0] = Client(client, active, clients, box);
|
ch.reveal_child = false;
|
||||||
fixed.put(...newClient);
|
ch.attribute.to_destroy = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set a timeout here to have an animation when the icon first appears
|
|
||||||
timeout(1, () => {
|
|
||||||
newClient[0].child.child.className = `window ${active}`;
|
|
||||||
newClient[0].child.child.setCss(IconStyle(client));
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
},
|
||||||
fixed.show_all();
|
);
|
||||||
toRemove.forEach((ch) => {
|
|
||||||
if (ch._toDestroy) {
|
|
||||||
ch.destroy();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
ch.revealChild = false;
|
|
||||||
ch._toDestroy = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}).catch(print);
|
}).catch(print);
|
||||||
};
|
};
|
||||||
|
|
|
@ -11,23 +11,39 @@ const DEFAULT_STYLE = `
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {import('types/widgets/box').default} Box
|
||||||
|
* @typedef {import('types/widgets/revealer').default} Revealer
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
export const Highlighter = () => Box({
|
export const Highlighter = () => Box({
|
||||||
vpack: 'start',
|
vpack: 'start',
|
||||||
hpack: 'start',
|
hpack: 'start',
|
||||||
className: 'workspace active',
|
class_name: 'workspace active',
|
||||||
css: DEFAULT_STYLE,
|
css: DEFAULT_STYLE,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Box} main
|
||||||
|
* @param {Box} highlighter
|
||||||
|
*/
|
||||||
export const updateCurrentWorkspace = (main, highlighter) => {
|
export const updateCurrentWorkspace = (main, highlighter) => {
|
||||||
const currentId = Hyprland.active.workspace.id;
|
const currentId = Hyprland.active.workspace.id;
|
||||||
const row = Math.floor((currentId - 1) / VARS.WORKSPACE_PER_ROW);
|
const row = Math.floor((currentId - 1) / VARS.WORKSPACE_PER_ROW);
|
||||||
|
|
||||||
|
// @ts-expect-error
|
||||||
const rowObject = main.children[0].children[row];
|
const rowObject = main.children[0].children[row];
|
||||||
const workspaces = rowObject.child.centerWidget.child
|
const workspaces = rowObject.child.centerWidget.child
|
||||||
.get_children().filter((w) => w.revealChild);
|
.get_children().filter(
|
||||||
|
/** @param {Revealer} w */
|
||||||
|
(w) => w.reveal_child,
|
||||||
|
);
|
||||||
|
|
||||||
const currentIndex = workspaces.findIndex((w) => w._id === currentId);
|
const currentIndex = workspaces.findIndex(
|
||||||
|
/** @param {Revealer} w */
|
||||||
|
(w) => w.attribute.id === currentId,
|
||||||
|
);
|
||||||
const left = currentIndex * ((VARS.SCREEN.X * VARS.SCALE) + PADDING);
|
const left = currentIndex * ((VARS.SCREEN.X * VARS.SCALE) + PADDING);
|
||||||
const height = row * ((VARS.SCREEN.Y * VARS.SCALE) + (PADDING / 2));
|
const height = row * ((VARS.SCREEN.Y * VARS.SCALE) + (PADDING / 2));
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,15 @@ 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)];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {import('types/widgets/button').default} Button
|
||||||
|
* @typedef {import('types/widgets/button').ButtonProps} ButtonProps
|
||||||
|
* @typedef {import('types/widgets/eventbox').EventBoxProps=} EventBoxProps
|
||||||
|
* @typedef {import('types/widgets/box').default} Box
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/** @param {Button} widget */
|
||||||
const createSurfaceFromWidget = (widget) => {
|
const createSurfaceFromWidget = (widget) => {
|
||||||
const alloc = widget.get_allocation();
|
const alloc = widget.get_allocation();
|
||||||
const surface = new Cairo.ImageSurface(
|
const surface = new Cairo.ImageSurface(
|
||||||
|
@ -29,16 +37,19 @@ const createSurfaceFromWidget = (widget) => {
|
||||||
|
|
||||||
let hidden = 0;
|
let hidden = 0;
|
||||||
|
|
||||||
export const WorkspaceDrop = (props) => EventBox({
|
/** @params {EventBoxProps} props */
|
||||||
|
export const WorkspaceDrop = ({ ...props }) => EventBox({
|
||||||
...props,
|
...props,
|
||||||
setup: (self) => {
|
setup: (self) => {
|
||||||
self.drag_dest_set(Gtk.DestDefaults.ALL, TARGET, Gdk.DragAction.COPY);
|
self.drag_dest_set(Gtk.DestDefaults.ALL, TARGET, Gdk.DragAction.COPY);
|
||||||
|
|
||||||
self.on('drag-data-received', (_, _c, _x, _y, data) => {
|
self.on('drag-data-received', (_, _c, _x, _y, data) => {
|
||||||
let id = self.get_parent()._id;
|
// @ts-expect-error
|
||||||
|
let id = self.get_parent()?.attribute.id;
|
||||||
|
|
||||||
if (id < -1) {
|
if (id < -1) {
|
||||||
id = self.get_parent()._name;
|
// @ts-expect-error
|
||||||
|
id = self.get_parent()?.attribute.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (id === -1) {
|
else if (id === -1) {
|
||||||
|
@ -56,11 +67,17 @@ export const WorkspaceDrop = (props) => EventBox({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {ButtonProps & {
|
||||||
|
* address: string
|
||||||
|
* mainBox: Box
|
||||||
|
* }} o
|
||||||
|
*/
|
||||||
export const WindowButton = ({
|
export const WindowButton = ({
|
||||||
address,
|
address,
|
||||||
mainBox,
|
mainBox,
|
||||||
...props
|
...props
|
||||||
} = {}) => Button({
|
}) => Button({
|
||||||
...props,
|
...props,
|
||||||
|
|
||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
|
@ -78,11 +95,12 @@ export const WindowButton = ({
|
||||||
|
|
||||||
self.on('drag-begin', (_, context) => {
|
self.on('drag-begin', (_, context) => {
|
||||||
Gtk.drag_set_icon_surface(context, createSurfaceFromWidget(self));
|
Gtk.drag_set_icon_surface(context, createSurfaceFromWidget(self));
|
||||||
self.get_parent().revealChild = false;
|
// @ts-expect-error
|
||||||
|
self.get_parent()?.set_reveal_child(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
self.on('drag-end', () => {
|
self.on('drag-end', () => {
|
||||||
self.get_parent().destroy();
|
self.get_parent()?.destroy();
|
||||||
|
|
||||||
updateClients(mainBox);
|
updateClients(mainBox);
|
||||||
});
|
});
|
||||||
|
|
|
@ -9,21 +9,30 @@ import { Highlighter, updateCurrentWorkspace } from './current-workspace.js';
|
||||||
import { updateClients } from './clients.js';
|
import { updateClients } from './clients.js';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// TODO: have a 'page' for each monitor, arrows on both sides to loop through
|
// TODO: have a 'page' for each monitor, arrows on both sides to loop through
|
||||||
export const Overview = () => {
|
export const Overview = () => {
|
||||||
const highlighter = Highlighter();
|
const highlighter = Highlighter();
|
||||||
|
|
||||||
const mainBox = Box({
|
const mainBox = Box({
|
||||||
// Do this for scss hierarchy
|
// Do this for scss hierarchy
|
||||||
className: 'overview',
|
class_name: 'overview',
|
||||||
css: 'all: unset',
|
css: 'all: unset',
|
||||||
|
|
||||||
vertical: true,
|
vertical: true,
|
||||||
vpack: 'center',
|
vpack: 'center',
|
||||||
hpack: 'center',
|
hpack: 'center',
|
||||||
|
|
||||||
|
attribute: {
|
||||||
|
workspaces: [],
|
||||||
|
|
||||||
|
update: () => {
|
||||||
|
getWorkspaces(mainBox);
|
||||||
|
updateWorkspaces(mainBox);
|
||||||
|
updateClients(mainBox);
|
||||||
|
updateCurrentWorkspace(mainBox, highlighter);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
children: [
|
children: [
|
||||||
Box({
|
Box({
|
||||||
vertical: true,
|
vertical: true,
|
||||||
|
@ -42,31 +51,24 @@ export const Overview = () => {
|
||||||
|
|
||||||
setup: (self) => {
|
setup: (self) => {
|
||||||
self.hook(Hyprland, () => {
|
self.hook(Hyprland, () => {
|
||||||
if (!App.getWindow('overview').visible) {
|
if (!App.getWindow('overview')?.visible) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.update();
|
self?.attribute.update();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
properties: [
|
|
||||||
['workspaces'],
|
|
||||||
],
|
|
||||||
});
|
});
|
||||||
|
|
||||||
mainBox.update = () => {
|
|
||||||
getWorkspaces(mainBox);
|
|
||||||
updateWorkspaces(mainBox);
|
|
||||||
updateClients(mainBox);
|
|
||||||
updateCurrentWorkspace(mainBox, highlighter);
|
|
||||||
};
|
|
||||||
|
|
||||||
const widget = Overlay({
|
const widget = Overlay({
|
||||||
overlays: [highlighter, mainBox],
|
overlays: [highlighter, mainBox],
|
||||||
|
|
||||||
|
attribute: {
|
||||||
|
get_child: () => mainBox,
|
||||||
|
},
|
||||||
|
|
||||||
child: Box({
|
child: Box({
|
||||||
className: 'overview',
|
class_name: 'overview',
|
||||||
css: `
|
css: `
|
||||||
min-height: ${mainBox.get_allocated_height()}px;
|
min-height: ${mainBox.get_allocated_height()}px;
|
||||||
min-width: ${mainBox.get_allocated_width()}px;
|
min-width: ${mainBox.get_allocated_width()}px;
|
||||||
|
@ -77,6 +79,7 @@ export const Overview = () => {
|
||||||
setup: (self) => {
|
setup: (self) => {
|
||||||
self.on('get-child-position', (_, ch) => {
|
self.on('get-child-position', (_, ch) => {
|
||||||
if (ch === mainBox) {
|
if (ch === mainBox) {
|
||||||
|
// @ts-expect-error
|
||||||
self.child.setCss(`
|
self.child.setCss(`
|
||||||
transition: min-height 0.2s ease, min-width 0.2s ease;
|
transition: min-height 0.2s ease, min-width 0.2s ease;
|
||||||
min-height: ${mainBox.get_allocated_height()}px;
|
min-height: ${mainBox.get_allocated_height()}px;
|
||||||
|
@ -87,8 +90,6 @@ export const Overview = () => {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
widget.getChild = () => mainBox;
|
|
||||||
|
|
||||||
return widget;
|
return widget;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -99,7 +100,7 @@ export default () => {
|
||||||
close_on_unfocus: 'none',
|
close_on_unfocus: 'none',
|
||||||
onOpen: () => {
|
onOpen: () => {
|
||||||
win.attribute.set_child(Overview());
|
win.attribute.set_child(Overview());
|
||||||
win.attribute.get_child().getChild().update();
|
win.attribute.get_child().attribute.get_child().attribute.update();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -10,42 +10,69 @@ const DEFAULT_STYLE = `
|
||||||
min-height: ${VARS.SCREEN.Y * VARS.SCALE}px;
|
min-height: ${VARS.SCREEN.Y * VARS.SCALE}px;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {import('types/widgets/box').default} Box
|
||||||
|
* @typedef {import('types/widgets/revealer').default} Revealer
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/** @param {Box} box */
|
||||||
export const getWorkspaces = (box) => {
|
export const getWorkspaces = (box) => {
|
||||||
const children = [];
|
const children = [];
|
||||||
|
|
||||||
box.children.forEach((type) => {
|
box.children.forEach((type) => {
|
||||||
type.children.forEach((row) => {
|
// @ts-expect-error
|
||||||
row.child.centerWidget.child.children.forEach((ch) => {
|
type.children.forEach(
|
||||||
children.push(ch);
|
/** @param {Revealer} row */
|
||||||
});
|
(row) => {
|
||||||
});
|
// @ts-expect-error
|
||||||
|
row.child.centerWidget.child.children.forEach(
|
||||||
|
/** @param {Revealer} workspace */
|
||||||
|
(workspace) => {
|
||||||
|
children.push(workspace);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
});
|
});
|
||||||
box._workspaces = children.sort((a, b) => a._id - b._id);
|
box.attribute.workspaces = children.sort((a, b) =>
|
||||||
|
a.attribute.id - b.attribute.id);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {number} id
|
||||||
|
* @param {string} name
|
||||||
|
* @param {boolean} normal
|
||||||
|
*/
|
||||||
const Workspace = (id, name, normal = true) => {
|
const Workspace = (id, name, normal = true) => {
|
||||||
const fixed = Fixed();
|
const fixed = Fixed();
|
||||||
|
|
||||||
const workspace = Revealer({
|
const workspace = Revealer({
|
||||||
transition: 'slide_right',
|
transition: 'slide_right',
|
||||||
transitionDuration: 500,
|
transition_duration: 500,
|
||||||
|
|
||||||
|
attribute: {
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
get_fixed: () => fixed,
|
||||||
|
},
|
||||||
|
|
||||||
setup: (self) => {
|
setup: (self) => {
|
||||||
if (normal) {
|
if (normal) {
|
||||||
self.hook(Hyprland, () => {
|
self.hook(Hyprland, () => {
|
||||||
const activeId = Hyprland.active.workspace.id;
|
const activeId = Hyprland.active.workspace.id;
|
||||||
const active = activeId === self._id;
|
const active = activeId === self.attribute.id;
|
||||||
|
const ws = Hyprland.getWorkspace(self.attribute.id);
|
||||||
|
|
||||||
self.revealChild = Hyprland.getWorkspace(self._id)
|
self.reveal_child =
|
||||||
?.windows > 0 || active;
|
(ws?.windows && ws.windows > 0) || active;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
child: WorkspaceDrop({
|
child: WorkspaceDrop({
|
||||||
child: Box({
|
child: Box({
|
||||||
className: 'workspace',
|
class_name: 'workspace',
|
||||||
css: normal ?
|
css: normal ?
|
||||||
|
|
||||||
DEFAULT_STYLE :
|
DEFAULT_STYLE :
|
||||||
|
@ -70,23 +97,23 @@ const Workspace = (id, name, normal = true) => {
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
workspace._id = id;
|
|
||||||
workspace._name = name;
|
|
||||||
workspace.getFixed = () => fixed;
|
|
||||||
|
|
||||||
return workspace;
|
return workspace;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const WorkspaceRow = (className, i) => {
|
/**
|
||||||
|
* @param {string} class_name
|
||||||
|
* @param {number} i
|
||||||
|
*/
|
||||||
|
export const WorkspaceRow = (class_name, i) => {
|
||||||
const addWorkspace = Workspace(
|
const addWorkspace = Workspace(
|
||||||
className === 'special' ? -1 : 1000,
|
class_name === 'special' ? -1 : 1000,
|
||||||
className === 'special' ? 'special' : '',
|
class_name === 'special' ? 'special' : '',
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
|
|
||||||
return Revealer({
|
return Revealer({
|
||||||
transition: 'slide_down',
|
transition: 'slide_down',
|
||||||
hpack: className === 'special' ? '' : 'start',
|
hpack: class_name === 'special' ? 'fill' : 'start',
|
||||||
|
|
||||||
setup: (self) => {
|
setup: (self) => {
|
||||||
self.hook(Hyprland, (rev) => {
|
self.hook(Hyprland, (rev) => {
|
||||||
|
@ -101,18 +128,18 @@ export const WorkspaceRow = (className, i) => {
|
||||||
return isInRow && (hasClients || isActive);
|
return isInRow && (hasClients || isActive);
|
||||||
});
|
});
|
||||||
|
|
||||||
rev.revealChild = rowExists;
|
rev.reveal_child = rowExists;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
child: CenterBox({
|
child: CenterBox({
|
||||||
children: [null, EventBox({
|
center_widget: EventBox({
|
||||||
setup: (self) => {
|
setup: (self) => {
|
||||||
self.hook(Hyprland, () => {
|
self.hook(Hyprland, () => {
|
||||||
const maxId = (i + 1) * VARS.WORKSPACE_PER_ROW;
|
const maxId = (i + 1) * VARS.WORKSPACE_PER_ROW;
|
||||||
const activeId = Hyprland.active.workspace.id;
|
const activeId = Hyprland.active.workspace.id;
|
||||||
|
|
||||||
const isSpecial = className === 'special';
|
const isSpecial = class_name === 'special';
|
||||||
const nextRowExists = Hyprland.workspaces.some((ws) => {
|
const nextRowExists = Hyprland.workspaces.some((ws) => {
|
||||||
const isInNextRow = ws.id > maxId;
|
const isInNextRow = ws.id > maxId;
|
||||||
const hasClients = ws.windows > 0;
|
const hasClients = ws.windows > 0;
|
||||||
|
@ -121,22 +148,26 @@ export const WorkspaceRow = (className, i) => {
|
||||||
return isInNextRow && (hasClients || isActive);
|
return isInNextRow && (hasClients || isActive);
|
||||||
});
|
});
|
||||||
|
|
||||||
addWorkspace.revealChild = isSpecial || !nextRowExists;
|
addWorkspace.reveal_child = isSpecial || !nextRowExists;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
child: Box({
|
child: Box({
|
||||||
className,
|
class_name,
|
||||||
children: [addWorkspace],
|
children: [addWorkspace],
|
||||||
}),
|
}),
|
||||||
}), null],
|
}),
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** @param {Box} box */
|
||||||
export const updateWorkspaces = (box) => {
|
export const updateWorkspaces = (box) => {
|
||||||
Hyprland.workspaces.forEach((ws) => {
|
Hyprland.workspaces.forEach((ws) => {
|
||||||
const currentWs = box._workspaces.find((ch) => ch._id === ws.id);
|
const currentWs = box.attribute.workspaces.find(
|
||||||
|
/** @param {Revealer} ch */
|
||||||
|
(ch) => ch.attribute.id === ws.id,
|
||||||
|
);
|
||||||
|
|
||||||
if (!currentWs) {
|
if (!currentWs) {
|
||||||
let type = 0;
|
let type = 0;
|
||||||
|
@ -148,16 +179,19 @@ export const updateWorkspaces = (box) => {
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
rowNo = Math.floor((ws.id - 1) / VARS.WORKSPACE_PER_ROW);
|
rowNo = Math.floor((ws.id - 1) / VARS.WORKSPACE_PER_ROW);
|
||||||
|
// @ts-expect-error
|
||||||
const wsQty = box.children[type].children.length;
|
const wsQty = box.children[type].children.length;
|
||||||
|
|
||||||
if (rowNo >= wsQty) {
|
if (rowNo >= wsQty) {
|
||||||
for (let i = wsQty; i <= rowNo; ++i) {
|
for (let i = wsQty; i <= rowNo; ++i) {
|
||||||
|
// @ts-expect-error
|
||||||
box.children[type].add(WorkspaceRow(
|
box.children[type].add(WorkspaceRow(
|
||||||
type ? 'special' : 'normal', i,
|
type ? 'special' : 'normal', i,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// @ts-expect-error
|
||||||
const row = box.children[type].children[rowNo]
|
const row = box.children[type].children[rowNo]
|
||||||
.child.centerWidget.child;
|
.child.centerWidget.child;
|
||||||
|
|
||||||
|
@ -166,8 +200,15 @@ export const updateWorkspaces = (box) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
// Make sure the order is correct
|
// Make sure the order is correct
|
||||||
box._workspaces.forEach((workspace, i) => {
|
box.attribute.workspaces.forEach(
|
||||||
workspace.get_parent().reorder_child(workspace, i);
|
/**
|
||||||
});
|
* @param {Revealer} workspace
|
||||||
|
* @param {number} i
|
||||||
|
*/
|
||||||
|
(workspace, i) => {
|
||||||
|
// @ts-expect-error
|
||||||
|
workspace?.get_parent()?.reorder_child(workspace, i);
|
||||||
|
},
|
||||||
|
);
|
||||||
box.show_all();
|
box.show_all();
|
||||||
};
|
};
|
||||||
|
|
|
@ -13,7 +13,7 @@ const PowermenuWidget = () => CenterBox({
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
vertical: false,
|
vertical: false,
|
||||||
|
|
||||||
startWidget: CursorBox({
|
start_widget: CursorBox({
|
||||||
class_name: 'shutdown',
|
class_name: 'shutdown',
|
||||||
on_primary_click_release: () => execAsync(['systemctl', 'poweroff'])
|
on_primary_click_release: () => execAsync(['systemctl', 'poweroff'])
|
||||||
.catch(print),
|
.catch(print),
|
||||||
|
@ -23,7 +23,7 @@ const PowermenuWidget = () => CenterBox({
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
|
|
||||||
centerWidget: CursorBox({
|
center_widget: CursorBox({
|
||||||
class_name: 'reboot',
|
class_name: 'reboot',
|
||||||
on_primary_click_release: () => execAsync(['systemctl', 'reboot'])
|
on_primary_click_release: () => execAsync(['systemctl', 'reboot'])
|
||||||
.catch(print),
|
.catch(print),
|
||||||
|
@ -33,7 +33,7 @@ const PowermenuWidget = () => CenterBox({
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
|
|
||||||
endWidget: CursorBox({
|
end_widget: CursorBox({
|
||||||
class_name: 'logout',
|
class_name: 'logout',
|
||||||
on_primary_click_release: () => Hyprland.sendMessage('dispatch exit')
|
on_primary_click_release: () => Hyprland.sendMessage('dispatch exit')
|
||||||
.catch(print),
|
.catch(print),
|
||||||
|
|
|
@ -8,56 +8,62 @@ import CursorBox from '../misc/cursorbox.js';
|
||||||
const SCROLL_THRESH_H = 200;
|
const SCROLL_THRESH_H = 200;
|
||||||
const SCROLL_THRESH_N = 7;
|
const SCROLL_THRESH_N = 7;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {import('types/widgets/box').default} Box
|
||||||
|
* @typedef {import('types/service/bluetooth').BluetoothDevice} BluetoothDevice
|
||||||
|
*/
|
||||||
|
|
||||||
const BluetoothDevice = (dev) => {
|
/** @param {BluetoothDevice} dev */
|
||||||
const widget = Box({
|
const BluetoothDevice = (dev) => Box({
|
||||||
className: 'menu-item',
|
class_name: 'menu-item',
|
||||||
});
|
|
||||||
|
|
||||||
const child = Box({
|
attribute: { dev },
|
||||||
hexpand: true,
|
|
||||||
children: [
|
|
||||||
Icon({
|
|
||||||
binds: [['icon', dev, 'icon-name']],
|
|
||||||
}),
|
|
||||||
|
|
||||||
Label({
|
children: [Revealer({
|
||||||
binds: [['label', dev, 'name']],
|
reveal_child: true,
|
||||||
}),
|
|
||||||
|
|
||||||
Icon({
|
|
||||||
icon: 'object-select-symbolic',
|
|
||||||
hexpand: true,
|
|
||||||
hpack: 'end',
|
|
||||||
setup: (self) => {
|
|
||||||
self.hook(dev, () => {
|
|
||||||
self.setCss(`opacity: ${dev.paired ? '1' : '0'};`);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
});
|
|
||||||
|
|
||||||
widget.dev = dev;
|
|
||||||
widget.add(Revealer({
|
|
||||||
revealChild: true,
|
|
||||||
transition: 'slide_down',
|
transition: 'slide_down',
|
||||||
|
|
||||||
child: CursorBox({
|
child: CursorBox({
|
||||||
on_primary_click_release: () => dev.setConnection(true),
|
on_primary_click_release: () => dev.setConnection(true),
|
||||||
child,
|
|
||||||
}),
|
|
||||||
}));
|
|
||||||
|
|
||||||
return widget;
|
child: Box({
|
||||||
};
|
hexpand: true,
|
||||||
|
|
||||||
|
children: [
|
||||||
|
Icon({
|
||||||
|
icon: dev.bind('icon_name'),
|
||||||
|
}),
|
||||||
|
|
||||||
|
Label({
|
||||||
|
label: dev.bind('name'),
|
||||||
|
}),
|
||||||
|
|
||||||
|
Icon({
|
||||||
|
icon: 'object-select-symbolic',
|
||||||
|
hexpand: true,
|
||||||
|
hpack: 'end',
|
||||||
|
|
||||||
|
}).hook(dev, (self) => {
|
||||||
|
self.setCss(`opacity: ${dev.paired ?
|
||||||
|
'1' :
|
||||||
|
'0'};
|
||||||
|
`);
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
})],
|
||||||
|
});
|
||||||
|
|
||||||
export const BluetoothMenu = () => {
|
export const BluetoothMenu = () => {
|
||||||
const DevList = new Map();
|
const DevList = new Map();
|
||||||
|
|
||||||
const topArrow = Revealer({
|
const topArrow = Revealer({
|
||||||
transition: 'slide_down',
|
transition: 'slide_down',
|
||||||
|
|
||||||
child: Icon({
|
child: Icon({
|
||||||
icon: `${App.configDir }/icons/down-large.svg`,
|
icon: `${App.configDir }/icons/down-large.svg`,
|
||||||
className: 'scrolled-indicator',
|
class_name: 'scrolled-indicator',
|
||||||
size: 16,
|
size: 16,
|
||||||
css: '-gtk-icon-transform: rotate(180deg);',
|
css: '-gtk-icon-transform: rotate(180deg);',
|
||||||
}),
|
}),
|
||||||
|
@ -65,15 +71,17 @@ export const BluetoothMenu = () => {
|
||||||
|
|
||||||
const bottomArrow = Revealer({
|
const bottomArrow = Revealer({
|
||||||
transition: 'slide_up',
|
transition: 'slide_up',
|
||||||
|
|
||||||
child: Icon({
|
child: Icon({
|
||||||
icon: `${App.configDir }/icons/down-large.svg`,
|
icon: `${App.configDir }/icons/down-large.svg`,
|
||||||
className: 'scrolled-indicator',
|
class_name: 'scrolled-indicator',
|
||||||
size: 16,
|
size: 16,
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
return Overlay({
|
return Overlay({
|
||||||
pass_through: true,
|
pass_through: true,
|
||||||
|
|
||||||
overlays: [
|
overlays: [
|
||||||
Box({
|
Box({
|
||||||
vpack: 'start',
|
vpack: 'start',
|
||||||
|
@ -91,7 +99,7 @@ export const BluetoothMenu = () => {
|
||||||
],
|
],
|
||||||
|
|
||||||
child: Box({
|
child: Box({
|
||||||
className: 'menu',
|
class_name: 'menu',
|
||||||
|
|
||||||
child: Scrollable({
|
child: Scrollable({
|
||||||
hscroll: 'never',
|
hscroll: 'never',
|
||||||
|
@ -101,28 +109,36 @@ export const BluetoothMenu = () => {
|
||||||
self.on('edge-reached', (_, pos) => {
|
self.on('edge-reached', (_, pos) => {
|
||||||
// Manage scroll indicators
|
// Manage scroll indicators
|
||||||
if (pos === 2) {
|
if (pos === 2) {
|
||||||
topArrow.revealChild = false;
|
topArrow.reveal_child = false;
|
||||||
bottomArrow.revealChild = true;
|
bottomArrow.reveal_child = true;
|
||||||
}
|
}
|
||||||
else if (pos === 3) {
|
else if (pos === 3) {
|
||||||
topArrow.revealChild = true;
|
topArrow.reveal_child = true;
|
||||||
bottomArrow.revealChild = false;
|
bottomArrow.reveal_child = false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
child: ListBox({
|
child: ListBox({
|
||||||
setup: (self) => {
|
setup: (self) => {
|
||||||
self.set_sort_func((a, b) => {
|
// @ts-expect-error
|
||||||
return b.get_children()[0].dev.paired -
|
self.set_sort_func(
|
||||||
a.get_children()[0].dev.paired;
|
/**
|
||||||
});
|
* @param {Box} a
|
||||||
|
* @param {Box} b
|
||||||
|
*/
|
||||||
|
(a, b) => {
|
||||||
|
// @ts-expect-error
|
||||||
|
return b.get_children()[0].attribute.dev.paired - // eslint-disable-line
|
||||||
|
// @ts-expect-error
|
||||||
|
a.get_children()[0].attribute.dev.paired;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
self.hook(Bluetooth, () => {
|
self.hook(Bluetooth, () => {
|
||||||
// Get all devices
|
// Get all devices
|
||||||
const Devices = [].concat(
|
const Devices = Bluetooth.devices.concat(
|
||||||
Bluetooth.devices,
|
Bluetooth.connected_devices,
|
||||||
Bluetooth.connectedDevices,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// Add missing devices
|
// Add missing devices
|
||||||
|
@ -130,6 +146,7 @@ export const BluetoothMenu = () => {
|
||||||
if (!DevList.has(dev) && dev.name) {
|
if (!DevList.has(dev) && dev.name) {
|
||||||
DevList.set(dev, BluetoothDevice(dev));
|
DevList.set(dev, BluetoothDevice(dev));
|
||||||
|
|
||||||
|
// @ts-expect-error
|
||||||
self.add(DevList.get(dev));
|
self.add(DevList.get(dev));
|
||||||
self.show_all();
|
self.show_all();
|
||||||
}
|
}
|
||||||
|
@ -151,7 +168,7 @@ export const BluetoothMenu = () => {
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
devWidget.children[0]
|
devWidget.children[0]
|
||||||
.revealChild = false;
|
.reveal_child = false;
|
||||||
devWidget.toDestroy = true;
|
devWidget.toDestroy = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -160,34 +177,48 @@ export const BluetoothMenu = () => {
|
||||||
// Start scrolling after a specified height
|
// Start scrolling after a specified height
|
||||||
// is reached by the children
|
// is reached by the children
|
||||||
const height = Math.max(
|
const height = Math.max(
|
||||||
self.get_parent().get_allocated_height(),
|
self.get_parent()?.get_allocated_height() || 0,
|
||||||
SCROLL_THRESH_H,
|
SCROLL_THRESH_H,
|
||||||
);
|
);
|
||||||
|
|
||||||
const scroll = self.get_parent().get_parent();
|
const scroll = self.get_parent()?.get_parent();
|
||||||
|
|
||||||
if (self.get_children().length > SCROLL_THRESH_N) {
|
if (scroll) {
|
||||||
scroll.vscroll = 'always';
|
// @ts-expect-error
|
||||||
scroll.setCss(`min-height: ${height}px;`);
|
const n_child = self.get_children().length;
|
||||||
|
|
||||||
// Make bottom scroll indicator appear only
|
if (n_child > SCROLL_THRESH_N) {
|
||||||
// when first getting overflowing children
|
// @ts-expect-error
|
||||||
if (!(bottomArrow.revealChild === true ||
|
scroll.vscroll = 'always';
|
||||||
topArrow.revealChild === true)) {
|
// @ts-expect-error
|
||||||
bottomArrow.revealChild = true;
|
scroll.setCss(`min-height: ${height}px;`);
|
||||||
|
|
||||||
|
// Make bottom scroll indicator appear only
|
||||||
|
// when first getting overflowing children
|
||||||
|
if (!(bottomArrow.reveal_child === true ||
|
||||||
|
topArrow.reveal_child === true)) {
|
||||||
|
bottomArrow.reveal_child = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// @ts-expect-error
|
||||||
|
scroll.vscroll = 'never';
|
||||||
|
// @ts-expect-error
|
||||||
|
scroll.setCss('');
|
||||||
|
topArrow.reveal_child = false;
|
||||||
|
bottomArrow.reveal_child = false;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else {
|
|
||||||
scroll.vscroll = 'never';
|
|
||||||
scroll.setCss('');
|
|
||||||
topArrow.revealChild = false;
|
|
||||||
bottomArrow.revealChild = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Trigger sort_func
|
// Trigger sort_func
|
||||||
self.get_children().forEach((ch) => {
|
// @ts-expect-error
|
||||||
ch.changed();
|
self.get_children().forEach(
|
||||||
});
|
/** @param {Box} ListBoxRow */
|
||||||
|
(ListBoxRow) => {
|
||||||
|
// @ts-expect-error
|
||||||
|
ListBoxRow.changed();
|
||||||
|
},
|
||||||
|
);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import App from 'resource:///com/github/Aylur/ags/app.js';
|
import App from 'resource:///com/github/Aylur/ags/app.js';
|
||||||
// @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 Network from 'resource:///com/github/Aylur/ags/service/network.js';
|
import Network from 'resource:///com/github/Aylur/ags/service/network.js';
|
||||||
import Variable from 'resource:///com/github/Aylur/ags/variable.js';
|
import Variable from 'resource:///com/github/Aylur/ags/variable.js';
|
||||||
|
@ -17,16 +16,23 @@ import { BluetoothMenu } from './bluetooth.js';
|
||||||
const SPACING = 28;
|
const SPACING = 28;
|
||||||
const ButtonStates = [];
|
const ButtonStates = [];
|
||||||
|
|
||||||
/** @typedef {import('types/widgets/widget').default} Widget */
|
/**
|
||||||
|
* @typedef {import('types/widgets/widget').default} Widget
|
||||||
|
* @typedef {import('types/widgets/box').default} Box
|
||||||
|
* @typedef {import('types/widgets/icon').default} Icon
|
||||||
|
* @typedef {import('types/widgets/label').default} Label
|
||||||
|
* @typedef {import('types/widgets/revealer').default} Revealer
|
||||||
|
* @typedef {[any, function, (string|undefined)?]} BindTuple
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {{
|
* @param {{
|
||||||
* command?: function
|
* command?: function
|
||||||
* secondary_command?: function
|
* secondary_command?: function
|
||||||
* onOpen?: function(Widget):void
|
* onOpen?: function(Revealer):void
|
||||||
* icon: any
|
* icon: string|BindTuple
|
||||||
* indicator?: any
|
* indicator?: BindTuple
|
||||||
* menu?: any
|
* menu?: any
|
||||||
* }} o
|
* }} o
|
||||||
*/
|
*/
|
||||||
|
@ -41,10 +47,13 @@ const GridButton = ({
|
||||||
const Activated = Variable(false);
|
const Activated = Variable(false);
|
||||||
|
|
||||||
ButtonStates.push(Activated);
|
ButtonStates.push(Activated);
|
||||||
|
let iconWidget;
|
||||||
|
/** @type Label */
|
||||||
|
let indicatorWidget = Label();
|
||||||
|
|
||||||
// Allow setting icon dynamically or statically
|
// Allow setting icon dynamically or statically
|
||||||
if (typeof icon === 'string') {
|
if (typeof icon === 'string') {
|
||||||
icon = Icon({
|
iconWidget = Icon({
|
||||||
class_name: 'grid-label',
|
class_name: 'grid-label',
|
||||||
icon,
|
icon,
|
||||||
setup: (self) => {
|
setup: (self) => {
|
||||||
|
@ -56,11 +65,12 @@ const GridButton = ({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else {
|
else if (Array.isArray(icon)) {
|
||||||
icon = Icon({
|
iconWidget = Icon({
|
||||||
class_name: 'grid-label',
|
class_name: 'grid-label',
|
||||||
setup: (self) => {
|
setup: (self) => {
|
||||||
self
|
self
|
||||||
|
// @ts-expect-error
|
||||||
.hook(...icon)
|
.hook(...icon)
|
||||||
.hook(Activated, () => {
|
.hook(Activated, () => {
|
||||||
self.setCss(`color: ${Activated.value ?
|
self.setCss(`color: ${Activated.value ?
|
||||||
|
@ -72,12 +82,13 @@ const GridButton = ({
|
||||||
}
|
}
|
||||||
|
|
||||||
if (indicator) {
|
if (indicator) {
|
||||||
indicator = Label({
|
indicatorWidget = Label({
|
||||||
class_name: 'sub-label',
|
class_name: 'sub-label',
|
||||||
justification: 'left',
|
justification: 'left',
|
||||||
truncate: 'end',
|
truncate: 'end',
|
||||||
maxWidthChars: 12,
|
max_width_chars: 12,
|
||||||
setup: (self) => {
|
setup: (self) => {
|
||||||
|
// @ts-expect-error
|
||||||
self.hook(...indicator);
|
self.hook(...indicator);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -87,7 +98,7 @@ const GridButton = ({
|
||||||
menu = Revealer({
|
menu = Revealer({
|
||||||
transition: 'slide_down',
|
transition: 'slide_down',
|
||||||
child: menu,
|
child: menu,
|
||||||
binds: [['revealChild', Activated, 'value']],
|
reveal_child: Activated.bind(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,7 +121,7 @@ const GridButton = ({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
child: icon,
|
child: iconWidget,
|
||||||
}),
|
}),
|
||||||
|
|
||||||
CursorBox({
|
CursorBox({
|
||||||
|
@ -131,10 +142,13 @@ const GridButton = ({
|
||||||
self.get_parent()
|
self.get_parent()
|
||||||
?.get_parent()?.get_parent()
|
?.get_parent()?.get_parent()
|
||||||
?.get_parent()?.get_parent()
|
?.get_parent()?.get_parent()
|
||||||
|
// @ts-expect-error
|
||||||
?.children[1];
|
?.children[1];
|
||||||
|
|
||||||
const isSetup = rowMenu.get_children()
|
const isSetup = rowMenu.get_children().find(
|
||||||
.find((ch) => ch === menu);
|
/** @param {Box} ch */
|
||||||
|
(ch) => ch === menu,
|
||||||
|
);
|
||||||
|
|
||||||
if (!isSetup) {
|
if (!isSetup) {
|
||||||
rowMenu.add(menu);
|
rowMenu.add(menu);
|
||||||
|
@ -165,14 +179,14 @@ const GridButton = ({
|
||||||
|
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
indicator,
|
indicatorWidget,
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
return widget;
|
return widget;
|
||||||
};
|
};
|
||||||
|
|
||||||
const Row = ({ buttons } = {}) => {
|
const Row = ({ buttons }) => {
|
||||||
const widget = Box({
|
const widget = Box({
|
||||||
vertical: true,
|
vertical: true,
|
||||||
|
|
||||||
|
@ -188,10 +202,13 @@ const Row = ({ buttons } = {}) => {
|
||||||
|
|
||||||
for (let i = 0; i < buttons.length; ++i) {
|
for (let i = 0; i < buttons.length; ++i) {
|
||||||
if (i === buttons.length - 1) {
|
if (i === buttons.length - 1) {
|
||||||
|
// @ts-expect-error
|
||||||
widget.children[0].add(buttons[i]);
|
widget.children[0].add(buttons[i]);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
// @ts-expect-error
|
||||||
widget.children[0].add(buttons[i]);
|
widget.children[0].add(buttons[i]);
|
||||||
|
// @ts-expect-error
|
||||||
widget.children[0].add(Separator(SPACING));
|
widget.children[0].add(Separator(SPACING));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -209,13 +226,17 @@ const FirstRow = () => Row({
|
||||||
// TODO: connection editor
|
// TODO: connection editor
|
||||||
},
|
},
|
||||||
|
|
||||||
icon: [Network, (icon) => {
|
icon: [Network,
|
||||||
icon.icon = Network.wifi?.iconName;
|
/** @param {Icon} self */
|
||||||
}],
|
(self) => {
|
||||||
|
self.icon = Network.wifi?.icon_name;
|
||||||
|
}],
|
||||||
|
|
||||||
indicator: [Network, (self) => {
|
indicator: [Network,
|
||||||
self.label = Network.wifi?.ssid || Network.wired?.internet;
|
/** @param {Label} self */
|
||||||
}],
|
(self) => {
|
||||||
|
self.label = Network.wifi?.ssid || Network.wired?.internet;
|
||||||
|
}],
|
||||||
|
|
||||||
menu: NetworkMenu(),
|
menu: NetworkMenu(),
|
||||||
onOpen: () => Network.wifi.scan(),
|
onOpen: () => Network.wifi.scan(),
|
||||||
|
@ -241,26 +262,30 @@ const FirstRow = () => Row({
|
||||||
// TODO: bluetooth connection editor
|
// TODO: bluetooth connection editor
|
||||||
},
|
},
|
||||||
|
|
||||||
icon: [Bluetooth, (self) => {
|
icon: [Bluetooth,
|
||||||
if (Bluetooth.enabled) {
|
/** @param {Icon} self */
|
||||||
self.icon = Bluetooth.connectedDevices[0] ?
|
(self) => {
|
||||||
Bluetooth.connectedDevices[0].iconName :
|
if (Bluetooth.enabled) {
|
||||||
'bluetooth-active-symbolic';
|
self.icon = Bluetooth.connected_devices[0] ?
|
||||||
}
|
Bluetooth.connected_devices[0].icon_name :
|
||||||
else {
|
'bluetooth-active-symbolic';
|
||||||
self.icon = 'bluetooth-disabled-symbolic';
|
}
|
||||||
}
|
else {
|
||||||
}],
|
self.icon = 'bluetooth-disabled-symbolic';
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
|
||||||
indicator: [Bluetooth, (self) => {
|
indicator: [Bluetooth,
|
||||||
self.label = Bluetooth.connectedDevices[0] ?
|
/** @param {Label} self */
|
||||||
`${Bluetooth.connectedDevices[0]}` :
|
(self) => {
|
||||||
'Disconnected';
|
self.label = Bluetooth.connected_devices[0] ?
|
||||||
}, 'notify::connected-devices'],
|
`${Bluetooth.connected_devices[0]}` :
|
||||||
|
'Disconnected';
|
||||||
|
}, 'notify::connected-devices'],
|
||||||
|
|
||||||
menu: BluetoothMenu(),
|
menu: BluetoothMenu(),
|
||||||
onOpen: (menu) => {
|
onOpen: (menu) => {
|
||||||
execAsync(`bluetoothctl scan ${menu.revealChild ?
|
execAsync(`bluetoothctl scan ${menu.reveal_child ?
|
||||||
'on' :
|
'on' :
|
||||||
'off'}`).catch(print);
|
'off'}`).catch(print);
|
||||||
},
|
},
|
||||||
|
@ -282,9 +307,11 @@ const SecondRow = () => Row({
|
||||||
.catch(print);
|
.catch(print);
|
||||||
},
|
},
|
||||||
|
|
||||||
icon: [SpeakerIcon, (self) => {
|
icon: [SpeakerIcon,
|
||||||
self.icon = SpeakerIcon.value;
|
/** @param {Icon} self */
|
||||||
}],
|
(self) => {
|
||||||
|
self.icon = SpeakerIcon.value;
|
||||||
|
}],
|
||||||
}),
|
}),
|
||||||
|
|
||||||
GridButton({
|
GridButton({
|
||||||
|
@ -298,9 +325,11 @@ const SecondRow = () => Row({
|
||||||
.catch(print);
|
.catch(print);
|
||||||
},
|
},
|
||||||
|
|
||||||
icon: [MicIcon, (self) => {
|
icon: [MicIcon,
|
||||||
self.icon = MicIcon.value;
|
/** @param {Icon} self */
|
||||||
}],
|
(self) => {
|
||||||
|
self.icon = MicIcon.value;
|
||||||
|
}],
|
||||||
}),
|
}),
|
||||||
|
|
||||||
GridButton({
|
GridButton({
|
||||||
|
|
|
@ -14,18 +14,18 @@ const QuickSettingsWidget = () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
return Box({
|
return Box({
|
||||||
className: 'qs-container',
|
class_name: 'qs-container',
|
||||||
vertical: true,
|
vertical: true,
|
||||||
children: [
|
children: [
|
||||||
|
|
||||||
Box({
|
Box({
|
||||||
className: 'quick-settings',
|
class_name: 'quick-settings',
|
||||||
vertical: true,
|
vertical: true,
|
||||||
children: [
|
children: [
|
||||||
|
|
||||||
Label({
|
Label({
|
||||||
label: 'Control Center',
|
label: 'Control Center',
|
||||||
className: 'title',
|
class_name: 'title',
|
||||||
hpack: 'start',
|
hpack: 'start',
|
||||||
css: `
|
css: `
|
||||||
margin-left: 20px;
|
margin-left: 20px;
|
||||||
|
|
|
@ -10,42 +10,41 @@ import CursorBox from '../misc/cursorbox.js';
|
||||||
const SCROLL_THRESH_H = 200;
|
const SCROLL_THRESH_H = 200;
|
||||||
const SCROLL_THRESH_N = 7;
|
const SCROLL_THRESH_N = 7;
|
||||||
|
|
||||||
|
/** @typedef {import('types/widgets/box').default} Box */
|
||||||
|
|
||||||
|
|
||||||
|
/** @param {any} ap */
|
||||||
const AccessPoint = (ap) => {
|
const AccessPoint = (ap) => {
|
||||||
const widget = Box({
|
const widget = Box({
|
||||||
className: 'menu-item',
|
class_name: 'menu-item',
|
||||||
|
attribute: {
|
||||||
|
ap: Variable(ap),
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
widget.ap = Variable(ap);
|
|
||||||
|
|
||||||
const child = Box({
|
const child = Box({
|
||||||
hexpand: true,
|
hexpand: true,
|
||||||
children: [
|
children: [
|
||||||
Icon({
|
Icon().hook(widget.attribute.ap, (self) => {
|
||||||
setup: (self) => {
|
self.icon = widget.attribute.ap.value.iconName;
|
||||||
self.hook(widget.ap, () => {
|
|
||||||
self.icon = widget.ap.value.iconName;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
Label({
|
Label().hook(widget.attribute.ap, (self) => {
|
||||||
setup: (self) => {
|
self.label = widget.attribute.ap.value.ssid || '';
|
||||||
self.hook(widget.ap, () => {
|
|
||||||
self.label = widget.ap.value.ssid || '';
|
|
||||||
});
|
|
||||||
},
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
Icon({
|
Icon({
|
||||||
icon: 'object-select-symbolic',
|
icon: 'object-select-symbolic',
|
||||||
hexpand: true,
|
hexpand: true,
|
||||||
hpack: 'end',
|
hpack: 'end',
|
||||||
|
|
||||||
setup: (self) => {
|
setup: (self) => {
|
||||||
self.hook(Network, () => {
|
self.hook(Network, () => {
|
||||||
self.setCss(
|
self.setCss(
|
||||||
`opacity: ${
|
`opacity: ${
|
||||||
widget.ap.value.ssid === Network.wifi.ssid ?
|
widget.attribute.ap.value.ssid ===
|
||||||
|
Network.wifi.ssid ?
|
||||||
'1' :
|
'1' :
|
||||||
'0'
|
'0'
|
||||||
};
|
};
|
||||||
|
@ -58,12 +57,13 @@ const AccessPoint = (ap) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
widget.add(Revealer({
|
widget.add(Revealer({
|
||||||
revealChild: true,
|
reveal_child: true,
|
||||||
transition: 'slide_down',
|
transition: 'slide_down',
|
||||||
|
|
||||||
child: CursorBox({
|
child: CursorBox({
|
||||||
on_primary_click_release: () => {
|
on_primary_click_release: () => {
|
||||||
execAsync(`nmcli device wifi connect
|
execAsync(`nmcli device wifi connect
|
||||||
${widget.ap.value.bssid}`).catch(print);
|
${widget.attribute.ap.value.bssid}`).catch(print);
|
||||||
},
|
},
|
||||||
child,
|
child,
|
||||||
}),
|
}),
|
||||||
|
@ -74,11 +74,13 @@ const AccessPoint = (ap) => {
|
||||||
|
|
||||||
export const NetworkMenu = () => {
|
export const NetworkMenu = () => {
|
||||||
const APList = new Map();
|
const APList = new Map();
|
||||||
|
|
||||||
const topArrow = Revealer({
|
const topArrow = Revealer({
|
||||||
transition: 'slide_down',
|
transition: 'slide_down',
|
||||||
|
|
||||||
child: Icon({
|
child: Icon({
|
||||||
icon: `${App.configDir }/icons/down-large.svg`,
|
icon: `${App.configDir }/icons/down-large.svg`,
|
||||||
className: 'scrolled-indicator',
|
class_name: 'scrolled-indicator',
|
||||||
size: 16,
|
size: 16,
|
||||||
css: '-gtk-icon-transform: rotate(180deg);',
|
css: '-gtk-icon-transform: rotate(180deg);',
|
||||||
}),
|
}),
|
||||||
|
@ -86,15 +88,17 @@ export const NetworkMenu = () => {
|
||||||
|
|
||||||
const bottomArrow = Revealer({
|
const bottomArrow = Revealer({
|
||||||
transition: 'slide_up',
|
transition: 'slide_up',
|
||||||
|
|
||||||
child: Icon({
|
child: Icon({
|
||||||
icon: `${App.configDir }/icons/down-large.svg`,
|
icon: `${App.configDir }/icons/down-large.svg`,
|
||||||
className: 'scrolled-indicator',
|
class_name: 'scrolled-indicator',
|
||||||
size: 16,
|
size: 16,
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
return Overlay({
|
return Overlay({
|
||||||
pass_through: true,
|
pass_through: true,
|
||||||
|
|
||||||
overlays: [
|
overlays: [
|
||||||
Box({
|
Box({
|
||||||
vpack: 'start',
|
vpack: 'start',
|
||||||
|
@ -112,7 +116,7 @@ export const NetworkMenu = () => {
|
||||||
],
|
],
|
||||||
|
|
||||||
child: Box({
|
child: Box({
|
||||||
className: 'menu',
|
class_name: 'menu',
|
||||||
|
|
||||||
child: Scrollable({
|
child: Scrollable({
|
||||||
hscroll: 'never',
|
hscroll: 'never',
|
||||||
|
@ -122,22 +126,32 @@ export const NetworkMenu = () => {
|
||||||
self.on('edge-reached', (_, pos) => {
|
self.on('edge-reached', (_, pos) => {
|
||||||
// Manage scroll indicators
|
// Manage scroll indicators
|
||||||
if (pos === 2) {
|
if (pos === 2) {
|
||||||
topArrow.revealChild = false;
|
topArrow.reveal_child = false;
|
||||||
bottomArrow.revealChild = true;
|
bottomArrow.reveal_child = true;
|
||||||
}
|
}
|
||||||
else if (pos === 3) {
|
else if (pos === 3) {
|
||||||
topArrow.revealChild = true;
|
topArrow.reveal_child = true;
|
||||||
bottomArrow.revealChild = false;
|
bottomArrow.reveal_child = false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
child: ListBox({
|
child: ListBox({
|
||||||
setup: (self) => {
|
setup: (self) => {
|
||||||
self.set_sort_func((a, b) => {
|
// @ts-expect-error
|
||||||
return b.get_children()[0].ap.value.strength -
|
self.set_sort_func(
|
||||||
a.get_children()[0].ap.value.strength;
|
/**
|
||||||
});
|
* @param {Box} a
|
||||||
|
* @param {Box} b
|
||||||
|
*/
|
||||||
|
(a, b) => {
|
||||||
|
return b.get_children()[0]
|
||||||
|
// @ts-expect-error
|
||||||
|
.attribute.ap.value.strength -
|
||||||
|
// @ts-expect-error
|
||||||
|
a.get_children()[0].attribute.ap.value.strength;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
self.hook(Network, () => {
|
self.hook(Network, () => {
|
||||||
// Add missing APs
|
// Add missing APs
|
||||||
|
@ -145,15 +159,17 @@ export const NetworkMenu = () => {
|
||||||
if (ap.ssid !== 'Unknown') {
|
if (ap.ssid !== 'Unknown') {
|
||||||
if (APList.has(ap.ssid)) {
|
if (APList.has(ap.ssid)) {
|
||||||
const accesPoint = APList.get(ap.ssid)
|
const accesPoint = APList.get(ap.ssid)
|
||||||
.ap.value;
|
.attribute.ap.value;
|
||||||
|
|
||||||
if (accesPoint.strength < ap.strength) {
|
if (accesPoint.strength < ap.strength) {
|
||||||
APList.get(ap.ssid).ap.value = ap;
|
APList.get(ap.ssid).attribute
|
||||||
|
.ap.value = ap;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
APList.set(ap.ssid, AccessPoint(ap));
|
APList.set(ap.ssid, AccessPoint(ap));
|
||||||
|
|
||||||
|
// @ts-expect-error
|
||||||
self.add(APList.get(ap.ssid));
|
self.add(APList.get(ap.ssid));
|
||||||
self.show_all();
|
self.show_all();
|
||||||
}
|
}
|
||||||
|
@ -176,7 +192,7 @@ export const NetworkMenu = () => {
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
apWidget.children[0]
|
apWidget.children[0]
|
||||||
.revealChild = false;
|
.reveal_child = false;
|
||||||
apWidget.toDestroy = true;
|
apWidget.toDestroy = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -185,34 +201,48 @@ export const NetworkMenu = () => {
|
||||||
// Start scrolling after a specified height
|
// Start scrolling after a specified height
|
||||||
// is reached by the children
|
// is reached by the children
|
||||||
const height = Math.max(
|
const height = Math.max(
|
||||||
self.get_parent().get_allocated_height(),
|
self.get_parent()?.get_allocated_height() || 0,
|
||||||
SCROLL_THRESH_H,
|
SCROLL_THRESH_H,
|
||||||
);
|
);
|
||||||
|
|
||||||
const scroll = self.get_parent().get_parent();
|
const scroll = self.get_parent()?.get_parent();
|
||||||
|
|
||||||
if (self.get_children().length > SCROLL_THRESH_N) {
|
if (scroll) {
|
||||||
scroll.vscroll = 'always';
|
// @ts-expect-error
|
||||||
scroll.setCss(`min-height: ${height}px;`);
|
const n_child = self.get_children().length;
|
||||||
|
|
||||||
// Make bottom scroll indicator appear only
|
if (n_child > SCROLL_THRESH_N) {
|
||||||
// when first getting overflowing children
|
// @ts-expect-error
|
||||||
if (!(bottomArrow.revealChild === true ||
|
scroll.vscroll = 'always';
|
||||||
topArrow.revealChild === true)) {
|
// @ts-expect-error
|
||||||
bottomArrow.revealChild = true;
|
scroll.setCss(`min-height: ${height}px;`);
|
||||||
|
|
||||||
|
// Make bottom scroll indicator appear only
|
||||||
|
// when first getting overflowing children
|
||||||
|
if (!(bottomArrow.reveal_child === true ||
|
||||||
|
topArrow.reveal_child === true)) {
|
||||||
|
bottomArrow.reveal_child = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// @ts-expect-error
|
||||||
|
scroll.vscroll = 'never';
|
||||||
|
// @ts-expect-error
|
||||||
|
scroll.setCss('');
|
||||||
|
topArrow.reveal_child = false;
|
||||||
|
bottomArrow.reveal_child = false;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else {
|
|
||||||
scroll.vscroll = 'never';
|
|
||||||
scroll.setCss('');
|
|
||||||
topArrow.revealChild = false;
|
|
||||||
bottomArrow.revealChild = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Trigger sort_func
|
// Trigger sort_func
|
||||||
self.get_children().forEach((ch) => {
|
// @ts-expect-error
|
||||||
ch.changed();
|
self.get_children().forEach(
|
||||||
});
|
/** @param {Box} ListBoxRow */
|
||||||
|
(ListBoxRow) => {
|
||||||
|
// @ts-expect-error
|
||||||
|
ListBoxRow.changed();
|
||||||
|
},
|
||||||
|
);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
|
|
@ -7,20 +7,20 @@ import { SpeakerIcon } from '../misc/audio-icons.js';
|
||||||
|
|
||||||
|
|
||||||
export default () => Box({
|
export default () => Box({
|
||||||
className: 'slider-box',
|
class_name: 'slider-box',
|
||||||
vertical: true,
|
vertical: true,
|
||||||
hpack: 'center',
|
hpack: 'center',
|
||||||
children: [
|
children: [
|
||||||
|
|
||||||
Box({
|
Box({
|
||||||
className: 'slider',
|
class_name: 'slider',
|
||||||
vpack: 'start',
|
vpack: 'start',
|
||||||
hpack: 'center',
|
hpack: 'center',
|
||||||
|
|
||||||
children: [
|
children: [
|
||||||
Icon({
|
Icon({
|
||||||
size: 26,
|
size: 26,
|
||||||
className: 'slider-label',
|
class_name: 'slider-label',
|
||||||
binds: [['icon', SpeakerIcon, 'value']],
|
binds: [['icon', SpeakerIcon, 'value']],
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
@ -30,14 +30,16 @@ export default () => Box({
|
||||||
max: 0.999,
|
max: 0.999,
|
||||||
draw_value: false,
|
draw_value: false,
|
||||||
|
|
||||||
onChange: ({ value }) => {
|
on_change: ({ value }) => {
|
||||||
Audio.speaker.volume = value;
|
if (Audio.speaker) {
|
||||||
|
Audio.speaker.volume = value;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
setup: (self) => {
|
setup: (self) => {
|
||||||
self
|
self
|
||||||
.hook(Audio, () => {
|
.hook(Audio, () => {
|
||||||
self.value = Audio.speaker?.volume;
|
self.value = Audio.speaker?.volume || 0;
|
||||||
}, 'speaker-changed')
|
}, 'speaker-changed')
|
||||||
|
|
||||||
.on('button-press-event', () => {
|
.on('button-press-event', () => {
|
||||||
|
@ -53,13 +55,13 @@ export default () => Box({
|
||||||
}),
|
}),
|
||||||
|
|
||||||
Box({
|
Box({
|
||||||
className: 'slider',
|
class_name: 'slider',
|
||||||
vpack: 'start',
|
vpack: 'start',
|
||||||
hpack: 'center',
|
hpack: 'center',
|
||||||
|
|
||||||
children: [
|
children: [
|
||||||
Icon({
|
Icon({
|
||||||
className: 'slider-label',
|
class_name: 'slider-label',
|
||||||
binds: [['icon', Brightness, 'screen-icon']],
|
binds: [['icon', Brightness, 'screen-icon']],
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
@ -68,7 +70,7 @@ export default () => Box({
|
||||||
vpack: 'center',
|
vpack: 'center',
|
||||||
draw_value: false,
|
draw_value: false,
|
||||||
|
|
||||||
onChange: ({ value }) => {
|
on_change: ({ value }) => {
|
||||||
Brightness.screen = value;
|
Brightness.screen = value;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ import Mpris from 'resource:///com/github/Aylur/ags/service/mpris.js';
|
||||||
import { CenterBox, Icon, ToggleButton } from 'resource:///com/github/Aylur/ags/widget.js';
|
import { CenterBox, Icon, ToggleButton } from 'resource:///com/github/Aylur/ags/widget.js';
|
||||||
|
|
||||||
|
|
||||||
|
/** @param {import('types/widgets/revealer').default} rev */
|
||||||
export default (rev) => CenterBox({
|
export default (rev) => CenterBox({
|
||||||
center_widget: ToggleButton({
|
center_widget: ToggleButton({
|
||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
|
@ -11,30 +12,32 @@ export default (rev) => CenterBox({
|
||||||
setup: (self) => {
|
setup: (self) => {
|
||||||
// Open at startup if there are players
|
// Open at startup if there are players
|
||||||
const id = Mpris.connect('changed', () => {
|
const id = Mpris.connect('changed', () => {
|
||||||
|
// @ts-expect-error
|
||||||
self.set_active(Mpris.players.length > 0);
|
self.set_active(Mpris.players.length > 0);
|
||||||
Mpris.disconnect(id);
|
Mpris.disconnect(id);
|
||||||
});
|
});
|
||||||
|
|
||||||
self.on('toggled', () => {
|
self.on('toggled', () => {
|
||||||
|
// @ts-expect-error
|
||||||
if (self.get_active()) {
|
if (self.get_active()) {
|
||||||
self.get_children()[0]
|
self.child
|
||||||
.setCss('-gtk-icon-transform: rotate(0deg);');
|
// @ts-expect-error
|
||||||
rev.revealChild = true;
|
?.setCss('-gtk-icon-transform: rotate(0deg);');
|
||||||
|
rev.reveal_child = true;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
self.get_children()[0]
|
self.child
|
||||||
.setCss('-gtk-icon-transform: rotate(180deg);');
|
// @ts-expect-error
|
||||||
rev.revealChild = false;
|
?.setCss('-gtk-icon-transform: rotate(180deg);');
|
||||||
|
rev.reveal_child = false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
child: Icon({
|
child: Icon({
|
||||||
icon: `${App.configDir }/icons/down-large.svg`,
|
icon: `${App.configDir }/icons/down-large.svg`,
|
||||||
className: 'arrow',
|
class_name: 'arrow',
|
||||||
css: '-gtk-icon-transform: rotate(180deg);',
|
css: '-gtk-icon-transform: rotate(180deg);',
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
start_widget: null,
|
|
||||||
end_widget: null,
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -114,12 +114,18 @@ class Brightness extends Service {
|
||||||
|
|
||||||
#monitorKbdState() {
|
#monitorKbdState() {
|
||||||
Variable(0, {
|
Variable(0, {
|
||||||
poll: [INTERVAL, `brightnessctl -d ${KBD} g`, (out) => {
|
poll: [
|
||||||
if (out !== this.#kbd) {
|
INTERVAL,
|
||||||
this.#kbd = out;
|
`brightnessctl -d ${KBD} g`,
|
||||||
this.emit('kbd', this.#kbd);
|
(out) => {
|
||||||
}
|
if (parseInt(out) !== this.#kbd) {
|
||||||
}],
|
this.#kbd = parseInt(out);
|
||||||
|
this.emit('kbd', this.#kbd);
|
||||||
|
|
||||||
|
return this.#kbd;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue