hyprpanel
This commit is contained in:
1 parent
10080a83ee
commit
5151ac24da
322 files changed
+38276
No files matched your search
@@ -0,0 +1,71 @@
|
||||
import { BoxWidget } from 'lib/types/widget';
|
||||
import { BluetoothDevice } from 'types/service/bluetooth';
|
||||
|
||||
const connectedControls = (dev: BluetoothDevice, connectedDevices: BluetoothDevice[]): BoxWidget => {
|
||||
if (!connectedDevices.includes(dev.address)) {
|
||||
return Widget.Box({});
|
||||
}
|
||||
|
||||
return Widget.Box({
|
||||
vpack: 'start',
|
||||
class_name: 'bluetooth-controls',
|
||||
children: [
|
||||
Widget.Button({
|
||||
class_name: 'menu-icon-button unpair bluetooth',
|
||||
child: Widget.Label({
|
||||
tooltip_text: dev.paired ? 'Unpair' : 'Pair',
|
||||
class_name: 'menu-icon-button-label unpair bluetooth txt-icon',
|
||||
label: dev.paired ? '' : '',
|
||||
}),
|
||||
on_primary_click: () =>
|
||||
Utils.execAsync([
|
||||
'bash',
|
||||
'-c',
|
||||
`bluetoothctl ${dev.paired ? 'unpair' : 'pair'} ${dev.address}`,
|
||||
]).catch((err) =>
|
||||
console.error(`bluetoothctl ${dev.paired ? 'unpair' : 'pair'} ${dev.address}`, err),
|
||||
),
|
||||
}),
|
||||
Widget.Button({
|
||||
class_name: 'menu-icon-button disconnect bluetooth',
|
||||
child: Widget.Label({
|
||||
tooltip_text: dev.connected ? 'Disconnect' : 'Connect',
|
||||
class_name: 'menu-icon-button-label disconnect bluetooth txt-icon',
|
||||
label: dev.connected ? '' : '',
|
||||
}),
|
||||
on_primary_click: () => dev.setConnection(!dev.connected),
|
||||
}),
|
||||
Widget.Button({
|
||||
class_name: 'menu-icon-button untrust bluetooth',
|
||||
child: Widget.Label({
|
||||
tooltip_text: dev.trusted ? 'Untrust' : 'Trust',
|
||||
class_name: 'menu-icon-button-label untrust bluetooth txt-icon',
|
||||
label: dev.trusted ? '' : '',
|
||||
}),
|
||||
on_primary_click: () =>
|
||||
Utils.execAsync([
|
||||
'bash',
|
||||
'-c',
|
||||
`bluetoothctl ${dev.trusted ? 'untrust' : 'trust'} ${dev.address}`,
|
||||
]).catch((err) =>
|
||||
console.error(`bluetoothctl ${dev.trusted ? 'untrust' : 'trust'} ${dev.address}`, err),
|
||||
),
|
||||
}),
|
||||
Widget.Button({
|
||||
class_name: 'menu-icon-button delete bluetooth',
|
||||
child: Widget.Label({
|
||||
tooltip_text: 'Forget',
|
||||
class_name: 'menu-icon-button-label delete bluetooth txt-icon',
|
||||
label: '',
|
||||
}),
|
||||
on_primary_click: () => {
|
||||
Utils.execAsync(['bash', '-c', `bluetoothctl remove ${dev.address}`]).catch((err) =>
|
||||
console.error('Bluetooth Remove', err),
|
||||
);
|
||||
},
|
||||
}),
|
||||
],
|
||||
});
|
||||
};
|
||||
|
||||
export { connectedControls };
|
||||
@@ -0,0 +1,136 @@
|
||||
import { Bluetooth } from 'types/service/bluetooth.js';
|
||||
import Box from 'types/widgets/box.js';
|
||||
import { connectedControls } from './connectedControls.js';
|
||||
import { getBluetoothIcon } from '../utils.js';
|
||||
import Gtk from 'types/@girs/gtk-3.0/gtk-3.0.js';
|
||||
import { Attribute, Child } from 'lib/types/widget.js';
|
||||
|
||||
const devices = (bluetooth: Bluetooth, self: Box<Gtk.Widget, unknown>): Box<Child, Attribute> => {
|
||||
return self.hook(bluetooth, () => {
|
||||
if (!bluetooth.enabled) {
|
||||
return (self.child = Widget.Box({
|
||||
class_name: 'bluetooth-items',
|
||||
vertical: true,
|
||||
expand: true,
|
||||
vpack: 'center',
|
||||
hpack: 'center',
|
||||
children: [
|
||||
Widget.Label({
|
||||
class_name: 'bluetooth-disabled dim',
|
||||
hexpand: true,
|
||||
label: 'Bluetooth is disabled',
|
||||
}),
|
||||
],
|
||||
}));
|
||||
}
|
||||
|
||||
const availableDevices = bluetooth.devices
|
||||
.filter((btDev) => btDev.name !== null)
|
||||
.sort((a, b) => {
|
||||
if (a.connected || a.paired) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (b.connected || b.paired) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return b.name - a.name;
|
||||
});
|
||||
|
||||
const conDevNames = availableDevices.filter((d) => d.connected || d.paired).map((d) => d.address);
|
||||
|
||||
if (!availableDevices.length) {
|
||||
return (self.child = Widget.Box({
|
||||
class_name: 'bluetooth-items',
|
||||
vertical: true,
|
||||
expand: true,
|
||||
vpack: 'center',
|
||||
hpack: 'center',
|
||||
children: [
|
||||
Widget.Label({
|
||||
class_name: 'no-bluetooth-devices dim',
|
||||
hexpand: true,
|
||||
label: 'No devices currently found',
|
||||
}),
|
||||
Widget.Label({
|
||||
class_name: 'search-bluetooth-label dim',
|
||||
hexpand: true,
|
||||
label: "Press '' to search",
|
||||
}),
|
||||
],
|
||||
}));
|
||||
}
|
||||
|
||||
return (self.child = Widget.Box({
|
||||
vertical: true,
|
||||
children: availableDevices.map((device) => {
|
||||
return Widget.Box({
|
||||
children: [
|
||||
Widget.Button({
|
||||
hexpand: true,
|
||||
class_name: `bluetooth-element-item ${device}`,
|
||||
on_primary_click: () => {
|
||||
if (!conDevNames.includes(device.address)) device.setConnection(true);
|
||||
},
|
||||
child: Widget.Box({
|
||||
hexpand: true,
|
||||
children: [
|
||||
Widget.Box({
|
||||
hexpand: true,
|
||||
hpack: 'start',
|
||||
class_name: 'menu-button-container',
|
||||
children: [
|
||||
Widget.Label({
|
||||
vpack: 'start',
|
||||
class_name: `menu-button-icon bluetooth ${conDevNames.includes(device.address) ? 'active' : ''} txt-icon`,
|
||||
label: getBluetoothIcon(`${device['icon_name']}-symbolic`),
|
||||
}),
|
||||
Widget.Box({
|
||||
vertical: true,
|
||||
vpack: 'center',
|
||||
children: [
|
||||
Widget.Label({
|
||||
vpack: 'center',
|
||||
hpack: 'start',
|
||||
class_name: 'menu-button-name bluetooth',
|
||||
truncate: 'end',
|
||||
wrap: true,
|
||||
label: device.alias,
|
||||
}),
|
||||
Widget.Revealer({
|
||||
hpack: 'start',
|
||||
reveal_child: device.connected || device.paired,
|
||||
child: Widget.Label({
|
||||
hpack: 'start',
|
||||
class_name: 'connection-status dim',
|
||||
label: device.connected ? 'Connected' : 'Paired',
|
||||
}),
|
||||
}),
|
||||
],
|
||||
}),
|
||||
],
|
||||
}),
|
||||
Widget.Box({
|
||||
hpack: 'end',
|
||||
children: device.connecting
|
||||
? [
|
||||
Widget.Spinner({
|
||||
vpack: 'start',
|
||||
class_name: 'spinner bluetooth',
|
||||
}),
|
||||
]
|
||||
: [],
|
||||
}),
|
||||
],
|
||||
}),
|
||||
}),
|
||||
connectedControls(device, conDevNames),
|
||||
],
|
||||
});
|
||||
}),
|
||||
}));
|
||||
});
|
||||
};
|
||||
|
||||
export { devices };
|
||||
@@ -0,0 +1,26 @@
|
||||
const bluetooth = await Service.import('bluetooth');
|
||||
import { label } from './label.js';
|
||||
import { devices } from './devicelist.js';
|
||||
import { BoxWidget } from 'lib/types/widget.js';
|
||||
|
||||
const Devices = (): BoxWidget => {
|
||||
return Widget.Box({
|
||||
class_name: 'menu-section-container',
|
||||
vertical: true,
|
||||
children: [
|
||||
label(bluetooth),
|
||||
Widget.Box({
|
||||
class_name: 'menu-items-section',
|
||||
child: Widget.Box({
|
||||
class_name: 'menu-content',
|
||||
vertical: true,
|
||||
setup: (self) => {
|
||||
devices(bluetooth, self);
|
||||
},
|
||||
}),
|
||||
}),
|
||||
],
|
||||
});
|
||||
};
|
||||
|
||||
export { Devices };
|
||||
@@ -0,0 +1,65 @@
|
||||
import { BoxWidget } from 'lib/types/widget';
|
||||
import { Bluetooth } from 'types/service/bluetooth';
|
||||
|
||||
const label = (bluetooth: Bluetooth): BoxWidget => {
|
||||
const searchInProgress = Variable(false);
|
||||
|
||||
const startRotation = (): void => {
|
||||
searchInProgress.value = true;
|
||||
setTimeout(() => {
|
||||
searchInProgress.value = false;
|
||||
}, 10 * 1000);
|
||||
};
|
||||
|
||||
return Widget.Box({
|
||||
class_name: 'menu-label-container',
|
||||
hpack: 'fill',
|
||||
vpack: 'start',
|
||||
children: [
|
||||
Widget.Label({
|
||||
class_name: 'menu-label',
|
||||
vpack: 'center',
|
||||
hpack: 'start',
|
||||
label: 'Bluetooth',
|
||||
}),
|
||||
Widget.Box({
|
||||
class_name: 'controls-container',
|
||||
vpack: 'start',
|
||||
children: [
|
||||
Widget.Switch({
|
||||
class_name: 'menu-switch bluetooth',
|
||||
hexpand: true,
|
||||
hpack: 'end',
|
||||
active: bluetooth.bind('enabled'),
|
||||
on_activate: ({ active }) => {
|
||||
searchInProgress.value = false;
|
||||
Utils.execAsync(['bash', '-c', `bluetoothctl power ${active ? 'on' : 'off'}`]).catch(
|
||||
(err) => console.error(`bluetoothctl power ${active ? 'on' : 'off'}`, err),
|
||||
);
|
||||
},
|
||||
}),
|
||||
Widget.Separator({
|
||||
class_name: 'menu-separator bluetooth',
|
||||
}),
|
||||
Widget.Button({
|
||||
vpack: 'center',
|
||||
class_name: 'menu-icon-button search',
|
||||
on_primary_click: () => {
|
||||
startRotation();
|
||||
Utils.execAsync(['bash', '-c', 'bluetoothctl --timeout 120 scan on']).catch((err) => {
|
||||
searchInProgress.value = false;
|
||||
console.error('bluetoothctl --timeout 120 scan on', err);
|
||||
});
|
||||
},
|
||||
child: Widget.Icon({
|
||||
class_name: searchInProgress.bind('value').as((v) => (v ? 'spinning' : '')),
|
||||
icon: 'view-refresh-symbolic',
|
||||
}),
|
||||
}),
|
||||
],
|
||||
}),
|
||||
],
|
||||
});
|
||||
};
|
||||
|
||||
export { label };
|
||||
@@ -0,0 +1,24 @@
|
||||
import Window from 'types/widgets/window.js';
|
||||
import DropdownMenu from '../shared/dropdown/index.js';
|
||||
import { Devices } from './devices/index.js';
|
||||
import { Attribute, Child } from 'lib/types/widget.js';
|
||||
import options from 'options.js';
|
||||
|
||||
export default (): Window<Child, Attribute> => {
|
||||
return DropdownMenu({
|
||||
name: 'bluetoothmenu',
|
||||
transition: options.menus.transition.bind('value'),
|
||||
child: Widget.Box({
|
||||
class_name: 'menu-items bluetooth',
|
||||
hpack: 'fill',
|
||||
hexpand: true,
|
||||
child: Widget.Box({
|
||||
vertical: true,
|
||||
hpack: 'fill',
|
||||
hexpand: true,
|
||||
class_name: 'menu-items-container bluetooth',
|
||||
child: Devices(),
|
||||
}),
|
||||
}),
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,29 @@
|
||||
const getBluetoothIcon = (iconName: string): string => {
|
||||
const deviceIconMap = [
|
||||
['^audio-card*', ''],
|
||||
['^audio-headphones*', ''],
|
||||
['^audio-headset*', ''],
|
||||
['^audio-input*', ''],
|
||||
['^audio-speakers*', ''],
|
||||
['^bluetooth*', ''],
|
||||
['^camera*', ''],
|
||||
['^computer*', ''],
|
||||
['^input-gaming*', ''],
|
||||
['^input-keyboard*', ''],
|
||||
['^input-mouse*', ''],
|
||||
['^input-tablet*', ''],
|
||||
['^media*', ''],
|
||||
['^modem*', ''],
|
||||
['^network*', ''],
|
||||
['^phone*', ''],
|
||||
['^printer*', ''],
|
||||
['^scanner*', ''],
|
||||
['^video-camera*', ''],
|
||||
];
|
||||
|
||||
const foundMatch = deviceIconMap.find((icon) => RegExp(icon[0]).test(iconName.toLowerCase()));
|
||||
|
||||
return foundMatch ? foundMatch[1] : '';
|
||||
};
|
||||
|
||||
export { getBluetoothIcon };
|
||||
Reference in new issue
Block a user