2023-12-05 11:35:40 -05:00
|
|
|
import App from 'resource:///com/github/Aylur/ags/app.js';
|
|
|
|
import Bluetooth from 'resource:///com/github/Aylur/ags/service/bluetooth.js';
|
|
|
|
|
|
|
|
import { Box, Icon, Label, ListBox, Overlay, Revealer, Scrollable } from 'resource:///com/github/Aylur/ags/widget.js';
|
|
|
|
|
2023-12-18 23:20:32 -05:00
|
|
|
import CursorBox from '../misc/cursorbox.js';
|
2023-12-05 11:35:40 -05:00
|
|
|
|
2023-12-17 00:01:58 -05:00
|
|
|
const SCROLL_THRESH_H = 200;
|
|
|
|
const SCROLL_THRESH_N = 7;
|
2023-12-05 11:35:40 -05:00
|
|
|
|
2023-12-23 01:14:21 -05:00
|
|
|
/**
|
|
|
|
* @typedef {import('types/widgets/box').default} Box
|
|
|
|
* @typedef {import('types/service/bluetooth').BluetoothDevice} BluetoothDevice
|
|
|
|
*/
|
2023-12-05 11:35:40 -05:00
|
|
|
|
2023-12-23 01:14:21 -05:00
|
|
|
/** @param {BluetoothDevice} dev */
|
|
|
|
const BluetoothDevice = (dev) => Box({
|
|
|
|
class_name: 'menu-item',
|
2023-12-05 11:35:40 -05:00
|
|
|
|
2023-12-23 01:14:21 -05:00
|
|
|
attribute: { dev },
|
2023-12-05 11:35:40 -05:00
|
|
|
|
2023-12-23 01:14:21 -05:00
|
|
|
children: [Revealer({
|
|
|
|
reveal_child: true,
|
2023-12-05 11:35:40 -05:00
|
|
|
transition: 'slide_down',
|
2023-12-23 01:14:21 -05:00
|
|
|
|
2023-12-18 23:20:32 -05:00
|
|
|
child: CursorBox({
|
|
|
|
on_primary_click_release: () => dev.setConnection(true),
|
2023-12-05 11:35:40 -05:00
|
|
|
|
2023-12-23 01:14:21 -05:00
|
|
|
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'};
|
|
|
|
`);
|
|
|
|
}),
|
|
|
|
],
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
})],
|
|
|
|
});
|
2023-12-05 11:35:40 -05:00
|
|
|
|
|
|
|
export const BluetoothMenu = () => {
|
|
|
|
const DevList = new Map();
|
2023-12-23 01:14:21 -05:00
|
|
|
|
2023-12-05 11:35:40 -05:00
|
|
|
const topArrow = Revealer({
|
|
|
|
transition: 'slide_down',
|
2023-12-23 01:14:21 -05:00
|
|
|
|
2023-12-05 11:35:40 -05:00
|
|
|
child: Icon({
|
|
|
|
icon: `${App.configDir }/icons/down-large.svg`,
|
2023-12-23 01:14:21 -05:00
|
|
|
class_name: 'scrolled-indicator',
|
2023-12-05 11:35:40 -05:00
|
|
|
size: 16,
|
|
|
|
css: '-gtk-icon-transform: rotate(180deg);',
|
|
|
|
}),
|
|
|
|
});
|
|
|
|
|
|
|
|
const bottomArrow = Revealer({
|
|
|
|
transition: 'slide_up',
|
2023-12-23 01:14:21 -05:00
|
|
|
|
2023-12-05 11:35:40 -05:00
|
|
|
child: Icon({
|
|
|
|
icon: `${App.configDir }/icons/down-large.svg`,
|
2023-12-23 01:14:21 -05:00
|
|
|
class_name: 'scrolled-indicator',
|
2023-12-05 11:35:40 -05:00
|
|
|
size: 16,
|
|
|
|
}),
|
|
|
|
});
|
|
|
|
|
|
|
|
return Overlay({
|
|
|
|
pass_through: true,
|
2023-12-23 01:14:21 -05:00
|
|
|
|
2023-12-05 11:35:40 -05:00
|
|
|
overlays: [
|
|
|
|
Box({
|
|
|
|
vpack: 'start',
|
|
|
|
hpack: 'center',
|
|
|
|
css: 'margin-top: 12px',
|
|
|
|
children: [topArrow],
|
|
|
|
}),
|
|
|
|
|
|
|
|
Box({
|
|
|
|
vpack: 'end',
|
|
|
|
hpack: 'center',
|
|
|
|
css: 'margin-bottom: 12px',
|
|
|
|
children: [bottomArrow],
|
|
|
|
}),
|
|
|
|
],
|
|
|
|
|
|
|
|
child: Box({
|
2023-12-23 01:14:21 -05:00
|
|
|
class_name: 'menu',
|
2023-12-05 11:35:40 -05:00
|
|
|
|
|
|
|
child: Scrollable({
|
|
|
|
hscroll: 'never',
|
|
|
|
vscroll: 'never',
|
|
|
|
|
2023-12-17 00:01:58 -05:00
|
|
|
setup: (self) => {
|
|
|
|
self.on('edge-reached', (_, pos) => {
|
|
|
|
// Manage scroll indicators
|
|
|
|
if (pos === 2) {
|
2023-12-23 01:14:21 -05:00
|
|
|
topArrow.reveal_child = false;
|
|
|
|
bottomArrow.reveal_child = true;
|
2023-12-17 00:01:58 -05:00
|
|
|
}
|
|
|
|
else if (pos === 3) {
|
2023-12-23 01:14:21 -05:00
|
|
|
topArrow.reveal_child = true;
|
|
|
|
bottomArrow.reveal_child = false;
|
2023-12-17 00:01:58 -05:00
|
|
|
}
|
|
|
|
});
|
|
|
|
},
|
2023-12-05 11:35:40 -05:00
|
|
|
|
|
|
|
child: ListBox({
|
|
|
|
setup: (self) => {
|
2023-12-23 01:14:21 -05:00
|
|
|
// @ts-expect-error
|
|
|
|
self.set_sort_func(
|
|
|
|
/**
|
|
|
|
* @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;
|
|
|
|
},
|
|
|
|
);
|
2023-12-05 11:35:40 -05:00
|
|
|
|
2023-12-17 00:01:58 -05:00
|
|
|
self.hook(Bluetooth, () => {
|
|
|
|
// Get all devices
|
2023-12-23 01:14:21 -05:00
|
|
|
const Devices = Bluetooth.devices.concat(
|
|
|
|
Bluetooth.connected_devices,
|
2023-12-17 00:01:58 -05:00
|
|
|
);
|
2023-12-05 11:35:40 -05:00
|
|
|
|
2023-12-17 00:01:58 -05:00
|
|
|
// Add missing devices
|
|
|
|
Devices.forEach((dev) => {
|
|
|
|
if (!DevList.has(dev) && dev.name) {
|
|
|
|
DevList.set(dev, BluetoothDevice(dev));
|
2023-12-05 11:35:40 -05:00
|
|
|
|
2023-12-23 01:14:21 -05:00
|
|
|
// @ts-expect-error
|
2023-12-17 00:01:58 -05:00
|
|
|
self.add(DevList.get(dev));
|
|
|
|
self.show_all();
|
|
|
|
}
|
|
|
|
});
|
2023-12-05 11:35:40 -05:00
|
|
|
|
2023-12-17 00:01:58 -05:00
|
|
|
// Delete ones that don't exist anymore
|
|
|
|
const difference = Array.from(DevList.keys())
|
|
|
|
.filter((dev) => !Devices
|
|
|
|
.find((d) => dev === d) &&
|
2023-12-05 11:35:40 -05:00
|
|
|
dev.name);
|
|
|
|
|
2023-12-17 00:01:58 -05:00
|
|
|
difference.forEach((dev) => {
|
|
|
|
const devWidget = DevList.get(dev);
|
|
|
|
|
|
|
|
if (devWidget) {
|
|
|
|
if (devWidget.toDestroy) {
|
|
|
|
devWidget.get_parent().destroy();
|
|
|
|
DevList.delete(dev);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
devWidget.children[0]
|
2023-12-23 01:14:21 -05:00
|
|
|
.reveal_child = false;
|
2023-12-17 00:01:58 -05:00
|
|
|
devWidget.toDestroy = true;
|
|
|
|
}
|
2023-12-05 11:35:40 -05:00
|
|
|
}
|
2023-12-17 00:01:58 -05:00
|
|
|
});
|
|
|
|
|
|
|
|
// Start scrolling after a specified height
|
|
|
|
// is reached by the children
|
|
|
|
const height = Math.max(
|
2023-12-23 01:14:21 -05:00
|
|
|
self.get_parent()?.get_allocated_height() || 0,
|
2023-12-17 00:01:58 -05:00
|
|
|
SCROLL_THRESH_H,
|
|
|
|
);
|
|
|
|
|
2023-12-23 01:14:21 -05:00
|
|
|
const scroll = self.get_parent()?.get_parent();
|
|
|
|
|
|
|
|
if (scroll) {
|
|
|
|
// @ts-expect-error
|
|
|
|
const n_child = self.get_children().length;
|
2023-12-17 00:01:58 -05:00
|
|
|
|
2023-12-23 01:14:21 -05:00
|
|
|
if (n_child > SCROLL_THRESH_N) {
|
|
|
|
// @ts-expect-error
|
|
|
|
scroll.vscroll = 'always';
|
|
|
|
// @ts-expect-error
|
|
|
|
scroll.setCss(`min-height: ${height}px;`);
|
2023-12-17 00:01:58 -05:00
|
|
|
|
2023-12-23 01:14:21 -05:00
|
|
|
// 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;
|
2023-12-05 11:35:40 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-12-17 00:01:58 -05:00
|
|
|
// Trigger sort_func
|
2023-12-23 01:14:21 -05:00
|
|
|
// @ts-expect-error
|
|
|
|
self.get_children().forEach(
|
|
|
|
/** @param {Box} ListBoxRow */
|
|
|
|
(ListBoxRow) => {
|
|
|
|
// @ts-expect-error
|
|
|
|
ListBoxRow.changed();
|
|
|
|
},
|
|
|
|
);
|
2023-12-05 11:35:40 -05:00
|
|
|
});
|
2023-12-17 00:01:58 -05:00
|
|
|
},
|
2023-12-05 11:35:40 -05:00
|
|
|
}),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
});
|
|
|
|
};
|