Merge branch 'master' into master
This commit is contained in:
@@ -49,7 +49,6 @@
|
|||||||
pkgs.glib
|
pkgs.glib
|
||||||
pkgs.bluez-tools
|
pkgs.bluez-tools
|
||||||
pkgs.grimblast
|
pkgs.grimblast
|
||||||
pkgs.gpu-screen-recorder
|
|
||||||
pkgs.brightnessctl
|
pkgs.brightnessctl
|
||||||
pkgs.gnome-bluetooth
|
pkgs.gnome-bluetooth
|
||||||
(pkgs.python3.withPackages (python-pkgs: with python-pkgs; [
|
(pkgs.python3.withPackages (python-pkgs: with python-pkgs; [
|
||||||
@@ -67,7 +66,7 @@
|
|||||||
pkgs.gvfs
|
pkgs.gvfs
|
||||||
pkgs.swww
|
pkgs.swww
|
||||||
pkgs.pywal
|
pkgs.pywal
|
||||||
];
|
] ++ (nixpkgs.lib.optionals (system == "x86_64-linux") [pkgs.gpu-screen-recorder]);
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -3,14 +3,19 @@
|
|||||||
check_arch_updates() {
|
check_arch_updates() {
|
||||||
official_updates=0
|
official_updates=0
|
||||||
aur_updates=0
|
aur_updates=0
|
||||||
|
if command -v paru &> /dev/null; then
|
||||||
|
aur_helper="paru"
|
||||||
|
else
|
||||||
|
aur_helper="yay"
|
||||||
|
fi
|
||||||
|
|
||||||
if [ "$1" = "-y" ]; then
|
if [ "$1" = "-y" ]; then
|
||||||
aur_updates=$(yay -Qum 2>/dev/null | wc -l)
|
aur_updates=$($aur_helper -Qum 2>/dev/null | wc -l)
|
||||||
elif [ "$1" = "-p" ]; then
|
elif [ "$1" = "-p" ]; then
|
||||||
official_updates=$(checkupdates 2>/dev/null | wc -l)
|
official_updates=$(checkupdates 2>/dev/null | wc -l)
|
||||||
else
|
else
|
||||||
official_updates=$(checkupdates 2>/dev/null | wc -l)
|
official_updates=$(checkupdates 2>/dev/null | wc -l)
|
||||||
aur_updates=$(yay -Qum 2>/dev/null | wc -l)
|
aur_updates=$($aur_helper -Qum 2>/dev/null | wc -l)
|
||||||
fi
|
fi
|
||||||
|
|
||||||
total_updates=$((official_updates + aur_updates))
|
total_updates=$((official_updates + aur_updates))
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ const {
|
|||||||
rateUnit,
|
rateUnit,
|
||||||
dynamicIcon,
|
dynamicIcon,
|
||||||
icon,
|
icon,
|
||||||
|
networkInLabel,
|
||||||
|
networkOutLabel,
|
||||||
round,
|
round,
|
||||||
leftClick,
|
leftClick,
|
||||||
rightClick,
|
rightClick,
|
||||||
@@ -47,11 +49,11 @@ export const Netstat = (): BarBoxChild => {
|
|||||||
const renderNetworkLabel = (lblType: NetstatLabelType, networkService: NetworkResourceData): string => {
|
const renderNetworkLabel = (lblType: NetstatLabelType, networkService: NetworkResourceData): string => {
|
||||||
switch (lblType) {
|
switch (lblType) {
|
||||||
case 'in':
|
case 'in':
|
||||||
return `↓ ${networkService.in}`;
|
return `${networkInLabel.get()} ${networkService.in}`;
|
||||||
case 'out':
|
case 'out':
|
||||||
return `↑ ${networkService.out}`;
|
return `${networkOutLabel.get()} ${networkService.out}`;
|
||||||
default:
|
default:
|
||||||
return `↓ ${networkService.in} ↑ ${networkService.out}`;
|
return `${networkInLabel.get()} ${networkService.in} ${networkOutLabel.get()} ${networkService.out}`;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ const {
|
|||||||
updateCommand,
|
updateCommand,
|
||||||
label,
|
label,
|
||||||
padZero,
|
padZero,
|
||||||
|
autoHide,
|
||||||
pollingInterval,
|
pollingInterval,
|
||||||
icon,
|
icon,
|
||||||
leftClick,
|
leftClick,
|
||||||
@@ -21,6 +22,7 @@ const {
|
|||||||
|
|
||||||
const pendingUpdates: Variable<string> = Variable('0');
|
const pendingUpdates: Variable<string> = Variable('0');
|
||||||
const postInputUpdater = Variable(true);
|
const postInputUpdater = Variable(true);
|
||||||
|
const isVis = Variable(!autoHide.get());
|
||||||
|
|
||||||
const processUpdateCount = (updateCount: string): string => {
|
const processUpdateCount = (updateCount: string): string => {
|
||||||
if (!padZero.get()) return updateCount;
|
if (!padZero.get()) return updateCount;
|
||||||
@@ -37,9 +39,14 @@ const updatesPoller = new BashPoller<string, []>(
|
|||||||
|
|
||||||
updatesPoller.initialize('updates');
|
updatesPoller.initialize('updates');
|
||||||
|
|
||||||
|
Variable.derive([bind(autoHide)], (autoHideModule) => {
|
||||||
|
isVis.set(!autoHideModule || (autoHideModule && parseFloat(pendingUpdates.get()) > 0));
|
||||||
|
});
|
||||||
|
|
||||||
const updatesIcon = Variable.derive(
|
const updatesIcon = Variable.derive(
|
||||||
[bind(icon.pending), bind(icon.updated), bind(pendingUpdates)],
|
[bind(icon.pending), bind(icon.updated), bind(pendingUpdates)],
|
||||||
(pendingIcon, updatedIcon, pUpdates) => {
|
(pendingIcon, updatedIcon, pUpdates) => {
|
||||||
|
isVis.set(!autoHide.get() || (autoHide.get() && parseFloat(pUpdates) > 0));
|
||||||
return parseFloat(pUpdates) === 0 ? updatedIcon : pendingIcon;
|
return parseFloat(pUpdates) === 0 ? updatedIcon : pendingIcon;
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@@ -49,6 +56,7 @@ export const Updates = (): BarBoxChild => {
|
|||||||
textIcon: updatesIcon(),
|
textIcon: updatesIcon(),
|
||||||
tooltipText: bind(pendingUpdates).as((v) => `${v} updates available`),
|
tooltipText: bind(pendingUpdates).as((v) => `${v} updates available`),
|
||||||
boxClass: 'updates',
|
boxClass: 'updates',
|
||||||
|
isVis: isVis,
|
||||||
label: bind(pendingUpdates),
|
label: bind(pendingUpdates),
|
||||||
showLabelBinding: bind(label),
|
showLabelBinding: bind(label),
|
||||||
props: {
|
props: {
|
||||||
|
|||||||
@@ -162,7 +162,7 @@ const navigateWorkspace = (
|
|||||||
while (attempts < workspacesList.length) {
|
while (attempts < workspacesList.length) {
|
||||||
const targetWS = workspacesList[newIndex];
|
const targetWS = workspacesList[newIndex];
|
||||||
if (!isWorkspaceIgnored(ignoredWorkspaces, targetWS)) {
|
if (!isWorkspaceIgnored(ignoredWorkspaces, targetWS)) {
|
||||||
hyprlandService.message_async(`dispatch workspace ${targetWS}`);
|
hyprlandService.dispatch('workspace', targetWS.toString());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
newIndex = (newIndex + step + workspacesList.length) % workspacesList.length;
|
newIndex = (newIndex + step + workspacesList.length) % workspacesList.length;
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
import options from 'src/options';
|
import options from 'src/options';
|
||||||
import { createThrottledScrollHandlers, getCurrentMonitorWorkspaces } from './helpers';
|
import { createThrottledScrollHandlers, getCurrentMonitorWorkspaces } from './helpers';
|
||||||
import { BarBoxChild, SelfButton } from 'src/lib/types/bar';
|
import { BarBoxChild } from 'src/lib/types/bar';
|
||||||
import { WorkspaceModule } from './workspaces';
|
import { WorkspaceModule } from './workspaces';
|
||||||
import { bind, Variable } from 'astal';
|
import { bind, Variable } from 'astal';
|
||||||
import { GtkWidget } from 'src/lib/types/widget';
|
import { GtkWidget } from 'src/lib/types/widget';
|
||||||
import { Gdk } from 'astal/gtk3';
|
import { Astal, Gdk } from 'astal/gtk3';
|
||||||
|
import { isScrollDown, isScrollUp } from 'src/lib/utils';
|
||||||
|
|
||||||
const { workspaces, scroll_speed } = options.bar.workspaces;
|
const { workspaces, scroll_speed } = options.bar.workspaces;
|
||||||
|
|
||||||
@@ -27,23 +28,27 @@ const Workspaces = (monitor = -1): BarBoxChild => {
|
|||||||
boxClass: 'workspaces',
|
boxClass: 'workspaces',
|
||||||
isBox: true,
|
isBox: true,
|
||||||
props: {
|
props: {
|
||||||
setup: (self: SelfButton): void => {
|
setup: (self: Astal.EventBox): void => {
|
||||||
|
let scrollHandlers: number;
|
||||||
Variable.derive([bind(scroll_speed)], (scroll_speed) => {
|
Variable.derive([bind(scroll_speed)], (scroll_speed) => {
|
||||||
|
if (scrollHandlers) {
|
||||||
|
self.disconnect(scrollHandlers);
|
||||||
|
}
|
||||||
|
|
||||||
const { throttledScrollUp, throttledScrollDown } = createThrottledScrollHandlers(
|
const { throttledScrollUp, throttledScrollDown } = createThrottledScrollHandlers(
|
||||||
scroll_speed,
|
scroll_speed,
|
||||||
currentMonitorWorkspaces,
|
currentMonitorWorkspaces,
|
||||||
);
|
);
|
||||||
|
|
||||||
const scrollHandlers = self.connect('scroll-event', (_: GtkWidget, event: Gdk.Event) => {
|
scrollHandlers = self.connect('scroll-event', (_: GtkWidget, event: Gdk.Event) => {
|
||||||
const eventDirection = event.get_scroll_direction()[1];
|
if (isScrollUp(event)) {
|
||||||
if (eventDirection === Gdk.ScrollDirection.UP) {
|
|
||||||
throttledScrollUp();
|
|
||||||
} else if (eventDirection === Gdk.ScrollDirection.DOWN) {
|
|
||||||
throttledScrollDown();
|
throttledScrollDown();
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
self.disconnect(scrollHandlers);
|
if (isScrollDown(event)) {
|
||||||
|
throttledScrollUp();
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -154,6 +154,12 @@ export const CustomModuleSettings = (): JSX.Element => {
|
|||||||
/>
|
/>
|
||||||
<Option opt={options.bar.customModules.netstat.icon} title="Netstat Icon" type="string" />
|
<Option opt={options.bar.customModules.netstat.icon} title="Netstat Icon" type="string" />
|
||||||
<Option opt={options.bar.customModules.netstat.label} title="Show Label" type="boolean" />
|
<Option opt={options.bar.customModules.netstat.label} title="Show Label" type="boolean" />
|
||||||
|
<Option opt={options.bar.customModules.netstat.networkInLabel} title="Network In Label" type="string" />
|
||||||
|
<Option
|
||||||
|
opt={options.bar.customModules.netstat.networkOutLabel}
|
||||||
|
title="Network Out Label"
|
||||||
|
type="string"
|
||||||
|
/>
|
||||||
<Option
|
<Option
|
||||||
opt={options.bar.customModules.netstat.rateUnit}
|
opt={options.bar.customModules.netstat.rateUnit}
|
||||||
title="Rate Unit"
|
title="Rate Unit"
|
||||||
@@ -170,7 +176,7 @@ export const CustomModuleSettings = (): JSX.Element => {
|
|||||||
<Option opt={options.bar.customModules.netstat.round} title="Round" type="boolean" />
|
<Option opt={options.bar.customModules.netstat.round} title="Round" type="boolean" />
|
||||||
<Option
|
<Option
|
||||||
opt={options.bar.customModules.netstat.pollingInterval}
|
opt={options.bar.customModules.netstat.pollingInterval}
|
||||||
title="Polling Interval"
|
title="Polling Interval (ms)"
|
||||||
type="number"
|
type="number"
|
||||||
min={100}
|
min={100}
|
||||||
max={60 * 24 * 1000}
|
max={60 * 24 * 1000}
|
||||||
@@ -221,6 +227,12 @@ export const CustomModuleSettings = (): JSX.Element => {
|
|||||||
/>
|
/>
|
||||||
<Option opt={options.bar.customModules.updates.icon.updated} title="No Updates Icon" type="string" />
|
<Option opt={options.bar.customModules.updates.icon.updated} title="No Updates Icon" type="string" />
|
||||||
<Option opt={options.bar.customModules.updates.label} title="Show Label" type="boolean" />
|
<Option opt={options.bar.customModules.updates.label} title="Show Label" type="boolean" />
|
||||||
|
<Option
|
||||||
|
opt={options.bar.customModules.updates.autoHide}
|
||||||
|
title="Auto Hide"
|
||||||
|
subtitle="Hides module when no updates are available."
|
||||||
|
type="boolean"
|
||||||
|
/>
|
||||||
<Option opt={options.bar.customModules.updates.padZero} title="Pad with 0" type="boolean" />
|
<Option opt={options.bar.customModules.updates.padZero} title="Pad with 0" type="boolean" />
|
||||||
<Option opt={options.theme.bar.buttons.modules.updates.spacing} title="Spacing" type="string" />
|
<Option opt={options.theme.bar.buttons.modules.updates.spacing} title="Spacing" type="string" />
|
||||||
<Option
|
<Option
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ export const Module = ({
|
|||||||
label,
|
label,
|
||||||
tooltipText,
|
tooltipText,
|
||||||
boxClass,
|
boxClass,
|
||||||
|
isVis,
|
||||||
props = {},
|
props = {},
|
||||||
showLabelBinding = bind(undefinedVar),
|
showLabelBinding = bind(undefinedVar),
|
||||||
showLabel,
|
showLabel,
|
||||||
@@ -86,7 +87,7 @@ export const Module = ({
|
|||||||
return {
|
return {
|
||||||
component,
|
component,
|
||||||
tooltip_text: tooltipText,
|
tooltip_text: tooltipText,
|
||||||
isVisible: true,
|
isVis: isVis,
|
||||||
boxClass,
|
boxClass,
|
||||||
props,
|
props,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -26,9 +26,9 @@ export const WidgetContainer = (child: BarBoxChild): JSX.Element => {
|
|||||||
|
|
||||||
if (child.isBox) {
|
if (child.isBox) {
|
||||||
return (
|
return (
|
||||||
<box className={buttonClassName} visible={computeVisible(child)}>
|
<eventbox visible={computeVisible(child)} {...child.props}>
|
||||||
{child.component}
|
<box className={buttonClassName}>{child.component}</box>
|
||||||
</box>
|
</eventbox>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ const Header = (): JSX.Element => (
|
|||||||
<box className={'menu-label-container volume selected'} halign={Gtk.Align.FILL}>
|
<box className={'menu-label-container volume selected'} halign={Gtk.Align.FILL}>
|
||||||
<label className={'menu-label audio volume'} halign={Gtk.Align.START} hexpand label={'Volume'} />
|
<label className={'menu-label audio volume'} halign={Gtk.Align.START} hexpand label={'Volume'} />
|
||||||
<button
|
<button
|
||||||
className={'menu-label slider-toggle'}
|
className={'menu-icon-button menu-label slider-toggle volume'}
|
||||||
onClick={(_, event) => {
|
onClick={(_, event) => {
|
||||||
if (!isPrimaryClick(event)) {
|
if (!isPrimaryClick(event)) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { bind } from 'astal';
|
import { bind } from 'astal';
|
||||||
import { Gdk, Gtk } from 'astal/gtk3';
|
import { Gdk, Gtk } from 'astal/gtk3';
|
||||||
import AstalWp from 'gi://AstalWp?version=0.1';
|
import AstalWp from 'gi://AstalWp?version=0.1';
|
||||||
import { capitalizeFirstLetter } from 'src/lib/utils';
|
import { capitalizeFirstLetter, isScrollDown, isScrollUp } from 'src/lib/utils';
|
||||||
import options from 'src/options';
|
import options from 'src/options';
|
||||||
|
|
||||||
const { raiseMaximumVolume } = options.menus.volume;
|
const { raiseMaximumVolume } = options.menus.volume;
|
||||||
@@ -34,15 +34,14 @@ export const Slider = ({ device, type }: SliderProps): JSX.Element => {
|
|||||||
}}
|
}}
|
||||||
setup={(self) => {
|
setup={(self) => {
|
||||||
self.connect('scroll-event', (_, event: Gdk.Event) => {
|
self.connect('scroll-event', (_, event: Gdk.Event) => {
|
||||||
const [directionSuccess, direction] = event.get_scroll_direction();
|
if (isScrollUp(event)) {
|
||||||
const [deltasSuccess, , yScroll] = event.get_scroll_deltas();
|
const newVolume = device.volume + 0.05;
|
||||||
|
device.set_volume(Math.min(newVolume, 1));
|
||||||
|
}
|
||||||
|
|
||||||
if (directionSuccess) {
|
if (isScrollDown(event)) {
|
||||||
const newVolume = device.volume + (direction === Gdk.ScrollDirection.DOWN ? 0.05 : -0.05);
|
const newVolume = device.volume - 0.05;
|
||||||
device.set_volume(Math.min(newVolume, 1));
|
device.set_volume(newVolume);
|
||||||
} else if (deltasSuccess) {
|
|
||||||
const newVolume = device.volume - yScroll / 100;
|
|
||||||
device.set_volume(Math.min(newVolume, 1));
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { isDiscovering } from './helper';
|
|||||||
|
|
||||||
export const DiscoverButton = (): JSX.Element => (
|
export const DiscoverButton = (): JSX.Element => (
|
||||||
<button
|
<button
|
||||||
className="menu-icon-button search"
|
className="menu-icon-button search bluetooth"
|
||||||
valign={Gtk.Align.CENTER}
|
valign={Gtk.Align.CENTER}
|
||||||
onClick={(_, self) => {
|
onClick={(_, self) => {
|
||||||
if (!isPrimaryClick(self)) {
|
if (!isPrimaryClick(self)) {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { bind, exec } from 'astal';
|
import { bind, exec } from 'astal';
|
||||||
import GdkPixbuf from 'gi://GdkPixbuf';
|
|
||||||
import { Gtk } from 'astal/gtk3';
|
import { Gtk } from 'astal/gtk3';
|
||||||
import options from 'src/options.js';
|
import options from 'src/options.js';
|
||||||
|
import { normalizePath, isAnImage } from 'src/lib/utils.js';
|
||||||
|
|
||||||
const { image, name } = options.menus.dashboard.powermenu.avatar;
|
const { image, name } = options.menus.dashboard.powermenu.avatar;
|
||||||
|
|
||||||
@@ -11,12 +11,11 @@ const ProfilePicture = (): JSX.Element => {
|
|||||||
className={'profile-picture'}
|
className={'profile-picture'}
|
||||||
halign={Gtk.Align.CENTER}
|
halign={Gtk.Align.CENTER}
|
||||||
css={bind(image).as((img) => {
|
css={bind(image).as((img) => {
|
||||||
try {
|
if (isAnImage(img)) {
|
||||||
GdkPixbuf.Pixbuf.new_from_file(img);
|
return `background-image: url("${normalizePath(img)}")`;
|
||||||
return `background-image: url("${img}")`;
|
|
||||||
} catch {
|
|
||||||
return `background-image: url("${SRC_DIR}/assets/hyprpanel.png")`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return `background-image: url("${SRC_DIR}/assets/hyprpanel.png")`;
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -24,36 +24,60 @@ const ShortcutButton = ({ shortcut, ...props }: ShortcutButtonProps): JSX.Elemen
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const LeftShortcut1 = (): JSX.Element => {
|
export const LeftShortcut1 = (): JSX.Element => {
|
||||||
|
if (!hasCommand(left.shortcut1)) {
|
||||||
|
return <box />;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ShortcutButton
|
<ShortcutButton
|
||||||
shortcut={left.shortcut1}
|
shortcut={left.shortcut1}
|
||||||
className={`dashboard-button top-button ${hasCommand(left.shortcut1) ? 'paired' : ''}`}
|
className={`dashboard-button top-button ${hasCommand(left.shortcut2) ? 'paired' : ''}`}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const LeftShortcut2 = (): JSX.Element => {
|
export const LeftShortcut2 = (): JSX.Element => {
|
||||||
|
if (!hasCommand(left.shortcut2)) {
|
||||||
|
return <box />;
|
||||||
|
}
|
||||||
|
|
||||||
return <ShortcutButton shortcut={left.shortcut2} className={`dashboard-button`} />;
|
return <ShortcutButton shortcut={left.shortcut2} className={`dashboard-button`} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const LeftShortcut3 = (): JSX.Element => {
|
export const LeftShortcut3 = (): JSX.Element => {
|
||||||
|
if (!hasCommand(left.shortcut3)) {
|
||||||
|
return <box />;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ShortcutButton
|
<ShortcutButton
|
||||||
shortcut={left.shortcut3}
|
shortcut={left.shortcut3}
|
||||||
className={`dashboard-button top-button ${hasCommand(left.shortcut3) ? 'paired' : ''}`}
|
className={`dashboard-button top-button ${hasCommand(left.shortcut4) ? 'paired' : ''}`}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const LeftShortcut4 = (): JSX.Element => {
|
export const LeftShortcut4 = (): JSX.Element => {
|
||||||
|
if (!hasCommand(left.shortcut4)) {
|
||||||
|
return <box />;
|
||||||
|
}
|
||||||
|
|
||||||
return <ShortcutButton shortcut={left.shortcut4} className={`dashboard-button `} />;
|
return <ShortcutButton shortcut={left.shortcut4} className={`dashboard-button `} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const RightShortcut1 = (): JSX.Element => {
|
export const RightShortcut1 = (): JSX.Element => {
|
||||||
|
if (!hasCommand(right.shortcut1)) {
|
||||||
|
return <box />;
|
||||||
|
}
|
||||||
|
|
||||||
return <ShortcutButton shortcut={right.shortcut1} className={`dashboard-button top-button paired`} />;
|
return <ShortcutButton shortcut={right.shortcut1} className={`dashboard-button top-button paired`} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const RightShortcut3 = (): JSX.Element => {
|
export const RightShortcut3 = (): JSX.Element => {
|
||||||
|
if (!hasCommand(right.shortcut3)) {
|
||||||
|
return <box />;
|
||||||
|
}
|
||||||
|
|
||||||
return <ShortcutButton shortcut={right.shortcut3} className={`dashboard-button top-button paired`} />;
|
return <ShortcutButton shortcut={right.shortcut3} className={`dashboard-button top-button paired`} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { BindableChild } from 'astal/gtk3/astalify';
|
import { BindableChild } from 'astal/gtk3/astalify';
|
||||||
|
|
||||||
export const LeftColumn = ({ visibleClass, children }: LeftColumnProps): JSX.Element => {
|
export const LeftColumn = ({ isVisible, children }: LeftColumnProps): JSX.Element => {
|
||||||
return (
|
return (
|
||||||
<box className={`card-button-section-container ${visibleClass ? 'visible' : ''}`}>
|
<box className={`card-button-section-container ${isVisible ? 'visible' : ''}`}>
|
||||||
{visibleClass ? (
|
{isVisible ? (
|
||||||
<box vertical hexpand vexpand>
|
<box vertical hexpand vexpand>
|
||||||
{children}
|
{children}
|
||||||
</box>
|
</box>
|
||||||
@@ -25,7 +25,7 @@ export const RightColumn = ({ children }: RightColumnProps): JSX.Element => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
interface LeftColumnProps {
|
interface LeftColumnProps {
|
||||||
visibleClass?: boolean;
|
isVisible?: boolean;
|
||||||
children?: BindableChild | BindableChild[];
|
children?: BindableChild | BindableChild[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ export const LeftShortcuts = (): JSX.Element => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<box className={'container most-used dashboard-card'}>
|
<box className={'container most-used dashboard-card'}>
|
||||||
<LeftColumn visibleClass={isVisibleRight && isVisibleLeft}>
|
<LeftColumn isVisible={isVisibleRight && isVisibleLeft}>
|
||||||
<LeftShortcut1 />
|
<LeftShortcut1 />
|
||||||
<LeftShortcut2 />
|
<LeftShortcut2 />
|
||||||
</LeftColumn>
|
</LeftColumn>
|
||||||
@@ -78,7 +78,7 @@ export const RightShortcuts = (): JSX.Element => {
|
|||||||
{Variable.derive(rightBindings, () => {
|
{Variable.derive(rightBindings, () => {
|
||||||
return (
|
return (
|
||||||
<box className={`container utilities dashboard-card ${!leftCardHidden.get() ? 'paired' : ''}`}>
|
<box className={`container utilities dashboard-card ${!leftCardHidden.get() ? 'paired' : ''}`}>
|
||||||
<LeftColumn visibleClass={!leftCardHidden.get()}>
|
<LeftColumn isVisible={true}>
|
||||||
<RightShortcut1 />
|
<RightShortcut1 />
|
||||||
<SettingsButton />
|
<SettingsButton />
|
||||||
</LeftColumn>
|
</LeftColumn>
|
||||||
|
|||||||
@@ -8,6 +8,14 @@ const { enable, duration, active_monitor, monitor } = options.theme.osd;
|
|||||||
|
|
||||||
let count = 0;
|
let count = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* So the OSD doesn't show on startup for no reason
|
||||||
|
*/
|
||||||
|
let isStartingUp = true;
|
||||||
|
timeout(3000, () => {
|
||||||
|
isStartingUp = false;
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles the reveal state of a Widget.Revealer.
|
* Handles the reveal state of a Widget.Revealer.
|
||||||
*
|
*
|
||||||
@@ -69,6 +77,10 @@ export const handleRevealWindow = (self: Widget.Window, property: 'revealChild'
|
|||||||
* @param property The property to check, either 'revealChild' or 'visible'.
|
* @param property The property to check, either 'revealChild' or 'visible'.
|
||||||
*/
|
*/
|
||||||
export const handleReveal = (self: Widget.Revealer | Widget.Window, property: 'revealChild' | 'visible'): void => {
|
export const handleReveal = (self: Widget.Revealer | Widget.Window, property: 'revealChild' | 'visible'): void => {
|
||||||
|
if (isStartingUp) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (self instanceof Widget.Revealer) {
|
if (self instanceof Widget.Revealer) {
|
||||||
handleRevealRevealer(self, property);
|
handleRevealRevealer(self, property);
|
||||||
} else if (self instanceof Widget.Window) {
|
} else if (self instanceof Widget.Window) {
|
||||||
|
|||||||
@@ -29,19 +29,19 @@ export function warnOnLowBattery(): void {
|
|||||||
// To avoid double notifications, we check each of the thresholds and set the correct `sentNotification`, but then
|
// To avoid double notifications, we check each of the thresholds and set the correct `sentNotification`, but then
|
||||||
// combine them into one notification only
|
// combine them into one notification only
|
||||||
let sendNotification = false;
|
let sendNotification = false;
|
||||||
if (!sentLowNotification && batteryPercentage < lowThreshold) {
|
if (!sentLowNotification && batteryPercentage <= lowThreshold) {
|
||||||
sentLowNotification = true;
|
sentLowNotification = true;
|
||||||
sendNotification = true;
|
sendNotification = true;
|
||||||
}
|
}
|
||||||
if (!sentHalfLowNotification && batteryPercentage < lowThreshold / 2) {
|
if (!sentHalfLowNotification && batteryPercentage <= lowThreshold / 2) {
|
||||||
sentHalfLowNotification = true;
|
sentHalfLowNotification = true;
|
||||||
sendNotification = true;
|
sendNotification = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sendNotification) {
|
if (sendNotification) {
|
||||||
Notify({
|
Notify({
|
||||||
summary: lowBatteryNotificationTitle.get().replace('$POWER_LEVEL', batteryPercentage.toString()),
|
summary: lowBatteryNotificationTitle.get().replaceAll('$POWER_LEVEL', batteryPercentage.toString()),
|
||||||
body: lowBatteryNotificationText.get().replace('$POWER_LEVEL', batteryPercentage.toString()),
|
body: lowBatteryNotificationText.get().replaceAll('$POWER_LEVEL', batteryPercentage.toString()),
|
||||||
iconName: icons.ui.warning,
|
iconName: icons.ui.warning,
|
||||||
urgency: 'critical',
|
urgency: 'critical',
|
||||||
});
|
});
|
||||||
|
|||||||
3
src/lib/types/bar.d.ts
vendored
3
src/lib/types/bar.d.ts
vendored
@@ -13,7 +13,7 @@ export type BarBoxChild = {
|
|||||||
isBox?: boolean;
|
isBox?: boolean;
|
||||||
boxClass: string;
|
boxClass: string;
|
||||||
tooltip_text?: string | Binding<string>;
|
tooltip_text?: string | Binding<string>;
|
||||||
} & ({ isBox: true; props: Widget.BoxProps } | { isBox?: false; props: Widget.ButtonProps });
|
} & ({ isBox: true; props: Widget.EventBoxProps } | { isBox?: false; props: Widget.ButtonProps });
|
||||||
|
|
||||||
export type SelfButton = Button<Child, Attribute>;
|
export type SelfButton = Button<Child, Attribute>;
|
||||||
|
|
||||||
@@ -29,6 +29,7 @@ export type BarModule = {
|
|||||||
boundLabel?: string;
|
boundLabel?: string;
|
||||||
tooltipText?: string | Binding<string>;
|
tooltipText?: string | Binding<string>;
|
||||||
boxClass: string;
|
boxClass: string;
|
||||||
|
isVis?: Variable<boolean>;
|
||||||
props?: Widget.ButtonProps;
|
props?: Widget.ButtonProps;
|
||||||
showLabel?: boolean;
|
showLabel?: boolean;
|
||||||
showLabelBinding?: Binding;
|
showLabelBinding?: Binding;
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import options from '../options';
|
|||||||
import { Astal, Gdk, Gtk } from 'astal/gtk3';
|
import { Astal, Gdk, Gtk } from 'astal/gtk3';
|
||||||
import AstalApps from 'gi://AstalApps?version=0.1';
|
import AstalApps from 'gi://AstalApps?version=0.1';
|
||||||
import { exec, execAsync } from 'astal/process';
|
import { exec, execAsync } from 'astal/process';
|
||||||
import { Gio } from 'astal';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles errors by throwing a new Error with a message.
|
* Handles errors by throwing a new Error with a message.
|
||||||
@@ -228,25 +227,40 @@ export function launchApp(app: AstalApps.Application): void {
|
|||||||
* This function attempts to load an image from the specified filepath using GdkPixbuf.
|
* This function attempts to load an image from the specified filepath using GdkPixbuf.
|
||||||
* If the image is successfully loaded, it returns true. Otherwise, it logs an error and returns false.
|
* If the image is successfully loaded, it returns true. Otherwise, it logs an error and returns false.
|
||||||
*
|
*
|
||||||
|
* Note: Unlike GdkPixbuf, this function will normalize the given path.
|
||||||
|
*
|
||||||
* @param imgFilePath The path to the image file.
|
* @param imgFilePath The path to the image file.
|
||||||
*
|
*
|
||||||
* @returns True if the filepath is a valid image, false otherwise.
|
* @returns True if the filepath is a valid image, false otherwise.
|
||||||
*/
|
*/
|
||||||
export function isAnImage(imgFilePath: string): boolean {
|
export function isAnImage(imgFilePath: string): boolean {
|
||||||
try {
|
try {
|
||||||
const file = Gio.File.new_for_path(imgFilePath);
|
GdkPixbuf.Pixbuf.new_from_file(normalizePath(imgFilePath));
|
||||||
if (!file.query_exists(null)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
GdkPixbuf.Pixbuf.new_from_file(imgFilePath);
|
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.info(error);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Normalize a path to the absolute representation of the path.
|
||||||
|
*
|
||||||
|
* Note: This will only expand '~' if present. Path traversal is not supported.
|
||||||
|
*
|
||||||
|
* @param path The path to normalize.
|
||||||
|
*
|
||||||
|
* @returns The normalized path.
|
||||||
|
*/
|
||||||
|
export function normalizePath(path: string): string {
|
||||||
|
if (path.charAt(0) == '~') {
|
||||||
|
// Replace will only replace the first match, in this case, the first character
|
||||||
|
return path.replace('~', GLib.get_home_dir());
|
||||||
|
}
|
||||||
|
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends a notification using the `notify-send` command.
|
* Sends a notification using the `notify-send` command.
|
||||||
*
|
*
|
||||||
@@ -399,7 +413,20 @@ export const isMiddleClick = (event: Astal.ClickEvent): boolean => event.button
|
|||||||
*
|
*
|
||||||
* @returns True if the event is a scroll up, false otherwise.
|
* @returns True if the event is a scroll up, false otherwise.
|
||||||
*/
|
*/
|
||||||
export const isScrollUp = (event: Astal.ScrollEvent): boolean => event.direction === Gdk.ScrollDirection.UP;
|
export const isScrollUp = (event: Gdk.Event): boolean => {
|
||||||
|
const [directionSuccess, direction] = event.get_scroll_direction();
|
||||||
|
const [deltaSuccess, , yScroll] = event.get_scroll_deltas();
|
||||||
|
|
||||||
|
if (directionSuccess && direction === Gdk.ScrollDirection.UP) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (deltaSuccess && yScroll < 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if an event is a scroll down.
|
* Checks if an event is a scroll down.
|
||||||
@@ -410,4 +437,17 @@ export const isScrollUp = (event: Astal.ScrollEvent): boolean => event.direction
|
|||||||
*
|
*
|
||||||
* @returns True if the event is a scroll down, false otherwise.
|
* @returns True if the event is a scroll down, false otherwise.
|
||||||
*/
|
*/
|
||||||
export const isScrollDown = (event: Astal.ScrollEvent): boolean => event.direction === Gdk.ScrollDirection.DOWN;
|
export const isScrollDown = (event: Gdk.Event): boolean => {
|
||||||
|
const [directionSuccess, direction] = event.get_scroll_direction();
|
||||||
|
const [deltaSuccess, , yScroll] = event.get_scroll_deltas();
|
||||||
|
|
||||||
|
if (directionSuccess && direction === Gdk.ScrollDirection.DOWN) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (deltaSuccess && yScroll > 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|||||||
@@ -1072,6 +1072,8 @@ const options = mkOptions(CONFIG, {
|
|||||||
networkInterface: opt(''),
|
networkInterface: opt(''),
|
||||||
dynamicIcon: opt(false),
|
dynamicIcon: opt(false),
|
||||||
icon: opt(''),
|
icon: opt(''),
|
||||||
|
networkInLabel: opt('↓'),
|
||||||
|
networkOutLabel: opt('↑'),
|
||||||
round: opt(true),
|
round: opt(true),
|
||||||
labelType: opt<NetstatLabelType>('full'),
|
labelType: opt<NetstatLabelType>('full'),
|
||||||
rateUnit: opt<RateUnit>('auto'),
|
rateUnit: opt<RateUnit>('auto'),
|
||||||
@@ -1094,6 +1096,7 @@ const options = mkOptions(CONFIG, {
|
|||||||
updateCommand: opt(`${SRC_DIR}/scripts/checkUpdates.sh -arch`),
|
updateCommand: opt(`${SRC_DIR}/scripts/checkUpdates.sh -arch`),
|
||||||
label: opt(true),
|
label: opt(true),
|
||||||
padZero: opt(true),
|
padZero: opt(true),
|
||||||
|
autoHide: opt(false),
|
||||||
icon: {
|
icon: {
|
||||||
pending: opt(''),
|
pending: opt(''),
|
||||||
updated: opt(''),
|
updated: opt(''),
|
||||||
@@ -1199,7 +1202,7 @@ const options = mkOptions(CONFIG, {
|
|||||||
logout: opt('hyprctl dispatch exit'),
|
logout: opt('hyprctl dispatch exit'),
|
||||||
shutdown: opt('systemctl poweroff'),
|
shutdown: opt('systemctl poweroff'),
|
||||||
avatar: {
|
avatar: {
|
||||||
image: opt('$HOME/.face.icon'),
|
image: opt('~/.face.icon'),
|
||||||
name: opt<'system' | string>('system'),
|
name: opt<'system' | string>('system'),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import icons from '../lib/icons/icons';
|
import icons from '../lib/icons/icons';
|
||||||
import { bash, dependencies, Notify, isAnImage } from '../lib/utils';
|
import { bash, dependencies, Notify, isAnImage, normalizePath } from '../lib/utils';
|
||||||
import options from '../options';
|
import options from '../options';
|
||||||
import Wallpaper from 'src/services/Wallpaper';
|
import Wallpaper from 'src/services/Wallpaper';
|
||||||
|
|
||||||
@@ -8,7 +8,7 @@ const { matugen } = options.theme;
|
|||||||
const ensureMatugenWallpaper = (): void => {
|
const ensureMatugenWallpaper = (): void => {
|
||||||
const wallpaperPath = options.wallpaper.image.get();
|
const wallpaperPath = options.wallpaper.image.get();
|
||||||
|
|
||||||
if (matugen.get() && (!options.wallpaper.image.get().length || !isAnImage(wallpaperPath))) {
|
if (matugen.get() && (!wallpaperPath.length || !isAnImage(normalizePath(wallpaperPath)))) {
|
||||||
Notify({
|
Notify({
|
||||||
summary: 'Matugen Failed',
|
summary: 'Matugen Failed',
|
||||||
body: "Please select a wallpaper in 'Theming > General' first.",
|
body: "Please select a wallpaper in 'Theming > General' first.",
|
||||||
|
|||||||
@@ -31,6 +31,18 @@
|
|||||||
color: if($bar-menus-monochrome, $bar-menus-icons-active, $bar-menus-menu-volume-icons-active);
|
color: if($bar-menus-monochrome, $bar-menus-icons-active, $bar-menus-menu-volume-icons-active);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.menu-icon-button.volume {
|
||||||
|
color: if($bar-menus-monochrome, $bar-menus-iconbuttons-passive, $bar-menus-menu-volume-iconbutton-passive);
|
||||||
|
padding: 0em 1em;
|
||||||
|
|
||||||
|
label {
|
||||||
|
font-size: 1.25em;
|
||||||
|
}
|
||||||
|
&:hover {
|
||||||
|
color: if($bar-menus-monochrome, $bar-menus-iconbuttons-active, $bar-menus-menu-volume-iconbutton-active);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.menu-slider.playback {
|
.menu-slider.playback {
|
||||||
trough {
|
trough {
|
||||||
background: if(
|
background: if(
|
||||||
@@ -213,18 +225,6 @@
|
|||||||
border-radius: 0em;
|
border-radius: 0em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.slider-toggle {
|
|
||||||
color: if($bar-menus-monochrome, $bar-menus-iconbuttons-passive, $bar-menus-menu-volume-iconbutton-passive);
|
|
||||||
padding: 0em 1em;
|
|
||||||
label {
|
|
||||||
font-size: 1.25em;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: if($bar-menus-monochrome, $bar-menus-iconbuttons-active, $bar-menus-menu-volume-iconbutton-active);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.active-playbacks-scrollable {
|
.active-playbacks-scrollable {
|
||||||
min-height: 12.5em;
|
min-height: 12.5em;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user