From 71108d185236fda392f2a42d85b4d5e8a73a271b Mon Sep 17 00:00:00 2001 From: matt1432 Date: Tue, 21 Nov 2023 01:29:46 -0500 Subject: [PATCH] refactor(ags lint): customise eslint and apply to config --- devices/wim/config/ags/.eslintrc.json | 148 ++++++++++--- .../wim/config/ags/js/applauncher/app-item.js | 41 ++-- devices/wim/config/ags/js/applauncher/main.js | 57 +++-- .../wim/config/ags/js/bar/buttons/audio.js | 22 +- .../wim/config/ags/js/bar/buttons/battery.js | 21 +- .../config/ags/js/bar/buttons/brightness.js | 16 +- .../wim/config/ags/js/bar/buttons/clock.js | 37 ++-- .../ags/js/bar/buttons/current-window.js | 1 + .../wim/config/ags/js/bar/buttons/heart.js | 7 +- .../ags/js/bar/buttons/keyboard-layout.js | 29 +-- .../config/ags/js/bar/buttons/notif-button.js | 34 +-- .../config/ags/js/bar/buttons/osk-toggle.js | 4 +- .../ags/js/bar/buttons/quick-settings.js | 7 +- .../wim/config/ags/js/bar/buttons/systray.js | 69 +++--- .../ags/js/bar/buttons/tablet-toggle.js | 4 +- .../config/ags/js/bar/buttons/workspaces.js | 86 +++++--- devices/wim/config/ags/js/bar/fullscreen.js | 27 ++- devices/wim/config/ags/js/bar/main.js | 47 +++-- devices/wim/config/ags/js/corners/main.js | 3 + .../config/ags/js/corners/screen-corners.js | 25 ++- devices/wim/config/ags/js/date.js | 14 +- .../wim/config/ags/js/media-player/gesture.js | 23 +- .../wim/config/ags/js/media-player/mpris.js | 135 +++++++----- .../wim/config/ags/js/media-player/player.js | 83 +++++--- devices/wim/config/ags/js/misc/audio-icons.js | 12 +- devices/wim/config/ags/js/misc/closer.js | 4 +- devices/wim/config/ags/js/misc/cursorbox.js | 20 +- devices/wim/config/ags/js/misc/popup.js | 39 ++-- devices/wim/config/ags/js/misc/separator.js | 22 +- .../wim/config/ags/js/notifications/base.js | 80 ++++--- .../wim/config/ags/js/notifications/center.js | 54 +++-- .../config/ags/js/notifications/gesture.js | 95 +++++---- .../wim/config/ags/js/notifications/popup.js | 30 +-- .../ags/js/on-screen-keyboard/gesture.js | 57 +++-- .../ags/js/on-screen-keyboard/keyboard.js | 56 +++-- .../config/ags/js/on-screen-keyboard/keys.js | 95 ++++++--- .../config/ags/js/on-screen-keyboard/main.js | 8 +- devices/wim/config/ags/js/osd/audio.js | 17 +- devices/wim/config/ags/js/osd/brightness.js | 6 +- devices/wim/config/ags/js/osd/caps.js | 8 +- devices/wim/config/ags/js/osd/kbd.js | 8 +- devices/wim/config/ags/js/osd/main.js | 22 +- devices/wim/config/ags/js/osd/mic.js | 9 +- devices/wim/config/ags/js/overview/clients.js | 84 +++++--- .../ags/js/overview/current-workspace.js | 17 +- .../wim/config/ags/js/overview/dragndrop.js | 36 +++- devices/wim/config/ags/js/overview/main.js | 20 +- .../wim/config/ags/js/overview/workspaces.js | 197 +++++++++++------- devices/wim/config/ags/js/powermenu.js | 12 +- .../ags/js/quick-settings/button-grid.js | 187 +++++++++++------ .../wim/config/ags/js/quick-settings/main.js | 13 +- .../ags/js/quick-settings/slider-box.js | 42 +++- .../ags/js/quick-settings/toggle-button.js | 12 +- devices/wim/config/ags/js/setup.js | 21 +- devices/wim/config/ags/package.json | 3 + devices/wim/config/ags/services/brightness.js | 67 +++--- devices/wim/config/ags/services/pointers.js | 134 +++++++----- devices/wim/config/ags/services/tablet.js | 131 ++++++------ .../wim/config/ags/services/touch-gestures.js | 68 +++--- 59 files changed, 1690 insertions(+), 936 deletions(-) diff --git a/devices/wim/config/ags/.eslintrc.json b/devices/wim/config/ags/.eslintrc.json index f0d61e84..451dcd96 100644 --- a/devices/wim/config/ags/.eslintrc.json +++ b/devices/wim/config/ags/.eslintrc.json @@ -8,33 +8,133 @@ "ecmaVersion": "latest", "sourceType": "module" }, + "plugins": ["@stylistic"], "rules": { - "arrow-parens": ["error", "as-needed"], - "comma-dangle": ["error", "always-multiline"], - "comma-spacing": ["error", { "before": false, "after": true }], - "comma-style": ["error", "last"], - "curly": ["error", "multi-or-nest", "consistent"], - "dot-location": ["error", "property"], - "eol-last": 0, - "indent": ["error", 4, { "SwitchCase": 1 }], - "keyword-spacing": ["error", { "before": true }], - "lines-between-class-members": ["error", "always", { "exceptAfterSingleLine": true }], - "padded-blocks": ["error", "never", { "allowSingleLineBlocks": false }], - "prefer-const": "error", - "quotes": ["error", "single", { "avoidEscape": true }], - "semi": ["error", "always"], - "nonblock-statement-body-position": ["error", "below"], - "no-trailing-spaces": ["error"], - "no-dupe-class-members": 0, - "array-bracket-spacing": ["error", "never"], - "key-spacing": ["error", { "beforeColon": false, "afterColon": true }], - "object-curly-spacing": ["error", "always"], - "no-useless-escape": ["off"] + "array-callback-return": ["error", { + "allowImplicit": true, + "checkForEach": true + }], + "no-constructor-return": ["error"], + "no-unreachable-loop": ["error", { "ignore": [ + "ForInStatement", "ForOfStatement" + ]}], + "no-use-before-define": ["error", { + "functions": false + }], + + + "block-scoped-var": ["error"], + "capitalized-comments": ["warn", "always", { + "ignoreConsecutiveComments": true + }], + "class-methods-use-this": ["error"], + "curly": ["warn"], + "default-case-last": ["warn"], + "default-param-last": ["error"], + "dot-notation": ["warn", { "allowPattern": ".*-|_.*" }], + "eqeqeq": ["error", "smart"], + "func-names": ["warn", "never"], + "func-style": ["warn", "expression"], + "logical-assignment-operators": ["warn", "always"], + "no-array-constructor": ["error"], + "no-else-return": ["error"], + "no-empty-function": ["warn"], + "no-empty-static-block": ["warn"], + "no-extend-native": ["error"], + "no-extra-bind": ["warn"], + "no-implicit-coercion": ["warn"], + "no-iterator": ["error"], + "no-labels": ["error"], + "no-lone-blocks": ["error"], + "no-lonely-if": ["error"], + "no-loop-func": ["error"], + "no-magic-numbers": ["error", { + "ignore": [-1, 0.1, 0, 1, 2, 3, 10, 33, 66, 100, 255, 360, 450, 1000], + "ignoreDefaultValues": true + }], + "no-multi-assign": ["error"], + "no-negated-condition": ["warn"], + "no-new": ["error"], + "no-new-func": ["error"], + "no-new-wrappers": ["error"], + "no-object-constructor": ["error"], + "no-proto": ["error"], + "no-return-assign": ["error"], + "no-sequences": ["error"], + "no-shadow": ["error", { "builtinGlobals": true }], + "no-undef-init": ["warn"], + "no-undefined": ["error"], + "no-useless-constructor": ["warn"], + "no-useless-escape": ["off"], + "no-useless-return": ["error"], + "no-var": ["error"], + "no-void": ["error"], + "no-with": ["error"], + "object-shorthand": ["error", "always"], + "one-var": ["error", "never"], + "operator-assignment": ["warn", "always"], + "prefer-arrow-callback": ["error"], + "prefer-const": ["error"], + "prefer-object-has-own": ["error"], + "prefer-regex-literals": ["error"], + "prefer-template": ["warn"], + + + "@stylistic/array-bracket-newline": ["warn", "consistent"], + "@stylistic/array-bracket-spacing": ["warn", "never"], + "@stylistic/arrow-parens": ["warn", "always"], + "@stylistic/brace-style": ["warn", "stroustrup"], + "@stylistic/comma-dangle": ["warn", "always-multiline"], + "@stylistic/comma-spacing": ["warn", { "before": false, "after": true }], + "@stylistic/comma-style": ["error", "last"], + "@stylistic/dot-location": ["error", "property"], + "@stylistic/function-call-argument-newline": ["warn", "consistent"], + "@stylistic/function-paren-newline": ["warn", "consistent"], + "@stylistic/indent": ["warn", 4, { + "SwitchCase": 1, + "ignoreComments": true + }], + "@stylistic/key-spacing": ["warn", { "beforeColon": false, "afterColon": true }], + "@stylistic/keyword-spacing": ["warn", { "before": true }], + "@stylistic/linebreak-style": ["error", "unix"], + "@stylistic/lines-between-class-members": ["warn", "always", { "exceptAfterSingleLine": true }], + "@stylistic/max-len": ["warn", { + "code": 80, + "ignoreComments": true, + "ignoreTrailingComments": true, + "ignoreUrls": true + }], + "@stylistic/multiline-ternary": ["warn", "always-multiline"], + "@stylistic/new-parens": ["error"], + "@stylistic/no-mixed-operators": ["warn"], + "@stylistic/no-mixed-spaces-and-tabs": ["error"], + "@stylistic/no-multi-spaces": ["error"], + "@stylistic/no-tabs": ["error"], + "@stylistic/no-trailing-spaces": ["error"], + "@stylistic/no-whitespace-before-property": ["warn"], + "@stylistic/nonblock-statement-body-position": ["error", "below"], + "@stylistic/object-curly-newline": ["warn", { "consistent": true }], + "@stylistic/object-curly-spacing": ["warn", "always"], + "@stylistic/operator-linebreak": ["warn", "after"], + "@stylistic/padded-blocks": ["error", "never"], + "@stylistic/padding-line-between-statements": ["warn", + { "blankLine": "always", "prev": "*", "next": "return" }, + { "blankLine": "always", "prev": ["const", "let", "var"], "next": "*"}, + { "blankLine": "any", "prev": ["const", "let", "var"], "next": ["const", "let", "var"]}, + { "blankLine": "always", "prev": ["case", "default"], "next": "*" } + ], + "@stylistic/quote-props": ["error", "consistent-as-needed"], + "@stylistic/quotes": ["error", "single", { "avoidEscape": true }], + "@stylistic/semi": ["error", "always"], + "@stylistic/semi-spacing": ["warn"], + "@stylistic/space-before-blocks": ["warn"], + "@stylistic/space-before-function-paren": ["warn", "never"], + "@stylistic/space-infix-ops": ["warn"], + "@stylistic/spaced-comment": ["warn", "always"], + "@stylistic/switch-colon-spacing": ["warn"], + "@stylistic/wrap-regex": ["warn"] }, "globals": { - "Tablet": "readonly", - "Pointers": "readonly", - "Brightness": "readonly", "ARGV": "readonly", "imports": "readonly", "print": "readonly", diff --git a/devices/wim/config/ags/js/applauncher/app-item.js b/devices/wim/config/ags/js/applauncher/app-item.js index f8ea26da..94bb3d32 100644 --- a/devices/wim/config/ags/js/applauncher/app-item.js +++ b/devices/wim/config/ags/js/applauncher/app-item.js @@ -1,13 +1,14 @@ -import Hyprland from 'resource:///com/github/Aylur/ags/service/hyprland.js'; import App from 'resource:///com/github/Aylur/ags/app.js'; -import Widget from 'resource:///com/github/Aylur/ags/widget.js'; +import Hyprland from 'resource:///com/github/Aylur/ags/service/hyprland.js'; + +import { Box, Button, Icon, Label } from 'resource:///com/github/Aylur/ags/widget.js'; import { lookUpIcon } from 'resource:///com/github/Aylur/ags/utils.js'; import Separator from '../misc/separator.js'; -export default app => { - const title = Widget.Label({ +export default (app) => { + const title = Label({ class_name: 'title', label: app.name, xalign: 0, @@ -15,7 +16,7 @@ export default app => { truncate: 'end', }); - const description = Widget.Label({ + const description = Label({ class_name: 'description', label: app.description || '', wrap: true, @@ -24,28 +25,42 @@ export default app => { vpack: 'center', }); - const icon = Widget.Icon({ - icon: lookUpIcon(app.icon_name) ? app.icon_name : - app.app.get_string('Icon') !== 'nix-snowflake' ? app.app.get_string('Icon') : '', + const icon = Icon({ + icon: lookUpIcon(app.icon_name) ? + app.icon_name : + app.app.get_string('Icon') === 'nix-snowflake' ? + '' : + app.app.get_string('Icon'), size: 42, }); - const textBox = Widget.Box({ + const textBox = Box({ vertical: true, vpack: 'center', children: [title, description], }); - return Widget.Button({ + const ENTRY_SPACING = 16; + + return Button({ class_name: 'app', - setup: self => self.app = app, + + setup: (self) => { + self.app = app; + }, + on_clicked: () => { App.closeWindow('applauncher'); Hyprland.sendMessage(`dispatch exec sh -c ${app.executable}`); ++app.frequency; }, - child: Widget.Box({ - children: [icon, Separator(16), textBox], + + child: Box({ + children: [ + icon, + Separator(ENTRY_SPACING), + textBox, + ], }), }); }; diff --git a/devices/wim/config/ags/js/applauncher/main.js b/devices/wim/config/ags/js/applauncher/main.js index b79df862..1cb2ac5d 100644 --- a/devices/wim/config/ags/js/applauncher/main.js +++ b/devices/wim/config/ags/js/applauncher/main.js @@ -1,4 +1,4 @@ -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 Hyprland from 'resource:///com/github/Aylur/ags/service/hyprland.js'; @@ -10,17 +10,20 @@ import AppItem from './app-item.js'; const Applauncher = ({ window_name = 'applauncher' } = {}) => { + const ICON_SEPARATION = 4; + const children = () => [ - ...Applications.query('').flatMap(app => { + ...Applications.query('').flatMap((app) => { const item = AppItem(app); + return [ - Separator(4, { + Separator(ICON_SEPARATION, { binds: [['visible', item, 'visible']], }), item, ]; }), - Separator(4), + Separator(ICON_SEPARATION), ]; const list = Box({ @@ -28,43 +31,46 @@ const Applauncher = ({ window_name = 'applauncher' } = {}) => { children: children(), }); - const entry = Entry({ - hexpand: true, - placeholder_text: 'Search', + const placeholder = Label({ + label: " Couldn't find a match", + className: 'placeholder', + }); - // set some text so on-change works the first time + const entry = Entry({ + // Set some text so on-change works the first time text: '-', + hexpand: true, + on_accept: ({ text }) => { - const list = Applications.query(text || ''); - if (list[0]) { + const appList = Applications.query(text || ''); + + if (appList[0]) { App.toggleWindow(window_name); - Hyprland.sendMessage(`dispatch exec sh -c ${list[0]}`); - ++list[0].frequency; + Hyprland.sendMessage(`dispatch exec sh -c ${appList[0]}`); + ++appList[0].frequency; } }, + on_change: ({ text }) => { let visibleApps = 0; - list.children.map(item => { + + list.children.forEach((item) => { if (item.app) { item.visible = item.app.match(text); - if (item.app.match(text)) + if (item.app.match(text)) { ++visibleApps; + } } }); placeholder.visible = visibleApps <= 0; }, }); - const placeholder = Label({ - label: " Couldn't find a match", - className: 'placeholder', - }); - - return Box({ className: 'applauncher', vertical: true, + children: [ Box({ className: 'header', @@ -82,15 +88,20 @@ const Applauncher = ({ window_name = 'applauncher' } = {}) => { }), }), ], + connections: [[App, (_, name, visible) => { - if (name !== window_name) + if (name !== window_name) { return; + } entry.text = ''; - if (visible) + + if (visible) { entry.grab_focus(); - else + } + else { list.children = children(); + } }]], }); }; diff --git a/devices/wim/config/ags/js/bar/buttons/audio.js b/devices/wim/config/ags/js/bar/buttons/audio.js index 4537517f..b6e9880a 100644 --- a/devices/wim/config/ags/js/bar/buttons/audio.js +++ b/devices/wim/config/ags/js/bar/buttons/audio.js @@ -1,33 +1,39 @@ import Audio from 'resource:///com/github/Aylur/ags/service/audio.js'; + import { Label, Box, Icon } from 'resource:///com/github/Aylur/ags/widget.js'; import { execAsync } from 'resource:///com/github/Aylur/ags/utils.js'; import { SpeakerIcon } from '../../misc/audio-icons.js'; import Separator from '../../misc/separator.js'; -import EventBox from '../../misc/cursorbox.js'; +import EventBox from '../../misc/cursorbox.js'; -const SpeakerIndicator = props => Icon({ +const SpeakerIndicator = (props) => Icon({ ...props, binds: [['icon', SpeakerIcon, 'value']], }); -const SpeakerPercentLabel = props => Label({ +const SpeakerPercentLabel = (props) => Label({ ...props, - connections: [[Audio, label => { - if (Audio.speaker) - label.label = Math.round(Audio.speaker.volume * 100) + '%'; + connections: [[Audio, (label) => { + if (Audio.speaker) { + label.label = `${Math.round(Audio.speaker.volume * 100)}%`; + } }, 'speaker-changed']], }); +const SPACING = 5; + export default () => EventBox({ - onPrimaryClickRelease: () => execAsync(['pavucontrol']).catch(print), className: 'toggle-off', + + onPrimaryClickRelease: () => execAsync(['pavucontrol']).catch(print), + child: Box({ className: 'audio', children: [ SpeakerIndicator(), - Separator(5), + Separator(SPACING), SpeakerPercentLabel(), ], }), diff --git a/devices/wim/config/ags/js/bar/buttons/battery.js b/devices/wim/config/ags/js/bar/buttons/battery.js index 337d444a..e0f81e6b 100644 --- a/devices/wim/config/ags/js/bar/buttons/battery.js +++ b/devices/wim/config/ags/js/bar/buttons/battery.js @@ -1,30 +1,41 @@ import Battery from 'resource:///com/github/Aylur/ags/service/battery.js'; + import { Label, Icon, Box } from 'resource:///com/github/Aylur/ags/widget.js'; import Separator from '../../misc/separator.js'; +const LOW_BATT = 20; + const Indicator = () => Icon({ className: 'battery-indicator', + binds: [['icon', Battery, 'icon-name']], - connections: [[Battery, self => { + + connections: [[Battery, (self) => { self.toggleClassName('charging', Battery.charging); self.toggleClassName('charged', Battery.charged); - self.toggleClassName('low', Battery.percent < 20); + self.toggleClassName('low', Battery.percent < LOW_BATT); }]], }); -const LevelLabel = props => Label({ +const LevelLabel = (props) => Label({ ...props, className: 'label', - connections: [[Battery, self => self.label = `${Battery.percent}%`]], + + connections: [[Battery, (self) => { + self.label = `${Battery.percent}%`; + }]], }); +const SPACING = 5; + export default () => Box({ className: 'toggle-off battery', + children: [ Indicator(), - Separator(5), + Separator(SPACING), LevelLabel(), ], }); diff --git a/devices/wim/config/ags/js/bar/buttons/brightness.js b/devices/wim/config/ags/js/bar/buttons/brightness.js index 126c6ab5..c28fca9d 100644 --- a/devices/wim/config/ags/js/bar/buttons/brightness.js +++ b/devices/wim/config/ags/js/bar/buttons/brightness.js @@ -1,22 +1,30 @@ import { ProgressBar, Overlay, Box } from 'resource:///com/github/Aylur/ags/widget.js'; +import Brightness from '../../../services/brightness.js'; import Separator from '../../misc/separator.js'; -import Heart from './heart.js'; +import Heart from './heart.js'; + +const SPACING = 25; +const BAR_CUTOFF = 0.33; export default () => Overlay({ tooltipText: 'Brightness', + child: ProgressBar({ className: 'toggle-off brightness', - connections: [[Brightness, self => { - self.value = Brightness.screen > 0.33 ? Brightness.screen : 0.33; + connections: [[Brightness, (self) => { + self.value = Brightness.screen > BAR_CUTOFF ? + Brightness.screen : + BAR_CUTOFF; }, 'screen']], }), + overlays: [ Box({ css: 'color: #CBA6F7;', children: [ - Separator(25), + Separator(SPACING), Heart(), ], }), diff --git a/devices/wim/config/ags/js/bar/buttons/clock.js b/devices/wim/config/ags/js/bar/buttons/clock.js index a3f73dff..a9394041 100644 --- a/devices/wim/config/ags/js/bar/buttons/clock.js +++ b/devices/wim/config/ags/js/bar/buttons/clock.js @@ -1,5 +1,6 @@ import App from 'resource:///com/github/Aylur/ags/app.js'; -import { Box, Label } from 'resource:///com/github/Aylur/ags/widget.js'; + +import { Label } from 'resource:///com/github/Aylur/ags/widget.js'; import GLib from 'gi://GLib'; const { DateTime } = GLib; @@ -10,29 +11,33 @@ import EventBox from '../../misc/cursorbox.js'; const ClockModule = ({ interval = 1000, ...props -}) => Label({ - ...props, - className: 'clock', - connections: [ - [interval, self => { - var time = DateTime.new_now_local(); +} = {}) => { + return Label({ + ...props, + className: 'clock', + + connections: [[interval, (self) => { + const time = DateTime.new_now_local(); + self.label = time.format('%a. ') + - time.get_day_of_month() + - time.format(' %b. %H:%M'); - }], - ], -}); + time.get_day_of_month() + + time.format(' %b. %H:%M'); + }]], + }); +}; export default () => EventBox({ className: 'toggle-off', + onPrimaryClickRelease: () => App.toggleWindow('calendar'), + connections: [ [App, (self, windowName, visible) => { - if (windowName == 'calendar') + if (windowName === 'calendar') { self.toggleClassName('toggle-on', visible); + } }], ], - child: Box({ - child: ClockModule({}), - }), + + child: ClockModule(), }); diff --git a/devices/wim/config/ags/js/bar/buttons/current-window.js b/devices/wim/config/ags/js/bar/buttons/current-window.js index 46919f63..fd00002d 100644 --- a/devices/wim/config/ags/js/bar/buttons/current-window.js +++ b/devices/wim/config/ags/js/bar/buttons/current-window.js @@ -1,4 +1,5 @@ import Hyprland from 'resource:///com/github/Aylur/ags/service/hyprland.js'; + import { Label } from 'resource:///com/github/Aylur/ags/widget.js'; diff --git a/devices/wim/config/ags/js/bar/buttons/heart.js b/devices/wim/config/ags/js/bar/buttons/heart.js index 37a86b90..65609a28 100644 --- a/devices/wim/config/ags/js/bar/buttons/heart.js +++ b/devices/wim/config/ags/js/bar/buttons/heart.js @@ -17,10 +17,13 @@ export default () => EventBox({ child: Label({ label: '', - setup: self => { + + setup: (self) => { subprocess( ['bash', '-c', 'tail -f /home/matt/.config/.heart'], - output => self.label = ' ' + output, + (output) => { + self.label = ` ${output}`; + }, ); }, }), diff --git a/devices/wim/config/ags/js/bar/buttons/keyboard-layout.js b/devices/wim/config/ags/js/bar/buttons/keyboard-layout.js index 08a1b571..ea4bcc2a 100644 --- a/devices/wim/config/ags/js/bar/buttons/keyboard-layout.js +++ b/devices/wim/config/ags/js/bar/buttons/keyboard-layout.js @@ -1,4 +1,5 @@ import Hyprland from 'resource:///com/github/Aylur/ags/service/hyprland.js'; + import { Box, Icon, Label } from 'resource:///com/github/Aylur/ags/widget.js'; const DEFAULT_KB = 'at-translated-set-2-keyboard'; @@ -7,17 +8,29 @@ const DEFAULT_KB = 'at-translated-set-2-keyboard'; export default () => Box({ className: 'toggle-off', css: 'padding: 0 10px;', + children: [ Icon({ icon: 'input-keyboard-symbolic', css: 'margin-right: 4px;', }), + Label({ connections: [[Hyprland, (self, _n, layout) => { - if (!layout) { - Hyprland.sendMessage('j/devices').then(obj => { - const kb = JSON.parse(obj)['keyboards'] - .find(val => val.name === DEFAULT_KB); + if (layout) { + if (layout === 'error') { + return; + } + + const shortName = layout.match(/\(([A-Za-z]+)\)/); + + self.label = shortName ? shortName[1] : layout; + } + else { + // At launch, kb layout is undefined + Hyprland.sendMessage('j/devices').then((obj) => { + const kb = JSON.parse(obj).keyboards + .find((val) => val.name === DEFAULT_KB); layout = kb['active_keymap']; @@ -26,14 +39,6 @@ export default () => Box({ self.label = shortName ? shortName[1] : layout; }).catch(print); } - else { - if (layout === 'error') - return; - - const shortName = layout.match(/\(([A-Za-z]+)\)/); - - self.label = shortName ? shortName[1] : layout; - } }, 'keyboard-layout']], }), ], diff --git a/devices/wim/config/ags/js/bar/buttons/notif-button.js b/devices/wim/config/ags/js/bar/buttons/notif-button.js index 51e57dc8..4446a02c 100644 --- a/devices/wim/config/ags/js/bar/buttons/notif-button.js +++ b/devices/wim/config/ags/js/bar/buttons/notif-button.js @@ -1,44 +1,52 @@ -import App from 'resource:///com/github/Aylur/ags/app.js'; +import App from 'resource:///com/github/Aylur/ags/app.js'; import Notifications from 'resource:///com/github/Aylur/ags/service/notifications.js'; + import { Box, Icon, Label } from 'resource:///com/github/Aylur/ags/widget.js'; import Separator from '../../misc/separator.js'; -import EventBox from '../../misc/cursorbox.js'; +import EventBox from '../../misc/cursorbox.js'; + +const L_PADDING = 28; +const R_PADDING = 8; export default () => EventBox({ className: 'toggle-off', + onPrimaryClickRelease: () => App.toggleWindow('notification-center'), + connections: [[App, (self, windowName, visible) => { - if (windowName == 'notification-center') + if (windowName === 'notification-center') { self.toggleClassName('toggle-on', visible); + } }]], + child: Box({ className: 'notif-panel', vertical: false, + children: [ - Separator(28), + Separator(L_PADDING), Icon({ - connections: [[Notifications, self => { + connections: [[Notifications, (self) => { if (Notifications.dnd) { self.icon = 'notification-disabled-symbolic'; } + else if (Notifications.notifications.length > 0) { + self.icon = 'notification-new-symbolic'; + } else { - if (Notifications.notifications.length > 0) - self.icon = 'notification-new-symbolic'; - else - self.icon = 'notification-symbolic'; + self.icon = 'notification-symbolic'; } }]], }), - Separator(8), + Separator(R_PADDING), Label({ - binds: [ - ['label', Notifications, 'notifications', n => String(n.length)], - ], + binds: [['label', Notifications, 'notifications', + (n) => String(n.length)]], }), ], diff --git a/devices/wim/config/ags/js/bar/buttons/osk-toggle.js b/devices/wim/config/ags/js/bar/buttons/osk-toggle.js index 8ea37445..7670a3bc 100644 --- a/devices/wim/config/ags/js/bar/buttons/osk-toggle.js +++ b/devices/wim/config/ags/js/bar/buttons/osk-toggle.js @@ -1,13 +1,15 @@ import { Box, Label } from 'resource:///com/github/Aylur/ags/widget.js'; +import Tablet from '../../../services/tablet.js'; import EventBox from '../../misc/cursorbox.js'; export default () => EventBox({ className: 'toggle-off', + onPrimaryClickRelease: () => Tablet.toggleOsk(), - connections: [[Tablet, self => { + connections: [[Tablet, (self) => { self.toggleClassName('toggle-on', Tablet.oskState); }, 'osk-toggled']], diff --git a/devices/wim/config/ags/js/bar/buttons/quick-settings.js b/devices/wim/config/ags/js/bar/buttons/quick-settings.js index bfafedec..a22384b7 100644 --- a/devices/wim/config/ags/js/bar/buttons/quick-settings.js +++ b/devices/wim/config/ags/js/bar/buttons/quick-settings.js @@ -1,4 +1,5 @@ import App from 'resource:///com/github/Aylur/ags/app.js'; + import { Box, Label } from 'resource:///com/github/Aylur/ags/widget.js'; import EventBox from '../../misc/cursorbox.js'; @@ -6,11 +7,15 @@ import EventBox from '../../misc/cursorbox.js'; export default () => EventBox({ className: 'toggle-off', + onPrimaryClickRelease: () => App.toggleWindow('quick-settings'), + connections: [[App, (self, windowName, visible) => { - if (windowName == 'quick-settings') + if (windowName === 'quick-settings') { self.toggleClassName('toggle-on', visible); + } }]], + child: Box({ className: 'quick-settings-toggle', vertical: false, diff --git a/devices/wim/config/ags/js/bar/buttons/systray.js b/devices/wim/config/ags/js/bar/buttons/systray.js index 611366d0..cb68f60b 100644 --- a/devices/wim/config/ags/js/bar/buttons/systray.js +++ b/devices/wim/config/ags/js/bar/buttons/systray.js @@ -1,17 +1,24 @@ import SystemTray from 'resource:///com/github/Aylur/ags/service/systemtray.js'; + import { timeout } from 'resource:///com/github/Aylur/ags/utils.js'; import { Box, Icon, MenuItem, MenuBar, Revealer } from 'resource:///com/github/Aylur/ags/widget.js'; import Separator from '../../misc/separator.js'; +const REVEAL_DURATION = 500; +const SPACING = 12; -const SysTrayItem = item => { - if (item.id === 'spotify-client') + +const SysTrayItem = (item) => { + if (item.id === 'spotify-client') { return; + } return MenuItem({ child: Revealer({ transition: 'slide_right', + transitionDuration: REVEAL_DURATION, + child: Icon({ size: 24, binds: [['icon', item, 'icon']], @@ -23,18 +30,22 @@ const SysTrayItem = item => { }; const SysTray = () => MenuBar({ - setup: self => { + setup: (self) => { self.items = new Map(); - self.onAdded = id => { + self.onAdded = (id) => { const item = SystemTray.getItem(id); - if (self.items.has(id) || !item) + + if (self.items.has(id) || !item) { return; + } const w = SysTrayItem(item); + // Early return if item is in blocklist - if (!w) + if (!w) { return; + } self.items.set(id, w); self.add(w); @@ -42,12 +53,13 @@ const SysTray = () => MenuBar({ w.child.revealChild = true; }; - self.onRemoved = id => { - if (!self.items.has(id)) + self.onRemoved = (id) => { + if (!self.items.has(id)) { return; + } self.items.get(id).child.revealChild = false; - timeout(400, () => { + timeout(REVEAL_DURATION, () => { self.items.get(id).destroy(); self.items.delete(id); }); @@ -59,20 +71,25 @@ const SysTray = () => MenuBar({ ], }); -export default () => Revealer({ - transition: 'slide_right', - connections: [[SystemTray, rev => { - rev.revealChild = rev.child.children[0].get_children().length > 0; - }]], - child: Box({ - children: [ - Box({ - className: 'sys-tray', - children: [ - SysTray(), - ], - }), - Separator(12), - ], - }), -}); +export default () => { + const systray = SysTray(); + + return Revealer({ + transition: 'slide_right', + + connections: [[SystemTray, (rev) => { + rev.revealChild = systray.get_children().length > 0; + }]], + + child: Box({ + children: [ + Box({ + className: 'sys-tray', + children: [systray], + }), + + Separator(SPACING), + ], + }), + }); +}; diff --git a/devices/wim/config/ags/js/bar/buttons/tablet-toggle.js b/devices/wim/config/ags/js/bar/buttons/tablet-toggle.js index feeed33c..85bc9c77 100644 --- a/devices/wim/config/ags/js/bar/buttons/tablet-toggle.js +++ b/devices/wim/config/ags/js/bar/buttons/tablet-toggle.js @@ -1,13 +1,15 @@ import { Box, Label } from 'resource:///com/github/Aylur/ags/widget.js'; +import Tablet from '../../../services/tablet.js'; import EventBox from '../../misc/cursorbox.js'; export default () => EventBox({ className: 'toggle-off', + onPrimaryClickRelease: () => Tablet.toggleMode(), - connections: [[Tablet, self => { + connections: [[Tablet, (self) => { self.toggleClassName('toggle-on', Tablet.tabletMode); }, 'mode-toggled']], diff --git a/devices/wim/config/ags/js/bar/buttons/workspaces.js b/devices/wim/config/ags/js/bar/buttons/workspaces.js index a5b06092..697a8550 100644 --- a/devices/wim/config/ags/js/bar/buttons/workspaces.js +++ b/devices/wim/config/ags/js/bar/buttons/workspaces.js @@ -1,24 +1,33 @@ import Hyprland from 'resource:///com/github/Aylur/ags/service/hyprland.js'; + import { timeout } from 'resource:///com/github/Aylur/ags/utils.js'; import { Box, Overlay, Revealer } from 'resource:///com/github/Aylur/ags/widget.js'; import EventBox from '../../misc/cursorbox.js'; +const URGENT_DURATION = 1000; -const Workspace = ({ i } = {}) => - Revealer({ + +const Workspace = ({ i } = {}) => { + return Revealer({ transition: 'slide_right', properties: [['id', i]], child: EventBox({ tooltipText: `${i}`, - onPrimaryClickRelease: () => Hyprland.sendMessage(`dispatch workspace ${i}`), + + onPrimaryClickRelease: () => { + Hyprland.sendMessage(`dispatch workspace ${i}`); + }, + child: Box({ vpack: 'center', className: 'button', - setup: self => { - self.update = addr => { + + setup: (self) => { + self.update = (addr) => { const occupied = Hyprland.getWorkspace(i)?.windows > 0; + self.toggleClassName('occupied', occupied); self.toggleClassName('empty', !occupied); @@ -27,36 +36,47 @@ const Workspace = ({ i } = {}) => self.toggleClassName('urgent', true); // Only show for a sec when urgent is current workspace - if (Hyprland.active.workspace.id === i) - timeout(1000, () => self.toggleClassName('urgent', false)); + if (Hyprland.active.workspace.id === i) { + timeout(URGENT_DURATION, () => { + self.toggleClassName('urgent', false); + }); + } } }; }, + connections: [ - [Hyprland, self => self.update()], + [Hyprland, (self) => self.update()], // Deal with urgent windows - [Hyprland, (self, addr) => self.update(addr), 'urgent-window'], - [Hyprland.active.workspace, self => { - if (Hyprland.active.workspace.id === i) + [Hyprland, (self, a) => self.update(a), 'urgent-window'], + [Hyprland.active.workspace, (self) => { + if (Hyprland.active.workspace.id === i) { self.toggleClassName('urgent', false); + } }], ], }), }), }); +}; export default () => { - const updateHighlight = () => { + const L_PADDING = 16; + const WS_WIDTH = 30; + + const updateHighlight = (self) => { const currentId = Hyprland.active.workspace.id; - const indicators = highlight.get_parent().get_children()[0].child.children; - const currentIndex = indicators.findIndex(w => w._id == currentId); + const indicators = self.get_parent().get_children()[0].child.children; + const currentIndex = indicators.findIndex((w) => w._id === currentId); - if (currentIndex < 0) + if (currentIndex < 0) { return; + } - highlight.setCss(`margin-left: ${16 + currentIndex * 30}px`); + self.setCss(`margin-left: ${L_PADDING + (currentIndex * WS_WIDTH)}px`); }; + const highlight = Box({ vpack: 'center', hpack: 'start', @@ -70,21 +90,28 @@ export default () => { child: EventBox({ child: Box({ className: 'workspaces', + properties: [ ['workspaces'], - ['refresh', self => { - self.children.forEach(rev => rev.reveal_child = false); - self._workspaces.forEach(ws => { + ['refresh', (self) => { + self.children.forEach((rev) => { + rev.reveal_child = false; + }); + self._workspaces.forEach((ws) => { ws.revealChild = true; }); }], - ['updateWorkspaces', self => { - Hyprland.workspaces.forEach(ws => { - const currentWs = self.children.find(ch => ch._id == ws.id); - if (!currentWs && ws.id > 0) + ['updateWorkspaces', (self) => { + Hyprland.workspaces.forEach((ws) => { + const currentWs = self.children.find((ch) => { + return ch._id === ws.id; + }); + + if (!currentWs && ws.id > 0) { self.add(Workspace({ i: ws.id })); + } }); self.show_all(); @@ -94,16 +121,21 @@ export default () => { }); }], ], - connections: [[Hyprland, self => { - self._workspaces = self.children.filter(ch => { - return Hyprland.workspaces.find(ws => ws.id == ch._id); + + connections: [[Hyprland, (self) => { + self._workspaces = self.children.filter((ch) => { + return Hyprland.workspaces.find((ws) => { + return ws.id === ch._id; + }); }).sort((a, b) => a._id - b._id); self._updateWorkspaces(self); self._refresh(self); // Make sure the highlight doesn't go too far - timeout(10, updateHighlight); + const TEMP_TIMEOUT = 10; + + timeout(TEMP_TIMEOUT, () => updateHighlight(highlight)); }]], }), }), diff --git a/devices/wim/config/ags/js/bar/fullscreen.js b/devices/wim/config/ags/js/bar/fullscreen.js index c8f2aeaf..4eb3fa4f 100644 --- a/devices/wim/config/ags/js/bar/fullscreen.js +++ b/devices/wim/config/ags/js/bar/fullscreen.js @@ -4,23 +4,25 @@ import Variable from 'resource:///com/github/Aylur/ags/variable.js'; import { Box, EventBox, Revealer, Window } from 'resource:///com/github/Aylur/ags/widget.js'; -const BarCloser = variable => Window({ +const BarCloser = (variable) => Window({ name: 'bar-closer', visible: false, anchor: ['top', 'bottom', 'left', 'right'], layer: 'overlay', + child: EventBox({ - onHover: self => { + onHover: (self) => { variable.value = false; self.get_parent().visible = false; }, + child: Box({ css: 'padding: 1px', }), }), }); -export default props => { +export default (props) => { const Revealed = Variable(true); const barCloser = BarCloser(Revealed); @@ -28,28 +30,39 @@ export default props => { css: 'min-height: 1px', hexpand: true, vertical: true, + connections: [ [Hyprland.active, () => { - const workspace = Hyprland.getWorkspace(Hyprland.active.workspace.id); + const workspace = Hyprland.getWorkspace( + Hyprland.active.workspace.id, + ); + Revealed.value = !workspace?.hasfullscreen; }], - [Hyprland, (_, fullscreen) => Revealed.value = !fullscreen, 'fullscreen'], + + [Hyprland, (_, fullscreen) => { + Revealed.value = !fullscreen; + }, 'fullscreen'], ], + children: [ Revealer({ + ...props, transition: 'slide_down', revealChild: true, + binds: [['revealChild', Revealed, 'value']], - ...props, }), Revealer({ - binds: [['revealChild', Revealed, 'value', v => !v]], + binds: [['revealChild', Revealed, 'value', (v) => !v]], + child: EventBox({ onHover: () => { barCloser.visible = true; Revealed.value = true; }, + child: Box({ css: 'min-height: 5px;', }), diff --git a/devices/wim/config/ags/js/bar/main.js b/devices/wim/config/ags/js/bar/main.js index 92efd5bc..d1dea957 100644 --- a/devices/wim/config/ags/js/bar/main.js +++ b/devices/wim/config/ags/js/bar/main.js @@ -1,21 +1,24 @@ import { Window, CenterBox, Box } from 'resource:///com/github/Aylur/ags/widget.js'; -import Separator from '../misc/separator.js'; -import CurrentWindow from './buttons/current-window.js'; -import Workspaces from './buttons/workspaces.js'; -import OskToggle from './buttons/osk-toggle.js'; -import TabletToggle from './buttons/tablet-toggle.js'; -import QsToggle from './buttons/quick-settings.js'; -import NotifButton from './buttons/notif-button.js'; -import Clock from './buttons/clock.js'; -import SysTray from './buttons/systray.js'; -import Battery from './buttons/battery.js'; -import Brightness from './buttons/brightness.js'; -import Audio from './buttons/audio.js'; +import Separator from '../misc/separator.js'; + +import Audio from './buttons/audio.js'; +import Battery from './buttons/battery.js'; +import Brightness from './buttons/brightness.js'; +import Clock from './buttons/clock.js'; +import CurrentWindow from './buttons/current-window.js'; import KeyboardLayout from './buttons/keyboard-layout.js'; +import NotifButton from './buttons/notif-button.js'; +import OskToggle from './buttons/osk-toggle.js'; +import QsToggle from './buttons/quick-settings.js'; +import SysTray from './buttons/systray.js'; +import TabletToggle from './buttons/tablet-toggle.js'; +import Workspaces from './buttons/workspaces.js'; import BarReveal from './fullscreen.js'; +const SPACING = 12; + export default () => Window({ name: 'bar', @@ -33,21 +36,21 @@ export default () => Window({ OskToggle(), - Separator(12), + Separator(SPACING), TabletToggle(), - Separator(12), + Separator(SPACING), SysTray(), Audio(), - Separator(12), + Separator(SPACING), Brightness(), - Separator(12), + Separator(SPACING), Workspaces(), @@ -56,11 +59,11 @@ export default () => Window({ centerWidget: Box({ children: [ - Separator(12), + Separator(SPACING), CurrentWindow(), - Separator(12), + Separator(SPACING), ], }), @@ -69,19 +72,19 @@ export default () => Window({ children: [ Battery(), - Separator(12), + Separator(SPACING), KeyboardLayout(), - Separator(12), + Separator(SPACING), Clock(), - Separator(12), + Separator(SPACING), NotifButton(), - Separator(12), + Separator(SPACING), QsToggle(), ], diff --git a/devices/wim/config/ags/js/corners/main.js b/devices/wim/config/ags/js/corners/main.js index 14df711a..5eb008b3 100644 --- a/devices/wim/config/ags/js/corners/main.js +++ b/devices/wim/config/ags/js/corners/main.js @@ -11,6 +11,7 @@ const TopLeft = () => Window({ visible: true, child: RoundedCorner('topleft'), }); + const TopRight = () => Window({ name: 'cornertr', layer: 'overlay', @@ -19,6 +20,7 @@ const TopRight = () => Window({ visible: true, child: RoundedCorner('topright'), }); + const BottomLeft = () => Window({ name: 'cornerbl', layer: 'overlay', @@ -27,6 +29,7 @@ const BottomLeft = () => Window({ visible: true, child: RoundedCorner('bottomleft'), }); + const BottomRight = () => Window({ name: 'cornerbr', layer: 'overlay', diff --git a/devices/wim/config/ags/js/corners/screen-corners.js b/devices/wim/config/ags/js/corners/screen-corners.js index 5a52b540..fb4bdc92 100644 --- a/devices/wim/config/ags/js/corners/screen-corners.js +++ b/devices/wim/config/ags/js/corners/screen-corners.js @@ -1,4 +1,5 @@ import { Box, DrawingArea } from 'resource:///com/github/Aylur/ags/widget.js'; + import Gtk from 'gi://Gtk'; const Lang = imports.lang; @@ -7,37 +8,39 @@ export default ( css = 'background-color: black;', ) => Box({ hpack: place.includes('left') ? 'start' : 'end', - vpack: place.includes('top') ? 'start' : 'end', + vpack: place.includes('top') ? 'start' : 'end', css: ` padding: 1px; margin: - ${place.includes('top') ? '-1px' : '0'} - ${place.includes('right') ? '-1px' : '0'} + ${place.includes('top') ? '-1px' : '0'} + ${place.includes('right') ? '-1px' : '0'} ${place.includes('bottom') ? '-1px' : '0'} - ${place.includes('left') ? '-1px' : '0'}; - `, + ${place.includes('left') ? '-1px' : '0'}; + `, child: DrawingArea({ css: ` border-radius: 18px; border-width: 0.068rem; - ` + css, - setup: widget => { - const r = widget.get_style_context() + ${css} + `, + setup: (widget) => { + let r = widget.get_style_context() .get_property('border-radius', Gtk.StateFlags.NORMAL); widget.set_size_request(r, r); - widget.connect('draw', Lang.bind(widget, (widget, cr) => { + widget.connect('draw', Lang.bind(widget, (_, cr) => { const c = widget.get_style_context() .get_property('background-color', Gtk.StateFlags.NORMAL); - const r = widget.get_style_context() + r = widget.get_style_context() .get_property('border-radius', Gtk.StateFlags.NORMAL); const borderColor = widget.get_style_context() .get_property('color', Gtk.StateFlags.NORMAL); - // ur going to write border-width: something anyway + // Ur going to write border-width: something anyway const borderWidth = widget.get_style_context() .get_border(Gtk.StateFlags.NORMAL).left; + widget.set_size_request(r, r); switch (place) { diff --git a/devices/wim/config/ags/js/date.js b/devices/wim/config/ags/js/date.js index 954f4238..b4774531 100644 --- a/devices/wim/config/ags/js/date.js +++ b/devices/wim/config/ags/js/date.js @@ -25,7 +25,7 @@ const Time = () => Box({ Label({ className: 'content', label: 'hour', - connections: [[1000, self => { + connections: [[1000, (self) => { self.label = DateTime.new_now_local().format('%H'); }]], }), @@ -35,7 +35,7 @@ const Time = () => Box({ Label({ className: 'content', label: 'minute', - connections: [[1000, self => { + connections: [[1000, (self) => { self.label = DateTime.new_now_local().format('%M'); }]], }), @@ -49,8 +49,9 @@ const Time = () => Box({ child: Label({ css: 'font-size: 20px', label: 'complete date', - connections: [[1000, self => { - var time = DateTime.new_now_local(); + connections: [[1000, (self) => { + const time = DateTime.new_now_local(); + self.label = time.format('%A, %B ') + time.get_day_of_month() + time.format(', %Y'); @@ -70,9 +71,12 @@ const CalendarWidget = () => Box({ }), }); +const TOP_MARGIN = 6; +const RIGHT_MARGIN = 182; + export default () => PopupWindow({ anchor: ['top', 'right'], - margins: [6, 182, 0, 0], + margins: [TOP_MARGIN, RIGHT_MARGIN, 0, 0], name: 'calendar', child: Box({ className: 'date', diff --git a/devices/wim/config/ags/js/media-player/gesture.js b/devices/wim/config/ags/js/media-player/gesture.js index a08d5d68..e54df80f 100644 --- a/devices/wim/config/ags/js/media-player/gesture.js +++ b/devices/wim/config/ags/js/media-player/gesture.js @@ -5,7 +5,9 @@ import Gtk from 'gi://Gtk'; const MAX_OFFSET = 200; const OFFSCREEN = 500; -const TRANSITION = 'transition: margin 0.5s ease, opacity 3s ease;'; +const ANIM_DURATION = 500; +const TRANSITION = `transition: margin ${ANIM_DURATION}ms ease, + opacity 3s ease;`; export default ({ @@ -25,16 +27,18 @@ export default ({ // Have empty PlayerBox to define the size of the widget child: Box({ className: 'player' }), + connections: [ ...connections, - [gesture, overlay => { + [gesture, (overlay) => { // Don't allow gesture when only one player - if (overlay.list().length <= 1) + if (overlay.list().length <= 1) { return; + } overlay._dragging = true; - var offset = gesture.get_offset()[1]; + let offset = gesture.get_offset()[1]; const playerBox = overlay.list().at(-1); @@ -58,10 +62,11 @@ export default ({ } }, 'drag-update'], - [gesture, overlay => { + [gesture, (overlay) => { // Don't allow gesture when only one player - if (overlay.list().length <= 1) + if (overlay.list().length <= 1) { return; + } overlay._dragging = false; const offset = gesture.get_offset()[1]; @@ -92,7 +97,7 @@ export default ({ opacity: 0; ${playerBox._bgStyle}`); } - timeout(500, () => { + timeout(ANIM_DURATION, () => { // Put the player in the back after anim overlay.reorder_overlay(playerBox, 0); // Recenter player @@ -108,7 +113,9 @@ export default ({ }, 'drag-end'], ], })); - widget.child.list = () => widget.child.get_children().filter(ch => ch._bgStyle !== undefined); + + widget.child.list = () => widget.child.get_children() + .filter((ch) => ch._bgStyle); return widget; }; diff --git a/devices/wim/config/ags/js/media-player/mpris.js b/devices/wim/config/ags/js/media-player/mpris.js index 2345a4f7..49746d59 100644 --- a/devices/wim/config/ags/js/media-player/mpris.js +++ b/devices/wim/config/ags/js/media-player/mpris.js @@ -1,4 +1,5 @@ import Mpris from 'resource:///com/github/Aylur/ags/service/mpris.js'; + import { Button, Icon, Label, Stack, Slider, CenterBox, Box } from 'resource:///com/github/Aylur/ags/widget.js'; import { execAsync, lookUpIcon, readFileAsync } from 'resource:///com/github/Aylur/ags/utils.js'; @@ -29,16 +30,18 @@ const icons = { export const CoverArt = (player, props) => CenterBox({ ...props, vertical: true, + properties: [['bgStyle', '']], - setup: self => { + + setup: (self) => { // Give temp cover art readFileAsync(player.coverPath).catch(() => { if (!player.colors.value && !player.trackCoverUrl) { player.colors.value = { - 'imageAccent': '#6b4fa2', - 'buttonAccent': '#ecdcff', - 'buttonText': '#25005a', - 'hoverAccent': '#d4baff', + imageAccent: '#6b4fa2', + buttonAccent: '#ecdcff', + buttonText: '#25005a', + hoverAccent: '#d4baff', }; self._bgStyle = ` @@ -53,12 +56,14 @@ export const CoverArt = (player, props) => CenterBox({ } }); }, - connections: [[player, self => { + + connections: [[player, (self) => { execAsync(['bash', '-c', `[[ -f "${player.coverPath}" ]] && coloryou "${player.coverPath}" | grep -v Warning`]) - .then(out => { - if (!Mpris.players.find(p => player === p)) + .then((out) => { + if (!Mpris.players.find((p) => player === p)) { return; + } player.colors.value = JSON.parse(out); @@ -71,11 +76,13 @@ export const CoverArt = (player, props) => CenterBox({ background-position: center; `; - if (!self.get_parent()._dragging) + if (!self.get_parent()._dragging) { self.setCss(self._bgStyle); - }).catch(err => { - if (err !== '') + } + }).catch((err) => { + if (err !== '') { print(err); + } }); }]], }); @@ -87,6 +94,7 @@ export const TitleLabel = (player, props) => Label({ truncate: 'end', justification: 'left', className: 'title', + binds: [['label', player, 'track-title']], }); @@ -97,7 +105,8 @@ export const ArtistLabel = (player, props) => Label({ truncate: 'end', justification: 'left', className: 'artist', - binds: [['label', player, 'track-artists', a => a.join(', ') || '']], + + binds: [['label', player, 'track-artists', (a) => a.join(', ') || '']], }); export const PlayerIcon = (player, { symbolic = true, ...props } = {}) => { @@ -107,28 +116,34 @@ export const PlayerIcon = (player, { symbolic = true, ...props } = {}) => { className: 'player-icon', size: 32, tooltipText: player.identity || '', - connections: [[player, self => { + + connections: [[player, (self) => { const name = `${player.entry}${symbolic ? '-symbolic' : ''}`; - lookUpIcon(name) ? self.icon = name - : self.icon = icons.mpris.fallback; + + lookUpIcon(name) ? + self.icon = name : + self.icon = icons.mpris.fallback; }]], }); + // Multiple player indicators return Box({ properties: [['overlay']], - connections: [[Mpris, self => { - if (!self._overlay) + connections: [[Mpris, (self) => { + if (!self._overlay) { self._overlay = self.get_parent().get_parent().get_parent(); + } const overlays = self._overlay.list(); - const player = overlays.find(overlay => { + const playerWidget = overlays.find((overlay) => { return overlay === self.get_parent().get_parent(); }); - const index = overlays.indexOf(player); + const index = overlays.indexOf(playerWidget); const children = []; + for (let i = 0; i < overlays.length; ++i) { if (i === index) { children.push(Separator(2)); @@ -152,22 +167,27 @@ export const PositionSlider = (player, props) => Slider({ vpack: 'center', hexpand: true, drawValue: false, + onChange: ({ value }) => { player.position = player.length * value; }, - properties: [['update', slider => { + + properties: [['update', (slider) => { if (!slider.dragging) { slider.visible = player.length > 0; - if (player.length > 0) + if (player.length > 0) { slider.value = player.position / player.length; + } } }]], + connections: [ - [player, s => s._update(s), 'position'], - [1000, s => s._update(s)], - [player.colors, s => { - const c = player.colors.value; + [1000, (s) => s._update(s)], + [player, (s) => s._update(s), 'position'], + [player.colors, (s) => { if (player.colors.value) { + const c = player.colors.value; + s.setCss(` highlight { background-color: ${c.buttonAccent}; } slider { background-color: ${c.buttonAccent}; } @@ -177,21 +197,27 @@ export const PositionSlider = (player, props) => Slider({ } }], - ['button-press-event', s => { s.cursor = 'grabbing'; }], - ['button-release-event', s => { s.cursor = 'pointer'; }], + ['button-press-event', (s) => { + s.cursor = 'grabbing'; + }], + ['button-release-event', (s) => { + s.cursor = 'pointer'; + }], ], }); const PlayerButton = ({ player, items, onClick, prop }) => EventBox({ child: Button({ - child: Stack({ items }), - onPrimaryClickRelease: () => player[onClick](), properties: [['hovered', false]], - onHover: self => { + child: Stack({ items }), + + onPrimaryClickRelease: () => player[onClick](), + + onHover: (self) => { self._hovered = true; - if (prop == 'playBackStatus') { - items.forEach(item => { + if (prop === 'playBackStatus') { + items.forEach((item) => { item[1].setCss(` background-color: ${player.colors.value.hoverAccent}; color: ${player.colors.value.buttonText}; @@ -203,10 +229,11 @@ const PlayerButton = ({ player, items, onClick, prop }) => EventBox({ }); } }, - onHoverLost: self => { + + onHoverLost: (self) => { self._hovered = false; - if (prop == 'playBackStatus') { - items.forEach(item => { + if (prop === 'playBackStatus') { + items.forEach((item) => { item[1].setCss(` background-color: ${player.colors.value.buttonAccent}; color: ${player.colors.value.buttonText}; @@ -216,22 +243,26 @@ const PlayerButton = ({ player, items, onClick, prop }) => EventBox({ }); } }, + connections: [ - [player, button => { + [player, (button) => { button.child.shown = `${player[prop]}`; }], - [player.colors, button => { - if (!Mpris.players.find(p => player === p)) + [player.colors, (button) => { + if (!Mpris.players.find((p) => player === p)) { return; + } if (player.colors.value) { - if (prop == 'playBackStatus') { + const c = player.colors.value; + + if (prop === 'playBackStatus') { if (button._hovered) { - items.forEach(item => { + items.forEach((item) => { item[1].setCss(` - background-color: ${player.colors.value.hoverAccent}; - color: ${player.colors.value.buttonText}; + background-color: ${c.hoverAccent}; + color: ${c.buttonText}; min-height: 40px; min-width: 36px; margin-bottom: 1px; @@ -240,10 +271,10 @@ const PlayerButton = ({ player, items, onClick, prop }) => EventBox({ }); } else { - items.forEach(item => { + items.forEach((item) => { item[1].setCss(` - background-color: ${player.colors.value.buttonAccent}; - color: ${player.colors.value.buttonText}; + background-color: ${c.buttonAccent}; + color: ${c.buttonText}; min-height: 42px; min-width: 38px;`); }); @@ -251,8 +282,8 @@ const PlayerButton = ({ player, items, onClick, prop }) => EventBox({ } else { button.setCss(` - * { color: ${player.colors.value.buttonAccent}; } - *:hover { color: ${player.colors.value.hoverAccent}; } + * { color: ${c.buttonAccent}; } + *:hover { color: ${c.hoverAccent}; } `); } } @@ -261,7 +292,7 @@ const PlayerButton = ({ player, items, onClick, prop }) => EventBox({ }), }); -export const ShuffleButton = player => PlayerButton({ +export const ShuffleButton = (player) => PlayerButton({ player, items: [ ['true', Label({ @@ -277,7 +308,7 @@ export const ShuffleButton = player => PlayerButton({ prop: 'shuffleStatus', }); -export const LoopButton = player => PlayerButton({ +export const LoopButton = (player) => PlayerButton({ player, items: [ ['None', Label({ @@ -297,7 +328,7 @@ export const LoopButton = player => PlayerButton({ prop: 'loopStatus', }); -export const PlayPauseButton = player => PlayerButton({ +export const PlayPauseButton = (player) => PlayerButton({ player, items: [ ['Playing', Label({ @@ -317,7 +348,7 @@ export const PlayPauseButton = player => PlayerButton({ prop: 'playBackStatus', }); -export const PreviousButton = player => PlayerButton({ +export const PreviousButton = (player) => PlayerButton({ player, items: [ ['true', Label({ @@ -333,7 +364,7 @@ export const PreviousButton = player => PlayerButton({ prop: 'canGoPrev', }); -export const NextButton = player => PlayerButton({ +export const NextButton = (player) => PlayerButton({ player, items: [ ['true', Label({ diff --git a/devices/wim/config/ags/js/media-player/player.js b/devices/wim/config/ags/js/media-player/player.js index 5dc42105..b50501ee 100644 --- a/devices/wim/config/ags/js/media-player/player.js +++ b/devices/wim/config/ags/js/media-player/player.js @@ -1,18 +1,20 @@ -import Mpris from 'resource:///com/github/Aylur/ags/service/mpris.js'; +import Mpris from 'resource:///com/github/Aylur/ags/service/mpris.js'; import Variable from 'resource:///com/github/Aylur/ags/variable.js'; + import { Box, CenterBox } from 'resource:///com/github/Aylur/ags/widget.js'; -import * as mpris from './mpris.js'; +import * as mpris from './mpris.js'; import PlayerGesture from './gesture.js'; -import Separator from '../misc/separator.js'; +import Separator from '../misc/separator.js'; const FAVE_PLAYER = 'org.mpris.MediaPlayer2.spotify'; -const Top = player => Box({ +const Top = (player) => Box({ className: 'top', hpack: 'start', vpack: 'start', + children: [ mpris.PlayerIcon(player, { symbolic: false, @@ -20,12 +22,13 @@ const Top = player => Box({ ], }); -const Center = player => Box({ +const Center = (player) => Box({ className: 'center', - children: [ + children: [ CenterBox({ vertical: true, + children: [ Box({ className: 'metadata', @@ -33,6 +36,7 @@ const Center = player => Box({ hpack: 'start', vpack: 'center', hexpand: true, + children: [ mpris.TitleLabel(player), mpris.ArtistLabel(player), @@ -45,6 +49,7 @@ const Center = player => Box({ CenterBox({ vertical: true, + children: [ null, mpris.PlayPauseButton(player), @@ -55,31 +60,35 @@ const Center = player => Box({ ], }); -const Bottom = player => Box({ +const SPACING = 8; + +const Bottom = (player) => Box({ className: 'bottom', + children: [ mpris.PreviousButton(player, { vpack: 'end', hpack: 'start', }), - Separator(8), + Separator(SPACING), mpris.PositionSlider(player), - Separator(8), + Separator(SPACING), mpris.NextButton(player), - Separator(8), + Separator(SPACING), mpris.ShuffleButton(player), - Separator(8), + Separator(SPACING), mpris.LoopButton(player), ], }); -const PlayerBox = player => mpris.CoverArt(player, { +const PlayerBox = (player) => mpris.CoverArt(player, { className: `player ${player.name}`, hexpand: true, + children: [ Top(player), Center(player), @@ -89,27 +98,36 @@ const PlayerBox = player => mpris.CoverArt(player, { export default () => Box({ className: 'media', + child: PlayerGesture({ properties: [ ['players', new Map()], ['setup', false], ], + connections: [ [Mpris, (overlay, busName) => { - if (overlay._players.has(busName)) + if (overlay._players.has(busName)) { return; + } // Sometimes the signal doesn't give the busName if (!busName) { - const player = Mpris.players.find(p => !overlay._players.has(p.busName)); - if (player) + const player = Mpris.players.find((p) => { + return !overlay._players.has(p.busName); + }); + + if (player) { busName = player.busName; - else + } + else { return; + } } // Get the one on top so it stays there - var previousFirst = overlay.get_children().at(-1); + let previousFirst = overlay.get_children().at(-1); + for (const [key, value] of overlay._players.entries()) { if (value === previousFirst) { previousFirst = key; @@ -118,12 +136,14 @@ export default () => Box({ } const player = Mpris.getPlayer(busName); + player.colors = Variable(); overlay._players.set(busName, PlayerBox(player)); const result = []; - overlay._players.forEach(widget => { + + overlay._players.forEach((widget) => { result.push(widget); }); @@ -131,21 +151,29 @@ export default () => Box({ // Select favorite player at startup if (!overlay._setup && overlay._players.has(FAVE_PLAYER)) { - overlay.reorder_overlay(overlay._players.get(FAVE_PLAYER), -1); + overlay.reorder_overlay( + overlay._players.get(FAVE_PLAYER), + -1, + ); overlay._setup = true; } else if (overlay._players.get(previousFirst)) { - overlay.reorder_overlay(overlay._players.get(previousFirst), -1); + overlay.reorder_overlay( + overlay._players.get(previousFirst), + -1, + ); } }, 'player-added'], [Mpris, (overlay, busName) => { - if (!busName || !overlay._players.has(busName)) + if (!busName || !overlay._players.has(busName)) { return; + } // Get the one on top so it stays there - var previousFirst = overlay.get_children().at(-1); + let previousFirst = overlay.get_children().at(-1); + for (const [key, value] of overlay._players.entries()) { if (value === previousFirst) { previousFirst = key; @@ -156,14 +184,19 @@ export default () => Box({ overlay._players.delete(busName); const result = []; - overlay._players.forEach(widget => { + + overlay._players.forEach((widget) => { result.push(widget); }); overlay.overlays = result; - if (overlay._players.has(previousFirst)) - overlay.reorder_overlay(overlay._players.get(previousFirst), -1); + if (overlay._players.has(previousFirst)) { + overlay.reorder_overlay( + overlay._players.get(previousFirst), + -1, + ); + } }, 'player-closed'], ], }), diff --git a/devices/wim/config/ags/js/misc/audio-icons.js b/devices/wim/config/ags/js/misc/audio-icons.js index cdeff354..ea416e70 100644 --- a/devices/wim/config/ags/js/misc/audio-icons.js +++ b/devices/wim/config/ags/js/misc/audio-icons.js @@ -19,8 +19,9 @@ const micIcons = { export const SpeakerIcon = Variable(); Audio.connect('speaker-changed', () => { - if (!Audio.speaker) + if (!Audio.speaker) { return; + } if (Audio.speaker.stream.isMuted) { SpeakerIcon.value = speakerIcons[0]; @@ -29,16 +30,18 @@ Audio.connect('speaker-changed', () => { const vol = Audio.speaker.volume * 100; for (const threshold of [-1, 0, 33, 66, 100]) { - if (vol > threshold + 1) + if (vol > threshold + 1) { SpeakerIcon.value = speakerIcons[threshold + 1]; + } } } }); export const MicIcon = Variable(); Audio.connect('microphone-changed', () => { - if (!Audio.microphone) + if (!Audio.microphone) { return; + } if (Audio.microphone.stream.isMuted) { MicIcon.value = micIcons[0]; @@ -47,8 +50,9 @@ Audio.connect('microphone-changed', () => { const vol = Audio.microphone.volume * 100; for (const threshold of [-1, 0, 33, 66]) { - if (vol > threshold + 1) + if (vol > threshold + 1) { MicIcon.value = micIcons[threshold + 1]; + } } } }); diff --git a/devices/wim/config/ags/js/misc/closer.js b/devices/wim/config/ags/js/misc/closer.js index 752962a9..39dc55ff 100644 --- a/devices/wim/config/ags/js/misc/closer.js +++ b/devices/wim/config/ags/js/misc/closer.js @@ -3,8 +3,8 @@ import App from 'resource:///com/github/Aylur/ags/app.js'; export default () => { Array.from(App.windows) - .filter(w => w[1].closeOnUnfocus && w[1].closeOnUnfocus !== 'stay') - .forEach(w => { + .filter((w) => w[1].closeOnUnfocus && w[1].closeOnUnfocus !== 'stay') + .forEach((w) => { App.closeWindow(w[0]); }); }; diff --git a/devices/wim/config/ags/js/misc/cursorbox.js b/devices/wim/config/ags/js/misc/cursorbox.js index 5e741225..21dd3cae 100644 --- a/devices/wim/config/ags/js/misc/cursorbox.js +++ b/devices/wim/config/ags/js/misc/cursorbox.js @@ -1,4 +1,5 @@ import Variable from 'resource:///com/github/Aylur/ags/variable.js'; + import { Button, EventBox } from 'resource:///com/github/Aylur/ags/widget.js'; import Gtk from 'gi://Gtk'; @@ -7,7 +8,7 @@ import Gtk from 'gi://Gtk'; // TODO: wrap in another EventBox for disabled cursor export default ({ isButton = false, - onPrimaryClickRelease = () => {}, + onPrimaryClickRelease = () => { /**/ }, ...props }) => { // Make this variable to know if the function should @@ -17,12 +18,13 @@ export default ({ const properties = { ...props, cursor: 'pointer', - onPrimaryClickRelease: self => { + onPrimaryClickRelease: (self) => { // Every click, do a one shot connect to // CanRun to wait for location of click const id = CanRun.connect('changed', () => { - if (CanRun.value) + if (CanRun.value) { onPrimaryClickRelease(self); + } CanRun.disconnect(id); }); @@ -30,10 +32,13 @@ export default ({ }; let widget; - if (!isButton) - widget = EventBox(properties); - else + + if (isButton) { widget = Button(properties); + } + else { + widget = EventBox(properties); + } const gesture = Gtk.GestureLongPress.new(widget); @@ -42,8 +47,9 @@ export default ({ const x = pointer[1]; const y = pointer[2]; - if ((!x || !y) || x === 0 && y === 0) + if ((!x || !y) || (x === 0 && y === 0)) { return; + } CanRun.value = !( x > widget.get_allocated_width() || diff --git a/devices/wim/config/ags/js/misc/popup.js b/devices/wim/config/ags/js/misc/popup.js index 55450285..d6ddf295 100644 --- a/devices/wim/config/ags/js/misc/popup.js +++ b/devices/wim/config/ags/js/misc/popup.js @@ -1,4 +1,5 @@ import App from 'resource:///com/github/Aylur/ags/app.js'; + import { Revealer, Box, Window } from 'resource:///com/github/Aylur/ags/widget.js'; @@ -9,8 +10,8 @@ export default ({ // Optional: execute a function whenever // the window pops up or goes away - onOpen = () => {}, - onClose = () => {}, + onOpen = () => { /**/ }, + onClose = () => { /**/ }, // Window props name, @@ -29,8 +30,9 @@ export default ({ // Add way to make window open on startup setup: () => { const id = App.connect('config-parsed', () => { - if (visible) + if (visible) { App.openWindow(name); + } App.disconnect(id); }); }, @@ -38,32 +40,39 @@ export default ({ // Wrapping the revealer inside a box is needed // to allocate some space for it even when not revealed child: Box({ - css: `min-height:1px; - min-width:1px; - padding: 1px;`, + css: ` + min-height:1px; + min-width:1px; + padding: 1px; + `, child: Revealer({ transition, transitionDuration, - connections: [[App, (rev, currentName, visible) => { - if (currentName === name) { - rev.revealChild = visible; - if (visible) + connections: [[App, (rev, currentName, isOpen) => { + if (currentName === name) { + rev.revealChild = isOpen; + + if (isOpen) { onOpen(child); - else + } + else { onClose(child); + } } }]], + child: child || Box(), }), }), }); - // Make getting the original child passed in - // this function easier when making more code - // for the widget + // Make getting the original child passed in this + // function easier when making more code for the widget window.getChild = () => window.child.children[0].child; - window.setChild = newChild => window.child.children[0].child = newChild; + window.setChild = (newChild) => { + window.child.children[0].child = newChild; + }; // This is for my custom pointers.js window.closeOnUnfocus = closeOnUnfocus; diff --git a/devices/wim/config/ags/js/misc/separator.js b/devices/wim/config/ags/js/misc/separator.js index d9d12a3f..5cc17eda 100644 --- a/devices/wim/config/ags/js/misc/separator.js +++ b/devices/wim/config/ags/js/misc/separator.js @@ -1,17 +1,13 @@ import { Box } from 'resource:///com/github/Aylur/ags/widget.js'; -export default (size, { vertical = false, css = '', ...props } = {}) => { - if (vertical) { - return Box({ - css: `min-height: ${size}px; ${css}`, - ...props, - }); - } - else { - return Box({ - css: `min-width: ${size}px; ${css}`, - ...props, - }); - } +export default (size, { + vertical = false, + css = '', + ...props +} = {}) => { + return Box({ + css: `${vertical ? 'min-height' : 'min-width'}: ${size}px; ${css}`, + ...props, + }); }; diff --git a/devices/wim/config/ags/js/notifications/base.js b/devices/wim/config/ags/js/notifications/base.js index 51b3cc0e..ff9f06b6 100644 --- a/devices/wim/config/ags/js/notifications/base.js +++ b/devices/wim/config/ags/js/notifications/base.js @@ -1,55 +1,63 @@ import Applications from 'resource:///com/github/Aylur/ags/service/applications.js'; import Hyprland from 'resource:///com/github/Aylur/ags/service/hyprland.js'; import Notifications from 'resource:///com/github/Aylur/ags/service/notifications.js'; -import Variable from 'resource:///com/github/Aylur/ags/variable.js'; +import Variable from 'resource:///com/github/Aylur/ags/variable.js'; + import { Box, Icon, Label, Button } from 'resource:///com/github/Aylur/ags/widget.js'; import { lookUpIcon } from 'resource:///com/github/Aylur/ags/utils.js'; import GLib from 'gi://GLib'; -const setTime = time => { +const setTime = (time) => { return GLib.DateTime .new_from_unix_local(time) .format('%H:%M'); }; -const getDragState = box => box.get_parent().get_parent() +const getDragState = (box) => box.get_parent().get_parent() .get_parent().get_parent().get_parent()._dragging; import Gesture from './gesture.js'; import EventBox from '../misc/cursorbox.js'; -const NotificationIcon = notif => { - let iconCmd = () => {}; +const NotificationIcon = (notif) => { + let iconCmd = () => { /**/ }; if (Applications.query(notif.appEntry).length > 0) { const app = Applications.query(notif.appEntry)[0]; let wmClass = app.app.get_string('StartupWMClass'); - if (app.app.get_filename().includes('discord')) + + if (app.app.get_filename().includes('discord')) { wmClass = 'discord'; + } if (wmClass != null) { - iconCmd = box => { + iconCmd = (box) => { if (!getDragState(box)) { if (wmClass === 'thunderbird') { - Hyprland.sendMessage('dispatch togglespecialworkspace thunder'); + Hyprland.sendMessage('dispatch ' + + 'togglespecialworkspace thunder'); } else if (wmClass === 'Spotify') { - Hyprland.sendMessage('dispatch togglespecialworkspace spot'); + Hyprland.sendMessage('dispatch ' + + 'togglespecialworkspace spot'); } else { - Hyprland.sendMessage('j/clients').then(out => { + Hyprland.sendMessage('j/clients').then((out) => { out = JSON.parse(out); const classes = []; + for (const key of out) { - if (key.class) + if (key.class) { classes.push(key.class); + } } if (classes.includes(wmClass)) { - Hyprland.sendMessage(`dispatch focuswindow ^(${wmClass})`); + Hyprland.sendMessage('dispatch ' + + `focuswindow ^(${wmClass})`); } else { Hyprland.sendMessage('[[BATCH]] ' + @@ -69,6 +77,7 @@ const NotificationIcon = notif => { if (notif.image) { return EventBox({ onPrimaryClickRelease: iconCmd, + child: Box({ vpack: 'start', hexpand: false, @@ -86,22 +95,28 @@ const NotificationIcon = notif => { } let icon = 'dialog-information-symbolic'; - if (lookUpIcon(notif.appIcon)) + + if (lookUpIcon(notif.appIcon)) { icon = notif.appIcon; + } - if (lookUpIcon(notif.appEntry)) + if (lookUpIcon(notif.appEntry)) { icon = notif.appEntry; + } return EventBox({ onPrimaryClickRelease: iconCmd, + child: Box({ vpack: 'start', hexpand: false, className: 'icon', - css: `min-width: 78px; - min-height: 78px;`, + css: ` + min-width: 78px; + min-height: 78px; + `, children: [Icon({ icon, size: 58, hpack: 'center', @@ -120,22 +135,24 @@ export const HasNotifs = Variable(false); export const Notification = ({ notif, slideIn = 'Left', - command = () => {}, + command = () => { /**/ }, } = {}) => { - if (!notif) + if (!notif) { return; - - HasNotifs.value = Notifications.notifications.length > 0; + } const BlockedApps = [ 'Spotify', ]; - if (BlockedApps.find(app => app == notif.appName)) { + if (BlockedApps.find((app) => app === notif.appName)) { notif.close(); + return; } + HasNotifs.value = Notifications.notifications.length > 0; + // Init notif const notifWidget = Gesture({ command, @@ -147,10 +164,12 @@ export const Notification = ({ notifWidget.child.add(Box({ className: `notification ${notif.urgency}`, vexpand: false, + // Notification child: Box({ vertical: true, children: [ + // Content Box({ children: [ @@ -159,9 +178,12 @@ export const Notification = ({ hexpand: true, vertical: true, children: [ + // Top of Content Box({ children: [ + + // Title Label({ className: 'title', xalign: 0, @@ -171,23 +193,31 @@ export const Notification = ({ truncate: 'end', wrap: true, label: notif.summary, - useMarkup: notif.summary.startsWith('<'), + useMarkup: notif.summary + .startsWith('<'), }), + + // Time Label({ className: 'time', vpack: 'start', label: setTime(notif.time), }), + + // Close button EventBox({ child: Button({ className: 'close-button', vpack: 'start', onClicked: () => notif.close(), - child: Icon('window-close-symbolic'), + child: Icon('window-close' + + '-symbolic'), }), }), ], }), + + // Description Label({ className: 'description', hexpand: true, @@ -201,10 +231,11 @@ export const Notification = ({ }), ], }), + // Actions Box({ className: 'actions', - children: notif.actions.map(action => Button({ + children: notif.actions.map((action) => Button({ className: 'action-button', onClicked: () => notif.invoke(action.id), hexpand: true, @@ -214,5 +245,6 @@ export const Notification = ({ ], }), })); + return notifWidget; }; diff --git a/devices/wim/config/ags/js/notifications/center.js b/devices/wim/config/ags/js/notifications/center.js index ca0e5212..51609784 100644 --- a/devices/wim/config/ags/js/notifications/center.js +++ b/devices/wim/config/ags/js/notifications/center.js @@ -1,24 +1,24 @@ import Notifications from 'resource:///com/github/Aylur/ags/service/notifications.js'; + import { Button, Label, Box, Icon, Scrollable, Revealer } from 'resource:///com/github/Aylur/ags/widget.js'; import { Notification, HasNotifs } from './base.js'; import PopupWindow from '../misc/popup.js'; -import EventBox from '../misc/cursorbox.js'; +import EventBox from '../misc/cursorbox.js'; const addNotif = (box, notif) => { - if (!notif) - return; + if (notif) { + const NewNotif = Notification({ + notif, + slideIn: 'Right', + command: () => notif.close(), + }); - const NewNotif = Notification({ - notif, - slideIn: 'Right', - command: () => notif.close(), - }); - - if (NewNotif) { - box.pack_end(NewNotif, false, false, 0); - box.show_all(); + if (NewNotif) { + box.pack_end(NewNotif, false, false, 0); + box.show_all(); + } } }; @@ -30,17 +30,23 @@ const NotificationList = () => Box({ connections: [ [Notifications, (box, id) => { // Handle cached notifs - if (box.children.length == 0) - Notifications.notifications.forEach(n => addNotif(box, n)); + if (box.children.length === 0) { + Notifications.notifications.forEach((n) => { + addNotif(box, n); + }); + } - else if (id) + else if (id) { addNotif(box, Notifications.getNotification(id)); + } }, 'notified'], [Notifications, (box, id) => { - const notif = box.children.find(ch => ch._id === id); - if (notif?.sensitive) + const notif = box.children.find((ch) => ch._id === id); + + if (notif?.sensitive) { notif.slideAway('Right'); + } }, 'closed'], ], }); @@ -54,9 +60,10 @@ const ClearButton = () => EventBox({ children: [ Label('Clear '), Icon({ - connections: [[Notifications, self => { - self.icon = Notifications.notifications.length > 0 - ? 'user-trash-full-symbolic' : 'user-trash-symbolic'; + connections: [[Notifications, (self) => { + self.icon = Notifications.notifications.length > 0 ? + 'user-trash-full-symbolic' : + 'user-trash-symbolic'; }]], }), ], @@ -78,7 +85,7 @@ const Header = () => Box({ const Placeholder = () => Revealer({ transition: 'crossfade', - binds: [['revealChild', HasNotifs, 'value', value => !value]], + binds: [['revealChild', HasNotifs, 'value', (value) => !value]], child: Box({ className: 'placeholder', vertical: true, @@ -118,9 +125,12 @@ const NotificationCenterWidget = () => Box({ ], }); +const TOP_MARGIN = 6; +const RIGHT_MARGIN = 60; + export default () => PopupWindow({ name: 'notification-center', anchor: ['top', 'right'], - margins: [6, 60, 0, 0], + margins: [TOP_MARGIN, RIGHT_MARGIN, 0, 0], child: NotificationCenterWidget(), }); diff --git a/devices/wim/config/ags/js/notifications/gesture.js b/devices/wim/config/ags/js/notifications/gesture.js index 3eaca792..6daaf211 100644 --- a/devices/wim/config/ags/js/notifications/gesture.js +++ b/devices/wim/config/ags/js/notifications/gesture.js @@ -1,4 +1,5 @@ import Notifications from 'resource:///com/github/Aylur/ags/service/notifications.js'; + import { Box, EventBox } from 'resource:///com/github/Aylur/ags/widget.js'; import { timeout } from 'resource:///com/github/Aylur/ags/utils.js'; @@ -6,26 +7,30 @@ import { HasNotifs } from './base.js'; import Gtk from 'gi://Gtk'; +const MAX_OFFSET = 200; +const OFFSCREEN = 300; +const ANIM_DURATION = 500; +const SLIDE_MIN_THRESHOLD = 10; + export default ({ id, slideIn = 'Left', - maxOffset = 200, - startMargin = 0, - endMargin = 300, - command = () => {}, + command = () => { /**/ }, ...props }) => { const widget = EventBox({ ...props, cursor: 'grab', - onHover: self => { - if (!self._hovered) + onHover: (self) => { + if (!self._hovered) { self._hovered = true; + } }, - onHoverLost: self => { - if (self._hovered) + onHoverLost: (self) => { + if (self._hovered) { self._hovered = false; + } }, }); @@ -40,33 +45,42 @@ export default ({ const TRANSITION = 'transition: margin 0.5s ease, opacity 0.5s ease;'; const SQUEEZED = 'margin-bottom: -70px; margin-top: -70px;'; const MAX_LEFT = ` - margin-left: -${Number(maxOffset + endMargin)}px; - margin-right: ${Number(maxOffset + endMargin)}px; + margin-left: -${Number(MAX_OFFSET + OFFSCREEN)}px; + margin-right: ${Number(MAX_OFFSET + OFFSCREEN)}px; `; const MAX_RIGHT = ` - margin-left: ${Number(maxOffset + endMargin)}px; - margin-right: -${Number(maxOffset + endMargin)}px; + margin-left: ${Number(MAX_OFFSET + OFFSCREEN)}px; + margin-right: -${Number(MAX_OFFSET + OFFSCREEN)}px; `; - const slideLeft = `${TRANSITION} ${MAX_LEFT} margin-top: 0px; margin-bottom: 0px; opacity: 0;`; - const squeezeLeft = `${TRANSITION} ${MAX_LEFT} ${SQUEEZED} opacity: 0;`; - const slideRight = `${TRANSITION} ${MAX_RIGHT} margin-top: 0px; margin-bottom: 0px; opacity: 0;`; + const slideLeft = `${TRANSITION} ${MAX_LEFT} + margin-top: 0px; + margin-bottom: 0px; + opacity: 0;`; + const squeezeLeft = `${TRANSITION} ${MAX_LEFT} ${SQUEEZED} opacity: 0;`; + + const slideRight = `${TRANSITION} ${MAX_RIGHT} + margin-top: 0px; + margin-bottom: 0px; + opacity: 0;`; const squeezeRight = `${TRANSITION} ${MAX_RIGHT} ${SQUEEZED} opacity: 0;`; + const defaultStyle = `${TRANSITION} margin: unset; opacity: 1;`; + // Notif methods - widget.slideAway = side => { + widget.slideAway = (side) => { // Slide away widget.child.setCss(side === 'Left' ? slideLeft : slideRight); // Makie it uninteractable widget.sensitive = false; - timeout(400, () => { + timeout(ANIM_DURATION - 100, () => { // Reduce height after sliding away widget.child?.setCss(side === 'Left' ? squeezeLeft : squeezeRight); - timeout(500, () => { + timeout(ANIM_DURATION, () => { // Kill notif and update HasNotifs after anim is done command(); HasNotifs.value = Notifications.notifications.length > 0; @@ -80,17 +94,20 @@ export default ({ connections: [ // When dragging - [gesture, self => { - var offset = gesture.get_offset()[1]; - if (offset === 0) + [gesture, (self) => { + let offset = gesture.get_offset()[1]; + + if (offset === 0) { return; + } // Slide right if (offset > 0) { self.setCss(` - margin-top: 0px; margin-bottom: 0px; opacity: 1; transition: none; - margin-left: ${Number(offset + startMargin)}px; - margin-right: -${Number(offset + startMargin)}px; + margin-top: 0px; margin-bottom: 0px; + opacity: 1; transition: none; + margin-left: ${offset}px; + margin-right: -${offset}px; `); } @@ -98,45 +115,51 @@ export default ({ else { offset = Math.abs(offset); self.setCss(` - margin-top: 0px; margin-bottom: 0px; opacity: 1; transition: none; - margin-right: ${Number(offset + startMargin)}px; - margin-left: -${Number(offset + startMargin)}px; + margin-top: 0px; margin-bottom: 0px; + opacity: 1; transition: none; + margin-right: ${offset}px; + margin-left: -${offset}px; `); } // Put a threshold on if a click is actually dragging - widget._dragging = Math.abs(offset) > 10; + widget._dragging = Math.abs(offset) > SLIDE_MIN_THRESHOLD; widget.cursor = 'grabbing'; }, 'drag-update'], // On drag end - [gesture, self => { + [gesture, (self) => { // Make it slide in on init if (!widget.ready) { // Reverse of slideAway, so it started at squeeze, then we go to slide self.setCss(slideIn === 'Left' ? slideLeft : slideRight); - timeout(500, () => { - // Then we got to center + timeout(ANIM_DURATION, () => { + // Then we go to center self.setCss(defaultStyle); - timeout(500, () => widget.ready = true); + timeout(ANIM_DURATION, () => { + widget.ready = true; + }); }); + return; } const offset = gesture.get_offset()[1]; // If crosses threshold after letting go, slide away - if (Math.abs(offset) > maxOffset) { - if (offset > 0) + if (Math.abs(offset) > MAX_OFFSET) { + if (offset > 0) { widget.slideAway('Right'); - else + } + else { widget.slideAway('Left'); + } } else { self.setCss(defaultStyle); - widget.cursor = 'grab', + widget.cursor = 'grab'; widget._dragging = false; } }, 'drag-end'], diff --git a/devices/wim/config/ags/js/notifications/popup.js b/devices/wim/config/ags/js/notifications/popup.js index 0197240c..92ca3d93 100644 --- a/devices/wim/config/ags/js/notifications/popup.js +++ b/devices/wim/config/ags/js/notifications/popup.js @@ -1,16 +1,20 @@ import Notifications from 'resource:///com/github/Aylur/ags/service/notifications.js'; + import { Box } from 'resource:///com/github/Aylur/ags/widget.js'; import { interval } from 'resource:///com/github/Aylur/ags/utils.js'; import GLib from 'gi://GLib'; import { Notification } from './base.js'; -import PopupWindow from '../misc/popup.js'; +import PopupWindow from '../misc/popup.js'; + +const DELAY = 2000; const addPopup = (box, id) => { - if (!id) + if (!id) { return; + } const notif = Notifications.getNotification(id); @@ -20,32 +24,32 @@ const addPopup = (box, id) => { }); if (NewNotif) { - // use this instead of add to put it at the top + // Use this instead of add to put it at the top box.pack_end(NewNotif, false, false, 0); box.show_all(); } }; const handleDismiss = (box, id, force = false) => { - const notif = box.children.find(ch => ch._id === id); - if (!notif) + const notif = box.children.find((ch) => ch._id === id); + + if (!notif) { return; + } // If notif isn't hovered or was closed, slide away if (!notif._hovered || force) { notif.slideAway('Left'); - return; } // If notif is hovered, delay close else if (notif._hovered) { - notif.interval = interval(2000, () => { + notif.interval = interval(DELAY, () => { if (!notif._hovered && notif.interval) { notif.slideAway('Left'); GLib.source_remove(notif.interval); - notif.interval = undefined; - return; + notif.interval = null; } }); } @@ -57,12 +61,14 @@ export default () => PopupWindow({ visible: true, transition: 'none', closeOnUnfocus: 'stay', + child: Box({ vertical: true, + connections: [ - [Notifications, (box, id) => addPopup(box, id), 'notified'], - [Notifications, (box, id) => handleDismiss(box, id), 'dismissed'], - [Notifications, (box, id) => handleDismiss(box, id, true), 'closed'], + [Notifications, (s, id) => addPopup(s, id), 'notified'], + [Notifications, (s, id) => handleDismiss(s, id), 'dismissed'], + [Notifications, (s, id) => handleDismiss(s, id, true), 'closed'], ], }), }); diff --git a/devices/wim/config/ags/js/on-screen-keyboard/gesture.js b/devices/wim/config/ags/js/on-screen-keyboard/gesture.js index b957c108..bc57fcc5 100644 --- a/devices/wim/config/ags/js/on-screen-keyboard/gesture.js +++ b/devices/wim/config/ags/js/on-screen-keyboard/gesture.js @@ -1,49 +1,60 @@ -import { execAsync, timeout } from 'resource:///com/github/Aylur/ags/utils.js'; - import Hyprland from 'resource:///com/github/Aylur/ags/service/hyprland.js'; +import { execAsync, timeout } from 'resource:///com/github/Aylur/ags/utils.js'; + import Gtk from 'gi://Gtk'; +import Tablet from '../../services/tablet.js'; + +const KEY_N = 249; +const HIDDEN_MARGIN = 340; +const ANIM_DURATION = 700; + const releaseAllKeys = () => { - const keycodes = Array.from(Array(249).keys()); + const keycodes = Array.from(Array(KEY_N).keys()); + execAsync([ 'ydotool', 'key', - ...keycodes.map(keycode => `${keycode}:0`), + ...keycodes.map((keycode) => `${keycode}:0`), ]).catch(print); }; -const hidden = 340; -export default window => { - window.child.setCss(`margin-bottom: -${hidden}px;`); +export default (window) => { + window.child.setCss(`margin-bottom: -${HIDDEN_MARGIN}px;`); const gesture = Gtk.GestureDrag.new(window); - window.setVisible = state => { + window.setVisible = (state) => { if (state) { window.visible = true; window.setSlideDown(); window.child.setCss(` - transition: margin-bottom 0.7s cubic-bezier(0.36, 0, 0.66, -0.56); + transition: margin-bottom 0.7s + cubic-bezier(0.36, 0, 0.66, -0.56); margin-bottom: 0px; `); } else { - timeout(710, () => { - if (!Tablet.tabletMode) + timeout(ANIM_DURATION + 10, () => { + if (!Tablet.tabletMode) { window.visible = false; + } }); releaseAllKeys(); window.setSlideUp(); window.child.setCss(` - transition: margin-bottom 0.7s cubic-bezier(0.36, 0, 0.66, -0.56); - margin-bottom: -${hidden}px; + transition: margin-bottom 0.7s + cubic-bezier(0.36, 0, 0.66, -0.56); + margin-bottom: -${HIDDEN_MARGIN}px; `); } }; gesture.signals = []; window.killGestureSigs = () => { - gesture.signals.forEach(id => gesture.disconnect(id)); + gesture.signals.forEach((id) => { + gesture.disconnect(id); + }); gesture.signals = []; }; @@ -53,7 +64,7 @@ export default window => { // Begin drag gesture.signals.push( gesture.connect('drag-begin', () => { - Hyprland.sendMessage('j/cursorpos').then(out => { + Hyprland.sendMessage('j/cursorpos').then((out) => { gesture.startY = JSON.parse(out).y; }); }), @@ -62,15 +73,16 @@ export default window => { // Update drag gesture.signals.push( gesture.connect('drag-update', () => { - Hyprland.sendMessage('j/cursorpos').then(out => { + Hyprland.sendMessage('j/cursorpos').then((out) => { const currentY = JSON.parse(out).y; const offset = gesture.startY - currentY; - if (offset < 0) + if (offset < 0) { return; + } window.child.setCss(` - margin-bottom: ${offset - hidden}px; + margin-bottom: ${offset - HIDDEN_MARGIN}px; `); }); }), @@ -81,7 +93,7 @@ export default window => { gesture.connect('drag-end', () => { window.child.setCss(` transition: margin-bottom 0.5s ease-in-out; - margin-bottom: -${hidden}px; + margin-bottom: -${HIDDEN_MARGIN}px; `); }), ); @@ -93,7 +105,7 @@ export default window => { // Begin drag gesture.signals.push( gesture.connect('drag-begin', () => { - Hyprland.sendMessage('j/cursorpos').then(out => { + Hyprland.sendMessage('j/cursorpos').then((out) => { gesture.startY = JSON.parse(out).y; }); }), @@ -102,12 +114,13 @@ export default window => { // Update drag gesture.signals.push( gesture.connect('drag-update', () => { - Hyprland.sendMessage('j/cursorpos').then(out => { + Hyprland.sendMessage('j/cursorpos').then((out) => { const currentY = JSON.parse(out).y; const offset = gesture.startY - currentY; - if (offset > 0) + if (offset > 0) { return; + } window.child.setCss(` margin-bottom: ${offset}px; diff --git a/devices/wim/config/ags/js/on-screen-keyboard/keyboard.js b/devices/wim/config/ags/js/on-screen-keyboard/keyboard.js index de51cb79..a28d6110 100644 --- a/devices/wim/config/ags/js/on-screen-keyboard/keyboard.js +++ b/devices/wim/config/ags/js/on-screen-keyboard/keyboard.js @@ -8,48 +8,59 @@ import { defaultOskLayout, oskLayouts } from './keyboard-layouts.js'; const keyboardLayout = defaultOskLayout; const keyboardJson = oskLayouts[keyboardLayout]; -const L_KEY_PER_ROW = [8, 7, 6, 6, 6, 4]; +const L_KEY_PER_ROW = [8, 7, 6, 6, 6, 4]; // eslint-disable-line +const COLOR = 'rgba(0, 0, 0, 0.3)'; +const SPACING = 4; -const color = 'rgba(0, 0, 0, 0.3)'; -export default window => Box({ +export default (window) => Box({ vertical: true, children: [ CenterBox({ hpack: 'center', + start_widget: RoundedCorner('bottomright', ` - background-color: ${color}; + background-color: ${COLOR}; `), + center_widget: Box({ class_name: 'thingy', - css: `background: ${color};`, + css: `background: ${COLOR};`, }), + end_widget: RoundedCorner('bottomleft', ` - background-color: ${color}; + background-color: ${COLOR}; `), }), CenterBox({ - css: `background: ${color};`, + css: `background: ${COLOR};`, class_name: 'osk', hexpand: true, + start_widget: Box({ class_name: 'left-side side', hpack: 'start', vertical: true, + children: keyboardJson.keys.map((row, rowIndex) => Box({ vertical: true, + children: [ Box({ class_name: 'row', + children: [ - Separator(4), + Separator(SPACING), ...row.map((key, keyIndex) => { - return keyIndex < L_KEY_PER_ROW[rowIndex] ? Key(key) : null; + return keyIndex < L_KEY_PER_ROW[rowIndex] ? + Key(key) : + null; }), ], }), - Separator(4, { vertical: true }), + + Separator(SPACING, { vertical: true }), ], })), }), @@ -57,19 +68,28 @@ export default window => Box({ center_widget: Box({ hpack: 'center', vpack: 'center', + children: [ Box({ class_name: 'settings', + children: [ ToggleButton({ cursor: 'pointer', class_name: 'button', active: true, vpack: 'center', - connections: [['toggled', self => { - self.toggleClassName('toggled', self.get_active()); - window.exclusivity = self.get_active() ? 'exclusive' : 'normal'; + + connections: [['toggled', (self) => { + self.toggleClassName( + 'toggled', + self.get_active(), + ); + window.exclusivity = self.get_active() ? + 'exclusive' : + 'normal'; }]], + child: Label('Exclusive'), }), ], @@ -81,17 +101,23 @@ export default window => Box({ class_name: 'right-side side', hpack: 'end', vertical: true, + children: keyboardJson.keys.map((row, rowIndex) => Box({ vertical: true, + children: [ Box({ hpack: 'end', class_name: 'row', + children: row.map((key, keyIndex) => { - return keyIndex >= L_KEY_PER_ROW[rowIndex] ? Key(key) : null; + return keyIndex >= L_KEY_PER_ROW[rowIndex] ? + Key(key) : + null; }), }), - Separator(4, { vertical: true }), + + Separator(SPACING, { vertical: true }), ], })), }), diff --git a/devices/wim/config/ags/js/on-screen-keyboard/keys.js b/devices/wim/config/ags/js/on-screen-keyboard/keys.js index 002b8894..fd96d535 100644 --- a/devices/wim/config/ags/js/on-screen-keyboard/keys.js +++ b/devices/wim/config/ags/js/on-screen-keyboard/keys.js @@ -1,5 +1,6 @@ import Variable from 'resource:///com/github/Aylur/ags/variable.js'; import Brightness from '../../services/brightness.js'; + import { Box, EventBox, Label } from 'resource:///com/github/Aylur/ags/widget.js'; import { execAsync } from 'resource:///com/github/Aylur/ags/utils.js'; @@ -19,59 +20,73 @@ const AltGr = Variable(false); const RCtrl = Variable(false); const Caps = Variable(false); -Brightness.connect('caps', (_, state) => Caps.value = state); + +Brightness.connect('caps', (_, state) => { + Caps.value = state; +}); // Assume both shifts are the same for key.labelShift const LShift = Variable(false); const RShift = Variable(false); const Shift = Variable(false); -LShift.connect('changed', () => Shift.value = LShift.value || RShift.value); -RShift.connect('changed', () => Shift.value = LShift.value || RShift.value); + +LShift.connect('changed', () => { + Shift.value = LShift.value || RShift.value; +}); +RShift.connect('changed', () => { + Shift.value = LShift.value || RShift.value; +}); + +const SPACING = 4; +const LSHIFT_CODE = 42; +const LALT_CODE = 56; +const LCTRL_CODE = 29; -export default key => { - if (key.keytype === 'normal') - return RegularKey(key); - else - return ModKey(key); -}; - -const ModKey = key => { +const ModKey = (key) => { let Mod; - if (key.label === 'Super') + + if (key.label === 'Super') { Mod = Super; + } // Differentiate left and right mods - else if (key.label === 'Shift' && key.keycode === 42) + else if (key.label === 'Shift' && key.keycode === LSHIFT_CODE) { Mod = LShift; + } - else if (key.label === 'Alt' && key.keycode === 56) + else if (key.label === 'Alt' && key.keycode === LALT_CODE) { Mod = LAlt; + } - else if (key.label === 'Ctrl' && key.keycode === 29) + else if (key.label === 'Ctrl' && key.keycode === LCTRL_CODE) { Mod = LCtrl; + } - else if (key.label === 'Shift') + else if (key.label === 'Shift') { Mod = RShift; + } - else if (key.label === 'AltGr') + else if (key.label === 'AltGr') { Mod = AltGr; + } - else if (key.label === 'Ctrl') + else if (key.label === 'Ctrl') { Mod = RCtrl; + } const button = EventBox({ cursor: 'pointer', class_name: 'key', - onPrimaryClickRelease: self => { + onPrimaryClickRelease: (self) => { console.log('mod toggled'); execAsync(`ydotool key ${key.keycode}:${Mod.value ? 0 : 1}`); self.child.toggleClassName('active', !Mod.value); Mod.value = !Mod.value; }, - connections: [[NormalClick, self => { + connections: [[NormalClick, (self) => { Mod.value = false; self.child.toggleClassName('active', false); execAsync(`ydotool key ${key.keycode}:0`); @@ -85,42 +100,49 @@ const ModKey = key => { return Box({ children: [ button, - Separator(4), + Separator(SPACING), ], }); }; -const RegularKey = key => { +const RegularKey = (key) => { const widget = EventBox({ cursor: 'pointer', class_name: 'key', + child: Label({ class_name: `normal ${key.label}`, label: key.label, + connections: [ - [Shift, self => { - if (!key.labelShift) + [Shift, (self) => { + if (!key.labelShift) { return; + } self.label = Shift.value ? key.labelShift : key.label; }], - [Caps, self => { + [Caps, (self) => { if (key.label === 'Caps') { self.toggleClassName('active', Caps.value); + return; } - if (!key.labelShift) + if (!key.labelShift) { return; + } - if (key.label.match(/[A-Za-z]/)) + if (key.label.match(/[A-Za-z]/)) { self.label = Caps.value ? key.labelShift : key.label; + } }], - [AltGr, self => { - if (!key.labelAltGr) + [AltGr, (self) => { + if (!key.labelAltGr) { return; + } self.toggleClassName('altgr', AltGr.value); self.label = AltGr.value ? key.labelAltGr : key.label; @@ -130,6 +152,7 @@ const RegularKey = key => { }); const gesture = Gtk.GestureLongPress.new(widget); + gesture.delay_factor = 1.0; // Long press @@ -138,22 +161,24 @@ const RegularKey = key => { const x = pointer[1]; const y = pointer[2]; - if ((!x || !y) || x === 0 && y === 0) + if ((!x || !y) || (x === 0 && y === 0)) { return; + } console.log('Not implemented yet'); // TODO: popup menu for accents }, 'pressed'); - // onPrimaryClickRelease + // OnPrimaryClickRelease widget.connectTo(gesture, () => { const pointer = gesture.get_point(null); const x = pointer[1]; const y = pointer[2]; - if ((!x || !y) || x === 0 && y === 0) + if ((!x || !y) || (x === 0 && y === 0)) { return; + } console.log('key clicked'); @@ -165,7 +190,11 @@ const RegularKey = key => { return Box({ children: [ widget, - Separator(4), + Separator(SPACING), ], }); }; + +export default (key) => key.keytype === 'normal' ? + RegularKey(key) : + ModKey(key); diff --git a/devices/wim/config/ags/js/on-screen-keyboard/main.js b/devices/wim/config/ags/js/on-screen-keyboard/main.js index 45b588b3..2d1950e5 100644 --- a/devices/wim/config/ags/js/on-screen-keyboard/main.js +++ b/devices/wim/config/ags/js/on-screen-keyboard/main.js @@ -1,11 +1,12 @@ import { Window } from 'resource:///com/github/Aylur/ags/widget.js'; import { execAsync } from 'resource:///com/github/Aylur/ags/utils.js'; +import Tablet from '../../services/tablet.js'; import Gesture from './gesture.js'; import Keyboard from './keyboard.js'; -// start ydotool daemon +// Start ydotool daemon execAsync('ydotoold').catch(print); // Window @@ -14,17 +15,20 @@ export default () => { name: 'osk', visible: false, anchor: ['left', 'bottom', 'right'], + connections: [ [Tablet, (self, state) => { self.setVisible(state); }, 'osk-toggled'], [Tablet, () => { - if (!Tablet.tabletMode && !Tablet.oskState) + if (!Tablet.tabletMode && !Tablet.oskState) { window.visible = false; + } }, 'mode-toggled'], ], }); + window.child = Keyboard(window); return Gesture(window); diff --git a/devices/wim/config/ags/js/osd/audio.js b/devices/wim/config/ags/js/osd/audio.js index 319e3930..0cd6dcf1 100644 --- a/devices/wim/config/ags/js/osd/audio.js +++ b/devices/wim/config/ags/js/osd/audio.js @@ -1,8 +1,11 @@ import Audio from 'resource:///com/github/Aylur/ags/service/audio.js'; + import { Box, Icon, ProgressBar } from 'resource:///com/github/Aylur/ags/widget.js'; import { SpeakerIcon } from '../misc/audio-icons.js'; +const AUDIO_MAX = 1.5; + export default () => Box({ className: 'osd', @@ -14,14 +17,20 @@ export default () => Box({ ProgressBar({ vpack: 'center', - connections: [[Audio, self => { - if (!Audio.speaker) - return; - self.value = Audio.speaker ? Audio.speaker.volume / 1.5 : 0; + connections: [[Audio, (self) => { + if (!Audio.speaker) { + return; + } + + self.value = Audio.speaker ? + Audio.speaker.volume / AUDIO_MAX : + 0; + self.sensitive = !Audio.speaker?.stream.isMuted; const stack = self.get_parent().get_parent(); + stack.shown = 'audio'; stack.resetTimer(); }, 'speaker-changed']], diff --git a/devices/wim/config/ags/js/osd/brightness.js b/devices/wim/config/ags/js/osd/brightness.js index e8d059a3..abec4bb3 100644 --- a/devices/wim/config/ags/js/osd/brightness.js +++ b/devices/wim/config/ags/js/osd/brightness.js @@ -1,5 +1,7 @@ import { Box, Icon, ProgressBar } from 'resource:///com/github/Aylur/ags/widget.js'; +import Brightness from '../../services/brightness.js'; + export default () => Box({ className: 'osd', @@ -11,10 +13,12 @@ export default () => Box({ ProgressBar({ vpack: 'center', - connections: [[Brightness, self => { + + connections: [[Brightness, (self) => { self.value = Brightness.screen; const stack = self.get_parent().get_parent(); + stack.shown = 'brightness'; stack.resetTimer(); }, 'screen']], diff --git a/devices/wim/config/ags/js/osd/caps.js b/devices/wim/config/ags/js/osd/caps.js index b2b09a23..60066749 100644 --- a/devices/wim/config/ags/js/osd/caps.js +++ b/devices/wim/config/ags/js/osd/caps.js @@ -1,5 +1,7 @@ import { Box, Icon, Label } from 'resource:///com/github/Aylur/ags/widget.js'; +import Brightness from '../../services/brightness.js'; + export default () => Box({ className: 'osd', @@ -7,10 +9,14 @@ export default () => Box({ Icon({ hpack: 'start', icon: 'caps-lock-symbolic', + connections: [[Brightness, (self, state) => { - self.icon = state ? 'caps-lock-symbolic' : 'capslock-disabled-symbolic'; + self.icon = state ? + 'caps-lock-symbolic' : + 'capslock-disabled-symbolic'; const stack = self.get_parent().get_parent(); + stack.shown = 'caps'; stack.resetTimer(); }, 'caps']], diff --git a/devices/wim/config/ags/js/osd/kbd.js b/devices/wim/config/ags/js/osd/kbd.js index 8b94c187..91216aa2 100644 --- a/devices/wim/config/ags/js/osd/kbd.js +++ b/devices/wim/config/ags/js/osd/kbd.js @@ -1,8 +1,11 @@ import { Box, Icon, ProgressBar } from 'resource:///com/github/Aylur/ags/widget.js'; +import Brightness from '../../services/brightness.js'; + export default () => Box({ className: 'osd', + children: [ Icon({ hpack: 'start', @@ -11,15 +14,18 @@ export default () => Box({ ProgressBar({ vpack: 'center', - connections: [[Brightness, self => { + + connections: [[Brightness, (self) => { if (!self.value) { self.value = Brightness.kbd / 2; + return; } self.value = Brightness.kbd / 2; self.sensitive = Brightness.kbd !== 0; const stack = self.get_parent().get_parent(); + stack.shown = 'kbd'; stack.resetTimer(); }, 'kbd']], diff --git a/devices/wim/config/ags/js/osd/main.js b/devices/wim/config/ags/js/osd/main.js index d3a1d13a..108dbab3 100644 --- a/devices/wim/config/ags/js/osd/main.js +++ b/devices/wim/config/ags/js/osd/main.js @@ -1,15 +1,18 @@ import App from 'resource:///com/github/Aylur/ags/app.js'; + import { timeout } from 'resource:///com/github/Aylur/ags/utils.js'; import { Stack } from 'resource:///com/github/Aylur/ags/widget.js'; import GLib from 'gi://GLib'; import PopupWindow from '../misc/popup.js'; -import Audio from './audio.js'; -import Brightness from './brightness.js'; -import CapsLock from './caps.js'; -import Keyboard from './kbd.js'; -import Microphone from './mic.js'; +import Audio from './audio.js'; +import Brightness from './brightness.js'; +import CapsLock from './caps.js'; +import Keyboard from './kbd.js'; +import Microphone from './mic.js'; + +const HIDE_DELAY = 2000; export default () => { @@ -37,20 +40,23 @@ export default () => { }); let timer; + stack.resetTimer = () => { // Each osd calls resetTimer once at startup if (setup <= stack.items.length + 1) { ++setup; + return; } App.openWindow('osd'); - if (timer) + if (timer) { GLib.source_remove(timer); + } - timer = timeout(2000, () => { + timer = timeout(HIDE_DELAY, () => { App.closeWindow('osd'); - timer = undefined; + timer = null; }); }; diff --git a/devices/wim/config/ags/js/osd/mic.js b/devices/wim/config/ags/js/osd/mic.js index 3e05e595..eb9c228d 100644 --- a/devices/wim/config/ags/js/osd/mic.js +++ b/devices/wim/config/ags/js/osd/mic.js @@ -1,4 +1,5 @@ import Audio from 'resource:///com/github/Aylur/ags/service/audio.js'; + import { Box, Icon, ProgressBar } from 'resource:///com/github/Aylur/ags/widget.js'; import { MicIcon } from '../misc/audio-icons.js'; @@ -6,6 +7,7 @@ import { MicIcon } from '../misc/audio-icons.js'; export default () => Box({ className: 'osd', + children: [ Icon({ hpack: 'start', @@ -14,14 +16,17 @@ export default () => Box({ ProgressBar({ vpack: 'center', - connections: [[Audio, self => { - if (!Audio.microphone) + + connections: [[Audio, (self) => { + if (!Audio.microphone) { return; + } self.value = Audio.microphone ? Audio.microphone.volume : 0; self.sensitive = !Audio.microphone?.stream.isMuted; const stack = self.get_parent().get_parent(); + stack.shown = 'mic'; stack.resetTimer(); }, 'microphone-changed']], diff --git a/devices/wim/config/ags/js/overview/clients.js b/devices/wim/config/ags/js/overview/clients.js index 480c7cd7..fdb1b8be 100644 --- a/devices/wim/config/ags/js/overview/clients.js +++ b/devices/wim/config/ags/js/overview/clients.js @@ -1,21 +1,23 @@ -import App from 'resource:///com/github/Aylur/ags/app.js'; +import App from 'resource:///com/github/Aylur/ags/app.js'; import Hyprland from 'resource:///com/github/Aylur/ags/service/hyprland.js'; + import { Icon, Revealer } from 'resource:///com/github/Aylur/ags/widget.js'; import { timeout } from 'resource:///com/github/Aylur/ags/utils.js'; import { WindowButton } from './dragndrop.js'; import * as VARS from './variables.js'; -const scale = size => size * VARS.SCALE - VARS.MARGIN; -const getFontSize = client => { +const scale = (size) => (size * VARS.SCALE) - VARS.MARGIN; +const getFontSize = (client) => { const valX = scale(client.size[0]) * VARS.ICON_SCALE; const valY = scale(client.size[1]) * VARS.ICON_SCALE; - var size = Math.min(valX, valY); + const size = Math.min(valX, valY); + return size <= 0 ? 0.1 : size; }; -const IconStyle = client => ` +const IconStyle = (client) => ` min-width: ${scale(client.size[0])}px; min-height: ${scale(client.size[1])}px; font-size: ${getFontSize(client)}px; @@ -29,43 +31,61 @@ const Client = (client, active, clients, box) => { return Revealer({ transition: 'crossfade', - setup: rev => rev.revealChild = true, + + setup: (rev) => { + rev.revealChild = true; + }, + properties: [ ['address', client.address], ['toDestroy', false], ], + child: WindowButton({ mainBox: box, address: client.address, - onSecondaryClickRelease: () => Hyprland.sendMessage(`dispatch closewindow ${addr}`), + + onSecondaryClickRelease: () => { + Hyprland.sendMessage(`dispatch closewindow ${addr}`); + }, onPrimaryClickRelease: () => { if (wsId < 0) { if (client.workspace.name === 'special') { - Hyprland.sendMessage(`dispatch movetoworkspacesilent special:${wsId},${addr}`) + Hyprland.sendMessage('dispatch ' + + `movetoworkspacesilent special:${wsId},${addr}`) .then( - Hyprland.sendMessage(`dispatch togglespecialworkspace ${wsId}`) + Hyprland.sendMessage('dispatch ' + + `togglespecialworkspace ${wsId}`) .then( () => App.closeWindow('overview'), ).catch(print), ).catch(print); } else { - Hyprland.sendMessage(`dispatch togglespecialworkspace ${wsName}`).then( + Hyprland.sendMessage('dispatch ' + + `togglespecialworkspace ${wsName}`).then( () => App.closeWindow('overview'), ).catch(print); } } else { - // close special workspace if one is opened + // Close special workspace if one is opened const activeAddress = Hyprland.active.client.address; - const currentActive = clients.find(c => c.address === activeAddress); - const currentSpecial = String(currentActive.workspace.name).replace('special:', ''); + + const currentActive = clients.find((c) => { + return c.address === activeAddress; + }); if (currentActive && currentActive.workspace.id < 0) { - Hyprland.sendMessage(`dispatch togglespecialworkspace ${currentSpecial}`) + const currentSpecial = `${currentActive.workspace.name}` + .replace('special:', ''); + + Hyprland.sendMessage('dispatch ' + + `togglespecialworkspace ${currentSpecial}`) .catch(print); } + Hyprland.sendMessage(`dispatch focuswindow ${addr}`).then( () => App.closeWindow('overview'), ).catch(print); @@ -74,26 +94,28 @@ const Client = (client, active, clients, box) => { child: Icon({ className: `window ${active}`, - css: IconStyle(client) + 'font-size: 10px;', + css: `${IconStyle(client) }font-size: 10px;`, icon: client.class, }), }), }); }; -export function updateClients(box) { - Hyprland.sendMessage('j/clients').then(out => { - const clients = JSON.parse(out).filter(client => client.class); +export const updateClients = (box) => { + Hyprland.sendMessage('j/clients').then((out) => { + const clients = JSON.parse(out).filter((client) => client.class); - box._workspaces.forEach(workspace => { + box._workspaces.forEach((workspace) => { const fixed = workspace.getFixed(); const toRemove = fixed.get_children(); - clients.filter(client => client.workspace.id == workspace._id) - .forEach(client => { + clients.filter((client) => client.workspace.id === workspace._id) + .forEach((client) => { let active = ''; - if (client.address == Hyprland.active.client.address) + + if (client.address === Hyprland.active.client.address) { active = 'active'; + } // TODO: fix multi monitor issue. this is just a temp fix client.at[1] -= 2920; @@ -109,19 +131,21 @@ export function updateClients(box) { } const newClient = [ - fixed.get_children().find(ch => ch._address == client.address), + fixed.get_children() + .find((ch) => ch._address === client.address), client.at[0] * VARS.SCALE, client.at[1] * VARS.SCALE, ]; - if (!newClient[0]) { - newClient[0] = Client(client, active, clients, box); - fixed.put(...newClient); - } - else { + // If it exists already + if (newClient[0]) { toRemove.splice(toRemove.indexOf(newClient[0]), 1); 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, () => { @@ -131,7 +155,7 @@ export function updateClients(box) { }); fixed.show_all(); - toRemove.forEach(ch => { + toRemove.forEach((ch) => { if (ch._toDestroy) { ch.destroy(); } @@ -142,4 +166,4 @@ export function updateClients(box) { }); }); }).catch(print); -} +}; diff --git a/devices/wim/config/ags/js/overview/current-workspace.js b/devices/wim/config/ags/js/overview/current-workspace.js index 7fd1fb3d..c654d186 100644 --- a/devices/wim/config/ags/js/overview/current-workspace.js +++ b/devices/wim/config/ags/js/overview/current-workspace.js @@ -1,10 +1,13 @@ import Hyprland from 'resource:///com/github/Aylur/ags/service/hyprland.js'; + import { Box } from 'resource:///com/github/Aylur/ags/widget.js'; import * as VARS from './variables.js'; +const PADDING = 34; +const MARGIN = 9; const DEFAULT_STYLE = ` min-width: ${VARS.SCREEN.X * VARS.SCALE}px; - min-height: ${VARS.SCREEN.Y * VARS.SCALE - 4}px; + min-height: ${(VARS.SCREEN.Y * VARS.SCALE) - (VARS.MARGIN / 2)}px; border-radius: 10px; `; @@ -21,14 +24,16 @@ export const updateCurrentWorkspace = (main, highlighter) => { const row = Math.floor((currentId - 1) / VARS.WORKSPACE_PER_ROW); const rowObject = main.children[0].children[row]; - const workspaces = rowObject.child.centerWidget.child.get_children().filter(w => w.revealChild); + const workspaces = rowObject.child.centerWidget.child + .get_children().filter((w) => w.revealChild); - const height = row * (VARS.SCREEN.Y * VARS.SCALE + 17); - const currentIndex = workspaces.findIndex(w => w._id == currentId); + const currentIndex = workspaces.findIndex((w) => w._id === currentId); + const left = currentIndex * ((VARS.SCREEN.X * VARS.SCALE) + PADDING); + const height = row * ((VARS.SCREEN.Y * VARS.SCALE) + (PADDING / 2)); highlighter.setCss(` ${DEFAULT_STYLE} - margin-left: ${9 + currentIndex * (VARS.SCREEN.X * VARS.SCALE + 34)}px; - margin-top: ${9 + height}px; + margin-left: ${MARGIN + left}px; + margin-top: ${MARGIN + height}px; `); }; diff --git a/devices/wim/config/ags/js/overview/dragndrop.js b/devices/wim/config/ags/js/overview/dragndrop.js index 1e0fb0a5..5537d4a6 100644 --- a/devices/wim/config/ags/js/overview/dragndrop.js +++ b/devices/wim/config/ags/js/overview/dragndrop.js @@ -1,4 +1,5 @@ import Hyprland from 'resource:///com/github/Aylur/ags/service/hyprland.js'; + import { EventBox } from 'resource:///com/github/Aylur/ags/widget.js'; import Gtk from 'gi://Gtk'; @@ -11,7 +12,7 @@ import { updateClients } from './clients.js'; const TARGET = [Gtk.TargetEntry.new('text/plain', Gtk.TargetFlags.SAME_APP, 0)]; -function createSurfaceFromWidget(widget) { +const createSurfaceFromWidget = (widget) => { const alloc = widget.get_allocation(); const surface = new Cairo.ImageSurface( Cairo.Format.ARGB32, @@ -19,33 +20,40 @@ function createSurfaceFromWidget(widget) { alloc.height, ); const cr = new Cairo.Context(surface); + cr.setSourceRGBA(255, 255, 255, 0); cr.rectangle(0, 0, alloc.width, alloc.height); cr.fill(); widget.draw(cr); return surface; -} +}; let hidden = 0; -export const WorkspaceDrop = props => EventBox({ + +export const WorkspaceDrop = (props) => EventBox({ ...props, connections: [['drag-data-received', (self, _c, _x, _y, data) => { let id = self.get_parent()._id; - if (id < -1) + if (id < -1) { id = self.get_parent()._name; + } - else if (id === -1) + else if (id === -1) { id = `special:${++hidden}`; + } - else if (id === 1000) + else if (id === 1000) { id = 'empty'; + } - Hyprland.sendMessage(`dispatch movetoworkspacesilent ${id},address:${data.get_text()}`) + Hyprland.sendMessage('dispatch ' + + `movetoworkspacesilent ${id},address:${data.get_text()}`) .catch(print); }]], - setup: self => { + + setup: (self) => { self.drag_dest_set(Gtk.DestDefaults.ALL, TARGET, Gdk.DragAction.COPY); }, }); @@ -53,15 +61,23 @@ export const WorkspaceDrop = props => EventBox({ export const WindowButton = ({ address, mainBox, ...props } = {}) => Button({ isButton: true, ...props, - setup: self => { - self.drag_source_set(Gdk.ModifierType.BUTTON1_MASK, TARGET, Gdk.DragAction.COPY); + + setup: (self) => { + self.drag_source_set( + Gdk.ModifierType.BUTTON1_MASK, + TARGET, + Gdk.DragAction.COPY, + ); + self.connect('drag-data-get', (_w, _c, data) => { data.set_text(address, address.length); }); + self.connect('drag-begin', (_, context) => { Gtk.drag_set_icon_surface(context, createSurfaceFromWidget(self)); self.get_parent().revealChild = false; }); + self.connect('drag-end', () => { self.get_parent().destroy(); diff --git a/devices/wim/config/ags/js/overview/main.js b/devices/wim/config/ags/js/overview/main.js index 67c90bac..d1330a4e 100644 --- a/devices/wim/config/ags/js/overview/main.js +++ b/devices/wim/config/ags/js/overview/main.js @@ -1,5 +1,6 @@ -import App from 'resource:///com/github/Aylur/ags/app.js'; +import App from 'resource:///com/github/Aylur/ags/app.js'; import Hyprland from 'resource:///com/github/Aylur/ags/service/hyprland.js'; + import { Box, Overlay } from 'resource:///com/github/Aylur/ags/widget.js'; import PopupWindow from '../misc/popup.js'; @@ -8,25 +9,26 @@ import { Highlighter, updateCurrentWorkspace } from './current-workspace.js'; import { updateClients } from './clients.js'; -function update(box, highlight) { +const update = (box, highlight) => { getWorkspaces(box); updateWorkspaces(box); updateClients(box); updateCurrentWorkspace(box, highlight); -} +}; // TODO: have a 'page' for each monitor, arrows on both sides to loop through export default () => { const highlighter = Highlighter(); const mainBox = Box({ - // do this for scss hierarchy + // Do this for scss hierarchy className: 'overview', css: 'all: unset', vertical: true, vpack: 'center', hpack: 'center', + children: [ Box({ vertical: true, @@ -34,6 +36,7 @@ export default () => { WorkspaceRow('normal', 0), ], }), + Box({ vertical: true, children: [ @@ -41,12 +44,15 @@ export default () => { ], }), ], - connections: [[Hyprland, self => { - if (!App.getWindow('overview').visible) + + connections: [[Hyprland, (self) => { + if (!App.getWindow('overview').visible) { return; + } update(self, highlighter); }]], + properties: [ ['workspaces'], ], @@ -67,6 +73,7 @@ export default () => { min-width: ${mainBox.get_allocated_width()}px; `, }), + // TODO: throttle his? connections: [['get-child-position', (self, ch) => { if (ch === mainBox) { @@ -80,5 +87,6 @@ export default () => { }), }); + return window; }; diff --git a/devices/wim/config/ags/js/overview/workspaces.js b/devices/wim/config/ags/js/overview/workspaces.js index ee7693f9..255a34c5 100644 --- a/devices/wim/config/ags/js/overview/workspaces.js +++ b/devices/wim/config/ags/js/overview/workspaces.js @@ -1,114 +1,142 @@ import Hyprland from 'resource:///com/github/Aylur/ags/service/hyprland.js'; + import { Revealer, CenterBox, Box, EventBox, Fixed, Label } from 'resource:///com/github/Aylur/ags/widget.js'; import { WorkspaceDrop } from './dragndrop.js'; import * as VARS from './variables.js'; -const DEFAULT_STYLE = `min-width: ${VARS.SCREEN.X * VARS.SCALE}px; - min-height: ${VARS.SCREEN.Y * VARS.SCALE}px;`; +const DEFAULT_STYLE = ` + min-width: ${VARS.SCREEN.X * VARS.SCALE}px; + min-height: ${VARS.SCREEN.Y * VARS.SCALE}px; +`; -export function getWorkspaces(box) { +export const getWorkspaces = (box) => { const children = []; - box.children.forEach(type => { - type.children.forEach(row => { - row.child.centerWidget.child.children.forEach(ch => { + + box.children.forEach((type) => { + type.children.forEach((row) => { + row.child.centerWidget.child.children.forEach((ch) => { children.push(ch); }); }); }); box._workspaces = children.sort((a, b) => a._id - b._id); -} +}; -export const WorkspaceRow = (className, i) => Revealer({ - transition: 'slide_down', - hpack: className === 'special' ? '' : 'start', - connections: [[Hyprland, rev => { - const minId = i * VARS.WORKSPACE_PER_ROW; - const activeId = Hyprland.active.workspace.id; - - rev.revealChild = Hyprland.workspaces - .some(ws => ws.id > minId && - (ws.windows > 0 || ws.id === activeId)); - }]], - child: CenterBox({ - children: [null, EventBox({ - connections: [[Hyprland, eventbox => { - const maxId = i * VARS.WORKSPACE_PER_ROW + VARS.WORKSPACE_PER_ROW; - const activeId = Hyprland.active.workspace.id; - - eventbox.child.children[0].revealChild = className === 'special' || - !Hyprland.workspaces.some(ws => ws.id > maxId && - (ws.windows > 0 || ws.id === activeId)); - }]], - child: Box({ - className: className, - children: [ - // the 'add' workspace - Workspace(className === 'special' ? -1 : 1000, - className === 'special' ? 'special' : '', - true), - ], - }), - }), null], - }), -}); - -const Workspace = (id, name, extra = false) => { - let workspace; +const Workspace = (id, name, normal = true) => { const fixed = Fixed(); - if (!extra) { - workspace = Revealer({ - transition: 'slide_right', - transitionDuration: 500, - connections: [[Hyprland, box => { + const workspace = Revealer({ + transition: 'slide_right', + transitionDuration: 500, + + connections: normal ? + + [[Hyprland, (box) => { const activeId = Hyprland.active.workspace.id; const active = activeId === box._id; - box.revealChild = Hyprland.getWorkspace(box._id)?.windows > 0 || active; - }]], - child: WorkspaceDrop({ - child: Box({ - className: 'workspace', - css: DEFAULT_STYLE, - child: fixed, - }), - }), - }); - } - // 'add' workspace - else { - workspace = Revealer({ - transition: 'slide_right', - child: WorkspaceDrop({ - child: Box({ - css: `min-width: ${VARS.SCREEN.X * VARS.SCALE / 2}px; - min-height: ${VARS.SCREEN.Y * VARS.SCALE}px;`, - children: [ + box.revealChild = Hyprland.getWorkspace(box._id) + ?.windows > 0 || active; + }]] : + + [], + + child: WorkspaceDrop({ + child: Box({ + className: 'workspace', + css: normal ? + + DEFAULT_STYLE : + + ` + min-width: ${VARS.SCREEN.X * VARS.SCALE / 2}px; + min-height: ${VARS.SCREEN.Y * VARS.SCALE}px; + `, + + children: normal ? + + [fixed] : + + [ fixed, Label({ label: ' +', css: 'font-size: 40px;', }), ], - }), }), - }); - } + }), + }); workspace._id = id; workspace._name = name; workspace.getFixed = () => fixed; + return workspace; }; -export function updateWorkspaces(box) { - Hyprland.workspaces.forEach(ws => { - const currentWs = box._workspaces.find(ch => ch._id == ws.id); +export const WorkspaceRow = (className, i) => { + const addWorkspace = Workspace( + className === 'special' ? -1 : 1000, + className === 'special' ? 'special' : '', + false, + ); + + return Revealer({ + transition: 'slide_down', + hpack: className === 'special' ? '' : 'start', + + connections: [[Hyprland, (rev) => { + const minId = i * VARS.WORKSPACE_PER_ROW; + const activeId = Hyprland.active.workspace.id; + + const rowExists = Hyprland.workspaces.some((ws) => { + const isInRow = ws.id > minId; + const hasClients = ws.windows > 0; + const isActive = ws.id === activeId; + + return isInRow && (hasClients || isActive); + }); + + rev.revealChild = rowExists; + }]], + + child: CenterBox({ + children: [null, EventBox({ + connections: [[Hyprland, () => { + const maxId = (i + 1) * VARS.WORKSPACE_PER_ROW; + const activeId = Hyprland.active.workspace.id; + + const isSpecial = className === 'special'; + const nextRowExists = Hyprland.workspaces.some((ws) => { + const isInNextRow = ws.id > maxId; + const hasClients = ws.windows > 0; + const isActive = ws.id === activeId; + + return isInNextRow && (hasClients || isActive); + }); + + addWorkspace.revealChild = isSpecial || !nextRowExists; + }]], + + child: Box({ + className, + children: [addWorkspace], + }), + }), null], + }), + }); +}; + +export const updateWorkspaces = (box) => { + Hyprland.workspaces.forEach((ws) => { + const currentWs = box._workspaces.find((ch) => ch._id === ws.id); + if (!currentWs) { - var type = 0; - var rowNo = 0; + let type = 0; + let rowNo = 0; if (ws.id < 0) { // This means it's a special workspace @@ -116,12 +144,19 @@ export function updateWorkspaces(box) { } else { rowNo = Math.floor((ws.id - 1) / VARS.WORKSPACE_PER_ROW); - if (rowNo >= box.children[type].children.length) { - for (let i = box.children[type].children.length; i <= rowNo; ++i) - box.children[type].add(WorkspaceRow(type ? 'special' : 'normal', i)); + const wsQty = box.children[type].children.length; + + if (rowNo >= wsQty) { + for (let i = wsQty; i <= rowNo; ++i) { + box.children[type].add(WorkspaceRow( + type ? 'special' : 'normal', i, + )); + } } } - var row = box.children[type].children[rowNo].child.centerWidget.child; + const row = box.children[type].children[rowNo] + .child.centerWidget.child; + row.add(Workspace(ws.id, type ? ws.name : '')); } }); @@ -131,4 +166,4 @@ export function updateWorkspaces(box) { workspace.get_parent().reorder_child(workspace, i); }); box.show_all(); -} +}; diff --git a/devices/wim/config/ags/js/powermenu.js b/devices/wim/config/ags/js/powermenu.js index 7e0677a4..bc1b6a4f 100644 --- a/devices/wim/config/ags/js/powermenu.js +++ b/devices/wim/config/ags/js/powermenu.js @@ -1,9 +1,10 @@ import Hyprland from 'resource:///com/github/Aylur/ags/service/hyprland.js'; + import { CenterBox, Label } from 'resource:///com/github/Aylur/ags/widget.js'; import { execAsync } from 'resource:///com/github/Aylur/ags/utils.js'; import PopupWindow from './misc/popup.js'; -import Button from './misc/cursorbox.js'; +import Button from './misc/cursorbox.js'; const PowermenuWidget = () => CenterBox({ @@ -13,7 +14,8 @@ const PowermenuWidget = () => CenterBox({ startWidget: Button({ isButton: true, className: 'shutdown', - onPrimaryClickRelease: () => execAsync(['systemctl', 'poweroff']).catch(print), + onPrimaryClickRelease: () => execAsync(['systemctl', 'poweroff']) + .catch(print), child: Label({ label: '襤', @@ -23,7 +25,8 @@ const PowermenuWidget = () => CenterBox({ centerWidget: Button({ isButton: true, className: 'reboot', - onPrimaryClickRelease: () => execAsync(['systemctl', 'reboot']).catch(print), + onPrimaryClickRelease: () => execAsync(['systemctl', 'reboot']) + .catch(print), child: Label({ label: '勒', @@ -33,7 +36,8 @@ const PowermenuWidget = () => CenterBox({ endWidget: Button({ isButton: true, className: 'logout', - onPrimaryClickRelease: () => Hyprland.sendMessage('dispatch exit').catch(print), + onPrimaryClickRelease: () => Hyprland.sendMessage('dispatch exit') + .catch(print), child: Label({ label: '', diff --git a/devices/wim/config/ags/js/quick-settings/button-grid.js b/devices/wim/config/ags/js/quick-settings/button-grid.js index f4d2beed..1b80b5c2 100644 --- a/devices/wim/config/ags/js/quick-settings/button-grid.js +++ b/devices/wim/config/ags/js/quick-settings/button-grid.js @@ -1,33 +1,39 @@ -import App from 'resource:///com/github/Aylur/ags/app.js'; +import App from 'resource:///com/github/Aylur/ags/app.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 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 { SpeakerIcon, MicIcon } from '../misc/audio-icons.js'; -import EventBox from '../misc/cursorbox.js'; +import EventBox from '../misc/cursorbox.js'; import Separator from '../misc/separator.js'; +const SPACING = 28; + const ButtonStates = []; const GridButton = ({ - command = () => {}, - secondaryCommand = () => {}, + command = () => { /**/ }, + secondaryCommand = () => { /**/ }, icon, indicator, menu, } = {}) => { const Activated = Variable(false); + ButtonStates.push(Activated); - // allow setting icon dynamically or statically + // 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'};`); + icon, + connections: [[Activated, (self) => { + self.setCss(`color: ${Activated.value ? + 'rgba(189, 147, 249, 0.8)' : + 'unset'};`); }]], }); } @@ -36,8 +42,10 @@ const GridButton = ({ className: 'grid-label', connections: [ icon, - [Activated, self => { - self.setCss(`color: ${Activated.value ? 'rgba(189, 147, 249, 0.8)' : 'unset'};`); + [Activated, (self) => { + self.setCss(`color: ${Activated.value ? + 'rgba(189, 147, 249, 0.8)' : + 'unset'};`); }], ], }); @@ -70,44 +78,60 @@ const GridButton = ({ EventBox({ className: 'left-part', + onPrimaryClickRelease: () => { - if (!Activated.value) - command(); - else + if (Activated.value) { secondaryCommand(); + } + else { + command(); + } }, + child: icon, }), EventBox({ className: 'right-part', + onPrimaryClickRelease: () => { - ButtonStates.forEach(state => { - if (state !== Activated) + ButtonStates.forEach((state) => { + if (state !== Activated) { state.value = false; + } }); Activated.value = !Activated.value; }, - onHover: self => { + + 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)) { + const isSetup = rowMenu.get_children() + .find((ch) => ch === menu); + + if (!isSetup) { 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);`); - }]], + icon: `${App.configDir }/icons/down-large.svg`, className: 'grid-chev', + + connections: [[Activated, (self) => { + let deg = 270; + + if (Activated.value) { + deg = menu ? 360 : 450; + } + self.setCss(` + -gtk-icon-transform: rotate(${deg}deg); + `); + }]], }), }), @@ -116,30 +140,34 @@ const GridButton = ({ 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) { + if (i === buttons.length - 1) { widget.children[0].add(buttons[i]); - widget.children[0].add(Separator(28)); } else { widget.children[0].add(buttons[i]); + widget.children[0].add(Separator(SPACING)); } } + return widget; }; @@ -148,33 +176,49 @@ const FirstRow = () => Row({ 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], + + 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', - }), - ], - }), - })), - ]], + + 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', + }), + ], + }), + })); + }]], }), }), @@ -183,45 +227,54 @@ const FirstRow = () => Row({ execAsync(['bash', '-c', '$AGS_PATH/qs-toggles.sh blue-toggle']) .catch(print); }, + secondaryCommand: () => { execAsync(['bash', '-c', 'blueberry']) .catch(print); }, - icon: [Bluetooth, self => { + + icon: [Bluetooth, (self) => { if (Bluetooth.enabled) { self.icon = 'bluetooth-active-symbolic'; - execAsync(['bash', '-c', 'echo 󰂯 > $HOME/.config/.bluetooth']) - .catch(print); + execAsync(['bash', '-c', + 'echo 󰂯 > $HOME/.config/.bluetooth']).catch(print); } else { self.icon = 'bluetooth-disabled-symbolic'; - execAsync(['bash', '-c', 'echo 󰂲 > $HOME/.config/.bluetooth']) - .catch(print); + execAsync(['bash', '-c', + 'echo 󰂲 > $HOME/.config/.bluetooth']).catch(print); } }, 'changed'], - indicator: [Bluetooth, self => { - if (Bluetooth.connectedDevices[0]) + + indicator: [Bluetooth, (self) => { + if (Bluetooth.connectedDevices[0]) { self.label = String(Bluetooth.connectedDevices[0]); - else + } + else { self.label = 'Disconnected'; + } }, 'changed'], }), // TODO: replace with vpn GridButton({ command: () => { - execAsync(['bash', '-c', '$AGS_PATH/qs-toggles.sh toggle-radio']) - .catch(print); + 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) + + icon: [Network, (self) => { + if (Network.wifi.enabled) { self.icon = 'airplane-mode-disabled-symbolic'; - else + } + else { self.icon = 'airplane-mode-symbolic'; + } }, 'changed'], }), @@ -230,11 +283,10 @@ const FirstRow = () => Row({ const SecondRow = () => Row({ buttons: [ - GridButton({ command: () => { - execAsync(['pactl', 'set-sink-mute', '@DEFAULT_SINK@', 'toggle']) - .catch(print); + execAsync(['pactl', 'set-sink-mute', + '@DEFAULT_SINK@', 'toggle']).catch(print); }, secondaryCommand: () => { @@ -242,15 +294,15 @@ const SecondRow = () => Row({ .catch(print); }, - icon: [SpeakerIcon, self => { + icon: [SpeakerIcon, (self) => { self.icon = SpeakerIcon.value; }], }), GridButton({ command: () => { - execAsync(['pactl', 'set-source-mute', '@DEFAULT_SOURCE@', 'toggle']) - .catch(print); + execAsync(['pactl', 'set-source-mute', + '@DEFAULT_SOURCE@', 'toggle']).catch(print); }, secondaryCommand: () => { @@ -258,7 +310,7 @@ const SecondRow = () => Row({ .catch(print); }, - icon: [MicIcon, self => { + icon: [MicIcon, (self) => { self.icon = MicIcon.value; }], }), @@ -271,7 +323,6 @@ const SecondRow = () => Row({ secondaryCommand: () => App.openWindow('powermenu'), icon: 'system-lock-screen-symbolic', }), - ], }); diff --git a/devices/wim/config/ags/js/quick-settings/main.js b/devices/wim/config/ags/js/quick-settings/main.js index 43cafbe0..7873dbb9 100644 --- a/devices/wim/config/ags/js/quick-settings/main.js +++ b/devices/wim/config/ags/js/quick-settings/main.js @@ -1,9 +1,9 @@ import { Box, Label, Revealer } from 'resource:///com/github/Aylur/ags/widget.js'; -import ButtonGrid from './button-grid.js'; -import SliderBox from './slider-box.js'; -import Player from '../media-player/player.js'; -import PopupWindow from '../misc/popup.js'; +import ButtonGrid from './button-grid.js'; +import SliderBox from './slider-box.js'; +import Player from '../media-player/player.js'; +import PopupWindow from '../misc/popup.js'; import ToggleButton from './toggle-button.js'; @@ -48,9 +48,12 @@ const QuickSettingsWidget = () => { }); }; +const TOP_MARGIN = 6; +const RIGHT_MARGIN = 5; + export default () => PopupWindow({ name: 'quick-settings', anchor: ['top', 'right'], - margins: [6, 5, 0], + margins: [TOP_MARGIN, RIGHT_MARGIN, 0, 0], child: QuickSettingsWidget(), }); diff --git a/devices/wim/config/ags/js/quick-settings/slider-box.js b/devices/wim/config/ags/js/quick-settings/slider-box.js index 46deb103..faa5e924 100644 --- a/devices/wim/config/ags/js/quick-settings/slider-box.js +++ b/devices/wim/config/ags/js/quick-settings/slider-box.js @@ -1,6 +1,8 @@ import Audio from 'resource:///com/github/Aylur/ags/service/audio.js'; + import { Box, Slider, Icon } from 'resource:///com/github/Aylur/ags/widget.js'; +import Brightness from '../../services/brightness.js'; import { SpeakerIcon } from '../misc/audio-icons.js'; @@ -14,6 +16,7 @@ export default () => Box({ className: 'slider', vpack: 'start', hpack: 'center', + children: [ Icon({ size: 26, @@ -24,17 +27,25 @@ export default () => Box({ Slider({ cursor: 'pointer', vpack: 'center', + max: 0.999, + draw_value: false, + + onChange: ({ value }) => { + Audio.speaker.volume = value; + }, + connections: [ - [Audio, slider => { + [Audio, (slider) => { slider.value = Audio.speaker?.volume; }, 'speaker-changed'], - ['button-press-event', s => { s.cursor = 'grabbing'; }], - ['button-release-event', s => { s.cursor = 'pointer'; }], + ['button-press-event', (s) => { + s.cursor = 'grabbing'; + }], + ['button-release-event', (s) => { + s.cursor = 'pointer'; + }], ], - onChange: ({ value }) => Audio.speaker.volume = value, - max: 0.999, - draw_value: false, }), ], }), @@ -43,6 +54,7 @@ export default () => Box({ className: 'slider', vpack: 'start', hpack: 'center', + children: [ Icon({ className: 'slider-label', @@ -52,16 +64,24 @@ export default () => Box({ Slider({ cursor: 'pointer', vpack: 'center', - onChange: ({ value }) => Brightness.screen = value, + draw_value: false, + + onChange: ({ value }) => { + Brightness.screen = value; + }, + connections: [ - [Brightness, slider => { + [Brightness, (slider) => { slider.value = Brightness.screen; }, 'screen'], - ['button-press-event', s => { s.cursor = 'grabbing'; }], - ['button-release-event', s => { s.cursor = 'pointer'; }], + ['button-press-event', (s) => { + s.cursor = 'grabbing'; + }], + ['button-release-event', (s) => { + s.cursor = 'pointer'; + }], ], - draw_value: false, }), ], }), diff --git a/devices/wim/config/ags/js/quick-settings/toggle-button.js b/devices/wim/config/ags/js/quick-settings/toggle-button.js index 46e44b0a..7e913dc6 100644 --- a/devices/wim/config/ags/js/quick-settings/toggle-button.js +++ b/devices/wim/config/ags/js/quick-settings/toggle-button.js @@ -1,12 +1,14 @@ -import App from 'resource:///com/github/Aylur/ags/app.js'; +import App from 'resource:///com/github/Aylur/ags/app.js'; import Mpris from 'resource:///com/github/Aylur/ags/service/mpris.js'; + import { CenterBox, Icon, ToggleButton } from 'resource:///com/github/Aylur/ags/widget.js'; -export default rev => CenterBox({ +export default (rev) => CenterBox({ center_widget: ToggleButton({ cursor: 'pointer', - setup: self => { + + setup: (self) => { // Open at startup if there are players const id = Mpris.connect('changed', () => { self.set_active(Mpris.players.length > 0); @@ -14,7 +16,7 @@ export default rev => CenterBox({ }); }, - connections: [['toggled', self => { + connections: [['toggled', (self) => { if (self.get_active()) { self.get_children()[0] .setCss('-gtk-icon-transform: rotate(0deg);'); @@ -28,7 +30,7 @@ export default rev => CenterBox({ }]], child: Icon({ - icon: App.configDir + '/icons/down-large.svg', + icon: `${App.configDir }/icons/down-large.svg`, className: 'arrow', css: '-gtk-icon-transform: rotate(180deg);', }), diff --git a/devices/wim/config/ags/js/setup.js b/devices/wim/config/ags/js/setup.js index d9c90a1a..50987e50 100644 --- a/devices/wim/config/ags/js/setup.js +++ b/devices/wim/config/ags/js/setup.js @@ -1,10 +1,11 @@ import App from 'resource:///com/github/Aylur/ags/app.js'; import Hyprland from 'resource:///com/github/Aylur/ags/service/hyprland.js'; + import { execAsync } from 'resource:///com/github/Aylur/ags/utils.js'; -import Brightness from '../services/brightness.js'; -import Pointers from '../services/pointers.js'; -import Tablet from '../services/tablet.js'; +import Brightness from '../services/brightness.js'; +import Pointers from '../services/pointers.js'; +import Tablet from '../services/tablet.js'; import TouchGestures from '../services/touch-gestures.js'; import closeAll from './misc/closer.js'; @@ -28,27 +29,33 @@ export default () => { name: 'oskOn', gesture: 'DU', edge: 'B', - command: 'busctl call --user sm.puri.OSK0 /sm/puri/OSK0 sm.puri.OSK0 SetVisible b true', + command: 'busctl call --user sm.puri.OSK0 /sm/puri/OSK0 sm.puri.OSK0 ' + + 'SetVisible b true', }); TouchGestures.addGesture({ name: 'oskOff', gesture: 'UD', edge: 'B', - command: 'busctl call --user sm.puri.OSK0 /sm/puri/OSK0 sm.puri.OSK0 SetVisible b false', + command: 'busctl call --user sm.puri.OSK0 /sm/puri/OSK0 sm.puri.OSK0 ' + + 'SetVisible b false', }); TouchGestures.addGesture({ name: 'swipeSpotify1', gesture: 'LR', edge: 'L', - command: () => Hyprland.sendMessage('dispatch togglespecialworkspace spot'), + command: () => Hyprland.sendMessage( + 'dispatch togglespecialworkspace spot', + ), }); TouchGestures.addGesture({ name: 'swipeSpotify2', gesture: 'RL', edge: 'L', - command: () => Hyprland.sendMessage('dispatch togglespecialworkspace spot'), + command: () => Hyprland.sendMessage( + 'dispatch togglespecialworkspace spot', + ), }); }; diff --git a/devices/wim/config/ags/package.json b/devices/wim/config/ags/package.json index cf6608b2..e9a841a4 100644 --- a/devices/wim/config/ags/package.json +++ b/devices/wim/config/ags/package.json @@ -9,5 +9,8 @@ "@typescript-eslint/parser": "^6.9.1", "eslint": "^8.52.0", "stylelint-config-standard-scss": "^11.0.0" + }, + "devDependencies": { + "@stylistic/eslint-plugin": "^1.4.0" } } diff --git a/devices/wim/config/ags/services/brightness.js b/devices/wim/config/ags/services/brightness.js index 91fb5278..e94d5af1 100644 --- a/devices/wim/config/ags/services/brightness.js +++ b/devices/wim/config/ags/services/brightness.js @@ -1,42 +1,54 @@ -import Service from 'resource:///com/github/Aylur/ags/service.js'; +import Service from 'resource:///com/github/Aylur/ags/service.js'; import Variable from 'resource:///com/github/Aylur/ags/variable.js'; import { exec, execAsync } from 'resource:///com/github/Aylur/ags/utils.js'; const KBD = 'tpacpi::kbd_backlight'; const CAPS = 'input0::capslock'; +const INTERVAL = 500; class Brightness extends Service { static { Service.register(this, { - 'screen': ['float'], - 'kbd': ['float'], - 'caps': ['int'], + screen: ['float'], + kbd: ['float'], + caps: ['int'], }); } - _kbd = 0; - _screen = 0; - _caps = 0; + #kbd = 0; + #screen = 0; + #caps = 0; - get kbd() { return this._kbd; } - get screen() { return this._screen; } - get caps() { return this._caps; } + get kbd() { + return this.#kbd; + } + + get screen() { + return this.#screen; + } + + get caps() { + return this.#caps; + } set kbd(value) { + this.#kbd = value; // TODO } set screen(percent) { - if (percent < 0) + if (percent < 0) { percent = 0; + } - if (percent > 1) + if (percent > 1) { percent = 1; + } execAsync(`brightnessctl s ${percent * 100}% -q`) .then(() => { - this._screen = percent; - this.emit('screen', this._screen); + this.#screen = percent; + this.emit('screen', this.#screen); }) .catch(console.error); } @@ -44,29 +56,31 @@ class Brightness extends Service { constructor() { super(); try { - this.monitorKbdState(); - this._caps = Number(exec(`brightnessctl -d ${CAPS} g`)); - this._screen = Number(exec('brightnessctl g')) / Number(exec('brightnessctl m')); - } catch (error) { + this.#monitorKbdState(); + this.#caps = Number(exec(`brightnessctl -d ${CAPS} g`)); + this.#screen = Number(exec('brightnessctl g')) / + Number(exec('brightnessctl m')); + } + catch (error) { console.error('missing dependancy: brightnessctl'); } } fetchCapsState() { execAsync(`brightnessctl -d ${CAPS} g`) - .then(out => { - this._caps = out; - this.emit('caps', this._caps); + .then((out) => { + this.#caps = out; + this.emit('caps', this.#caps); }) .catch(logError); } - monitorKbdState() { + #monitorKbdState() { Variable(0, { - poll: [500, `brightnessctl -d ${KBD} g`, out => { - if (out !== this._kbd) { - this._kbd = out; - this.emit('kbd', this._kbd); + poll: [INTERVAL, `brightnessctl -d ${KBD} g`, (out) => { + if (out !== this.#kbd) { + this.#kbd = out; + this.emit('kbd', this.#kbd); } }], }); @@ -74,4 +88,5 @@ class Brightness extends Service { } const brightnessService = new Brightness(); + export default brightnessService; diff --git a/devices/wim/config/ags/services/pointers.js b/devices/wim/config/ags/services/pointers.js index 74617be1..c9159c20 100644 --- a/devices/wim/config/ags/services/pointers.js +++ b/devices/wim/config/ags/services/pointers.js @@ -1,4 +1,4 @@ -import App from 'resource:///com/github/Aylur/ags/app.js'; +import App from 'resource:///com/github/Aylur/ags/app.js'; import Hyprland from 'resource:///com/github/Aylur/ags/service/hyprland.js'; import Service from 'resource:///com/github/Aylur/ags/service.js'; import { subprocess } from 'resource:///com/github/Aylur/ags/utils.js'; @@ -34,28 +34,36 @@ class Pointers extends Service { }); } - proc = undefined; - output = ''; - devices = []; - udevClient = GUdev.Client.new(['input']); + #process; + #lastLine = ''; + #pointers = []; + #udevClient = GUdev.Client.new(['input']); - get process() { return this.proc; } - get lastLine() { return this.output; } - get pointers() { return this.devices; } + get process() { + return this.#process; + } + + get lastLine() { + return this.#lastLine; + } + + get pointers() { + return this.#pointers; + } constructor() { super(); - this.initUdevConnection(); - this.initAppConnection(); + this.#initUdevConnection(); + this.#initAppConnection(); } // FIXME: logitech mouse screws everything up on disconnect - initUdevConnection() { - this.getDevices(); - this.udevClient.connect('uevent', (_, action) => { + #initUdevConnection() { + this.#getDevices(); + this.#udevClient.connect('uevent', (_, action) => { if (action === 'add' || action === 'remove') { this.getDevices(); - if (this.proc) { + if (this.#process) { this.killProc(); this.startProc(); } @@ -63,15 +71,19 @@ class Pointers extends Service { }); } - getDevices() { - this.devices = []; - this.udevClient.query_by_subsystem('input').forEach(dev => { - const isPointer = UDEV_POINTERS.some(p => dev.has_property(p)); + #getDevices() { + this.#pointers = []; + this.#udevClient.query_by_subsystem('input').forEach((dev) => { + const isPointer = UDEV_POINTERS.some((p) => dev.has_property(p)); + if (isPointer) { const hasEventFile = dev.has_property('DEVNAME') && - dev.get_property('DEVNAME').includes('event'); - if (hasEventFile) - this.devices.push(dev.get_property('DEVNAME')); + dev.get_property('DEVNAME') + .includes('event'); + + if (hasEventFile) { + this.#pointers.push(dev.get_property('DEVNAME')); + } } }); @@ -79,51 +91,54 @@ class Pointers extends Service { } startProc() { - if (this.proc) + if (this.#process) { return; + } const args = []; - this.devices.forEach(dev => { + + this.#pointers.forEach((dev) => { args.push('--device'); args.push(dev); }); - this.proc = subprocess( + this.#process = subprocess( ['libinput', 'debug-events', ...args], - output => { - if (output.includes('cancelled')) + (output) => { + if (output.includes('cancelled')) { return; + } - if (ON_RELEASE_TRIGGERS.some(p => output.includes(p))) { - this.output = output; - this.detectClickedOutside('released'); + if (ON_RELEASE_TRIGGERS.some((p) => output.includes(p))) { + this.#lastLine = output; + Pointers.detectClickedOutside('released'); this.emit('released', output); this.emit('new-line', output); } - if (ON_CLICK_TRIGGERS.some(p => output.includes(p))) { - this.output = output; - this.detectClickedOutside('clicked'); + if (ON_CLICK_TRIGGERS.some((p) => output.includes(p))) { + this.#lastLine = output; + Pointers.detectClickedOutside('clicked'); this.emit('clicked', output); this.emit('new-line', output); } }, - err => logError(err), + (err) => logError(err), ); this.emit('proc-started', true); } killProc() { - if (this.proc) { - this.proc.force_exit(); - this.proc = undefined; + if (this.#process) { + this.#process.force_exit(); + this.#process = null; this.emit('proc-destroyed', true); } } - initAppConnection() { + #initAppConnection() { App.connect('window-toggled', () => { - const anyVisibleAndClosable = Array.from(App.windows).some(w => { + const anyVisibleAndClosable = Array.from(App.windows).some((w) => { const closable = w[1].closeOnUnfocus && !(w[1].closeOnUnfocus === 'none' || w[1].closeOnUnfocus === 'stay'); @@ -131,51 +146,57 @@ class Pointers extends Service { return w[1].visible && closable; }); - if (anyVisibleAndClosable) + if (anyVisibleAndClosable) { this.startProc(); + } - else + else { this.killProc(); + } }); } - detectClickedOutside(clickStage) { - const toClose = Array.from(App.windows).some(w => { + static detectClickedOutside(clickStage) { + const toClose = Array.from(App.windows).some((w) => { const closable = (w[1].closeOnUnfocus && w[1].closeOnUnfocus === clickStage); return w[1].visible && closable; }); - if (!toClose) - return; - Hyprland.sendMessage('j/layers').then(layers => { + if (!toClose) { + return; + } + + Hyprland.sendMessage('j/layers').then((layers) => { layers = JSON.parse(layers); - Hyprland.sendMessage('j/cursorpos').then(pos => { + Hyprland.sendMessage('j/cursorpos').then((pos) => { pos = JSON.parse(pos); - Object.values(layers).forEach(key => { - const bar = key['levels']['3'] - .find(n => n.namespace === 'bar'); + Object.values(layers).forEach((key) => { + const bar = key.levels['3'] + .find((n) => n.namespace === 'bar'); - const widgets = key['levels']['3'].filter(n => { + const widgets = key.levels['3'].filter((n) => { const window = App.getWindow(n.namespace); - return window.closeOnUnfocus && - window.closeOnUnfocus === clickStage; + + return window?.closeOnUnfocus && + window?.closeOnUnfocus === clickStage; }); if (pos.x > bar.x && pos.x < bar.x + bar.w && pos.y > bar.y && pos.y < bar.y + bar.h) { - // don't handle clicks when on bar + // Don't handle clicks when on bar // TODO: make this configurable } else { - widgets.forEach(w => { + widgets.forEach((w) => { if (!(pos.x > w.x && pos.x < w.x + w.w && - pos.y > w.y && pos.y < w.y + w.h)) + pos.y > w.y && pos.y < w.y + w.h)) { App.closeWindow(w.namespace); + } }); } }); @@ -184,5 +205,4 @@ class Pointers extends Service { } } -const pointersService = new Pointers(); -export default pointersService; +export default new Pointers(); diff --git a/devices/wim/config/ags/services/tablet.js b/devices/wim/config/ags/services/tablet.js index 70adb63a..56af30fb 100644 --- a/devices/wim/config/ags/services/tablet.js +++ b/devices/wim/config/ags/services/tablet.js @@ -4,7 +4,7 @@ import TouchGestures from './touch-gestures.js'; import { execAsync, subprocess } from 'resource:///com/github/Aylur/ags/utils.js'; import GUdev from 'gi://GUdev'; -const ROTATION_MAPPING = { +const ROTATION_MAP = { 'normal': 0, 'right-up': 3, 'bottom-up': 2, @@ -28,42 +28,47 @@ class Tablet extends Service { }); } - tabletMode = false; - autorotate = undefined; - oskState = false; - blockedInputs = undefined; - udevClient = GUdev.Client.new(['input']); + #tabletMode = false; + #oskState = false; + #autorotate; + #blockedInputs; + #udevClient = GUdev.Client.new(['input']); - get tabletMode() { return this.tabletMode; } - get oskState() { return this.oskState; } + get tabletMode() { + return this.#tabletMode; + } + + get oskState() { + return this.#oskState; + } constructor() { super(); - this.initUdevConnection(); - this.listenOskState(); + this.#initUdevConnection(); + this.#listenOskState(); } - blockInputs() { - if (this.blockedInputs) + #blockInputs() { + if (this.#blockedInputs) { return; + } - this.blockedInputs = subprocess(['libinput', 'debug-events', '--grab', + this.#blockedInputs = subprocess(['libinput', 'debug-events', '--grab', '--device', '/dev/input/by-path/platform-i8042-serio-0-event-kbd', '--device', '/dev/input/by-path/platform-i8042-serio-1-event-mouse', '--device', '/dev/input/by-path/platform-AMDI0010:02-event-mouse', '--device', '/dev/input/by-path/platform-thinkpad_acpi-event', '--device', '/dev/video-bus', - '--device', '/dev/touchpad', - ], - () => {}, - err => logError(err)); + '--device', '/dev/touchpad'], + () => { /**/ }, + (err) => logError(err)); this.emit('inputs-blocked', true); } - unblockInputs() { - if (this.blockedInputs) { - this.blockedInputs.force_exit(); - this.blockedInputs = undefined; + #unblockInputs() { + if (this.#blockedInputs) { + this.#blockedInputs.force_exit(); + this.#blockedInputs = null; this.emit('inputs-unblocked', true); } } @@ -76,9 +81,9 @@ class Tablet extends Service { .catch(print); this.startAutorotate(); - this.blockInputs(); + this.#blockInputs(); - this.tabletMode = true; + this.#tabletMode = true; this.emit('tablet-mode', true); this.emit('mode-toggled', true); } @@ -91,38 +96,42 @@ class Tablet extends Service { .catch(print); this.killAutorotate(); - this.unblockInputs(); + this.#unblockInputs(); - this.tabletMode = false; + this.#tabletMode = false; this.emit('laptop-mode', true); this.emit('mode-toggled', true); } toggleMode() { - if (this.tabletMode) + if (this.#tabletMode) { this.setLaptopMode(); - else + } + else { this.setTabletMode(); + } this.emit('mode-toggled', true); } - initUdevConnection() { - this.getDevices(); - this.udevClient.connect('uevent', (_, action) => { - if (action === 'add' || action === 'remove') - this.getDevices(); + #initUdevConnection() { + this.#getDevices(); + this.#udevClient.connect('uevent', (_, action) => { + if (action === 'add' || action === 'remove') { + this.#getDevices(); + } }); } - getDevices() { + #getDevices() { this.devices = []; - Hyprland.sendMessage('j/devices').then(out => { + Hyprland.sendMessage('j/devices').then((out) => { const devices = JSON.parse(out); - devices['touch'].forEach(dev => { + + devices.touch.forEach((dev) => { this.devices.push(dev.name); }); - devices['tablets'].forEach(dev => { + devices.tablets.forEach((dev) => { this.devices.push(dev.name); }); }).catch(print); @@ -131,20 +140,23 @@ class Tablet extends Service { } startAutorotate() { - if (this.autorotate) + if (this.#autorotate) { return; + } - this.autorotate = subprocess( + this.#autorotate = subprocess( ['monitor-sensor'], - output => { + (output) => { if (output.includes('orientation changed')) { - const orientation = ROTATION_MAPPING[output.split(' ').at(-1)]; + const orientation = ROTATION_MAP[output.split(' ').at(-1)]; - Hyprland.sendMessage(`keyword monitor ${SCREEN},transform,${orientation}`) - .catch(print); + Hyprland.sendMessage( + `keyword monitor ${SCREEN},transform,${orientation}`, + ).catch(print); - const batchRotate = this.devices.map(dev => + const batchRotate = this.devices.map((dev) => `keyword device:${dev}:transform ${orientation}; `); + Hyprland.sendMessage(`[[BATCH]] ${batchRotate.flat()}`); if (TouchGestures.gestureDaemon) { @@ -153,40 +165,40 @@ class Tablet extends Service { } } }, - err => logError(err), + (err) => logError(err), ); this.emit('autorotate-started', true); } killAutorotate() { - if (this.autorotate) { - this.autorotate.force_exit(); - this.autorotate = undefined; + if (this.#autorotate) { + this.#autorotate.force_exit(); + this.#autorotate = null; this.emit('autorotate-destroyed', true); } } - listenOskState() { + #listenOskState() { subprocess( ['bash', '-c', 'busctl monitor --user sm.puri.OSK0'], - output => { + (output) => { if (output.includes('BOOLEAN')) { - this.oskState = output.match('true|false')[0] === 'true'; - this.emit('osk-toggled', this.oskState); + this.#oskState = output.match('true|false')[0] === 'true'; + this.emit('osk-toggled', this.#oskState); } }, - err => logError(err), + (err) => logError(err), ); } - openOsk() { + static openOsk() { execAsync(['busctl', 'call', '--user', 'sm.puri.OSK0', '/sm/puri/OSK0', 'sm.puri.OSK0', 'SetVisible', 'b', 'true']) .catch(print); } - closeOsk() { + static closeOsk() { execAsync(['busctl', 'call', '--user', 'sm.puri.OSK0', '/sm/puri/OSK0', 'sm.puri.OSK0', 'SetVisible', 'b', 'false']) @@ -194,12 +206,15 @@ class Tablet extends Service { } toggleOsk() { - if (this.oskState) - this.closeOsk(); - else - this.openOsk(); + if (this.#oskState) { + Tablet.closeOsk(); + } + else { + Tablet.openOsk(); + } } } const tabletService = new Tablet(); + export default tabletService; diff --git a/devices/wim/config/ags/services/touch-gestures.js b/devices/wim/config/ags/services/touch-gestures.js index 0dcd1912..2e6d5e7a 100644 --- a/devices/wim/config/ags/services/touch-gestures.js +++ b/devices/wim/config/ags/services/touch-gestures.js @@ -14,22 +14,22 @@ const GESTURE_VERIF = [ 'ULDR', // Up to Left to Down to Right (counter-clockwise from Up) ]; const EDGE_VERIF = [ - '*', // any - 'N', // none - 'L', // left - 'R', // right - 'T', // top - 'B', // bottom - 'TL', // top left - 'TR', // top right - 'BL', // bottom left - 'BR', // bottom right + '*', // Any + 'N', // None + 'L', // Left + 'R', // Right + 'T', // Top + 'B', // Bottom + 'TL', // Top left + 'TR', // Top right + 'BL', // Bottom left + 'BR', // Bottom right ]; const DISTANCE_VERIF = [ - '*', // any - 'S', // short - 'M', // medium - 'L', // large + '*', // Any + 'S', // Short + 'M', // Medium + 'L', // Large ]; @@ -43,13 +43,11 @@ class TouchGestures extends Service { }); } - gestures = new Map(); - gestureDaemon = undefined; + #gestures = new Map(); + #gestureDaemon; - get gestures() { return this.gestures; } - - constructor() { - super(); + get gestures() { + return this.#gestures; } addGesture({ @@ -63,18 +61,21 @@ class TouchGestures extends Service { gesture = String(gesture).toUpperCase(); if (!GESTURE_VERIF.includes(gesture)) { logError('Wrong gesture id'); + return; } edge = String(edge).toUpperCase(); if (!EDGE_VERIF.includes(edge)) { logError('Wrong edge id'); + return; } distance = String(distance).toUpperCase(); if (!DISTANCE_VERIF.includes(distance)) { logError('Wrong distance id'); + return; } @@ -83,23 +84,25 @@ class TouchGestures extends Service { command = `ags -r "${name}()"`; } - this.gestures.set(name, [ + this.#gestures.set(name, [ '-g', `${nFingers},${gesture},${edge},${distance},${command}`, ]); - if (this.gestureDaemon) + if (this.#gestureDaemon) { this.killDaemon(); + } this.startDaemon(); } startDaemon() { - if (this.gestureDaemon) + if (this.#gestureDaemon) { return; + } - var command = [ + let command = [ 'lisgd', '-d', SCREEN, - // orientation + // Orientation '-o', '0', // Threshold of gesture recognized '-t', '125', @@ -109,26 +112,27 @@ class TouchGestures extends Service { '-m', '3200', ]; - this.gestures.forEach(gesture => { + this.#gestures.forEach((gesture) => { command = command.concat(gesture); }); - this.gestureDaemon = subprocess( + this.#gestureDaemon = subprocess( command, - () => {}, - err => logError(err), + () => { /**/ }, + (err) => logError(err), ); this.emit('daemon-started', true); } killDaemon() { - if (this.gestureDaemon) { - this.gestureDaemon.force_exit(); - this.gestureDaemon = undefined; + if (this.#gestureDaemon) { + this.#gestureDaemon.force_exit(); + this.#gestureDaemon = null; this.emit('daemon-destroyed', true); } } } const touchGesturesService = new TouchGestures(); + export default touchGesturesService;