feat: Shortcuts widget

This commit is contained in:
Franek 2025-05-03 17:06:04 +02:00
parent 39d46c3311
commit faab05ec8b
9 changed files with 184 additions and 15 deletions

View File

@ -6,28 +6,30 @@ import Hyprland from "gi://AstalHyprland";
import QuickSettings from "widget/quick_settings/quick_settings";
import BluetoothWindow from "widget/quick_settings/bluetooth";
import Shortcuts from "./widget/launcher/shortcuts";
import Clipboard from "widget/launcher/clipboard";
import Launcher from "widget/launcher/launcher";
import BatteryInfo from "widget/bar/battery_info";
import Launcher from "widget/launcher/launcher";
import Bar from "widget/bar/bar";
const hypr = Hyprland.get_default();
const windows = new Map<number, Gtk.Window[]>();
const components = [
Bar,
Launcher,
QuickSettings,
Bar,
Launcher,
QuickSettings,
BluetoothWindow,
BatteryInfo,
Clipboard
Clipboard,
Shortcuts
];
const setupBars = async (monitor_id: number) => {
const windows = await Promise.all(
components.map(item => Promise.resolve(item(monitor_id)) as Promise<Gtk.Window>)
);
const windows = await Promise.all(
components.map(item => Promise.resolve(item(monitor_id)) as Promise<Gtk.Window>)
);
return windows;
return windows;
};
const STYLES = GLib.get_user_config_dir() + "/ags/styles/style.scss";
@ -55,7 +57,7 @@ App.start({
hypr.connect("monitor-added", async (_, monitor: Hyprland.Monitor) => {
if (!windows.has(monitor.id)) windows.set(monitor.id, await setupBars(monitor.id))
});
hypr.connect("monitor-removed", (_, monitor_id: number) => {
const monitorWindows = windows.get(monitor_id)
if (monitorWindows) {

View File

@ -5,6 +5,37 @@ export type Belongs<T, U> = {
[K in keyof T]: T[K] extends U ? K : never;
}[keyof T];
export const generateModmaskMap = () => {
const MODIFIERS: Record<number, string> = {
1: "SHIFT",
4: "CTRL",
8: "ALT",
64: "SUPER"
};
const modmaskMap: Record<number, string> = {};
const modifierValues = Object.keys(MODIFIERS).map(Number);
const totalCombinations = 1 << modifierValues.length;
for (let i = 0; i < totalCombinations; i++) {
let modmask = 0;
const labels: string[] = [];
modifierValues.forEach((value, index) => {
if (i & (1 << index)) {
modmask |= value;
labels.push(MODIFIERS[value]);
}
});
modmaskMap[modmask] = labels.reverse().join(" + ");
}
return modmaskMap;
};
export const hideWindow = (self: Gtk.Window, keyval: number) => {
const keys = ["quick_settings"].includes(self.name)
? [Gdk.KEY_Escape]
@ -27,6 +58,6 @@ export const limit = <T>(list: T[], max: number) => list.filter((_, i) => i <= (
export const skip = <T>(list: T[], items: number) => list.filter((_, i) => {
if (items < 0) items = 0;
if (items > (list.length - 1)) items = list.length;
return i > (items - 1);
});

View File

@ -1,5 +1,6 @@
@forward '_bar';
@forward '_launcher';
@forward '_clipboard';
@forward '_shortcuts';
@forward '_battery_info';
@forward '_quick_settings';

View File

@ -0,0 +1,52 @@
@use '../theme_colors.scss' as *;
.shortcuts {
.entry {
padding: 10px;
background: $base0B;
border-radius: 8px;
transition: background 0.2s ease;
&:hover {
background: $base00;
>label {
color: $base0B;
}
>.kbd {
background: $base0B;
color: $base00;
}
}
label {
color: $base00;
font-size: 14px;
font-weight: 600;
}
.kbd {
padding: 2px 6px;
font-size: 0.9em;
font-family: monospace;
border-radius: 4px;
margin: 0 2px;
background: $base00;
color: $base0B;
}
}
.not-found {
background: $base0B;
color: $base00;
font-size: 14px;
padding: 10px;
border-radius: 8px;
image {
margin-bottom: 8px;
}
}
}

View File

@ -69,7 +69,7 @@ export default async function Clipboard(_monitor_id: number) {
};
const setup = (self: Gtk.Window) => self.connect('notify::visible', async () => {
await getClipboardHistory(history)
if (self.is_visible()) await getClipboardHistory(history)
})
return <window

View File

@ -0,0 +1,82 @@
import { generateModmaskMap, hideWindow, limit, skip } from "@lib/utils";
import { App, Astal, Gtk } from "astal/gtk4";
import { Variable, bind } from "astal";
import Hyprland from "gi://AstalHyprland";
const modmasks = generateModmaskMap();
// Incomplete Astal types?
type Bind = Hyprland.Bind & { description: string };
function ShortcutEntry({ entry }: { entry: Bind }) {
return <box cssClasses={["entry"]} spacing={4}>
{entry.modmask !== 0 && <label
cssClasses={["kbd"]}
label={modmasks[entry.modmask]?.toString()} />
}
<label cssClasses={["kbd"]} label={entry.key.toString()} />
<label label={entry.description} />
</box>
}
export default async function Shortcuts(_monitor_id: number) {
const { TOP, BOTTOM, LEFT, RIGHT } = Astal.WindowAnchor;
const hypr = Hyprland.get_default();
const shortcuts: Variable<Bind[]> = Variable([]);
const toSkip = Variable(0);
const list = Variable.derive([
bind(shortcuts),
bind(toSkip)
], (page, count) => limit(
skip(page, count),
10
));
const isEmpty = bind(list).as(list => list.length === 0);
const onScroll = (dy: number) => {
const value = toSkip.get();
if (dy < 0) {
if ((value - 10) < 0) return;
toSkip.set(value - 10)
} else {
if ((value + 10) > shortcuts.get().length) return;
toSkip.set(value + 10)
}
};
const setup = (self: Gtk.Window) => self.connect('notify::visible', async () => {
if (self.is_visible()) shortcuts.set(hypr.get_binds() as Bind[])
})
return <window
visible={false}
monitor={bind(hypr, "focusedMonitor").as(monitor => monitor.id)}
name={"shortcuts"}
cssClasses={["shortcuts"]}
keymode={Astal.Keymode.EXCLUSIVE}
anchor={TOP | BOTTOM | LEFT | RIGHT}
onKeyPressed={hideWindow}
application={App}
setup={setup}>
<box hexpand={false} cssClasses={["shortcuts"]} vertical halign={Gtk.Align.CENTER} valign={Gtk.Align.CENTER}>
<box spacing={6} vertical onScroll={(_, __, dy) => onScroll(dy)}>
{bind(list).as(list => list.map(entry => (
<ShortcutEntry entry={entry} />
)))}
</box>
<box
halign={Gtk.Align.CENTER}
cssClasses={["not-found"]}
vertical
visible={isEmpty}>
<image iconName="system-search-symbolic" />
<label label="No match found" />
</box>
</box>
</window>
}

View File

@ -58,6 +58,7 @@ bindd = , PRINT, Take partial screenshot, exec, hyprshot
bindd = $mainMod, PRINT, Take fullscreen screenshot, exec, hyprshot -zm output -o $screenshot_dir
bindd = $mainMod, Space, Open app launcher, exec, ags toggle launcher
bindd = $mainMod, Slash, Open cheatsheet, exec, ags toggle shortcuts
bindd = $mainMod, V, Open clipboard history, exec, ags toggle clipboard
bindd = $mainMod + Shift, T, Toggle bar, exec, ~/.config/hypr/scripts/toggle-bar-visibility.sh
bindd = $mainMod, SUPER_L, Open quick settings, exec, ags toggle quick_settings

View File

@ -5,8 +5,8 @@ general {
}
background {
blur_passes = 2
contrast = 0.8
path = screenshot
blur_passes = 2
brightness = 0.4
}

View File

@ -1 +1 @@
/nix/store/89nadapq2lq48hfm9v2a1zzd759n5pw4-home-manager-files/.config/hypr/hyprpaper.conf
/nix/store/84hb9j3a4klrcx7fcd9m2989dbk6ha1l-home-manager-files/.config/hypr/hyprpaper.conf