mirror of
https://github.com/xsghetti/HyprCrux.git
synced 2025-07-03 13:50:38 -04:00
updates
This commit is contained in:
parent
1f8cb3c145
commit
610604e80f
253 changed files with 27055 additions and 44 deletions
106
.config/ags/modules/.commonwidgets/cairo_circularprogress.js
Normal file
106
.config/ags/modules/.commonwidgets/cairo_circularprogress.js
Normal file
|
@ -0,0 +1,106 @@
|
|||
const { Gtk } = imports.gi;
|
||||
const Lang = imports.lang;
|
||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js'
|
||||
import * as Utils from 'resource:///com/github/Aylur/ags/utils.js'
|
||||
|
||||
// -- Styling --
|
||||
// min-height for diameter
|
||||
// min-width for trough stroke
|
||||
// padding for space between trough and progress
|
||||
// margin for space between widget and parent
|
||||
// background-color for trough color
|
||||
// color for progress color
|
||||
// -- Usage --
|
||||
// font size for progress value (0-100px) (hacky i know, but i want animations)
|
||||
export const AnimatedCircProg = ({
|
||||
initFrom = 0,
|
||||
initTo = 0,
|
||||
initAnimTime = 2900,
|
||||
initAnimPoints = 1,
|
||||
extraSetup = () => { },
|
||||
...rest
|
||||
}) => Widget.DrawingArea({
|
||||
...rest,
|
||||
css: `${initFrom != initTo ? 'font-size: ' + initFrom + 'px; transition: ' + initAnimTime + 'ms linear;' : ''}`,
|
||||
setup: (area) => {
|
||||
const styleContext = area.get_style_context();
|
||||
const width = styleContext.get_property('min-height', Gtk.StateFlags.NORMAL);
|
||||
const height = styleContext.get_property('min-height', Gtk.StateFlags.NORMAL);
|
||||
const padding = styleContext.get_padding(Gtk.StateFlags.NORMAL).left;
|
||||
const marginLeft = styleContext.get_margin(Gtk.StateFlags.NORMAL).left;
|
||||
const marginRight = styleContext.get_margin(Gtk.StateFlags.NORMAL).right;
|
||||
const marginTop = styleContext.get_margin(Gtk.StateFlags.NORMAL).top;
|
||||
const marginBottom = styleContext.get_margin(Gtk.StateFlags.NORMAL).bottom;
|
||||
area.set_size_request(width + marginLeft + marginRight, height + marginTop + marginBottom);
|
||||
area.connect('draw', Lang.bind(area, (area, cr) => {
|
||||
const styleContext = area.get_style_context();
|
||||
const width = styleContext.get_property('min-height', Gtk.StateFlags.NORMAL);
|
||||
const height = styleContext.get_property('min-height', Gtk.StateFlags.NORMAL);
|
||||
const padding = styleContext.get_padding(Gtk.StateFlags.NORMAL).left;
|
||||
const marginLeft = styleContext.get_margin(Gtk.StateFlags.NORMAL).left;
|
||||
const marginRight = styleContext.get_margin(Gtk.StateFlags.NORMAL).right;
|
||||
const marginTop = styleContext.get_margin(Gtk.StateFlags.NORMAL).top;
|
||||
const marginBottom = styleContext.get_margin(Gtk.StateFlags.NORMAL).bottom;
|
||||
area.set_size_request(width + marginLeft + marginRight, height + marginTop + marginBottom);
|
||||
|
||||
const progressValue = styleContext.get_property('font-size', Gtk.StateFlags.NORMAL) / 100.0;
|
||||
|
||||
const bg_stroke = styleContext.get_property('min-width', Gtk.StateFlags.NORMAL);
|
||||
const fg_stroke = bg_stroke - padding;
|
||||
const radius = Math.min(width, height) / 2.0 - Math.max(bg_stroke, fg_stroke) / 2.0;
|
||||
const center_x = width / 2.0 + marginLeft;
|
||||
const center_y = height / 2.0 + marginTop;
|
||||
const start_angle = -Math.PI / 2.0;
|
||||
const end_angle = start_angle + (2 * Math.PI * progressValue);
|
||||
const start_x = center_x + Math.cos(start_angle) * radius;
|
||||
const start_y = center_y + Math.sin(start_angle) * radius;
|
||||
const end_x = center_x + Math.cos(end_angle) * radius;
|
||||
const end_y = center_y + Math.sin(end_angle) * radius;
|
||||
|
||||
// Draw background
|
||||
const background_color = styleContext.get_property('background-color', Gtk.StateFlags.NORMAL);
|
||||
cr.setSourceRGBA(background_color.red, background_color.green, background_color.blue, background_color.alpha);
|
||||
cr.arc(center_x, center_y, radius, 0, 2 * Math.PI);
|
||||
cr.setLineWidth(bg_stroke);
|
||||
cr.stroke();
|
||||
|
||||
if (progressValue == 0) return;
|
||||
|
||||
// Draw progress
|
||||
const color = styleContext.get_property('color', Gtk.StateFlags.NORMAL);
|
||||
cr.setSourceRGBA(color.red, color.green, color.blue, color.alpha);
|
||||
cr.arc(center_x, center_y, radius, start_angle, end_angle);
|
||||
cr.setLineWidth(fg_stroke);
|
||||
cr.stroke();
|
||||
|
||||
// Draw rounded ends for progress arcs
|
||||
cr.setLineWidth(0);
|
||||
cr.arc(start_x, start_y, fg_stroke / 2, 0, 0 - 0.01);
|
||||
cr.fill();
|
||||
cr.arc(end_x, end_y, fg_stroke / 2, 0, 0 - 0.01);
|
||||
cr.fill();
|
||||
}));
|
||||
|
||||
// Init animation
|
||||
if (initFrom != initTo) {
|
||||
area.css = `font-size: ${initFrom}px; transition: ${initAnimTime}ms linear;`;
|
||||
Utils.timeout(20, () => {
|
||||
area.css = `font-size: ${initTo}px;`;
|
||||
}, area)
|
||||
const transitionDistance = initTo - initFrom;
|
||||
const oneStep = initAnimTime / initAnimPoints;
|
||||
area.css = `
|
||||
font-size: ${initFrom}px;
|
||||
transition: ${oneStep}ms linear;
|
||||
`;
|
||||
for (let i = 0; i < initAnimPoints; i++) {
|
||||
Utils.timeout(Math.max(10, i * oneStep), () => {
|
||||
if(!area) return;
|
||||
area.css = `${initFrom != initTo ? 'font-size: ' + (initFrom + (transitionDistance / initAnimPoints * (i + 1))) + 'px;' : ''}`;
|
||||
});
|
||||
}
|
||||
}
|
||||
else area.css = 'font-size: 0px;';
|
||||
extraSetup(area);
|
||||
},
|
||||
})
|
|
@ -0,0 +1,71 @@
|
|||
const { Gtk } = imports.gi;
|
||||
const Lang = imports.lang;
|
||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
||||
|
||||
// min-height/min-width for height/width
|
||||
// background-color/color for background/indicator color
|
||||
// padding for pad of indicator
|
||||
// font-size for selected index (0-based)
|
||||
export const NavigationIndicator = ({count, vertical, ...props}) => Widget.DrawingArea({
|
||||
...props,
|
||||
setup: (area) => {
|
||||
const styleContext = area.get_style_context();
|
||||
const width = Math.max(styleContext.get_property('min-width', Gtk.StateFlags.NORMAL), area.get_allocated_width());
|
||||
const height = Math.max(styleContext.get_property('min-height', Gtk.StateFlags.NORMAL), area.get_allocated_height());
|
||||
area.set_size_request(width, height);
|
||||
|
||||
area.connect('draw', Lang.bind(area, (area, cr) => {
|
||||
const styleContext = area.get_style_context();
|
||||
const width = Math.max(styleContext.get_property('min-width', Gtk.StateFlags.NORMAL), area.get_allocated_width());
|
||||
const height = Math.max(styleContext.get_property('min-height', Gtk.StateFlags.NORMAL), area.get_allocated_height());
|
||||
// console.log('allocated width/height:', area.get_allocated_width(), '/', area.get_allocated_height())
|
||||
area.set_size_request(width, height);
|
||||
const paddingLeft = styleContext.get_padding(Gtk.StateFlags.NORMAL).left;
|
||||
const paddingRight = styleContext.get_padding(Gtk.StateFlags.NORMAL).right;
|
||||
const paddingTop = styleContext.get_padding(Gtk.StateFlags.NORMAL).top;
|
||||
const paddingBottom = styleContext.get_padding(Gtk.StateFlags.NORMAL).bottom;
|
||||
|
||||
const selectedCell = styleContext.get_property('font-size', Gtk.StateFlags.NORMAL);
|
||||
|
||||
let cellWidth = width;
|
||||
let cellHeight = height;
|
||||
if (vertical) cellHeight /= count;
|
||||
else cellWidth /= count;
|
||||
const indicatorWidth = cellWidth - paddingLeft - paddingRight;
|
||||
const indicatorHeight = cellHeight - paddingTop - paddingBottom;
|
||||
|
||||
const background_color = styleContext.get_property('background-color', Gtk.StateFlags.NORMAL);
|
||||
const color = styleContext.get_property('color', Gtk.StateFlags.NORMAL);
|
||||
cr.setLineWidth(2);
|
||||
// Background
|
||||
cr.setSourceRGBA(background_color.red, background_color.green, background_color.blue, background_color.alpha);
|
||||
cr.rectangle(0, 0, width, height);
|
||||
cr.fill();
|
||||
|
||||
// The indicator line
|
||||
cr.setSourceRGBA(color.red, color.green, color.blue, color.alpha);
|
||||
if (vertical) {
|
||||
cr.rectangle(paddingLeft, paddingTop + cellHeight * selectedCell + indicatorWidth / 2, indicatorWidth, indicatorHeight - indicatorWidth);
|
||||
cr.stroke();
|
||||
cr.rectangle(paddingLeft, paddingTop + cellHeight * selectedCell + indicatorWidth / 2, indicatorWidth, indicatorHeight - indicatorWidth);
|
||||
cr.fill();
|
||||
cr.arc(paddingLeft + indicatorWidth / 2, paddingTop + cellHeight * selectedCell + indicatorWidth / 2, indicatorWidth / 2, Math.PI, 2 * Math.PI);
|
||||
cr.fill();
|
||||
cr.arc(paddingLeft + indicatorWidth / 2, paddingTop + cellHeight * selectedCell + indicatorHeight - indicatorWidth / 2, indicatorWidth / 2, 0, Math.PI);
|
||||
cr.fill();
|
||||
}
|
||||
else {
|
||||
cr.rectangle(paddingLeft + cellWidth * selectedCell + indicatorHeight / 2, paddingTop, indicatorWidth - indicatorHeight, indicatorHeight);
|
||||
cr.stroke();
|
||||
cr.rectangle(paddingLeft + cellWidth * selectedCell + indicatorHeight / 2, paddingTop, indicatorWidth - indicatorHeight, indicatorHeight);
|
||||
cr.fill();
|
||||
cr.arc(paddingLeft + cellWidth * selectedCell + indicatorHeight / 2, paddingTop + indicatorHeight / 2, indicatorHeight / 2, 0.5 * Math.PI, 1.5 * Math.PI);
|
||||
cr.fill();
|
||||
cr.arc(paddingLeft + cellWidth * selectedCell + indicatorWidth - indicatorHeight / 2, paddingTop + indicatorHeight / 2, indicatorHeight / 2, -0.5 * Math.PI, 0.5 * Math.PI);
|
||||
cr.fill();
|
||||
}
|
||||
}))
|
||||
},
|
||||
})
|
||||
|
||||
|
50
.config/ags/modules/.commonwidgets/cairo_roundedcorner.js
Normal file
50
.config/ags/modules/.commonwidgets/cairo_roundedcorner.js
Normal file
|
@ -0,0 +1,50 @@
|
|||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
||||
const { Gtk } = imports.gi;
|
||||
const Lang = imports.lang;
|
||||
|
||||
export const RoundedCorner = (place, props) => Widget.DrawingArea({
|
||||
...props,
|
||||
hpack: place.includes('left') ? 'start' : 'end',
|
||||
vpack: place.includes('top') ? 'start' : 'end',
|
||||
setup: (widget) => Utils.timeout(1, () => {
|
||||
const c = widget.get_style_context().get_property('background-color', Gtk.StateFlags.NORMAL);
|
||||
const 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) => {
|
||||
const c = widget.get_style_context().get_property('background-color', Gtk.StateFlags.NORMAL);
|
||||
const r = widget.get_style_context().get_property('border-radius', Gtk.StateFlags.NORMAL);
|
||||
// const borderColor = widget.get_style_context().get_property('color', Gtk.StateFlags.NORMAL);
|
||||
// const borderWidth = widget.get_style_context().get_border(Gtk.StateFlags.NORMAL).left; // ur going to write border-width: something anyway
|
||||
widget.set_size_request(r, r);
|
||||
|
||||
switch (place) {
|
||||
case 'topleft':
|
||||
cr.arc(r, r, r, Math.PI, 3 * Math.PI / 2);
|
||||
cr.lineTo(0, 0);
|
||||
break;
|
||||
|
||||
case 'topright':
|
||||
cr.arc(0, r, r, 3 * Math.PI / 2, 2 * Math.PI);
|
||||
cr.lineTo(r, 0);
|
||||
break;
|
||||
|
||||
case 'bottomleft':
|
||||
cr.arc(r, 0, r, Math.PI / 2, Math.PI);
|
||||
cr.lineTo(0, r);
|
||||
break;
|
||||
|
||||
case 'bottomright':
|
||||
cr.arc(0, 0, r, 0, Math.PI / 2);
|
||||
cr.lineTo(r, r);
|
||||
break;
|
||||
}
|
||||
|
||||
cr.closePath();
|
||||
cr.setSourceRGBA(c.red, c.green, c.blue, c.alpha);
|
||||
cr.fill();
|
||||
// cr.setLineWidth(borderWidth);
|
||||
// cr.setSourceRGBA(borderColor.red, borderColor.green, borderColor.blue, borderColor.alpha);
|
||||
// cr.stroke();
|
||||
}));
|
||||
}),
|
||||
});
|
49
.config/ags/modules/.commonwidgets/cairo_slider.js
Normal file
49
.config/ags/modules/.commonwidgets/cairo_slider.js
Normal file
|
@ -0,0 +1,49 @@
|
|||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
||||
const { Gtk } = imports.gi;
|
||||
const Lang = imports.lang;
|
||||
|
||||
export const AnimatedSlider = ({
|
||||
className,
|
||||
value,
|
||||
...rest
|
||||
}) => {
|
||||
return Widget.DrawingArea({
|
||||
className: `${className}`,
|
||||
setup: (self) => {
|
||||
self.connect('draw', Lang.bind(self, (self, cr) => {
|
||||
const styleContext = self.get_style_context();
|
||||
const allocatedWidth = self.get_allocated_width();
|
||||
const allocatedHeight = self.get_allocated_height();
|
||||
console.log(allocatedHeight, allocatedWidth)
|
||||
const minWidth = styleContext.get_property('min-width', Gtk.StateFlags.NORMAL);
|
||||
const minHeight = styleContext.get_property('min-height', Gtk.StateFlags.NORMAL);
|
||||
const radius = styleContext.get_property('border-radius', Gtk.StateFlags.NORMAL);
|
||||
const bg = styleContext.get_property('background-color', Gtk.StateFlags.NORMAL);
|
||||
const fg = styleContext.get_property('color', Gtk.StateFlags.NORMAL);
|
||||
const value = styleContext.get_property('font-size', Gtk.StateFlags.NORMAL) / 100;
|
||||
self.set_size_request(-1, minHeight);
|
||||
const width = allocatedHeight;
|
||||
const height = minHeight;
|
||||
|
||||
cr.arc(radius, radius, radius, -1 * Math.PI, -0.5 * Math.PI); // Top-left
|
||||
cr.arc(width - radius, radius, radius, -0.5 * Math.PI, 0); // Top-right
|
||||
cr.arc(width - radius, height - radius, radius, 0, 0.5 * Math.PI); // Bottom-left
|
||||
cr.arc(radius, height - radius, radius, 0.5 * Math.PI, 1 * Math.PI); // Bottom-right
|
||||
cr.setSourceRGBA(bg.red, bg.green, bg.blue, bg.alpha);
|
||||
cr.closePath();
|
||||
cr.fill();
|
||||
|
||||
// const valueWidth = width * value;
|
||||
// cr.arc(radius, radius, radius, -1 * Math.PI, -0.5 * Math.PI); // Top-left
|
||||
// cr.arc(valueWidth - radius, radius, radius, -0.5 * Math.PI, 0); // Top-right
|
||||
// cr.arc(valueWidth - radius, height - radius, radius, 0, 0.5 * Math.PI); // Bottom-left
|
||||
// cr.arc(radius, height - radius, radius, 0.5 * Math.PI, 1 * Math.PI); // Bottom-right
|
||||
// cr.setSourceRGBA(fg.red, fg.green, fg.blue, fg.alpha);
|
||||
// cr.closePath();
|
||||
// cr.fill();
|
||||
|
||||
}));
|
||||
},
|
||||
...rest,
|
||||
})
|
||||
}
|
185
.config/ags/modules/.commonwidgets/configwidgets.js
Normal file
185
.config/ags/modules/.commonwidgets/configwidgets.js
Normal file
|
@ -0,0 +1,185 @@
|
|||
import Variable from 'resource:///com/github/Aylur/ags/variable.js';
|
||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
||||
import * as Utils from 'resource:///com/github/Aylur/ags/utils.js';
|
||||
import { MaterialIcon } from './materialicon.js';
|
||||
import { setupCursorHover } from '../.widgetutils/cursorhover.js';
|
||||
const { Box, Button, Label, Revealer } = Widget;
|
||||
|
||||
export const ConfigToggle = ({
|
||||
icon, name, desc = '', initValue,
|
||||
expandWidget = true,
|
||||
onChange = () => { }, extraSetup = () => { },
|
||||
...rest
|
||||
}) => {
|
||||
const enabled = Variable(initValue);
|
||||
const toggleIcon = Label({
|
||||
className: `icon-material txt-bold ${enabled.value ? '' : 'txt-poof'}`,
|
||||
label: `${enabled.value ? 'check' : ''}`,
|
||||
setup: (self) => self.hook(enabled, (self) => {
|
||||
self.toggleClassName('switch-fg-toggling-false', false);
|
||||
if (!enabled.value) {
|
||||
self.label = '';
|
||||
self.toggleClassName('txt-poof', true);
|
||||
}
|
||||
else Utils.timeout(1, () => {
|
||||
toggleIcon.label = 'check';
|
||||
toggleIcon.toggleClassName('txt-poof', false);
|
||||
})
|
||||
}),
|
||||
})
|
||||
const toggleButtonIndicator = Box({
|
||||
className: `switch-fg ${enabled.value ? 'switch-fg-true' : ''}`,
|
||||
vpack: 'center',
|
||||
hpack: 'start',
|
||||
homogeneous: true,
|
||||
children: [toggleIcon,],
|
||||
setup: (self) => self.hook(enabled, (self) => {
|
||||
self.toggleClassName('switch-fg-true', enabled.value);
|
||||
}),
|
||||
});
|
||||
const toggleButton = Box({
|
||||
hpack: 'end',
|
||||
className: `switch-bg ${enabled.value ? 'switch-bg-true' : ''}`,
|
||||
homogeneous: true,
|
||||
children: [toggleButtonIndicator],
|
||||
setup: (self) => self.hook(enabled, (self) => {
|
||||
self.toggleClassName('switch-bg-true', enabled.value);
|
||||
}),
|
||||
});
|
||||
const widgetContent = Box({
|
||||
tooltipText: desc,
|
||||
className: 'txt spacing-h-5 configtoggle-box',
|
||||
children: [
|
||||
(icon !== undefined ? MaterialIcon(icon, 'norm') : null),
|
||||
(name !== undefined ? Label({
|
||||
className: 'txt txt-small',
|
||||
label: name,
|
||||
}) : null),
|
||||
expandWidget ? Box({ hexpand: true }) : null,
|
||||
toggleButton,
|
||||
]
|
||||
});
|
||||
const interactionWrapper = Button({
|
||||
attribute: {
|
||||
toggle: (newValue) => {
|
||||
enabled.value = !enabled.value;
|
||||
onChange(interactionWrapper, enabled.value);
|
||||
}
|
||||
},
|
||||
child: widgetContent,
|
||||
onClicked: (self) => self.attribute.toggle(self),
|
||||
setup: (self) => {
|
||||
setupCursorHover(self);
|
||||
self.connect('pressed', () => { // mouse down
|
||||
toggleIcon.toggleClassName('txt-poof', true);
|
||||
toggleIcon.toggleClassName('switch-fg-true', false);
|
||||
if (!enabled.value) toggleIcon.toggleClassName('switch-fg-toggling-false', true);
|
||||
});
|
||||
extraSetup(self)
|
||||
},
|
||||
...rest,
|
||||
});
|
||||
interactionWrapper.enabled = enabled;
|
||||
return interactionWrapper;
|
||||
}
|
||||
|
||||
export const ConfigSegmentedSelection = ({
|
||||
icon, name, desc = '',
|
||||
options = [{ name: 'Option 1', value: 0 }, { name: 'Option 2', value: 1 }],
|
||||
initIndex = 0,
|
||||
onChange,
|
||||
...rest
|
||||
}) => {
|
||||
let lastSelected = initIndex;
|
||||
let value = options[initIndex].value;
|
||||
const widget = Box({
|
||||
tooltipText: desc,
|
||||
className: 'segment-container',
|
||||
// homogeneous: true,
|
||||
children: options.map((option, id) => {
|
||||
const selectedIcon = Revealer({
|
||||
revealChild: id == initIndex,
|
||||
transition: 'slide_right',
|
||||
transitionDuration: userOptions.animations.durationSmall,
|
||||
child: MaterialIcon('check', 'norm')
|
||||
});
|
||||
return Button({
|
||||
setup: setupCursorHover,
|
||||
className: `segment-btn ${id == initIndex ? 'segment-btn-enabled' : ''}`,
|
||||
child: Box({
|
||||
hpack: 'center',
|
||||
className: 'spacing-h-5',
|
||||
children: [
|
||||
selectedIcon,
|
||||
Label({
|
||||
label: option.name,
|
||||
})
|
||||
]
|
||||
}),
|
||||
onClicked: (self) => {
|
||||
value = option.value;
|
||||
const kids = widget.get_children();
|
||||
kids[lastSelected].toggleClassName('segment-btn-enabled', false);
|
||||
kids[lastSelected].get_children()[0].get_children()[0].revealChild = false;
|
||||
lastSelected = id;
|
||||
self.toggleClassName('segment-btn-enabled', true);
|
||||
selectedIcon.revealChild = true;
|
||||
onChange(option.value, option.name);
|
||||
}
|
||||
})
|
||||
}),
|
||||
...rest,
|
||||
});
|
||||
return widget;
|
||||
|
||||
}
|
||||
|
||||
export const ConfigMulipleSelection = ({
|
||||
icon, name, desc = '',
|
||||
optionsArr = [
|
||||
[{ name: 'Option 1', value: 0 }, { name: 'Option 2', value: 1 }],
|
||||
[{ name: 'Option 3', value: 0 }, { name: 'Option 4', value: 1 }],
|
||||
],
|
||||
initIndex = [0, 0],
|
||||
onChange,
|
||||
...rest
|
||||
}) => {
|
||||
let lastSelected = initIndex;
|
||||
let value = optionsArr[initIndex[0]][initIndex[1]].value;
|
||||
const widget = Box({
|
||||
tooltipText: desc,
|
||||
className: 'multipleselection-container spacing-v-3',
|
||||
vertical: true,
|
||||
children: optionsArr.map((options, grp) => {
|
||||
return Box({
|
||||
className: 'spacing-h-5',
|
||||
hpack: 'center',
|
||||
children: options.map((option, id) => {
|
||||
return Button({
|
||||
setup: setupCursorHover,
|
||||
className: `multipleselection-btn ${id == initIndex[1] && grp == initIndex[0] ? 'multipleselection-btn-enabled' : ''}`,
|
||||
label: option.name,
|
||||
onClicked: (self) => {
|
||||
const kidsg = widget.get_children();
|
||||
const kids = kidsg.flatMap(widget => widget.get_children());
|
||||
kids.forEach(kid => {
|
||||
kid.toggleClassName('multipleselection-btn-enabled', false);
|
||||
});
|
||||
lastSelected = id;
|
||||
self.toggleClassName('multipleselection-btn-enabled', true);
|
||||
onChange(option.value, option.name);
|
||||
}
|
||||
})
|
||||
}),
|
||||
})
|
||||
}),
|
||||
...rest,
|
||||
});
|
||||
return widget;
|
||||
|
||||
}
|
||||
|
||||
export const ConfigGap = ({ vertical = true, size = 5, ...rest }) => Box({
|
||||
className: `gap-${vertical ? 'v' : 'h'}-${size}`,
|
||||
...rest,
|
||||
})
|
7
.config/ags/modules/.commonwidgets/materialicon.js
Normal file
7
.config/ags/modules/.commonwidgets/materialicon.js
Normal file
|
@ -0,0 +1,7 @@
|
|||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
||||
|
||||
export const MaterialIcon = (icon, size, props = {}) => Widget.Label({
|
||||
className: `icon-material txt-${size}`,
|
||||
label: icon,
|
||||
...props,
|
||||
})
|
448
.config/ags/modules/.commonwidgets/notification.js
Normal file
448
.config/ags/modules/.commonwidgets/notification.js
Normal file
|
@ -0,0 +1,448 @@
|
|||
// This file is for the actual widget for each single notification
|
||||
const { GLib, Gdk, Gtk } = imports.gi;
|
||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js'
|
||||
import * as Utils from 'resource:///com/github/Aylur/ags/utils.js'
|
||||
const { Box, EventBox, Icon, Overlay, Label, Button, Revealer } = Widget;
|
||||
import { MaterialIcon } from './materialicon.js';
|
||||
import { setupCursorHover } from "../.widgetutils/cursorhover.js";
|
||||
import { AnimatedCircProg } from "./cairo_circularprogress.js";
|
||||
|
||||
function guessMessageType(summary) {
|
||||
const str = summary.toLowerCase();
|
||||
if (str.includes('reboot')) return 'restart_alt';
|
||||
if (str.includes('recording')) return 'screen_record';
|
||||
if (str.includes('battery') || summary.includes('power')) return 'power';
|
||||
if (str.includes('screenshot')) return 'screenshot_monitor';
|
||||
if (str.includes('welcome')) return 'waving_hand';
|
||||
if (str.includes('time')) return 'scheduleb';
|
||||
if (str.includes('installed')) return 'download';
|
||||
if (str.includes('update')) return 'update';
|
||||
if (str.startsWith('file')) return 'folder_copy';
|
||||
return 'chat';
|
||||
}
|
||||
|
||||
function exists(widget) {
|
||||
return widget !== null;
|
||||
}
|
||||
|
||||
const NotificationIcon = (notifObject) => {
|
||||
// { appEntry, appIcon, image }, urgency = 'normal'
|
||||
if (notifObject.image) {
|
||||
return Box({
|
||||
valign: Gtk.Align.CENTER,
|
||||
hexpand: false,
|
||||
className: 'notif-icon',
|
||||
css: `
|
||||
background-image: url("${notifObject.image}");
|
||||
background-size: auto 100%;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
`,
|
||||
});
|
||||
}
|
||||
|
||||
let icon = 'NO_ICON';
|
||||
if (Utils.lookUpIcon(notifObject.appIcon))
|
||||
icon = notifObject.appIcon;
|
||||
if (Utils.lookUpIcon(notifObject.appEntry))
|
||||
icon = notifObject.appEntry;
|
||||
|
||||
return Box({
|
||||
vpack: 'center',
|
||||
hexpand: false,
|
||||
className: `notif-icon notif-icon-material-${notifObject.urgency}`,
|
||||
homogeneous: true,
|
||||
children: [
|
||||
(icon != 'NO_ICON' ?
|
||||
Icon({
|
||||
vpack: 'center',
|
||||
icon: icon,
|
||||
})
|
||||
:
|
||||
MaterialIcon(`${notifObject.urgency == 'critical' ? 'release_alert' : guessMessageType(notifObject.summary.toLowerCase())}`, 'hugerass', {
|
||||
hexpand: true,
|
||||
})
|
||||
)
|
||||
],
|
||||
});
|
||||
};
|
||||
|
||||
export default ({
|
||||
notifObject,
|
||||
isPopup = false,
|
||||
props = {},
|
||||
} = {}) => {
|
||||
const popupTimeout = notifObject.timeout || (notifObject.urgency == 'critical' ? 8000 : 3000);
|
||||
const command = (isPopup ?
|
||||
() => notifObject.dismiss() :
|
||||
() => notifObject.close()
|
||||
)
|
||||
const destroyWithAnims = () => {
|
||||
widget.sensitive = false;
|
||||
notificationBox.setCss(middleClickClose);
|
||||
Utils.timeout(userOptions.animations.durationSmall, () => {
|
||||
if (wholeThing) wholeThing.revealChild = false;
|
||||
}, wholeThing);
|
||||
Utils.timeout(userOptions.animations.durationSmall * 2, () => {
|
||||
command();
|
||||
if (wholeThing) {
|
||||
wholeThing.destroy();
|
||||
wholeThing = null;
|
||||
}
|
||||
}, wholeThing);
|
||||
}
|
||||
const widget = EventBox({
|
||||
onHover: (self) => {
|
||||
self.window.set_cursor(Gdk.Cursor.new_from_name(display, 'grab'));
|
||||
if (!wholeThing.attribute.hovered)
|
||||
wholeThing.attribute.hovered = true;
|
||||
},
|
||||
onHoverLost: (self) => {
|
||||
self.window.set_cursor(null);
|
||||
if (wholeThing.attribute.hovered)
|
||||
wholeThing.attribute.hovered = false;
|
||||
if (isPopup) {
|
||||
command();
|
||||
}
|
||||
},
|
||||
onMiddleClick: (self) => {
|
||||
destroyWithAnims();
|
||||
},
|
||||
setup: (self) => {
|
||||
self.on("button-press-event", () => {
|
||||
wholeThing.attribute.held = true;
|
||||
notificationContent.toggleClassName(`${isPopup ? 'popup-' : ''}notif-clicked-${notifObject.urgency}`, true);
|
||||
Utils.timeout(800, () => {
|
||||
if (wholeThing?.attribute.held) {
|
||||
Utils.execAsync(['wl-copy', `${notifObject.body}`]).catch(print);
|
||||
notifTextSummary.label = notifObject.summary + " (copied)";
|
||||
Utils.timeout(3000, () => notifTextSummary.label = notifObject.summary)
|
||||
}
|
||||
})
|
||||
}).on("button-release-event", () => {
|
||||
wholeThing.attribute.held = false;
|
||||
notificationContent.toggleClassName(`${isPopup ? 'popup-' : ''}notif-clicked-${notifObject.urgency}`, false);
|
||||
})
|
||||
}
|
||||
});
|
||||
let wholeThing = Revealer({
|
||||
attribute: {
|
||||
'close': undefined,
|
||||
'destroyWithAnims': destroyWithAnims,
|
||||
'dragging': false,
|
||||
'held': false,
|
||||
'hovered': false,
|
||||
'id': notifObject.id,
|
||||
},
|
||||
revealChild: false,
|
||||
transition: 'slide_down',
|
||||
transitionDuration: userOptions.animations.durationLarge,
|
||||
child: Box({ // Box to make sure css-based spacing works
|
||||
homogeneous: true,
|
||||
}),
|
||||
});
|
||||
|
||||
const display = Gdk.Display.get_default();
|
||||
const notifTextPreview = Revealer({
|
||||
transition: 'slide_down',
|
||||
transitionDuration: userOptions.animations.durationSmall,
|
||||
revealChild: true,
|
||||
child: Label({
|
||||
xalign: 0,
|
||||
className: `txt-smallie notif-body-${notifObject.urgency}`,
|
||||
useMarkup: true,
|
||||
xalign: 0,
|
||||
justify: Gtk.Justification.LEFT,
|
||||
maxWidthChars: 24,
|
||||
truncate: 'end',
|
||||
label: notifObject.body.split("\n")[0],
|
||||
}),
|
||||
});
|
||||
const notifTextExpanded = Revealer({
|
||||
transition: 'slide_up',
|
||||
transitionDuration: userOptions.animations.durationSmall,
|
||||
revealChild: false,
|
||||
child: Box({
|
||||
vertical: true,
|
||||
className: 'spacing-v-10',
|
||||
children: [
|
||||
Label({
|
||||
xalign: 0,
|
||||
className: `txt-smallie notif-body-${notifObject.urgency}`,
|
||||
useMarkup: true,
|
||||
xalign: 0,
|
||||
justify: Gtk.Justification.LEFT,
|
||||
maxWidthChars: 24,
|
||||
wrap: true,
|
||||
label: notifObject.body,
|
||||
}),
|
||||
Box({
|
||||
className: 'notif-actions spacing-h-5',
|
||||
children: [
|
||||
Button({
|
||||
hexpand: true,
|
||||
className: `notif-action notif-action-${notifObject.urgency}`,
|
||||
onClicked: () => destroyWithAnims(),
|
||||
setup: setupCursorHover,
|
||||
child: Label({
|
||||
label: 'Close',
|
||||
}),
|
||||
}),
|
||||
...notifObject.actions.map(action => Widget.Button({
|
||||
hexpand: true,
|
||||
className: `notif-action notif-action-${notifObject.urgency}`,
|
||||
onClicked: () => notifObject.invoke(action.id),
|
||||
setup: setupCursorHover,
|
||||
child: Label({
|
||||
label: action.label,
|
||||
}),
|
||||
}))
|
||||
],
|
||||
})
|
||||
]
|
||||
}),
|
||||
});
|
||||
const notifIcon = Box({
|
||||
vpack: 'start',
|
||||
homogeneous: true,
|
||||
children: [
|
||||
Overlay({
|
||||
child: NotificationIcon(notifObject),
|
||||
overlays: isPopup ? [AnimatedCircProg({
|
||||
className: `notif-circprog-${notifObject.urgency}`,
|
||||
vpack: 'center', hpack: 'center',
|
||||
initFrom: (isPopup ? 100 : 0),
|
||||
initTo: 0,
|
||||
initAnimTime: popupTimeout,
|
||||
})] : [],
|
||||
}),
|
||||
]
|
||||
});
|
||||
let notifTime = '';
|
||||
const messageTime = GLib.DateTime.new_from_unix_local(notifObject.time);
|
||||
if (messageTime.get_day_of_year() == GLib.DateTime.new_now_local().get_day_of_year())
|
||||
notifTime = messageTime.format(userOptions.time.format);
|
||||
else if (messageTime.get_day_of_year() == GLib.DateTime.new_now_local().get_day_of_year() - 1)
|
||||
notifTime = 'Yesterday';
|
||||
else
|
||||
notifTime = messageTime.format(userOptions.time.dateFormat);
|
||||
const notifTextSummary = Label({
|
||||
xalign: 0,
|
||||
className: 'txt-small txt-semibold titlefont',
|
||||
justify: Gtk.Justification.LEFT,
|
||||
hexpand: true,
|
||||
maxWidthChars: 24,
|
||||
truncate: 'end',
|
||||
ellipsize: 3,
|
||||
useMarkup: notifObject.summary.startsWith('<'),
|
||||
label: notifObject.summary,
|
||||
});
|
||||
const notifTextBody = Label({
|
||||
vpack: 'center',
|
||||
justification: 'right',
|
||||
className: 'txt-smaller txt-semibold',
|
||||
label: notifTime,
|
||||
});
|
||||
const notifText = Box({
|
||||
valign: Gtk.Align.CENTER,
|
||||
vertical: true,
|
||||
hexpand: true,
|
||||
children: [
|
||||
Box({
|
||||
children: [
|
||||
notifTextSummary,
|
||||
notifTextBody,
|
||||
]
|
||||
}),
|
||||
notifTextPreview,
|
||||
notifTextExpanded,
|
||||
]
|
||||
});
|
||||
const notifExpandButton = Button({
|
||||
vpack: 'start',
|
||||
className: 'notif-expand-btn',
|
||||
onClicked: (self) => {
|
||||
if (notifTextPreview.revealChild) { // Expanding...
|
||||
notifTextPreview.revealChild = false;
|
||||
notifTextExpanded.revealChild = true;
|
||||
self.child.label = 'expand_less';
|
||||
expanded = true;
|
||||
}
|
||||
else {
|
||||
notifTextPreview.revealChild = true;
|
||||
notifTextExpanded.revealChild = false;
|
||||
self.child.label = 'expand_more';
|
||||
expanded = false;
|
||||
}
|
||||
},
|
||||
child: MaterialIcon('expand_more', 'norm', {
|
||||
vpack: 'center',
|
||||
}),
|
||||
setup: setupCursorHover,
|
||||
});
|
||||
const notificationContent = Box({
|
||||
...props,
|
||||
className: `${isPopup ? 'popup-' : ''}notif-${notifObject.urgency} spacing-h-10`,
|
||||
children: [
|
||||
notifIcon,
|
||||
Box({
|
||||
className: 'spacing-h-5',
|
||||
children: [
|
||||
notifText,
|
||||
notifExpandButton,
|
||||
]
|
||||
})
|
||||
]
|
||||
})
|
||||
|
||||
// Gesture stuff
|
||||
const gesture = Gtk.GestureDrag.new(widget);
|
||||
var initDirX = 0;
|
||||
var initDirVertical = -1; // -1: unset, 0: horizontal, 1: vertical
|
||||
var expanded = false;
|
||||
// in px
|
||||
const startMargin = 0;
|
||||
const MOVE_THRESHOLD = 10;
|
||||
const DRAG_CONFIRM_THRESHOLD = 100;
|
||||
// in rem
|
||||
const maxOffset = 10.227;
|
||||
const endMargin = 20.455;
|
||||
const disappearHeight = 6.818;
|
||||
const leftAnim1 = `transition: ${userOptions.animations.durationSmall}ms cubic-bezier(0.05, 0.7, 0.1, 1);
|
||||
margin-left: -${Number(maxOffset + endMargin)}rem;
|
||||
margin-right: ${Number(maxOffset + endMargin)}rem;
|
||||
opacity: 0;`;
|
||||
|
||||
const rightAnim1 = `transition: ${userOptions.animations.durationSmall}ms cubic-bezier(0.05, 0.7, 0.1, 1);
|
||||
margin-left: ${Number(maxOffset + endMargin)}rem;
|
||||
margin-right: -${Number(maxOffset + endMargin)}rem;
|
||||
opacity: 0;`;
|
||||
|
||||
const middleClickClose = `transition: ${userOptions.animations.durationSmall}ms cubic-bezier(0.85, 0, 0.15, 1);
|
||||
margin-left: ${Number(maxOffset + endMargin)}rem;
|
||||
margin-right: -${Number(maxOffset + endMargin)}rem;
|
||||
opacity: 0;`;
|
||||
|
||||
const notificationBox = Box({
|
||||
attribute: {
|
||||
'leftAnim1': leftAnim1,
|
||||
'rightAnim1': rightAnim1,
|
||||
'middleClickClose': middleClickClose,
|
||||
'ready': false,
|
||||
},
|
||||
homogeneous: true,
|
||||
children: [notificationContent],
|
||||
setup: (self) => self
|
||||
.hook(gesture, self => {
|
||||
var offset_x = gesture.get_offset()[1];
|
||||
var offset_y = gesture.get_offset()[2];
|
||||
// Which dir?
|
||||
if (initDirVertical == -1) {
|
||||
if (Math.abs(offset_y) > MOVE_THRESHOLD)
|
||||
initDirVertical = 1;
|
||||
if (initDirX == 0 && Math.abs(offset_x) > MOVE_THRESHOLD) {
|
||||
initDirVertical = 0;
|
||||
initDirX = (offset_x > 0 ? 1 : -1);
|
||||
}
|
||||
}
|
||||
// Horizontal drag
|
||||
if (initDirVertical == 0 && offset_x > MOVE_THRESHOLD) {
|
||||
if (initDirX < 0)
|
||||
self.setCss(`margin-left: 0px; margin-right: 0px;`);
|
||||
else
|
||||
self.setCss(`
|
||||
margin-left: ${Number(offset_x + startMargin - MOVE_THRESHOLD)}px;
|
||||
margin-right: -${Number(offset_x + startMargin - MOVE_THRESHOLD)}px;
|
||||
`);
|
||||
}
|
||||
else if (initDirVertical == 0 && offset_x < -MOVE_THRESHOLD) {
|
||||
if (initDirX > 0)
|
||||
self.setCss(`margin-left: 0px; margin-right: 0px;`);
|
||||
else {
|
||||
offset_x = Math.abs(offset_x);
|
||||
self.setCss(`
|
||||
margin-right: ${Number(offset_x + startMargin - MOVE_THRESHOLD)}px;
|
||||
margin-left: -${Number(offset_x + startMargin - MOVE_THRESHOLD)}px;
|
||||
`);
|
||||
}
|
||||
}
|
||||
// Update dragging
|
||||
wholeThing.attribute.dragging = Math.abs(offset_x) > MOVE_THRESHOLD;
|
||||
if (Math.abs(offset_x) > MOVE_THRESHOLD ||
|
||||
Math.abs(offset_y) > MOVE_THRESHOLD) wholeThing.attribute.held = false;
|
||||
widget.window?.set_cursor(Gdk.Cursor.new_from_name(display, 'grabbing'));
|
||||
// Vertical drag
|
||||
if (initDirVertical == 1 && offset_y > MOVE_THRESHOLD && !expanded) {
|
||||
notifTextPreview.revealChild = false;
|
||||
notifTextExpanded.revealChild = true;
|
||||
expanded = true;
|
||||
notifExpandButton.child.label = 'expand_less';
|
||||
}
|
||||
else if (initDirVertical == 1 && offset_y < -MOVE_THRESHOLD && expanded) {
|
||||
notifTextPreview.revealChild = true;
|
||||
notifTextExpanded.revealChild = false;
|
||||
expanded = false;
|
||||
notifExpandButton.child.label = 'expand_more';
|
||||
}
|
||||
|
||||
}, 'drag-update')
|
||||
.hook(gesture, self => {
|
||||
if (!self.attribute.ready) {
|
||||
wholeThing.revealChild = true;
|
||||
self.attribute.ready = true;
|
||||
return;
|
||||
}
|
||||
const offset_h = gesture.get_offset()[1];
|
||||
|
||||
if (Math.abs(offset_h) > DRAG_CONFIRM_THRESHOLD && offset_h * initDirX > 0) {
|
||||
if (offset_h > 0) {
|
||||
self.setCss(rightAnim1);
|
||||
widget.sensitive = false;
|
||||
}
|
||||
else {
|
||||
self.setCss(leftAnim1);
|
||||
widget.sensitive = false;
|
||||
}
|
||||
Utils.timeout(userOptions.animations.durationSmall, () => {
|
||||
if (wholeThing) wholeThing.revealChild = false;
|
||||
}, wholeThing);
|
||||
Utils.timeout(userOptions.animations.durationSmall * 2, () => {
|
||||
command();
|
||||
if (wholeThing) {
|
||||
wholeThing.destroy();
|
||||
wholeThing = null;
|
||||
}
|
||||
}, wholeThing);
|
||||
}
|
||||
else {
|
||||
self.setCss(`transition: margin 200ms cubic-bezier(0.05, 0.7, 0.1, 1), opacity 200ms cubic-bezier(0.05, 0.7, 0.1, 1);
|
||||
margin-left: ${startMargin}px;
|
||||
margin-right: ${startMargin}px;
|
||||
margin-bottom: unset; margin-top: unset;
|
||||
opacity: 1;`);
|
||||
if (widget.window)
|
||||
widget.window.set_cursor(Gdk.Cursor.new_from_name(display, 'grab'));
|
||||
|
||||
wholeThing.attribute.dragging = false;
|
||||
}
|
||||
initDirX = 0;
|
||||
initDirVertical = -1;
|
||||
}, 'drag-end')
|
||||
,
|
||||
})
|
||||
widget.add(notificationBox);
|
||||
wholeThing.child.children = [widget];
|
||||
if (isPopup) Utils.timeout(popupTimeout, () => {
|
||||
if (wholeThing) {
|
||||
wholeThing.revealChild = false;
|
||||
Utils.timeout(userOptions.animations.durationSmall, () => {
|
||||
if (wholeThing) {
|
||||
wholeThing.destroy();
|
||||
wholeThing = null;
|
||||
}
|
||||
command();
|
||||
}, wholeThing);
|
||||
}
|
||||
})
|
||||
return wholeThing;
|
||||
}
|
298
.config/ags/modules/.commonwidgets/statusicons.js
Normal file
298
.config/ags/modules/.commonwidgets/statusicons.js
Normal file
|
@ -0,0 +1,298 @@
|
|||
import App from 'resource:///com/github/Aylur/ags/app.js';
|
||||
import Audio from 'resource:///com/github/Aylur/ags/service/audio.js';
|
||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
||||
import * as Utils from 'resource:///com/github/Aylur/ags/utils.js';
|
||||
|
||||
import { MaterialIcon } from './materialicon.js';
|
||||
import Bluetooth from 'resource:///com/github/Aylur/ags/service/bluetooth.js';
|
||||
import Network from 'resource:///com/github/Aylur/ags/service/network.js';
|
||||
import Notifications from 'resource:///com/github/Aylur/ags/service/notifications.js';
|
||||
import { languages } from './statusicons_languages.js';
|
||||
|
||||
// A guessing func to try to support langs not listed in data/languages.js
|
||||
function isLanguageMatch(abbreviation, word) {
|
||||
const lowerAbbreviation = abbreviation.toLowerCase();
|
||||
const lowerWord = word.toLowerCase();
|
||||
let j = 0;
|
||||
for (let i = 0; i < lowerWord.length; i++) {
|
||||
if (lowerWord[i] === lowerAbbreviation[j]) {
|
||||
j++;
|
||||
}
|
||||
if (j === lowerAbbreviation.length) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export const MicMuteIndicator = () => Widget.Revealer({
|
||||
transition: 'slide_left',
|
||||
transitionDuration: userOptions.animations.durationSmall,
|
||||
revealChild: false,
|
||||
setup: (self) => self.hook(Audio, (self) => {
|
||||
self.revealChild = Audio.microphone?.stream?.isMuted;
|
||||
}),
|
||||
child: MaterialIcon('mic_off', 'norm'),
|
||||
});
|
||||
|
||||
export const NotificationIndicator = (notifCenterName = 'sideright') => {
|
||||
const widget = Widget.Revealer({
|
||||
transition: 'slide_left',
|
||||
transitionDuration: userOptions.animations.durationSmall,
|
||||
revealChild: false,
|
||||
setup: (self) => self
|
||||
.hook(Notifications, (self, id) => {
|
||||
if (!id || Notifications.dnd) return;
|
||||
if (!Notifications.getNotification(id)) return;
|
||||
self.revealChild = true;
|
||||
}, 'notified')
|
||||
.hook(App, (self, currentName, visible) => {
|
||||
if (visible && currentName === notifCenterName) {
|
||||
self.revealChild = false;
|
||||
}
|
||||
})
|
||||
,
|
||||
child: Widget.Box({
|
||||
children: [
|
||||
MaterialIcon('notifications', 'norm'),
|
||||
Widget.Label({
|
||||
className: 'txt-small titlefont',
|
||||
attribute: {
|
||||
unreadCount: 0,
|
||||
update: (self) => self.label = `${self.attribute.unreadCount}`,
|
||||
},
|
||||
setup: (self) => self
|
||||
.hook(Notifications, (self, id) => {
|
||||
if (!id || Notifications.dnd) return;
|
||||
if (!Notifications.getNotification(id)) return;
|
||||
self.attribute.unreadCount++;
|
||||
self.attribute.update(self);
|
||||
}, 'notified')
|
||||
.hook(App, (self, currentName, visible) => {
|
||||
if (visible && currentName === notifCenterName) {
|
||||
self.attribute.unreadCount = 0;
|
||||
self.attribute.update(self);
|
||||
}
|
||||
})
|
||||
,
|
||||
})
|
||||
]
|
||||
})
|
||||
});
|
||||
return widget;
|
||||
}
|
||||
|
||||
export const BluetoothIndicator = () => Widget.Stack({
|
||||
transition: 'slide_up_down',
|
||||
transitionDuration: userOptions.animations.durationSmall,
|
||||
children: {
|
||||
'false': Widget.Label({ className: 'txt-norm icon-material', label: 'bluetooth_disabled' }),
|
||||
'true': Widget.Label({ className: 'txt-norm icon-material', label: 'bluetooth' }),
|
||||
},
|
||||
setup: (self) => self
|
||||
.hook(Bluetooth, stack => {
|
||||
stack.shown = String(Bluetooth.enabled);
|
||||
})
|
||||
,
|
||||
});
|
||||
|
||||
const BluetoothDevices = () => Widget.Box({
|
||||
className: 'spacing-h-5',
|
||||
setup: self => self.hook(Bluetooth, self => {
|
||||
self.children = Bluetooth.connected_devices.map((device) => {
|
||||
return Widget.Box({
|
||||
className: 'bar-bluetooth-device spacing-h-5',
|
||||
vpack: 'center',
|
||||
tooltipText: device.name,
|
||||
children: [
|
||||
Widget.Icon(`${device.iconName}-symbolic`),
|
||||
(device.batteryPercentage ? Widget.Label({
|
||||
className: 'txt-smallie',
|
||||
label: `${device.batteryPercentage}`,
|
||||
setup: (self) => {
|
||||
self.hook(device, (self) => {
|
||||
self.label = `${device.batteryPercentage}`;
|
||||
}, 'notify::batteryPercentage')
|
||||
}
|
||||
}) : null),
|
||||
]
|
||||
});
|
||||
});
|
||||
self.visible = Bluetooth.connected_devices.length > 0;
|
||||
}, 'notify::connected-devices'),
|
||||
})
|
||||
|
||||
const NetworkWiredIndicator = () => Widget.Stack({
|
||||
transition: 'slide_up_down',
|
||||
transitionDuration: userOptions.animations.durationSmall,
|
||||
children: {
|
||||
'fallback': SimpleNetworkIndicator(),
|
||||
'unknown': Widget.Label({ className: 'txt-norm icon-material', label: 'wifi_off' }),
|
||||
'disconnected': Widget.Label({ className: 'txt-norm icon-material', label: 'signal_wifi_off' }),
|
||||
'connected': Widget.Label({ className: 'txt-norm icon-material', label: 'lan' }),
|
||||
'connecting': Widget.Label({ className: 'txt-norm icon-material', label: 'settings_ethernet' }),
|
||||
},
|
||||
setup: (self) => self.hook(Network, stack => {
|
||||
if (!Network.wired)
|
||||
return;
|
||||
|
||||
const { internet } = Network.wired;
|
||||
if (['connecting', 'connected'].includes(internet))
|
||||
stack.shown = internet;
|
||||
else if (Network.connectivity !== 'full')
|
||||
stack.shown = 'disconnected';
|
||||
else
|
||||
stack.shown = 'fallback';
|
||||
}),
|
||||
});
|
||||
|
||||
const SimpleNetworkIndicator = () => Widget.Icon({
|
||||
setup: (self) => self.hook(Network, self => {
|
||||
const icon = Network[Network.primary || 'wifi']?.iconName;
|
||||
self.icon = icon || '';
|
||||
self.visible = icon;
|
||||
}),
|
||||
});
|
||||
|
||||
const NetworkWifiIndicator = () => Widget.Stack({
|
||||
transition: 'slide_up_down',
|
||||
transitionDuration: userOptions.animations.durationSmall,
|
||||
children: {
|
||||
'disabled': Widget.Label({ className: 'txt-norm icon-material', label: 'wifi_off' }),
|
||||
'disconnected': Widget.Label({ className: 'txt-norm icon-material', label: 'signal_wifi_off' }),
|
||||
'connecting': Widget.Label({ className: 'txt-norm icon-material', label: 'settings_ethernet' }),
|
||||
'0': Widget.Label({ className: 'txt-norm icon-material', label: 'signal_wifi_0_bar' }),
|
||||
'1': Widget.Label({ className: 'txt-norm icon-material', label: 'network_wifi_1_bar' }),
|
||||
'2': Widget.Label({ className: 'txt-norm icon-material', label: 'network_wifi_2_bar' }),
|
||||
'3': Widget.Label({ className: 'txt-norm icon-material', label: 'network_wifi_3_bar' }),
|
||||
'4': Widget.Label({ className: 'txt-norm icon-material', label: 'signal_wifi_4_bar' }),
|
||||
},
|
||||
setup: (self) => self.hook(Network, (stack) => {
|
||||
if (!Network.wifi) {
|
||||
return;
|
||||
}
|
||||
if (Network.wifi.internet == 'connected') {
|
||||
stack.shown = String(Math.ceil(Network.wifi.strength / 25));
|
||||
}
|
||||
else if (["disconnected", "connecting"].includes(Network.wifi.internet)) {
|
||||
stack.shown = Network.wifi.internet;
|
||||
}
|
||||
}),
|
||||
});
|
||||
|
||||
export const NetworkIndicator = () => Widget.Stack({
|
||||
transition: 'slide_up_down',
|
||||
transitionDuration: userOptions.animations.durationSmall,
|
||||
children: {
|
||||
'fallback': SimpleNetworkIndicator(),
|
||||
'wifi': NetworkWifiIndicator(),
|
||||
'wired': NetworkWiredIndicator(),
|
||||
},
|
||||
setup: (self) => self.hook(Network, stack => {
|
||||
if (!Network.primary) {
|
||||
stack.shown = 'wifi';
|
||||
return;
|
||||
}
|
||||
const primary = Network.primary || 'fallback';
|
||||
if (['wifi', 'wired'].includes(primary))
|
||||
stack.shown = primary;
|
||||
else
|
||||
stack.shown = 'fallback';
|
||||
}),
|
||||
});
|
||||
|
||||
const HyprlandXkbKeyboardLayout = async ({ useFlag } = {}) => {
|
||||
try {
|
||||
const Hyprland = (await import('resource:///com/github/Aylur/ags/service/hyprland.js')).default;
|
||||
var initLangs = [];
|
||||
var languageStackArray = [];
|
||||
var currentKeyboard;
|
||||
|
||||
const updateCurrentKeyboards = () => {
|
||||
currentKeyboard = JSON.parse(Utils.exec('hyprctl -j devices')).keyboards
|
||||
.find(device => device.name === 'at-translated-set-2-keyboard');
|
||||
if (currentKeyboard) {
|
||||
initLangs = currentKeyboard.layout.split(',').map(lang => lang.trim());
|
||||
}
|
||||
languageStackArray = Array.from({ length: initLangs.length }, (_, i) => {
|
||||
const lang = languages.find(lang => lang.layout == initLangs[i]);
|
||||
// if (!lang) return [
|
||||
// initLangs[i],
|
||||
// Widget.Label({ label: initLangs[i] })
|
||||
// ];
|
||||
// return [
|
||||
// lang.layout,
|
||||
// Widget.Label({ label: (useFlag ? lang.flag : lang.layout) })
|
||||
// ];
|
||||
// Object
|
||||
if (!lang) return {
|
||||
[initLangs[i]]: Widget.Label({ label: initLangs[i] })
|
||||
};
|
||||
return {
|
||||
[lang.layout]: Widget.Label({ label: (useFlag ? lang.flag : lang.layout) })
|
||||
};
|
||||
});
|
||||
};
|
||||
updateCurrentKeyboards();
|
||||
const widgetRevealer = Widget.Revealer({
|
||||
transition: 'slide_left',
|
||||
transitionDuration: userOptions.animations.durationSmall,
|
||||
revealChild: languageStackArray.length > 1,
|
||||
});
|
||||
const widgetKids = {
|
||||
...languageStackArray.reduce((obj, lang) => {
|
||||
return { ...obj, ...lang };
|
||||
}, {}),
|
||||
'undef': Widget.Label({ label: '?' }),
|
||||
}
|
||||
const widgetContent = Widget.Stack({
|
||||
transition: 'slide_up_down',
|
||||
transitionDuration: userOptions.animations.durationSmall,
|
||||
children: widgetKids,
|
||||
setup: (self) => self.hook(Hyprland, (stack, kbName, layoutName) => {
|
||||
if (!kbName) {
|
||||
return;
|
||||
}
|
||||
var lang = languages.find(lang => layoutName.includes(lang.name));
|
||||
if (lang) {
|
||||
widgetContent.shown = lang.layout;
|
||||
}
|
||||
else { // Attempt to support langs not listed
|
||||
lang = languageStackArray.find(lang => isLanguageMatch(lang[0], layoutName));
|
||||
if (!lang) stack.shown = 'undef';
|
||||
else stack.shown = lang[0];
|
||||
}
|
||||
}, 'keyboard-layout'),
|
||||
});
|
||||
widgetRevealer.child = widgetContent;
|
||||
return widgetRevealer;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
const OptionalKeyboardLayout = async () => {
|
||||
try {
|
||||
return await HyprlandXkbKeyboardLayout({ useFlag: userOptions.appearance.keyboardUseFlag });
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
const optionalKeyboardLayoutInstance = await OptionalKeyboardLayout();
|
||||
|
||||
export const StatusIcons = (props = {}) => Widget.Box({
|
||||
...props,
|
||||
child: Widget.Box({
|
||||
className: 'spacing-h-15',
|
||||
children: [
|
||||
MicMuteIndicator(),
|
||||
optionalKeyboardLayoutInstance,
|
||||
NotificationIndicator(),
|
||||
NetworkIndicator(),
|
||||
Widget.Box({
|
||||
className: 'spacing-h-5',
|
||||
children: [BluetoothIndicator(), BluetoothDevices()]
|
||||
})
|
||||
]
|
||||
})
|
||||
});
|
62
.config/ags/modules/.commonwidgets/statusicons_languages.js
Normal file
62
.config/ags/modules/.commonwidgets/statusicons_languages.js
Normal file
|
@ -0,0 +1,62 @@
|
|||
// For keyboard layout in statusicons.js
|
||||
// This list is not exhaustive. It just includes known/possible languages of users of my dotfiles
|
||||
// Add your language here if you use multi-lang xkb input. Else, ignore
|
||||
// Note that something like "French (Canada)" should go before "French"
|
||||
// and "English (US)" should go before "English"
|
||||
export const languages = [
|
||||
{
|
||||
layout: 'us',
|
||||
name: 'English (US)',
|
||||
flag: '🇺🇸'
|
||||
},
|
||||
{
|
||||
layout: 'ru',
|
||||
name: 'Russian',
|
||||
flag: '🇷🇺',
|
||||
},
|
||||
{
|
||||
layout: 'pl',
|
||||
name: 'Polish',
|
||||
flag: '🇷🇵🇵🇱',
|
||||
},
|
||||
{
|
||||
layout: 'ro',
|
||||
name: 'Romanian',
|
||||
flag: '🇷🇴',
|
||||
},
|
||||
{
|
||||
layout: 'ca',
|
||||
name: 'French (Canada)',
|
||||
flag: '🇫🇷',
|
||||
},
|
||||
{
|
||||
layout: 'fr',
|
||||
name: 'French',
|
||||
flag: '🇫🇷',
|
||||
},
|
||||
{
|
||||
layout: 'tr',
|
||||
name: 'Turkish',
|
||||
flag: '🇹🇷',
|
||||
},
|
||||
{
|
||||
layout: 'jp',
|
||||
name: 'Japanese',
|
||||
flag: '🇯🇵',
|
||||
},
|
||||
{
|
||||
layout: 'cn',
|
||||
name: 'Chinese',
|
||||
flag: '🇨🇳',
|
||||
},
|
||||
{
|
||||
layout: 'vn',
|
||||
name: 'Vietnamese',
|
||||
flag: '🇻🇳',
|
||||
},
|
||||
{
|
||||
layout: 'undef',
|
||||
name: 'Undefined',
|
||||
flag: '🧐',
|
||||
},
|
||||
]
|
274
.config/ags/modules/.commonwidgets/tabcontainer.js
Normal file
274
.config/ags/modules/.commonwidgets/tabcontainer.js
Normal file
|
@ -0,0 +1,274 @@
|
|||
import Variable from 'resource:///com/github/Aylur/ags/variable.js';
|
||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
||||
const { Box, Button, EventBox, Label, Overlay, Stack } = Widget;
|
||||
import { MaterialIcon } from './materialicon.js';
|
||||
import { NavigationIndicator } from './cairo_navigationindicator.js';
|
||||
import { setupCursorHover } from '../.widgetutils/cursorhover.js';
|
||||
import { DoubleRevealer } from '../.widgethacks/advancedrevealers.js';
|
||||
|
||||
export const TabContainer = ({ icons, names, children, className = '', setup = () => { }, ...rest }) => {
|
||||
const shownIndex = Variable(0);
|
||||
let previousShownIndex = 0;
|
||||
const count = Math.min(icons.length, names.length, children.length);
|
||||
const tabs = Box({
|
||||
homogeneous: true,
|
||||
children: Array.from({ length: count }, (_, i) => Button({ // Tab button
|
||||
className: 'tab-btn',
|
||||
onClicked: () => shownIndex.value = i,
|
||||
setup: setupCursorHover,
|
||||
child: Box({
|
||||
hpack: 'center',
|
||||
vpack: 'center',
|
||||
className: 'spacing-h-5 txt-small',
|
||||
children: [
|
||||
MaterialIcon(icons[i], 'norm'),
|
||||
Label({
|
||||
label: names[i],
|
||||
})
|
||||
]
|
||||
})
|
||||
})),
|
||||
setup: (self) => self.hook(shownIndex, (self) => {
|
||||
self.children[previousShownIndex].toggleClassName('tab-btn-active', false);
|
||||
self.children[shownIndex.value].toggleClassName('tab-btn-active', true);
|
||||
previousShownIndex = shownIndex.value;
|
||||
}),
|
||||
});
|
||||
const tabIndicatorLine = Box({
|
||||
hexpand: true,
|
||||
vertical: true,
|
||||
homogeneous: true,
|
||||
setup: (self) => self.hook(shownIndex, (self) => {
|
||||
self.children[0].css = `font-size: ${shownIndex.value}px;`;
|
||||
}),
|
||||
children: [NavigationIndicator({
|
||||
className: 'tab-indicator',
|
||||
count: count,
|
||||
css: `font-size: ${shownIndex.value}px;`,
|
||||
})],
|
||||
});
|
||||
const tabSection = Box({
|
||||
homogeneous: true,
|
||||
children: [EventBox({
|
||||
onScrollUp: () => mainBox.prevTab(),
|
||||
onScrollDown: () => mainBox.nextTab(),
|
||||
child: Box({
|
||||
vertical: true,
|
||||
hexpand: true,
|
||||
children: [
|
||||
tabs,
|
||||
tabIndicatorLine
|
||||
]
|
||||
})
|
||||
})]
|
||||
});
|
||||
const contentStack = Stack({
|
||||
transition: 'slide_left_right',
|
||||
children: children.reduce((acc, currentValue, index) => {
|
||||
acc[index] = currentValue;
|
||||
return acc;
|
||||
}, {}),
|
||||
setup: (self) => self.hook(shownIndex, (self) => {
|
||||
self.shown = `${shownIndex.value}`;
|
||||
}),
|
||||
});
|
||||
const mainBox = Box({
|
||||
attribute: {
|
||||
children: children,
|
||||
shown: shownIndex,
|
||||
names: names,
|
||||
},
|
||||
vertical: true,
|
||||
className: `spacing-v-5 ${className}`,
|
||||
setup: (self) => {
|
||||
self.pack_start(tabSection, false, false, 0);
|
||||
self.pack_end(contentStack, true, true, 0);
|
||||
setup(self);
|
||||
},
|
||||
...rest,
|
||||
});
|
||||
mainBox.nextTab = () => shownIndex.value = Math.min(shownIndex.value + 1, count - 1);
|
||||
mainBox.prevTab = () => shownIndex.value = Math.max(shownIndex.value - 1, 0);
|
||||
mainBox.cycleTab = () => shownIndex.value = (shownIndex.value + 1) % count;
|
||||
|
||||
return mainBox;
|
||||
}
|
||||
|
||||
|
||||
export const IconTabContainer = ({
|
||||
iconWidgets, names, children, className = '',
|
||||
setup = () => { }, onChange = () => { },
|
||||
tabsHpack = 'center', tabSwitcherClassName = '',
|
||||
...rest
|
||||
}) => {
|
||||
const shownIndex = Variable(0);
|
||||
let previousShownIndex = 0;
|
||||
const count = Math.min(iconWidgets.length, names.length, children.length);
|
||||
const tabs = Box({
|
||||
homogeneous: true,
|
||||
hpack: tabsHpack,
|
||||
className: `spacing-h-5 ${tabSwitcherClassName}`,
|
||||
children: iconWidgets.map((icon, i) => Button({
|
||||
className: 'tab-icon',
|
||||
tooltipText: names[i],
|
||||
child: icon,
|
||||
setup: setupCursorHover,
|
||||
onClicked: () => shownIndex.value = i,
|
||||
})),
|
||||
setup: (self) => self.hook(shownIndex, (self) => {
|
||||
self.children[previousShownIndex].toggleClassName('tab-icon-active', false);
|
||||
self.children[shownIndex.value].toggleClassName('tab-icon-active', true);
|
||||
previousShownIndex = shownIndex.value;
|
||||
}),
|
||||
});
|
||||
const tabSection = Box({
|
||||
homogeneous: true,
|
||||
children: [EventBox({
|
||||
onScrollUp: () => mainBox.prevTab(),
|
||||
onScrollDown: () => mainBox.nextTab(),
|
||||
child: Box({
|
||||
vertical: true,
|
||||
hexpand: true,
|
||||
children: [
|
||||
tabs,
|
||||
]
|
||||
})
|
||||
})]
|
||||
});
|
||||
const contentStack = Stack({
|
||||
transition: 'slide_left_right',
|
||||
children: children.reduce((acc, currentValue, index) => {
|
||||
acc[index] = currentValue;
|
||||
return acc;
|
||||
}, {}),
|
||||
setup: (self) => self.hook(shownIndex, (self) => {
|
||||
self.shown = `${shownIndex.value}`;
|
||||
}),
|
||||
});
|
||||
const mainBox = Box({
|
||||
attribute: {
|
||||
children: children,
|
||||
shown: shownIndex,
|
||||
names: names,
|
||||
},
|
||||
vertical: true,
|
||||
className: `spacing-v-5 ${className}`,
|
||||
setup: (self) => {
|
||||
self.pack_start(tabSection, false, false, 0);
|
||||
self.pack_end(contentStack, true, true, 0);
|
||||
setup(self);
|
||||
self.hook(shownIndex, (self) => onChange(self, shownIndex.value));
|
||||
},
|
||||
...rest,
|
||||
});
|
||||
mainBox.nextTab = () => shownIndex.value = Math.min(shownIndex.value + 1, count - 1);
|
||||
mainBox.prevTab = () => shownIndex.value = Math.max(shownIndex.value - 1, 0);
|
||||
mainBox.cycleTab = () => shownIndex.value = (shownIndex.value + 1) % count;
|
||||
mainBox.shown = shownIndex;
|
||||
|
||||
return mainBox;
|
||||
}
|
||||
|
||||
export const ExpandingIconTabContainer = ({
|
||||
icons, names, children, className = '',
|
||||
setup = () => { }, onChange = () => { },
|
||||
tabsHpack = 'center', tabSwitcherClassName = '',
|
||||
...rest
|
||||
}) => {
|
||||
const shownIndex = Variable(0);
|
||||
let previousShownIndex = 0;
|
||||
const count = Math.min(icons.length, names.length, children.length);
|
||||
const tabs = Box({
|
||||
hpack: tabsHpack,
|
||||
className: `spacing-h-5 ${tabSwitcherClassName}`,
|
||||
children: icons.map((icon, i) => {
|
||||
const tabIcon = MaterialIcon(icon, 'norm', { hexpand: true });
|
||||
const tabName = DoubleRevealer({
|
||||
transition1: 'slide_right',
|
||||
transition2: 'crossfade',
|
||||
duration1: 0,
|
||||
duration2: 0,
|
||||
// duration1: userOptions.animations.durationSmall,
|
||||
// duration2: userOptions.animations.durationSmall,
|
||||
child: Label({
|
||||
className: 'margin-left-5 txt-small',
|
||||
label: names[i],
|
||||
}),
|
||||
revealChild: i === shownIndex.value,
|
||||
})
|
||||
const button = Button({
|
||||
className: 'tab-icon-expandable',
|
||||
tooltipText: names[i],
|
||||
child: Box({
|
||||
homogeneous: true,
|
||||
children: [Box({
|
||||
hpack: 'center',
|
||||
children: [
|
||||
tabIcon,
|
||||
tabName,
|
||||
]
|
||||
})],
|
||||
}),
|
||||
setup: setupCursorHover,
|
||||
onClicked: () => shownIndex.value = i,
|
||||
});
|
||||
button.toggleFocus = (value) => {
|
||||
tabIcon.hexpand = !value;
|
||||
button.toggleClassName('tab-icon-expandable-active', value);
|
||||
tabName.toggleRevealChild(value);
|
||||
}
|
||||
return button;
|
||||
}),
|
||||
setup: (self) => self.hook(shownIndex, (self) => {
|
||||
self.children[previousShownIndex].toggleFocus(false);
|
||||
self.children[shownIndex.value].toggleFocus(true);
|
||||
previousShownIndex = shownIndex.value;
|
||||
}),
|
||||
});
|
||||
const tabSection = Box({
|
||||
homogeneous: true,
|
||||
children: [EventBox({
|
||||
onScrollUp: () => mainBox.prevTab(),
|
||||
onScrollDown: () => mainBox.nextTab(),
|
||||
child: Box({
|
||||
vertical: true,
|
||||
hexpand: true,
|
||||
children: [
|
||||
tabs,
|
||||
]
|
||||
})
|
||||
})]
|
||||
});
|
||||
const contentStack = Stack({
|
||||
transition: 'slide_left_right',
|
||||
children: children.reduce((acc, currentValue, index) => {
|
||||
acc[index] = currentValue;
|
||||
return acc;
|
||||
}, {}),
|
||||
setup: (self) => self.hook(shownIndex, (self) => {
|
||||
self.shown = `${shownIndex.value}`;
|
||||
}),
|
||||
});
|
||||
const mainBox = Box({
|
||||
attribute: {
|
||||
children: children,
|
||||
shown: shownIndex,
|
||||
names: names,
|
||||
},
|
||||
vertical: true,
|
||||
className: `spacing-v-5 ${className}`,
|
||||
setup: (self) => {
|
||||
self.pack_start(tabSection, false, false, 0);
|
||||
self.pack_end(contentStack, true, true, 0);
|
||||
setup(self);
|
||||
self.hook(shownIndex, (self) => onChange(self, shownIndex.value));
|
||||
},
|
||||
...rest,
|
||||
});
|
||||
mainBox.nextTab = () => shownIndex.value = Math.min(shownIndex.value + 1, count - 1);
|
||||
mainBox.prevTab = () => shownIndex.value = Math.max(shownIndex.value - 1, 0);
|
||||
mainBox.cycleTab = () => shownIndex.value = (shownIndex.value + 1) % count;
|
||||
mainBox.shown = shownIndex;
|
||||
|
||||
return mainBox;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue