Merge branch 'master' into master
This commit is contained in:
@@ -49,7 +49,6 @@
|
||||
pkgs.glib
|
||||
pkgs.bluez-tools
|
||||
pkgs.grimblast
|
||||
pkgs.gpu-screen-recorder
|
||||
pkgs.brightnessctl
|
||||
pkgs.gnome-bluetooth
|
||||
(pkgs.python3.withPackages (python-pkgs: with python-pkgs; [
|
||||
@@ -67,7 +66,7 @@
|
||||
pkgs.gvfs
|
||||
pkgs.swww
|
||||
pkgs.pywal
|
||||
];
|
||||
] ++ (nixpkgs.lib.optionals (system == "x86_64-linux") [pkgs.gpu-screen-recorder]);
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
@@ -3,14 +3,19 @@
|
||||
check_arch_updates() {
|
||||
official_updates=0
|
||||
aur_updates=0
|
||||
if command -v paru &> /dev/null; then
|
||||
aur_helper="paru"
|
||||
else
|
||||
aur_helper="yay"
|
||||
fi
|
||||
|
||||
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
|
||||
official_updates=$(checkupdates 2>/dev/null | wc -l)
|
||||
else
|
||||
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
|
||||
|
||||
total_updates=$((official_updates + aur_updates))
|
||||
|
||||
@@ -19,6 +19,8 @@ const {
|
||||
rateUnit,
|
||||
dynamicIcon,
|
||||
icon,
|
||||
networkInLabel,
|
||||
networkOutLabel,
|
||||
round,
|
||||
leftClick,
|
||||
rightClick,
|
||||
@@ -47,11 +49,11 @@ export const Netstat = (): BarBoxChild => {
|
||||
const renderNetworkLabel = (lblType: NetstatLabelType, networkService: NetworkResourceData): string => {
|
||||
switch (lblType) {
|
||||
case 'in':
|
||||
return `↓ ${networkService.in}`;
|
||||
return `${networkInLabel.get()} ${networkService.in}`;
|
||||
case 'out':
|
||||
return `↑ ${networkService.out}`;
|
||||
return `${networkOutLabel.get()} ${networkService.out}`;
|
||||
default:
|
||||
return `↓ ${networkService.in} ↑ ${networkService.out}`;
|
||||
return `${networkInLabel.get()} ${networkService.in} ${networkOutLabel.get()} ${networkService.out}`;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ const {
|
||||
updateCommand,
|
||||
label,
|
||||
padZero,
|
||||
autoHide,
|
||||
pollingInterval,
|
||||
icon,
|
||||
leftClick,
|
||||
@@ -21,6 +22,7 @@ const {
|
||||
|
||||
const pendingUpdates: Variable<string> = Variable('0');
|
||||
const postInputUpdater = Variable(true);
|
||||
const isVis = Variable(!autoHide.get());
|
||||
|
||||
const processUpdateCount = (updateCount: string): string => {
|
||||
if (!padZero.get()) return updateCount;
|
||||
@@ -37,9 +39,14 @@ const updatesPoller = new BashPoller<string, []>(
|
||||
|
||||
updatesPoller.initialize('updates');
|
||||
|
||||
Variable.derive([bind(autoHide)], (autoHideModule) => {
|
||||
isVis.set(!autoHideModule || (autoHideModule && parseFloat(pendingUpdates.get()) > 0));
|
||||
});
|
||||
|
||||
const updatesIcon = Variable.derive(
|
||||
[bind(icon.pending), bind(icon.updated), bind(pendingUpdates)],
|
||||
(pendingIcon, updatedIcon, pUpdates) => {
|
||||
isVis.set(!autoHide.get() || (autoHide.get() && parseFloat(pUpdates) > 0));
|
||||
return parseFloat(pUpdates) === 0 ? updatedIcon : pendingIcon;
|
||||
},
|
||||
);
|
||||
@@ -49,6 +56,7 @@ export const Updates = (): BarBoxChild => {
|
||||
textIcon: updatesIcon(),
|
||||
tooltipText: bind(pendingUpdates).as((v) => `${v} updates available`),
|
||||
boxClass: 'updates',
|
||||
isVis: isVis,
|
||||
label: bind(pendingUpdates),
|
||||
showLabelBinding: bind(label),
|
||||
props: {
|
||||
|
||||
@@ -162,7 +162,7 @@ const navigateWorkspace = (
|
||||
while (attempts < workspacesList.length) {
|
||||
const targetWS = workspacesList[newIndex];
|
||||
if (!isWorkspaceIgnored(ignoredWorkspaces, targetWS)) {
|
||||
hyprlandService.message_async(`dispatch workspace ${targetWS}`);
|
||||
hyprlandService.dispatch('workspace', targetWS.toString());
|
||||
return;
|
||||
}
|
||||
newIndex = (newIndex + step + workspacesList.length) % workspacesList.length;
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import options from 'src/options';
|
||||
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 { bind, Variable } from 'astal';
|
||||
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;
|
||||
|
||||
@@ -27,23 +28,27 @@ const Workspaces = (monitor = -1): BarBoxChild => {
|
||||
boxClass: 'workspaces',
|
||||
isBox: true,
|
||||
props: {
|
||||
setup: (self: SelfButton): void => {
|
||||
setup: (self: Astal.EventBox): void => {
|
||||
let scrollHandlers: number;
|
||||
Variable.derive([bind(scroll_speed)], (scroll_speed) => {
|
||||
if (scrollHandlers) {
|
||||
self.disconnect(scrollHandlers);
|
||||
}
|
||||
|
||||
const { throttledScrollUp, throttledScrollDown } = createThrottledScrollHandlers(
|
||||
scroll_speed,
|
||||
currentMonitorWorkspaces,
|
||||
);
|
||||
|
||||
const scrollHandlers = self.connect('scroll-event', (_: GtkWidget, event: Gdk.Event) => {
|
||||
const eventDirection = event.get_scroll_direction()[1];
|
||||
if (eventDirection === Gdk.ScrollDirection.UP) {
|
||||
throttledScrollUp();
|
||||
} else if (eventDirection === Gdk.ScrollDirection.DOWN) {
|
||||
scrollHandlers = self.connect('scroll-event', (_: GtkWidget, event: Gdk.Event) => {
|
||||
if (isScrollUp(event)) {
|
||||
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.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
|
||||
opt={options.bar.customModules.netstat.rateUnit}
|
||||
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.pollingInterval}
|
||||
title="Polling Interval"
|
||||
title="Polling Interval (ms)"
|
||||
type="number"
|
||||
min={100}
|
||||
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.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.theme.bar.buttons.modules.updates.spacing} title="Spacing" type="string" />
|
||||
<Option
|
||||
|
||||
@@ -14,6 +14,7 @@ export const Module = ({
|
||||
label,
|
||||
tooltipText,
|
||||
boxClass,
|
||||
isVis,
|
||||
props = {},
|
||||
showLabelBinding = bind(undefinedVar),
|
||||
showLabel,
|
||||
@@ -86,7 +87,7 @@ export const Module = ({
|
||||
return {
|
||||
component,
|
||||
tooltip_text: tooltipText,
|
||||
isVisible: true,
|
||||
isVis: isVis,
|
||||
boxClass,
|
||||
props,
|
||||
};
|
||||
|
||||
@@ -26,9 +26,9 @@ export const WidgetContainer = (child: BarBoxChild): JSX.Element => {
|
||||
|
||||
if (child.isBox) {
|
||||
return (
|
||||
<box className={buttonClassName} visible={computeVisible(child)}>
|
||||
{child.component}
|
||||
</box>
|
||||
<eventbox visible={computeVisible(child)} {...child.props}>
|
||||
<box className={buttonClassName}>{child.component}</box>
|
||||
</eventbox>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ const Header = (): JSX.Element => (
|
||||
<box className={'menu-label-container volume selected'} halign={Gtk.Align.FILL}>
|
||||
<label className={'menu-label audio volume'} halign={Gtk.Align.START} hexpand label={'Volume'} />
|
||||
<button
|
||||
className={'menu-label slider-toggle'}
|
||||
className={'menu-icon-button menu-label slider-toggle volume'}
|
||||
onClick={(_, event) => {
|
||||
if (!isPrimaryClick(event)) {
|
||||
return;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { bind } from 'astal';
|
||||
import { Gdk, Gtk } from 'astal/gtk3';
|
||||
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';
|
||||
|
||||
const { raiseMaximumVolume } = options.menus.volume;
|
||||
@@ -34,15 +34,14 @@ export const Slider = ({ device, type }: SliderProps): JSX.Element => {
|
||||
}}
|
||||
setup={(self) => {
|
||||
self.connect('scroll-event', (_, event: Gdk.Event) => {
|
||||
const [directionSuccess, direction] = event.get_scroll_direction();
|
||||
const [deltasSuccess, , yScroll] = event.get_scroll_deltas();
|
||||
if (isScrollUp(event)) {
|
||||
const newVolume = device.volume + 0.05;
|
||||
device.set_volume(Math.min(newVolume, 1));
|
||||
}
|
||||
|
||||
if (directionSuccess) {
|
||||
const newVolume = device.volume + (direction === Gdk.ScrollDirection.DOWN ? 0.05 : -0.05);
|
||||
device.set_volume(Math.min(newVolume, 1));
|
||||
} else if (deltasSuccess) {
|
||||
const newVolume = device.volume - yScroll / 100;
|
||||
device.set_volume(Math.min(newVolume, 1));
|
||||
if (isScrollDown(event)) {
|
||||
const newVolume = device.volume - 0.05;
|
||||
device.set_volume(newVolume);
|
||||
}
|
||||
});
|
||||
}}
|
||||
|
||||
@@ -6,7 +6,7 @@ import { isDiscovering } from './helper';
|
||||
|
||||
export const DiscoverButton = (): JSX.Element => (
|
||||
<button
|
||||
className="menu-icon-button search"
|
||||
className="menu-icon-button search bluetooth"
|
||||
valign={Gtk.Align.CENTER}
|
||||
onClick={(_, self) => {
|
||||
if (!isPrimaryClick(self)) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { bind, exec } from 'astal';
|
||||
import GdkPixbuf from 'gi://GdkPixbuf';
|
||||
import { Gtk } from 'astal/gtk3';
|
||||
import options from 'src/options.js';
|
||||
import { normalizePath, isAnImage } from 'src/lib/utils.js';
|
||||
|
||||
const { image, name } = options.menus.dashboard.powermenu.avatar;
|
||||
|
||||
@@ -11,12 +11,11 @@ const ProfilePicture = (): JSX.Element => {
|
||||
className={'profile-picture'}
|
||||
halign={Gtk.Align.CENTER}
|
||||
css={bind(image).as((img) => {
|
||||
try {
|
||||
GdkPixbuf.Pixbuf.new_from_file(img);
|
||||
return `background-image: url("${img}")`;
|
||||
} catch {
|
||||
return `background-image: url("${SRC_DIR}/assets/hyprpanel.png")`;
|
||||
if (isAnImage(img)) {
|
||||
return `background-image: url("${normalizePath(img)}")`;
|
||||
}
|
||||
|
||||
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 => {
|
||||
if (!hasCommand(left.shortcut1)) {
|
||||
return <box />;
|
||||
}
|
||||
|
||||
return (
|
||||
<ShortcutButton
|
||||
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 => {
|
||||
if (!hasCommand(left.shortcut2)) {
|
||||
return <box />;
|
||||
}
|
||||
|
||||
return <ShortcutButton shortcut={left.shortcut2} className={`dashboard-button`} />;
|
||||
};
|
||||
|
||||
export const LeftShortcut3 = (): JSX.Element => {
|
||||
if (!hasCommand(left.shortcut3)) {
|
||||
return <box />;
|
||||
}
|
||||
|
||||
return (
|
||||
<ShortcutButton
|
||||
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 => {
|
||||
if (!hasCommand(left.shortcut4)) {
|
||||
return <box />;
|
||||
}
|
||||
|
||||
return <ShortcutButton shortcut={left.shortcut4} className={`dashboard-button `} />;
|
||||
};
|
||||
|
||||
export const RightShortcut1 = (): JSX.Element => {
|
||||
if (!hasCommand(right.shortcut1)) {
|
||||
return <box />;
|
||||
}
|
||||
|
||||
return <ShortcutButton shortcut={right.shortcut1} className={`dashboard-button top-button paired`} />;
|
||||
};
|
||||
|
||||
export const RightShortcut3 = (): JSX.Element => {
|
||||
if (!hasCommand(right.shortcut3)) {
|
||||
return <box />;
|
||||
}
|
||||
|
||||
return <ShortcutButton shortcut={right.shortcut3} className={`dashboard-button top-button paired`} />;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { BindableChild } from 'astal/gtk3/astalify';
|
||||
|
||||
export const LeftColumn = ({ visibleClass, children }: LeftColumnProps): JSX.Element => {
|
||||
export const LeftColumn = ({ isVisible, children }: LeftColumnProps): JSX.Element => {
|
||||
return (
|
||||
<box className={`card-button-section-container ${visibleClass ? 'visible' : ''}`}>
|
||||
{visibleClass ? (
|
||||
<box className={`card-button-section-container ${isVisible ? 'visible' : ''}`}>
|
||||
{isVisible ? (
|
||||
<box vertical hexpand vexpand>
|
||||
{children}
|
||||
</box>
|
||||
@@ -25,7 +25,7 @@ export const RightColumn = ({ children }: RightColumnProps): JSX.Element => {
|
||||
};
|
||||
|
||||
interface LeftColumnProps {
|
||||
visibleClass?: boolean;
|
||||
isVisible?: boolean;
|
||||
children?: BindableChild | BindableChild[];
|
||||
}
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@ export const LeftShortcuts = (): JSX.Element => {
|
||||
|
||||
return (
|
||||
<box className={'container most-used dashboard-card'}>
|
||||
<LeftColumn visibleClass={isVisibleRight && isVisibleLeft}>
|
||||
<LeftColumn isVisible={isVisibleRight && isVisibleLeft}>
|
||||
<LeftShortcut1 />
|
||||
<LeftShortcut2 />
|
||||
</LeftColumn>
|
||||
@@ -78,7 +78,7 @@ export const RightShortcuts = (): JSX.Element => {
|
||||
{Variable.derive(rightBindings, () => {
|
||||
return (
|
||||
<box className={`container utilities dashboard-card ${!leftCardHidden.get() ? 'paired' : ''}`}>
|
||||
<LeftColumn visibleClass={!leftCardHidden.get()}>
|
||||
<LeftColumn isVisible={true}>
|
||||
<RightShortcut1 />
|
||||
<SettingsButton />
|
||||
</LeftColumn>
|
||||
|
||||
@@ -8,6 +8,14 @@ const { enable, duration, active_monitor, monitor } = options.theme.osd;
|
||||
|
||||
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.
|
||||
*
|
||||
@@ -69,6 +77,10 @@ export const handleRevealWindow = (self: Widget.Window, property: 'revealChild'
|
||||
* @param property The property to check, either 'revealChild' or 'visible'.
|
||||
*/
|
||||
export const handleReveal = (self: Widget.Revealer | Widget.Window, property: 'revealChild' | 'visible'): void => {
|
||||
if (isStartingUp) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (self instanceof Widget.Revealer) {
|
||||
handleRevealRevealer(self, property);
|
||||
} 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
|
||||
// combine them into one notification only
|
||||
let sendNotification = false;
|
||||
if (!sentLowNotification && batteryPercentage < lowThreshold) {
|
||||
if (!sentLowNotification && batteryPercentage <= lowThreshold) {
|
||||
sentLowNotification = true;
|
||||
sendNotification = true;
|
||||
}
|
||||
if (!sentHalfLowNotification && batteryPercentage < lowThreshold / 2) {
|
||||
if (!sentHalfLowNotification && batteryPercentage <= lowThreshold / 2) {
|
||||
sentHalfLowNotification = true;
|
||||
sendNotification = true;
|
||||
}
|
||||
|
||||
if (sendNotification) {
|
||||
Notify({
|
||||
summary: lowBatteryNotificationTitle.get().replace('$POWER_LEVEL', batteryPercentage.toString()),
|
||||
body: lowBatteryNotificationText.get().replace('$POWER_LEVEL', batteryPercentage.toString()),
|
||||
summary: lowBatteryNotificationTitle.get().replaceAll('$POWER_LEVEL', batteryPercentage.toString()),
|
||||
body: lowBatteryNotificationText.get().replaceAll('$POWER_LEVEL', batteryPercentage.toString()),
|
||||
iconName: icons.ui.warning,
|
||||
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;
|
||||
boxClass: 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>;
|
||||
|
||||
@@ -29,6 +29,7 @@ export type BarModule = {
|
||||
boundLabel?: string;
|
||||
tooltipText?: string | Binding<string>;
|
||||
boxClass: string;
|
||||
isVis?: Variable<boolean>;
|
||||
props?: Widget.ButtonProps;
|
||||
showLabel?: boolean;
|
||||
showLabelBinding?: Binding;
|
||||
|
||||
@@ -11,7 +11,6 @@ import options from '../options';
|
||||
import { Astal, Gdk, Gtk } from 'astal/gtk3';
|
||||
import AstalApps from 'gi://AstalApps?version=0.1';
|
||||
import { exec, execAsync } from 'astal/process';
|
||||
import { Gio } from 'astal';
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* 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.
|
||||
*
|
||||
* @returns True if the filepath is a valid image, false otherwise.
|
||||
*/
|
||||
export function isAnImage(imgFilePath: string): boolean {
|
||||
try {
|
||||
const file = Gio.File.new_for_path(imgFilePath);
|
||||
if (!file.query_exists(null)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
GdkPixbuf.Pixbuf.new_from_file(imgFilePath);
|
||||
GdkPixbuf.Pixbuf.new_from_file(normalizePath(imgFilePath));
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
console.info(error);
|
||||
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.
|
||||
*
|
||||
@@ -399,7 +413,20 @@ export const isMiddleClick = (event: Astal.ClickEvent): boolean => event.button
|
||||
*
|
||||
* @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.
|
||||
@@ -410,4 +437,17 @@ export const isScrollUp = (event: Astal.ScrollEvent): boolean => event.direction
|
||||
*
|
||||
* @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(''),
|
||||
dynamicIcon: opt(false),
|
||||
icon: opt(''),
|
||||
networkInLabel: opt('↓'),
|
||||
networkOutLabel: opt('↑'),
|
||||
round: opt(true),
|
||||
labelType: opt<NetstatLabelType>('full'),
|
||||
rateUnit: opt<RateUnit>('auto'),
|
||||
@@ -1094,6 +1096,7 @@ const options = mkOptions(CONFIG, {
|
||||
updateCommand: opt(`${SRC_DIR}/scripts/checkUpdates.sh -arch`),
|
||||
label: opt(true),
|
||||
padZero: opt(true),
|
||||
autoHide: opt(false),
|
||||
icon: {
|
||||
pending: opt(''),
|
||||
updated: opt(''),
|
||||
@@ -1199,7 +1202,7 @@ const options = mkOptions(CONFIG, {
|
||||
logout: opt('hyprctl dispatch exit'),
|
||||
shutdown: opt('systemctl poweroff'),
|
||||
avatar: {
|
||||
image: opt('$HOME/.face.icon'),
|
||||
image: opt('~/.face.icon'),
|
||||
name: opt<'system' | string>('system'),
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
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 Wallpaper from 'src/services/Wallpaper';
|
||||
|
||||
@@ -8,7 +8,7 @@ const { matugen } = options.theme;
|
||||
const ensureMatugenWallpaper = (): void => {
|
||||
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({
|
||||
summary: 'Matugen Failed',
|
||||
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);
|
||||
}
|
||||
|
||||
.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 {
|
||||
trough {
|
||||
background: if(
|
||||
@@ -213,18 +225,6 @@
|
||||
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 {
|
||||
min-height: 12.5em;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user