feat: Shortcuts widget
This commit is contained in:
parent
39d46c3311
commit
faab05ec8b
@ -6,9 +6,10 @@ import Hyprland from "gi://AstalHyprland";
|
|||||||
|
|
||||||
import QuickSettings from "widget/quick_settings/quick_settings";
|
import QuickSettings from "widget/quick_settings/quick_settings";
|
||||||
import BluetoothWindow from "widget/quick_settings/bluetooth";
|
import BluetoothWindow from "widget/quick_settings/bluetooth";
|
||||||
|
import Shortcuts from "./widget/launcher/shortcuts";
|
||||||
import Clipboard from "widget/launcher/clipboard";
|
import Clipboard from "widget/launcher/clipboard";
|
||||||
import Launcher from "widget/launcher/launcher";
|
|
||||||
import BatteryInfo from "widget/bar/battery_info";
|
import BatteryInfo from "widget/bar/battery_info";
|
||||||
|
import Launcher from "widget/launcher/launcher";
|
||||||
import Bar from "widget/bar/bar";
|
import Bar from "widget/bar/bar";
|
||||||
|
|
||||||
const hypr = Hyprland.get_default();
|
const hypr = Hyprland.get_default();
|
||||||
@ -19,7 +20,8 @@ const components = [
|
|||||||
QuickSettings,
|
QuickSettings,
|
||||||
BluetoothWindow,
|
BluetoothWindow,
|
||||||
BatteryInfo,
|
BatteryInfo,
|
||||||
Clipboard
|
Clipboard,
|
||||||
|
Shortcuts
|
||||||
];
|
];
|
||||||
|
|
||||||
const setupBars = async (monitor_id: number) => {
|
const setupBars = async (monitor_id: number) => {
|
||||||
|
@ -5,6 +5,37 @@ export type Belongs<T, U> = {
|
|||||||
[K in keyof T]: T[K] extends U ? K : never;
|
[K in keyof T]: T[K] extends U ? K : never;
|
||||||
}[keyof T];
|
}[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) => {
|
export const hideWindow = (self: Gtk.Window, keyval: number) => {
|
||||||
const keys = ["quick_settings"].includes(self.name)
|
const keys = ["quick_settings"].includes(self.name)
|
||||||
? [Gdk.KEY_Escape]
|
? [Gdk.KEY_Escape]
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
@forward '_bar';
|
@forward '_bar';
|
||||||
@forward '_launcher';
|
@forward '_launcher';
|
||||||
@forward '_clipboard';
|
@forward '_clipboard';
|
||||||
|
@forward '_shortcuts';
|
||||||
@forward '_battery_info';
|
@forward '_battery_info';
|
||||||
@forward '_quick_settings';
|
@forward '_quick_settings';
|
52
ags/styles/components/_shortcuts.scss
Normal file
52
ags/styles/components/_shortcuts.scss
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -69,7 +69,7 @@ export default async function Clipboard(_monitor_id: number) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const setup = (self: Gtk.Window) => self.connect('notify::visible', async () => {
|
const setup = (self: Gtk.Window) => self.connect('notify::visible', async () => {
|
||||||
await getClipboardHistory(history)
|
if (self.is_visible()) await getClipboardHistory(history)
|
||||||
})
|
})
|
||||||
|
|
||||||
return <window
|
return <window
|
||||||
|
82
ags/widget/launcher/shortcuts.tsx
Normal file
82
ags/widget/launcher/shortcuts.tsx
Normal 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>
|
||||||
|
}
|
@ -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, PRINT, Take fullscreen screenshot, exec, hyprshot -zm output -o $screenshot_dir
|
||||||
|
|
||||||
bindd = $mainMod, Space, Open app launcher, exec, ags toggle launcher
|
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, V, Open clipboard history, exec, ags toggle clipboard
|
||||||
bindd = $mainMod + Shift, T, Toggle bar, exec, ~/.config/hypr/scripts/toggle-bar-visibility.sh
|
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
|
bindd = $mainMod, SUPER_L, Open quick settings, exec, ags toggle quick_settings
|
||||||
|
@ -5,8 +5,8 @@ general {
|
|||||||
}
|
}
|
||||||
|
|
||||||
background {
|
background {
|
||||||
|
path = screenshot
|
||||||
blur_passes = 2
|
blur_passes = 2
|
||||||
contrast = 0.8
|
|
||||||
brightness = 0.4
|
brightness = 0.4
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1 +1 @@
|
|||||||
/nix/store/89nadapq2lq48hfm9v2a1zzd759n5pw4-home-manager-files/.config/hypr/hyprpaper.conf
|
/nix/store/84hb9j3a4klrcx7fcd9m2989dbk6ha1l-home-manager-files/.config/hypr/hyprpaper.conf
|
Loading…
x
Reference in New Issue
Block a user