mirror of
https://github.com/danbulant/dots-hyprland
synced 2026-05-24 12:22:09 +00:00
cheatsheet
This commit is contained in:
parent
8daa1702d0
commit
455bcdde4d
7 changed files with 692 additions and 3 deletions
232
.config/quickshell/modules/cheatsheet/Cheatsheet.qml
Normal file
232
.config/quickshell/modules/cheatsheet/Cheatsheet.qml
Normal file
|
|
@ -0,0 +1,232 @@
|
|||
import "root:/"
|
||||
import "root:/services"
|
||||
import "root:/modules/common"
|
||||
import "root:/modules/common/widgets"
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Qt5Compat.GraphicalEffects
|
||||
import Quickshell.Io
|
||||
import Quickshell
|
||||
import Quickshell.Widgets
|
||||
import Quickshell.Wayland
|
||||
import Quickshell.Hyprland
|
||||
|
||||
Scope { // Scope
|
||||
id: root
|
||||
|
||||
Variants { // Window repeater
|
||||
id: cheatsheetVariants
|
||||
model: Quickshell.screens
|
||||
|
||||
PanelWindow { // Window
|
||||
id: cheatsheetRoot
|
||||
visible: false
|
||||
focusable: true
|
||||
|
||||
property var modelData
|
||||
|
||||
screen: modelData
|
||||
exclusiveZone: 0
|
||||
implicitWidth: cheatsheetBackground.width + Appearance.sizes.elevationMargin * 2
|
||||
implicitHeight: cheatsheetBackground.height + Appearance.sizes.elevationMargin * 2
|
||||
WlrLayershell.namespace: "quickshell:cheatsheet"
|
||||
// Hyprland 0.49: Focus is always exclusive and setting this breaks mouse focus grab
|
||||
// WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive
|
||||
color: "transparent"
|
||||
|
||||
mask: Region {
|
||||
item: cheatsheetBackground
|
||||
}
|
||||
|
||||
HyprlandFocusGrab { // Click outside to close
|
||||
id: grab
|
||||
windows: [ cheatsheetRoot ]
|
||||
active: false
|
||||
onCleared: () => {
|
||||
if (!active) cheatsheetRoot.visible = false
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: cheatsheetRoot
|
||||
function onVisibleChanged() {
|
||||
delayedGrabTimer.start()
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: delayedGrabTimer
|
||||
interval: ConfigOptions.hacks.arbitraryRaceConditionDelay
|
||||
repeat: false
|
||||
onTriggered: {
|
||||
grab.active = cheatsheetRoot.visible
|
||||
}
|
||||
}
|
||||
|
||||
// Background
|
||||
Rectangle {
|
||||
id: cheatsheetBackground
|
||||
anchors.centerIn: parent
|
||||
color: Appearance.colors.colLayer0
|
||||
radius: Appearance.rounding.windowRounding
|
||||
property real padding: 30
|
||||
implicitWidth: cheatsheetColumnLayout.implicitWidth + padding * 2
|
||||
implicitHeight: cheatsheetColumnLayout.implicitHeight + padding * 2
|
||||
|
||||
Keys.onPressed: (event) => { // Esc to close
|
||||
if (event.key === Qt.Key_Escape) {
|
||||
cheatsheetRoot.visible = false
|
||||
}
|
||||
}
|
||||
|
||||
Button { // Close button
|
||||
id: closeButton
|
||||
focus: cheatsheetRoot.visible
|
||||
implicitWidth: 40
|
||||
implicitHeight: 40
|
||||
anchors {
|
||||
top: parent.top
|
||||
right: parent.right
|
||||
topMargin: 20
|
||||
rightMargin: 20
|
||||
}
|
||||
|
||||
PointingHandInteraction {}
|
||||
onClicked: {
|
||||
cheatsheetRoot.visible = false
|
||||
}
|
||||
|
||||
background: Item {}
|
||||
contentItem: Rectangle {
|
||||
anchors.fill: parent
|
||||
radius: Appearance.rounding.full
|
||||
color: closeButton.pressed ? Appearance.colors.colLayer0Active :
|
||||
closeButton.hovered ? Appearance.colors.colLayer0Hover :
|
||||
Appearance.transparentize(Appearance.colors.colLayer0, 1)
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Appearance.animation.elementMoveFast.duration
|
||||
easing.type: Appearance.animation.elementMoveFast.type
|
||||
easing.bezierCurve: Appearance.animation.elementMoveFast.bezierCurve
|
||||
}
|
||||
}
|
||||
|
||||
MaterialSymbol {
|
||||
anchors.centerIn: parent
|
||||
font.pixelSize: Appearance.font.pixelSize.title
|
||||
text: "close"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout { // Real content
|
||||
id: cheatsheetColumnLayout
|
||||
anchors.centerIn: parent
|
||||
spacing: 20
|
||||
|
||||
StyledText {
|
||||
id: cheatsheetTitle
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
font.family: Appearance.font.family.title
|
||||
font.pixelSize: Appearance.font.pixelSize.title
|
||||
text: qsTr("Cheat sheet")
|
||||
}
|
||||
CheatsheetKeybinds {}
|
||||
}
|
||||
}
|
||||
|
||||
// Shadow
|
||||
DropShadow {
|
||||
anchors.fill: cheatsheetBackground
|
||||
horizontalOffset: 0
|
||||
verticalOffset: 2
|
||||
radius: Appearance.sizes.elevationMargin
|
||||
samples: Appearance.sizes.elevationMargin * 2 + 1 // Ideally should be 2 * radius + 1, see qt docs
|
||||
color: Appearance.colors.colShadow
|
||||
source: cheatsheetBackground
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
IpcHandler {
|
||||
target: "cheatsheet"
|
||||
|
||||
function toggle(): void {
|
||||
for (let i = 0; i < cheatsheetVariants.instances.length; i++) {
|
||||
let panelWindow = cheatsheetVariants.instances[i];
|
||||
if (panelWindow.modelData.name == Hyprland.focusedMonitor.name) {
|
||||
panelWindow.visible = !panelWindow.visible;
|
||||
if(panelWindow.visible) Notifications.timeoutAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function close(): void {
|
||||
for (let i = 0; i < cheatsheetVariants.instances.length; i++) {
|
||||
let panelWindow = cheatsheetVariants.instances[i];
|
||||
if (panelWindow.modelData.name == Hyprland.focusedMonitor.name) {
|
||||
panelWindow.visible = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function open(): void {
|
||||
for (let i = 0; i < cheatsheetVariants.instances.length; i++) {
|
||||
let panelWindow = cheatsheetVariants.instances[i];
|
||||
if (panelWindow.modelData.name == Hyprland.focusedMonitor.name) {
|
||||
panelWindow.visible = true;
|
||||
if(panelWindow.visible) Notifications.timeoutAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GlobalShortcut {
|
||||
name: "cheatsheetToggle"
|
||||
description: "Toggles cheatsheet on press"
|
||||
|
||||
onPressed: {
|
||||
for (let i = 0; i < cheatsheetVariants.instances.length; i++) {
|
||||
let panelWindow = cheatsheetVariants.instances[i];
|
||||
if (panelWindow.modelData.name == Hyprland.focusedMonitor.name) {
|
||||
panelWindow.visible = !panelWindow.visible;
|
||||
if(panelWindow.visible) Notifications.timeoutAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GlobalShortcut {
|
||||
name: "cheatsheetOpen"
|
||||
description: "Opens cheatsheet on press"
|
||||
|
||||
onPressed: {
|
||||
for (let i = 0; i < cheatsheetVariants.instances.length; i++) {
|
||||
let panelWindow = cheatsheetVariants.instances[i];
|
||||
if (panelWindow.modelData.name == Hyprland.focusedMonitor.name) {
|
||||
panelWindow.visible = true;
|
||||
if(panelWindow.visible) Notifications.timeoutAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GlobalShortcut {
|
||||
name: "cheatsheetClose"
|
||||
description: "Closes cheatsheet on press"
|
||||
|
||||
onPressed: {
|
||||
for (let i = 0; i < cheatsheetVariants.instances.length; i++) {
|
||||
let panelWindow = cheatsheetVariants.instances[i];
|
||||
if (panelWindow.modelData.name == Hyprland.focusedMonitor.name) {
|
||||
panelWindow.visible = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
147
.config/quickshell/modules/cheatsheet/CheatsheetKeybinds.qml
Normal file
147
.config/quickshell/modules/cheatsheet/CheatsheetKeybinds.qml
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
import "root:/"
|
||||
import "root:/services"
|
||||
import "root:/modules/common"
|
||||
import "root:/modules/common/widgets"
|
||||
import "root:/modules/common/functions/file_utils.js" as FileUtils
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import Quickshell.Widgets
|
||||
import Quickshell.Hyprland
|
||||
|
||||
Item {
|
||||
id: root
|
||||
readonly property var keybinds: HyprlandKeybinds.keybinds
|
||||
property real spacing: 20
|
||||
property real titleSpacing: 7
|
||||
implicitWidth: rowLayout.implicitWidth
|
||||
implicitHeight: rowLayout.implicitHeight
|
||||
|
||||
property var keyBlacklist: ["Super_L"]
|
||||
property var keySubstitutions: ({
|
||||
"Super": "",
|
||||
"mouse_up": "Scroll ↓", // ikr, weird
|
||||
"mouse_down": "Scroll ↑", // trust me bro
|
||||
"mouse:272": "LMB",
|
||||
"mouse:273": "RMB",
|
||||
"mouse:275": "MouseBack",
|
||||
"Slash": "/",
|
||||
"Hash": "#"
|
||||
})
|
||||
|
||||
RowLayout { // Keybind columns
|
||||
id: rowLayout
|
||||
spacing: root.spacing
|
||||
Repeater {
|
||||
model: keybinds.children
|
||||
|
||||
delegate: ColumnLayout { // Keybind sections
|
||||
spacing: root.spacing
|
||||
required property var modelData
|
||||
Layout.alignment: Qt.AlignTop
|
||||
Repeater {
|
||||
model: modelData.children
|
||||
|
||||
delegate: Item { // Section with real keybinds
|
||||
required property var modelData
|
||||
implicitWidth: sectionColumnLayout.implicitWidth
|
||||
implicitHeight: sectionColumnLayout.implicitHeight
|
||||
ColumnLayout {
|
||||
id: sectionColumnLayout
|
||||
anchors.centerIn: parent
|
||||
spacing: root.titleSpacing
|
||||
StyledText {
|
||||
id: sectionTitle
|
||||
font.family: Appearance.font.family.title
|
||||
font.pixelSize: Appearance.font.pixelSize.huge
|
||||
color: Appearance.colors.colOnLayer0
|
||||
text: modelData.name
|
||||
}
|
||||
|
||||
GridLayout {
|
||||
id: keybindGrid
|
||||
columns: 2
|
||||
Repeater {
|
||||
model: {
|
||||
var result = [];
|
||||
for (var i = 0; i < modelData.keybinds.length; i++) {
|
||||
const keybind = modelData.keybinds[i];
|
||||
result.push({
|
||||
"type": "keys",
|
||||
"mods": keybind.mods,
|
||||
"key": keybind.key,
|
||||
});
|
||||
result.push({
|
||||
"type": "comment",
|
||||
"comment": keybind.comment,
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
delegate: Item {
|
||||
required property var modelData
|
||||
implicitWidth: keybindLoader.implicitWidth
|
||||
implicitHeight: keybindLoader.implicitHeight
|
||||
|
||||
Loader {
|
||||
id: keybindLoader
|
||||
sourceComponent: (modelData.type === "keys") ? keysComponent : commentComponent
|
||||
}
|
||||
|
||||
Component {
|
||||
id: keysComponent
|
||||
RowLayout {
|
||||
spacing: 4
|
||||
Repeater {
|
||||
model: modelData.mods
|
||||
delegate: KeyboardKey {
|
||||
required property var modelData
|
||||
key: keySubstitutions[modelData] || modelData
|
||||
}
|
||||
}
|
||||
StyledText {
|
||||
id: keybindPlus
|
||||
visible: !keyBlacklist.includes(modelData.key) && modelData.mods.length > 0
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
text: "+"
|
||||
}
|
||||
KeyboardKey {
|
||||
id: keybindKey
|
||||
visible: !keyBlacklist.includes(modelData.key)
|
||||
key: keySubstitutions[modelData.key] || modelData.key
|
||||
color: Appearance.colors.colOnLayer0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: commentComponent
|
||||
Item {
|
||||
id: commentItem
|
||||
implicitWidth: commentText.implicitWidth + 5 * 2
|
||||
implicitHeight: commentText.implicitHeight
|
||||
|
||||
StyledText {
|
||||
id: commentText
|
||||
anchors.centerIn: parent
|
||||
font.pixelSize: Appearance.font.pixelSize.smaller
|
||||
text: modelData.comment
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -108,8 +108,8 @@ Singleton {
|
|||
property color colSubtext: m3colors.m3outline
|
||||
property color colLayer0: m3colors.m3background
|
||||
property color colOnLayer0: m3colors.m3onBackground
|
||||
property color colLayer0Hover: mix(colLayer0, colOnLayer0, 0.85)
|
||||
property color colLayer0Active: m3colors.m3surfaceContainerHigh
|
||||
property color colLayer0Hover: mix(colLayer0, colOnLayer0, 0.9)
|
||||
property color colLayer0Active: mix(colLayer0, colOnLayer0, 0.8)
|
||||
property color colLayer1: m3colors.m3surfaceContainerLow;
|
||||
property color colOnLayer1: m3colors.m3onSurfaceVariant;
|
||||
property color colOnLayer1Inactive: mix(colOnLayer1, colLayer1, 0.45);
|
||||
|
|
@ -155,7 +155,7 @@ Singleton {
|
|||
font: QtObject {
|
||||
property QtObject family: QtObject {
|
||||
property string main: "Rubik"
|
||||
property string title: "Rubik"
|
||||
property string title: "Gabarito"
|
||||
property string iconMaterial: "Material Symbols Rounded"
|
||||
property string iconNerd: "SpaceMono NF"
|
||||
property string monospace: "JetBrains Mono NF"
|
||||
|
|
|
|||
42
.config/quickshell/modules/common/widgets/KeyboardKey.qml
Normal file
42
.config/quickshell/modules/common/widgets/KeyboardKey.qml
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
import "root:/modules/common"
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
property string key
|
||||
|
||||
property real horizontalPadding: 7
|
||||
property real verticalPadding: 2
|
||||
property real borderWidth: 1
|
||||
property real bottomBorderWidth: 3
|
||||
property color borderColor: Appearance.colors.colOnLayer0
|
||||
property real borderRadius: 5
|
||||
property color keyColor: Appearance.m3colors.m3surfaceContainerLow
|
||||
implicitWidth: keyFace.implicitWidth + borderWidth * 2
|
||||
implicitHeight: keyFace.implicitHeight + borderWidth * 2 + bottomBorderWidth
|
||||
radius: borderRadius
|
||||
color: borderColor
|
||||
|
||||
Rectangle {
|
||||
id: keyFace
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: borderWidth
|
||||
implicitWidth: keyText.implicitWidth + horizontalPadding * 2
|
||||
implicitHeight: keyText.implicitHeight + verticalPadding * 2
|
||||
color: keyColor
|
||||
radius: borderRadius - borderWidth
|
||||
|
||||
StyledText {
|
||||
id: keyText
|
||||
anchors.centerIn: parent
|
||||
font.family: Appearance.font.family.monospace
|
||||
font.pixelSize: Appearance.font.pixelSize.smaller
|
||||
text: key
|
||||
}
|
||||
}
|
||||
}
|
||||
222
.config/quickshell/scripts/hyprland/get_keybinds.py
Executable file
222
.config/quickshell/scripts/hyprland/get_keybinds.py
Executable file
|
|
@ -0,0 +1,222 @@
|
|||
#!/usr/bin/env -S\_/bin/sh\_-c\_"source\_\$(eval\_echo\_\$ILLOGICAL_IMPULSE_VIRTUAL_ENV)/bin/activate&&exec\_python\_-E\_"\$0"\_"\$@""
|
||||
import argparse
|
||||
import re
|
||||
import os
|
||||
from os.path import expandvars as os_expandvars
|
||||
from typing import Dict, List
|
||||
|
||||
TITLE_REGEX = "#+!"
|
||||
HIDE_COMMENT = "[hidden]"
|
||||
MOD_SEPARATORS = ['+', ' ']
|
||||
COMMENT_BIND_PATTERN = "#/#"
|
||||
|
||||
parser = argparse.ArgumentParser(description='Hyprland keybind reader')
|
||||
parser.add_argument('--path', type=str, default="$HOME/.config/hypr/hyprland.conf", help='path to keybind file (sourcing isn\'t supported)')
|
||||
args = parser.parse_args()
|
||||
content_lines = []
|
||||
reading_line = 0
|
||||
|
||||
# Little Parser made for hyprland keybindings conf file
|
||||
Variables: Dict[str, str] = {}
|
||||
|
||||
|
||||
class KeyBinding(dict):
|
||||
def __init__(self, mods, key, dispatcher, params, comment) -> None:
|
||||
self["mods"] = mods
|
||||
self["key"] = key
|
||||
self["dispatcher"] = dispatcher
|
||||
self["params"] = params
|
||||
self["comment"] = comment
|
||||
|
||||
class Section(dict):
|
||||
def __init__(self, children, keybinds, name) -> None:
|
||||
self["children"] = children
|
||||
self["keybinds"] = keybinds
|
||||
self["name"] = name
|
||||
|
||||
|
||||
def read_content(path: str) -> str:
|
||||
if (not os.access(os.path.expanduser(os.path.expandvars(path)), os.R_OK)):
|
||||
return ("error")
|
||||
with open(os.path.expanduser(os.path.expandvars(path)), "r") as file:
|
||||
return file.read()
|
||||
|
||||
|
||||
def autogenerate_comment(dispatcher: str, params: str = "") -> str:
|
||||
match dispatcher:
|
||||
|
||||
case "resizewindow":
|
||||
return "Resize window"
|
||||
|
||||
case "movewindow":
|
||||
if(params == ""):
|
||||
return "Move window"
|
||||
else:
|
||||
return "Window: move in {} direction".format({
|
||||
"l": "left",
|
||||
"r": "right",
|
||||
"u": "up",
|
||||
"d": "down",
|
||||
}.get(params, "null"))
|
||||
|
||||
case "pin":
|
||||
return "Window: pin (show on all workspaces)"
|
||||
|
||||
case "splitratio":
|
||||
return "Window split ratio {}".format(params)
|
||||
|
||||
case "togglefloating":
|
||||
return "Float/unfloat window"
|
||||
|
||||
case "resizeactive":
|
||||
return "Resize window by {}".format(params)
|
||||
|
||||
case "killactive":
|
||||
return "Close window"
|
||||
|
||||
case "fullscreen":
|
||||
return "Toggle {}".format(
|
||||
{
|
||||
"0": "fullscreen",
|
||||
"1": "maximization",
|
||||
"2": "fullscreen on Hyprland's side",
|
||||
}.get(params, "null")
|
||||
)
|
||||
|
||||
case "fakefullscreen":
|
||||
return "Toggle fake fullscreen"
|
||||
|
||||
case "workspace":
|
||||
if params == "+1":
|
||||
return "Workspace: focus right"
|
||||
elif params == "-1":
|
||||
return "Workspace: focus left"
|
||||
return "Focus workspace {}".format(params)
|
||||
|
||||
case "movefocus":
|
||||
return "Window: move focus {}".format(
|
||||
{
|
||||
"l": "left",
|
||||
"r": "right",
|
||||
"u": "up",
|
||||
"d": "down",
|
||||
}.get(params, "null")
|
||||
)
|
||||
|
||||
case "swapwindow":
|
||||
return "Window: swap in {} direction".format(
|
||||
{
|
||||
"l": "left",
|
||||
"r": "right",
|
||||
"u": "up",
|
||||
"d": "down",
|
||||
}.get(params, "null")
|
||||
)
|
||||
|
||||
case "movetoworkspace":
|
||||
if params == "+1":
|
||||
return "Window: move to right workspace (non-silent)"
|
||||
elif params == "-1":
|
||||
return "Window: move to left workspace (non-silent)"
|
||||
return "Window: move to workspace {} (non-silent)".format(params)
|
||||
|
||||
case "movetoworkspacesilent":
|
||||
if params == "+1":
|
||||
return "Window: move to right workspace"
|
||||
elif params == "-1":
|
||||
return "Window: move to right workspace"
|
||||
return "Window: move to workspace {}".format(params)
|
||||
|
||||
case "togglespecialworkspace":
|
||||
return "Workspace: toggle special"
|
||||
|
||||
case "exec":
|
||||
return "Execute: {}".format(params)
|
||||
|
||||
case _:
|
||||
return ""
|
||||
|
||||
def get_keybind_at_line(line_number, line_start = 0):
|
||||
global content_lines
|
||||
line = content_lines[line_number]
|
||||
_, keys = line.split("=", 1)
|
||||
keys, *comment = keys.split("#", 1)
|
||||
|
||||
mods, key, dispatcher, *params = list(map(str.strip, keys.split(",", 4)))
|
||||
params = "".join(map(str.strip, params))
|
||||
|
||||
# Remove empty spaces
|
||||
comment = list(map(str.strip, comment))
|
||||
# Add comment if it exists, else generate it
|
||||
if comment:
|
||||
comment = comment[0]
|
||||
if comment.startswith("[hidden]"):
|
||||
return None
|
||||
else:
|
||||
comment = autogenerate_comment(dispatcher, params)
|
||||
|
||||
if mods:
|
||||
modstring = mods + MOD_SEPARATORS[0] # Add separator at end to ensure last mod is read
|
||||
mods = []
|
||||
p = 0
|
||||
for index, char in enumerate(modstring):
|
||||
if(char in MOD_SEPARATORS):
|
||||
if(index - p > 1):
|
||||
mods.append(modstring[p:index])
|
||||
p = index+1
|
||||
else:
|
||||
mods = []
|
||||
|
||||
return KeyBinding(mods, key, dispatcher, params, comment)
|
||||
|
||||
def get_binds_recursive(current_content, scope):
|
||||
global content_lines
|
||||
global reading_line
|
||||
# print("get_binds_recursive({0}, {1}) [@L{2}]".format(current_content, scope, reading_line + 1))
|
||||
while reading_line < len(content_lines): # TODO: Adjust condition
|
||||
line = content_lines[reading_line]
|
||||
heading_search_result = re.search(TITLE_REGEX, line)
|
||||
# print("Read line {0}: {1}\tisHeading: {2}".format(reading_line + 1, content_lines[reading_line], "[{0}, {1}, {2}]".format(heading_search_result.start(), heading_search_result.start() == 0, ((heading_search_result != None) and (heading_search_result.start() == 0))) if heading_search_result != None else "No"))
|
||||
if ((heading_search_result != None) and (heading_search_result.start() == 0)): # Found title
|
||||
# Determine scope
|
||||
heading_scope = line.find('!')
|
||||
# Lower? Return
|
||||
if(heading_scope <= scope):
|
||||
reading_line -= 1
|
||||
return current_content
|
||||
|
||||
section_name = line[(heading_scope+1):].strip()
|
||||
# print("[[ Found h{0} at line {1} ]] {2}".format(heading_scope, reading_line+1, content_lines[reading_line]))
|
||||
reading_line += 1
|
||||
current_content["children"].append(get_binds_recursive(Section([], [], section_name), heading_scope))
|
||||
|
||||
elif line.startswith(COMMENT_BIND_PATTERN):
|
||||
keybind = get_keybind_at_line(reading_line, line_start=len(COMMENT_BIND_PATTERN))
|
||||
if(keybind != None):
|
||||
current_content["keybinds"].append(keybind)
|
||||
|
||||
elif line == "" or not line.lstrip().startswith("bind"): # Comment, ignore
|
||||
pass
|
||||
|
||||
else: # Normal keybind
|
||||
keybind = get_keybind_at_line(reading_line)
|
||||
if(keybind != None):
|
||||
current_content["keybinds"].append(keybind)
|
||||
|
||||
reading_line += 1
|
||||
|
||||
return current_content;
|
||||
|
||||
def parse_keys(path: str) -> Dict[str, List[KeyBinding]]:
|
||||
global content_lines
|
||||
content_lines = read_content(path).splitlines()
|
||||
if content_lines[0] == "error":
|
||||
return "error"
|
||||
return get_binds_recursive(Section([], [], ""), 0)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import json
|
||||
|
||||
ParsedKeys = parse_keys(args.path)
|
||||
print(json.dumps(ParsedKeys))
|
||||
44
.config/quickshell/services/HyprlandKeybinds.qml
Normal file
44
.config/quickshell/services/HyprlandKeybinds.qml
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
pragma Singleton
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import "root:/modules/common"
|
||||
import "root:/modules/common/functions/file_utils.js" as FileUtils
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import Quickshell.Wayland
|
||||
import Quickshell.Hyprland
|
||||
|
||||
Singleton {
|
||||
id: root
|
||||
property var keybinds: []
|
||||
|
||||
Connections {
|
||||
target: Hyprland
|
||||
|
||||
function onRawEvent(event) {
|
||||
console.log("[CheatsheetKeybinds] Event:", event.name)
|
||||
if (event.name == "configreloaded") {
|
||||
getKeybinds.running = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: getKeybinds
|
||||
running: true
|
||||
command: [FileUtils.trimFileProtocol(`${XdgDirectories.config}/quickshell/scripts/hyprland/get_keybinds.py`),
|
||||
"--path", FileUtils.trimFileProtocol(`${XdgDirectories.config}/hypr/hyprland/keybinds.conf`),]
|
||||
|
||||
stdout: SplitParser {
|
||||
onRead: data => {
|
||||
try {
|
||||
root.keybinds = JSON.parse(data)
|
||||
} catch (e) {
|
||||
console.error("[CheatsheetKeybinds] Error parsing keybinds:", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
//@ pragma UseQApplication
|
||||
|
||||
import "./modules/bar/"
|
||||
import "./modules/cheatsheet/"
|
||||
import "./modules/notificationPopup/"
|
||||
import "./modules/onScreenDisplay/"
|
||||
import "./modules/overview/"
|
||||
|
|
@ -23,6 +24,7 @@ ShellRoot {
|
|||
}
|
||||
|
||||
Bar {}
|
||||
Cheatsheet {}
|
||||
NotificationPopup {}
|
||||
OnScreenDisplayBrightness {}
|
||||
OnScreenDisplayVolume {}
|
||||
|
|
|
|||
Loading…
Reference in a new issue