import App from 'resource:///com/github/Aylur/ags/app.js'; import Audio from 'resource:///com/github/Aylur/ags/service/audio.js'; import Bluetooth from 'resource:///com/github/Aylur/ags/service/bluetooth.js'; import Network from 'resource:///com/github/Aylur/ags/service/network.js'; import Variable from 'resource:///com/github/Aylur/ags/variable.js'; import { Box, Icon, Label, Revealer } from 'resource:///com/github/Aylur/ags/widget.js'; import { execAsync } from 'resource:///com/github/Aylur/ags/utils.js'; import EventBox from '../misc/cursorbox.js'; import Separator from '../misc/separator.js'; const ButtonStates = []; const GridButton = ({ command = () => {}, secondaryCommand = () => {}, icon, indicator, menu, } = {}) => { const Activated = Variable(false); ButtonStates.push(Activated); // allow setting icon dynamically or statically if (typeof icon === 'string') { icon = Icon({ className: 'grid-label', icon: icon, connections: [[Activated, self => { self.setCss(`color: ${Activated.value ? 'rgba(189, 147, 249, 0.8)' : 'unset'};`); }]], }); } else { icon = Icon({ className: 'grid-label', connections: [ icon, [Activated, self => { self.setCss(`color: ${Activated.value ? 'rgba(189, 147, 249, 0.8)' : 'unset'};`); }], ], }); } if (indicator) { indicator = Label({ className: 'sub-label', justification: 'left', truncate: 'end', maxWidthChars: 12, connections: [indicator], }); } if (menu) { menu = Revealer({ transition: 'slide_down', child: menu, binds: [['revealChild', Activated, 'value']], }); } const widget = Box({ vertical: true, children: [ Box({ className: 'grid-button', children: [ EventBox({ className: 'left-part', onPrimaryClickRelease: () => { if (!Activated.value) command(); else secondaryCommand(); }, child: icon, }), EventBox({ className: 'right-part', onPrimaryClickRelease: () => { ButtonStates.forEach(state => { if (state !== Activated) state.value = false; }); Activated.value = !Activated.value; }, onHover: self => { if (menu) { const rowMenu = self.get_parent().get_parent() .get_parent().get_parent().children[1]; if (!rowMenu.get_children().find(ch => ch === menu)) { rowMenu.add(menu); rowMenu.show_all(); } } }, child: Icon({ icon: App.configDir + '/icons/down-large.svg', connections: [[Activated, self => { let deg = 270; if (Activated.value) deg = menu ? 360 : 450; self.setCss(`-gtk-icon-transform: rotate(${deg}deg);`); }]], className: 'grid-chev', }), }), ], }), indicator, ], }); return widget; }; const Row = ({ buttons } = {}) => { const widget = Box({ vertical: true, children: [ Box({ className: 'button-row', hpack: 'center', }), Box(), ], }); for (let i = 0; i < buttons.length; ++i) { if (i !== buttons.length - 1) { widget.children[0].add(buttons[i]); widget.children[0].add(Separator(28)); } else { widget.children[0].add(buttons[i]); } } return widget; }; const FirstRow = () => Row({ buttons: [ GridButton({ command: () => Network.toggleWifi(), secondaryCommand: () => { execAsync(['bash', '-c', 'nm-connection-editor']) .catch(print); }, icon: [Network, icon => icon.icon = Network.wifi?.iconName], indicator: [Network, self => self.label = Network.wifi?.ssid || Network.wired?.internet], menu: Box({ className: 'menu', vertical: true, connections: [[Network, box => box.children = Network.wifi?.access_points.map(ap => EventBox({ isButton: true, on_clicked: () => execAsync(`nmcli device wifi connect ${ap.bssid}`).catch(print), child: Box({ children: [ Icon(ap.iconName), Label(ap.ssid || ''), ap.active && Icon({ icon: 'object-select-symbolic', hexpand: true, hpack: 'end', }), ], }), })), ]], }), }), GridButton({ command: () => { execAsync(['bash', '-c', '$AGS_PATH/qs-toggles.sh blue-toggle']) .catch(print); }, secondaryCommand: () => { execAsync(['bash', '-c', 'blueberry']) .catch(print); }, icon: [Bluetooth, self => { if (Bluetooth.enabled) { self.icon = 'bluetooth-active-symbolic'; execAsync(['bash', '-c', 'echo 󰂯 > $HOME/.config/.bluetooth']) .catch(print); } else { self.icon = 'bluetooth-disabled-symbolic'; execAsync(['bash', '-c', 'echo 󰂲 > $HOME/.config/.bluetooth']) .catch(print); } }, 'changed'], indicator: [Bluetooth, self => { if (Bluetooth.connectedDevices[0]) self.label = String(Bluetooth.connectedDevices[0]); else self.label = 'Disconnected'; }, 'changed'], }), // TODO: replace with vpn GridButton({ command: () => { execAsync(['bash', '-c', '$AGS_PATH/qs-toggles.sh toggle-radio']) .catch(print); }, secondaryCommand: () => { execAsync(['notify-send', 'set this up moron']) .catch(print); }, icon: [Network, self => { if (Network.wifi.enabled) self.icon = 'airplane-mode-disabled-symbolic'; else self.icon = 'airplane-mode-symbolic'; }, 'changed'], }), ], }); const items = { 101: 'audio-volume-overamplified-symbolic', 67: 'audio-volume-high-symbolic', 34: 'audio-volume-medium-symbolic', 1: 'audio-volume-low-symbolic', 0: 'audio-volume-muted-symbolic', }; const itemsMic = { 2: 'audio-input-microphone-high-symbolic', 1: 'audio-input-microphone-muted-symbolic', 0: 'audio-input-microphone-muted-symbolic', }; const SecondRow = () => Row({ buttons: [ GridButton({ command: () => { execAsync(['swayosd-client', '--output-volume', 'mute-toggle']) .catch(print); }, secondaryCommand: () => { execAsync(['bash', '-c', 'pavucontrol']) .catch(print); }, icon: [Audio, icon => { if (Audio.speaker) { if (Audio.speaker.stream.isMuted) { icon.icon = items[0]; } else { const vol = Audio.speaker.volume * 100; for (const threshold of [-1, 0, 33, 66, 100]) { if (vol > threshold + 1) icon.icon = items[threshold + 1]; } } } }, 'speaker-changed'], }), GridButton({ command: () => { execAsync(['swayosd-client', '--input-volume', 'mute-toggle']) .catch(print); }, secondaryCommand: () => { execAsync(['bash', '-c', 'pavucontrol']) .catch(print); }, icon: [Audio, icon => { if (Audio.microphone) { if (Audio.microphone.stream.isMuted) { icon.icon = itemsMic[0]; } else { const vol = Audio.microphone.volume * 100; for (const threshold of [-1, 0, 1]) { if (vol > threshold + 1) icon.icon = itemsMic[threshold + 1]; } } } }, 'microphone-changed'], }), GridButton({ command: () => { execAsync(['bash', '-c', '$LOCK_PATH/lock.sh']) .catch(print); }, secondaryCommand: () => App.openWindow('powermenu'), icon: 'system-lock-screen-symbolic', }), ], }); export default () => Box({ className: 'button-grid', vertical: true, hpack: 'center', children: [ FirstRow(), Separator(10, { vertical: true }), SecondRow(), ], });