refactor(ags lint): customise eslint and apply to config
This commit is contained in:
parent
a645fc5857
commit
71108d1852
59 changed files with 1690 additions and 936 deletions
|
@ -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",
|
||||
|
|
|
@ -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,
|
||||
],
|
||||
}),
|
||||
});
|
||||
};
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}]],
|
||||
});
|
||||
};
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
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';
|
||||
|
||||
|
@ -7,27 +8,32 @@ import Separator from '../../misc/separator.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(),
|
||||
],
|
||||
}),
|
||||
|
|
|
@ -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(),
|
||||
],
|
||||
});
|
||||
|
|
|
@ -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';
|
||||
|
||||
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(),
|
||||
],
|
||||
}),
|
||||
|
|
|
@ -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({
|
||||
} = {}) => {
|
||||
return Label({
|
||||
...props,
|
||||
className: 'clock',
|
||||
connections: [
|
||||
[interval, self => {
|
||||
var time = DateTime.new_now_local();
|
||||
|
||||
connections: [[interval, (self) => {
|
||||
const time = DateTime.new_now_local();
|
||||
|
||||
self.label = time.format('%a. ') +
|
||||
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(),
|
||||
});
|
||||
|
|
|
@ -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';
|
||||
|
||||
|
||||
|
|
|
@ -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}`;
|
||||
},
|
||||
);
|
||||
},
|
||||
}),
|
||||
|
|
|
@ -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']],
|
||||
}),
|
||||
],
|
||||
|
|
|
@ -1,44 +1,52 @@
|
|||
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';
|
||||
|
||||
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)
|
||||
else if (Notifications.notifications.length > 0) {
|
||||
self.icon = 'notification-new-symbolic';
|
||||
else
|
||||
}
|
||||
else {
|
||||
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)]],
|
||||
}),
|
||||
|
||||
],
|
||||
|
|
|
@ -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']],
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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({
|
||||
export default () => {
|
||||
const systray = SysTray();
|
||||
|
||||
return Revealer({
|
||||
transition: 'slide_right',
|
||||
connections: [[SystemTray, rev => {
|
||||
rev.revealChild = rev.child.children[0].get_children().length > 0;
|
||||
|
||||
connections: [[SystemTray, (rev) => {
|
||||
rev.revealChild = systray.get_children().length > 0;
|
||||
}]],
|
||||
|
||||
child: Box({
|
||||
children: [
|
||||
Box({
|
||||
className: 'sys-tray',
|
||||
children: [
|
||||
SysTray(),
|
||||
],
|
||||
children: [systray],
|
||||
}),
|
||||
Separator(12),
|
||||
|
||||
Separator(SPACING),
|
||||
],
|
||||
}),
|
||||
});
|
||||
};
|
||||
|
|
|
@ -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']],
|
||||
|
||||
|
|
|
@ -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));
|
||||
}]],
|
||||
}),
|
||||
}),
|
||||
|
|
|
@ -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;',
|
||||
}),
|
||||
|
|
|
@ -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 Audio from './buttons/audio.js';
|
||||
import Battery from './buttons/battery.js';
|
||||
import Brightness from './buttons/brightness.js';
|
||||
import Audio from './buttons/audio.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(),
|
||||
],
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { Box, DrawingArea } from 'resource:///com/github/Aylur/ags/widget.js';
|
||||
|
||||
import Gtk from 'gi://Gtk';
|
||||
const Lang = imports.lang;
|
||||
|
||||
|
@ -19,25 +20,27 @@ export default (
|
|||
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) {
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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({
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
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';
|
||||
|
@ -9,10 +10,11 @@ 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))
|
||||
return;
|
||||
|
||||
// Sometimes the signal doesn't give the busName
|
||||
if (!busName) {
|
||||
const player = Mpris.players.find(p => !overlay._players.has(p.busName));
|
||||
if (player)
|
||||
busName = player.busName;
|
||||
else
|
||||
if (overlay._players.has(busName)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Sometimes the signal doesn't give the busName
|
||||
if (!busName) {
|
||||
const player = Mpris.players.find((p) => {
|
||||
return !overlay._players.has(p.busName);
|
||||
});
|
||||
|
||||
if (player) {
|
||||
busName = player.busName;
|
||||
}
|
||||
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'],
|
||||
],
|
||||
}),
|
||||
|
|
|
@ -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];
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -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]);
|
||||
});
|
||||
};
|
||||
|
|
|
@ -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() ||
|
||||
|
|
|
@ -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;
|
||||
css: `
|
||||
min-height:1px;
|
||||
min-width:1px;
|
||||
padding: 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;
|
||||
|
|
|
@ -1,17 +1,13 @@
|
|||
import { Box } from 'resource:///com/github/Aylur/ags/widget.js';
|
||||
|
||||
|
||||
export default (size, { vertical = false, css = '', ...props } = {}) => {
|
||||
if (vertical) {
|
||||
export default (size, {
|
||||
vertical = false,
|
||||
css = '',
|
||||
...props
|
||||
} = {}) => {
|
||||
return Box({
|
||||
css: `min-height: ${size}px; ${css}`,
|
||||
css: `${vertical ? 'min-height' : 'min-width'}: ${size}px; ${css}`,
|
||||
...props,
|
||||
});
|
||||
}
|
||||
else {
|
||||
return Box({
|
||||
css: `min-width: ${size}px; ${css}`,
|
||||
...props,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
|
@ -2,54 +2,62 @@ import Applications from 'resource:///com/github/Aylur/ags/service/applications.
|
|||
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 { 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;
|
||||
};
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
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';
|
||||
|
@ -7,9 +8,7 @@ import EventBox from '../misc/cursorbox.js';
|
|||
|
||||
|
||||
const addNotif = (box, notif) => {
|
||||
if (!notif)
|
||||
return;
|
||||
|
||||
if (notif) {
|
||||
const NewNotif = Notification({
|
||||
notif,
|
||||
slideIn: 'Right',
|
||||
|
@ -20,6 +19,7 @@ const addNotif = (box, notif) => {
|
|||
box.pack_end(NewNotif, false, false, 0);
|
||||
box.show_all();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const NotificationList = () => Box({
|
||||
|
@ -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(),
|
||||
});
|
||||
|
|
|
@ -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 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 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'],
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
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';
|
||||
|
||||
|
@ -7,10 +8,13 @@ import GLib from 'gi://GLib';
|
|||
import { Notification } from './base.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'],
|
||||
],
|
||||
}),
|
||||
});
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 }),
|
||||
],
|
||||
})),
|
||||
}),
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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']],
|
||||
|
|
|
@ -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']],
|
||||
|
|
|
@ -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']],
|
||||
|
|
|
@ -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']],
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
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';
|
||||
|
||||
|
@ -11,6 +12,8 @@ import CapsLock from './caps.js';
|
|||
import Keyboard from './kbd.js';
|
||||
import Microphone from './mic.js';
|
||||
|
||||
const HIDE_DELAY = 2000;
|
||||
|
||||
|
||||
export default () => {
|
||||
let setup = 0;
|
||||
|
@ -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;
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -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']],
|
||||
|
|
|
@ -1,21 +1,23 @@
|
|||
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);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
`);
|
||||
};
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
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;
|
||||
};
|
||||
|
|
|
@ -1,91 +1,65 @@
|
|||
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({
|
||||
const workspace = Revealer({
|
||||
transition: 'slide_right',
|
||||
transitionDuration: 500,
|
||||
connections: [[Hyprland, box => {
|
||||
|
||||
connections: normal ?
|
||||
|
||||
[[Hyprland, (box) => {
|
||||
const activeId = Hyprland.active.workspace.id;
|
||||
const active = activeId === box._id;
|
||||
|
||||
box.revealChild = Hyprland.getWorkspace(box._id)?.windows > 0 || active;
|
||||
}]],
|
||||
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: [
|
||||
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: ' +',
|
||||
|
@ -95,20 +69,74 @@ const Workspace = (id, name, extra = false) => {
|
|||
}),
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
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';
|
||||
|
||||
|
@ -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: '',
|
||||
|
|
|
@ -2,6 +2,7 @@ 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 { Box, Icon, Label, Revealer } from 'resource:///com/github/Aylur/ags/widget.js';
|
||||
import { execAsync } from 'resource:///com/github/Aylur/ags/utils.js';
|
||||
|
||||
|
@ -9,25 +10,30 @@ import { SpeakerIcon, MicIcon } from '../misc/audio-icons.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,24 +176,40 @@ 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({
|
||||
|
||||
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),
|
||||
|
||||
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,
|
||||
|
@ -173,8 +217,8 @@ const FirstRow = () => Row({
|
|||
}),
|
||||
],
|
||||
}),
|
||||
})),
|
||||
]],
|
||||
}));
|
||||
}]],
|
||||
}),
|
||||
}),
|
||||
|
||||
|
@ -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',
|
||||
}),
|
||||
|
||||
],
|
||||
});
|
||||
|
||||
|
|
|
@ -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(),
|
||||
});
|
||||
|
|
|
@ -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,
|
||||
}),
|
||||
],
|
||||
}),
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
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);',
|
||||
}),
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
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';
|
||||
|
@ -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',
|
||||
),
|
||||
});
|
||||
};
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,39 +4,51 @@ 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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue