From 4835f660f9435fa64336daaaae1ea3cab1dfbee6 Mon Sep 17 00:00:00 2001 From: seraphim <18ycm9tx@anonaddy.me> Date: Fri, 31 Jul 2026 14:03:07 +0700 Subject: mouse --- .../.config/plugins/keybind-cheatsheet/Panel.qml | 687 --------------------- 1 file changed, 687 deletions(-) delete mode 100644 noctalia/.config/plugins/keybind-cheatsheet/Panel.qml (limited to 'noctalia/.config/plugins/keybind-cheatsheet/Panel.qml') diff --git a/noctalia/.config/plugins/keybind-cheatsheet/Panel.qml b/noctalia/.config/plugins/keybind-cheatsheet/Panel.qml deleted file mode 100644 index 946431e..0000000 --- a/noctalia/.config/plugins/keybind-cheatsheet/Panel.qml +++ /dev/null @@ -1,687 +0,0 @@ -import QtQuick -import QtQuick.Controls -import QtQuick.Layouts -import Quickshell -import Quickshell.Io -import qs.Commons -import qs.Services.UI -import qs.Services.Compositor -import qs.Widgets - -Item { - id: root - property var pluginApi: null - - // Settings - property var cfg: pluginApi?.pluginSettings || ({}) - property var defaults: pluginApi?.manifest?.metadata?.defaultSettings || ({}) - - // Settings values - property int settingsWidth: cfg.windowWidth ?? defaults.windowWidth ?? 1400 - property int settingsHeight: cfg.windowHeight ?? defaults.windowHeight ?? 0 - property bool autoHeight: cfg.autoHeight ?? defaults.autoHeight ?? true - property int columnCount: cfg.columnCount ?? defaults.columnCount ?? 3 - - // Bug 4 fix: re-evaluate rawCategories whenever Main.qml increments cheatsheetDataVersion - property int _dataVersion: pluginApi?.mainInstance?.cheatsheetDataVersion ?? 0 - property var rawCategories: { - var _v = _dataVersion; // force QML dependency on version counter - return pluginApi?.pluginSettings?.cheatsheetData || []; - } - property var categories: [] - - // Bug 5 fix: timeout if parsing never completes - property bool loadingTimedOut: false - - Timer { - id: loadingTimeoutTimer - interval: 4000 - repeat: false - running: false - onTriggered: { - if (root.isLoading) { - root.loadingTimedOut = true; - } - } - } - - Component.onCompleted: { - categories = processCategories(rawCategories); - // Start timeout timer if we have no data yet - if (root.isLoading) { - loadingTimeoutTimer.start(); - } - } - - - // Dynamic column items (up to 4 columns) - property var columnItems: [] - - // Memory leak prevention: debounce column updates - Timer { - id: columnUpdateDebounce - interval: 100 - repeat: false - onTriggered: updateColumnItemsNow() - } - - Component.onDestruction: { - // Stop timer to prevent firing after destruction - columnUpdateDebounce.stop(); - loadingTimeoutTimer.stop(); - - // Clear column items - columnItems = []; - } - - onRawCategoriesChanged: { - categories = processCategories(rawCategories); - updateColumnItems(); - } - - onCategoriesChanged: { - updateColumnItems(); - contentPreferredHeight = calculateDynamicHeight(); - } - - onColumnCountChanged: { - updateColumnItems(); - contentPreferredHeight = calculateDynamicHeight(); - } - - onPanelOpenScreenChanged: { - // Recalculate height when screen becomes available (important for bar widget opening) - contentPreferredHeight = calculateDynamicHeight(); - root.searchText = ""; - if (searchInput) searchInput.inputItem.forceActiveFocus(); - } - - onMaxScreenHeightChanged: { - contentPreferredHeight = calculateDynamicHeight(); - } - - function updateColumnItems() { - columnUpdateDebounce.restart(); - } - - function updateColumnItemsNow() { - columnItems = []; // Clear old items explicitly - var assignments = distributeCategories(); - var items = []; - for (var i = 0; i < columnCount; i++) { - items.push(buildColumnItems(assignments[i] || [])); - } - columnItems = items; - } - - // Screen height limit (90% of screen) - property var panelOpenScreen: pluginApi?.panelOpenScreen - property real maxScreenHeight: panelOpenScreen ? panelOpenScreen.height * 0.9 : 800 - - property string searchText: "" - - property real contentPreferredWidth: settingsWidth - property real contentPreferredHeight: calculateDynamicHeight() - readonly property var geometryPlaceholder: panelContainer - readonly property bool allowAttach: false - readonly property bool panelAnchorHorizontalCenter: true - readonly property bool panelAnchorVerticalCenter: true - anchors.fill: parent - - // Key badge colors — read from settings with manifest defaults as fallback - readonly property color keyColorAlt: cfg.keyColorAlt || defaults.keyColorAlt || "#FF6B6B" - readonly property color keyColorXF86: cfg.keyColorXF86 || defaults.keyColorXF86 || "#4ECDC4" - readonly property color keyColorPrint: cfg.keyColorPrint || defaults.keyColorPrint || "#95E1D3" - readonly property color keyColorNumeric: cfg.keyColorNumeric || defaults.keyColorNumeric || "#A8DADC" - readonly property color keyColorMouse: cfg.keyColorMouse || defaults.keyColorMouse || "#F38181" - // Empty string = use theme color (mPrimary/mSecondary/mTertiary) - readonly property string keyColorSuperOverride: cfg.keyColorSuper ?? defaults.keyColorSuper ?? "" - readonly property string keyColorCtrlOverride: cfg.keyColorCtrl ?? defaults.keyColorCtrl ?? "" - readonly property string keyColorShiftOverride: cfg.keyColorShift ?? defaults.keyColorShift ?? "" - readonly property color keyColorDefault: cfg.keyColorDefault || defaults.keyColorDefault || "#6C757D" - readonly property color keyLabelColor: cfg.keyLabelColor || defaults.keyLabelColor || "#FFFFFF" - // Empty string = theme-aware fallback (Color.mOnSurface). Any non-empty - // value is treated as an explicit user override. - readonly property string descriptionColorOverride: cfg.descriptionTextColor || defaults.descriptionTextColor || "" - readonly property color descriptionTextColor: descriptionColorOverride !== "" ? descriptionColorOverride : Color.mOnSurface - - // Per-category text color overrides (empty = fall back to keyLabelColor) - readonly property string keyTextSuperOverride: cfg.keyTextSuper ?? defaults.keyTextSuper ?? "" - readonly property string keyTextCtrlOverride: cfg.keyTextCtrl ?? defaults.keyTextCtrl ?? "" - readonly property string keyTextShiftOverride: cfg.keyTextShift ?? defaults.keyTextShift ?? "" - readonly property string keyTextAltOverride: cfg.keyTextAlt ?? defaults.keyTextAlt ?? "" - readonly property string keyTextXF86Override: cfg.keyTextXF86 ?? defaults.keyTextXF86 ?? "" - readonly property string keyTextPrintOverride: cfg.keyTextPrint ?? defaults.keyTextPrint ?? "" - readonly property string keyTextNumericOverride: cfg.keyTextNumeric ?? defaults.keyTextNumeric ?? "" - readonly property string keyTextMouseOverride: cfg.keyTextMouse ?? defaults.keyTextMouse ?? "" - readonly property string keyTextDefaultOverride: cfg.keyTextDefault ?? defaults.keyTextDefault ?? "" - - // The generic X11 mods (Mod2/Mod3/Mod5) are first-class, customizable keys: - // background colour, text colour, and a display-label override that renames - // them to something meaningful (e.g. Mod3 -> "Hyper" for caps:hyper). Empty - // colour = fall back to the default key colour; empty label = raw mod name. - readonly property string keyColorMod2Override: cfg.keyColorMod2 ?? defaults.keyColorMod2 ?? "" - readonly property string keyColorMod3Override: cfg.keyColorMod3 ?? defaults.keyColorMod3 ?? "" - readonly property string keyColorMod5Override: cfg.keyColorMod5 ?? defaults.keyColorMod5 ?? "" - readonly property string keyTextMod2Override: cfg.keyTextMod2 ?? defaults.keyTextMod2 ?? "" - readonly property string keyTextMod3Override: cfg.keyTextMod3 ?? defaults.keyTextMod3 ?? "" - readonly property string keyTextMod5Override: cfg.keyTextMod5 ?? defaults.keyTextMod5 ?? "" - readonly property string keyLabelMod2Override: cfg.keyLabelMod2 ?? defaults.keyLabelMod2 ?? "" - readonly property string keyLabelMod3Override: cfg.keyLabelMod3 ?? defaults.keyLabelMod3 ?? "" - readonly property string keyLabelMod5Override: cfg.keyLabelMod5 ?? defaults.keyLabelMod5 ?? "" - - // Workspace category split tuning - readonly property bool splitWorkspaces: cfg.splitLargeWorkspaceCategory ?? defaults.splitLargeWorkspaceCategory ?? true - readonly property int workspaceSplitThreshold: cfg.workspaceSplitThreshold ?? defaults.workspaceSplitThreshold ?? 12 - - // Data is loaded by Main.qml, we just display it - property bool isLoading: rawCategories.length === 0 - - function calculateDynamicHeight() { - // If auto height is disabled, use manual height (but still respect screen limit) - if (!autoHeight && settingsHeight > 0) { - return Math.min(settingsHeight, maxScreenHeight); - } - - if (categories.length === 0) return Math.min(400, maxScreenHeight); - - var assignments = distributeCategories(); - var maxColumnHeight = 0; - - for (var col = 0; col < columnCount; col++) { - var colHeight = 0; - var catIndices = assignments[col] || []; - - for (var i = 0; i < catIndices.length; i++) { - var catIndex = catIndices[i]; - if (catIndex >= categories.length) continue; - - var cat = categories[catIndex]; - colHeight += 26; // Header - colHeight += cat.binds.length * 20; // Binds - if (i < catIndices.length - 1) { - colHeight += 6; // Spacer - } - } - - if (colHeight > maxColumnHeight) { - maxColumnHeight = colHeight; - } - } - - // header (45) + content + margins (16) - var totalHeight = 45 + maxColumnHeight + 16 + 15 + 15; - // Limit to 80% of screen height - return Math.max(300, Math.min(totalHeight, maxScreenHeight)); - } - - // ========== UI ========== - Rectangle { - id: panelContainer - anchors.fill: parent - color: "transparent" - radius: Style.radiusL - clip: true - - Rectangle { - id: header - anchors.top: parent.top - anchors.left: parent.left - anchors.right: parent.right - height: 45 - color: Color.mSurfaceVariant - radius: Style.radiusL - - RowLayout { - anchors.fill: parent - anchors.leftMargin: Style.marginM - anchors.rightMargin: Style.marginM - spacing: Style.marginS - - // Title section (centered) - Item { Layout.fillWidth: true } - - NIcon { - icon: "keyboard" - pointSize: Style.fontSizeM - color: Color.mPrimary - } - NText { - text: CompositorService.isHyprland ? pluginApi?.tr("panel.title-hyprland") : - CompositorService.isNiri ? pluginApi?.tr("panel.title-niri") : - CompositorService.isMango ? pluginApi?.tr("panel.title-mango") : - pluginApi?.tr("panel.title") - font.pointSize: Style.fontSizeM - font.weight: Font.Bold - color: Color.mPrimary - } - - NTextInput { - id: searchInput - placeholderText: pluginApi?.tr("panel.search-placeholder") - text: root.searchText - - onTextChanged: { - root.searchText = text; - root.updateColumnItems(); - } - } - - Item { Layout.fillWidth: true } - - // Refresh button - NIconButton { - icon: "refresh" - onClicked: { - pluginApi?.mainInstance?.refresh(); - } - } - - // Settings button - NIconButton { - icon: "settings" - onClicked: { - var screen = pluginApi?.panelOpenScreen; - if (screen && pluginApi?.manifest) { - pluginApi.closePanel(screen); - BarService.openPluginSettings(screen, pluginApi.manifest); - } - } - } - } - } - - NText { - id: loadingText - anchors.centerIn: parent - text: root.loadingTimedOut ? pluginApi?.tr("panel.loading-timeout") : pluginApi?.tr("panel.loading") - visible: root.isLoading - font.pointSize: Style.fontSizeL - color: Color.mOnSurface - } - - NScrollView { - id: scrollView - visible: root.categories.length > 0 && !root.isLoading - anchors.top: header.bottom - anchors.bottom: parent.bottom - anchors.left: parent.left - anchors.right: parent.right - clip: true - leftPadding: 35 - rightPadding: -10 - topPadding: 15 - bottomPadding: 15 - - RowLayout { - id: mainLayout - width: scrollView.availableWidth - Style.marginS - spacing: Style.marginS - - Repeater { - model: root.columnItems.length - - ColumnLayout { - Layout.fillWidth: true - Layout.alignment: Qt.AlignTop - spacing: 2 - - property var colItems: root.columnItems[index] || [] - - Repeater { - model: colItems - Loader { - Layout.fillWidth: true - sourceComponent: modelData.type === "header" ? headerComponent : - (modelData.type === "spacer" ? spacerComponent : bindComponent) - property var itemData: modelData - - // Memory leak prevention: explicit cleanup - Component.onDestruction: { - active = false; - sourceComponent = undefined; - } - } - } - } - } - } - } - } - - Component { - id: headerComponent - ColumnLayout { - Layout.preferredWidth: 300 - Layout.topMargin: Style.marginM - Layout.bottomMargin: 4 - spacing: 0 - - Item { Layout.fillWidth: true; height: 1 } - - NText { - Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter - x: parent.width - implicitWidth - text: itemData.title - font.pointSize: Style.fontSizeM - font.weight: Font.Bold - color: Color.mPrimary - } - - Item { Layout.fillWidth: true; height: 1 } - } - } - - - Component { - id: spacerComponent - Item { - height: 10 - Layout.fillWidth: true - } - } - - Component { - id: bindComponent - RowLayout { - id: bindRow - spacing: Style.marginS - height: 22 - Layout.bottomMargin: 1 - - property bool editing: false - - Flow { - Layout.preferredWidth: 220 - Layout.alignment: Qt.AlignVCenter - spacing: 3 - Repeater { - model: itemData.keys.split(" + ") - Rectangle { - width: keyText.implicitWidth + 10 - height: 18 - color: getKeyColor(modelData) - radius: 3 - NText { - id: keyText - anchors.centerIn: parent - text: getKeyLabel(modelData) - font.pointSize: text.length > 12 ? 7 : 8 - font.weight: Font.Bold - color: getKeyTextColor(modelData) - } - } - } - } - - // Described bind: plain text (unchanged behaviour) - NText { - visible: !itemData.undescribed - Layout.fillWidth: true - Layout.alignment: Qt.AlignVCenter - text: itemData.desc - font.pointSize: Style.fontSizeXS - color: root.descriptionTextColor - elide: Text.ElideRight - } - - // Undescribed bind: placeholder + add-description / hide actions - NText { - visible: itemData.undescribed && !bindRow.editing - Layout.fillWidth: true - Layout.alignment: Qt.AlignVCenter - text: pluginApi?.tr("panel.no-description") - font.pointSize: Style.fontSizeXS - font.italic: true - color: Color.mOnSurfaceVariant - elide: Text.ElideRight - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { bindRow.editing = true; descInput.text = ""; descInput.inputItem.forceActiveFocus(); } - } - } - - NTextInput { - id: descInput - visible: itemData.undescribed && bindRow.editing - Layout.fillWidth: true - Layout.preferredHeight: Style.baseWidgetSize - placeholderText: pluginApi?.tr("panel.add-description-placeholder") - } - - NIconButton { - visible: itemData.undescribed && bindRow.editing - Layout.preferredHeight: 18 - icon: "check" - tooltipText: pluginApi?.tr("panel.save-description") - onClicked: { - root.saveBindDescription(itemData.bindId, descInput.text); - bindRow.editing = false; - } - } - - NIconButton { - visible: itemData.undescribed && bindRow.editing - Layout.preferredHeight: 18 - icon: "close" - tooltipText: pluginApi?.tr("panel.cancel") - onClicked: { bindRow.editing = false; } - } - - NIconButton { - visible: itemData.undescribed && !bindRow.editing - Layout.preferredHeight: 18 - icon: "edit" - tooltipText: pluginApi?.tr("panel.add-description") - onClicked: { bindRow.editing = true; descInput.text = ""; descInput.inputItem.forceActiveFocus(); } - } - - NIconButton { - visible: itemData.undescribed && !bindRow.editing - Layout.preferredHeight: 18 - icon: "eye-off" - tooltipText: pluginApi?.tr("panel.hide-bind") - onClicked: { root.hideBind(itemData.bindId); } - } - } - } - - function getKeyColor(keyName) { - if (keyName === "Super") return root.keyColorSuperOverride || Color.mPrimary; - if (keyName === "Ctrl") return root.keyColorCtrlOverride || Color.mSecondary; - if (keyName === "Shift") return root.keyColorShiftOverride || Color.mTertiary; - if (keyName === "Alt") return root.keyColorAlt; - if (keyName.startsWith("XF86")) return root.keyColorXF86; - if (keyName === "PRINT" || keyName === "Print") return root.keyColorPrint; - if (keyName.match(/^[0-9]$/)) return root.keyColorNumeric; - if (keyName.includes("MOUSE") || keyName.includes("Wheel")) return root.keyColorMouse; - if (keyName === "Mod2") return root.keyColorMod2Override || root.keyColorDefault; - if (keyName === "Mod3") return root.keyColorMod3Override || root.keyColorDefault; - if (keyName === "Mod5") return root.keyColorMod5Override || root.keyColorDefault; - return root.keyColorDefault; - } - - function getKeyTextColor(keyName) { - if (keyName === "Super") return root.keyTextSuperOverride || root.keyLabelColor; - if (keyName === "Ctrl") return root.keyTextCtrlOverride || root.keyLabelColor; - if (keyName === "Shift") return root.keyTextShiftOverride || root.keyLabelColor; - if (keyName === "Alt") return root.keyTextAltOverride || root.keyLabelColor; - if (keyName.startsWith("XF86")) return root.keyTextXF86Override || root.keyLabelColor; - if (keyName === "PRINT" || keyName === "Print") return root.keyTextPrintOverride || root.keyLabelColor; - if (keyName.match(/^[0-9]$/)) return root.keyTextNumericOverride || root.keyLabelColor; - if (keyName.includes("MOUSE") || keyName.includes("Wheel")) return root.keyTextMouseOverride || root.keyLabelColor; - if (keyName === "Mod2") return root.keyTextMod2Override || root.keyLabelColor; - if (keyName === "Mod3") return root.keyTextMod3Override || root.keyLabelColor; - if (keyName === "Mod5") return root.keyTextMod5Override || root.keyLabelColor; - return root.keyTextDefaultOverride || root.keyLabelColor; - } - - // Display label for a key token. Lets users rename the generic X11 modifier - // names (e.g. Mod3 -> "Hyper"); everything else renders as-is. Purely visual — - // colour lookup and the cached bind still use the original token. - function getKeyLabel(keyName) { - if (keyName === "Mod2" && root.keyLabelMod2Override) return root.keyLabelMod2Override; - if (keyName === "Mod3" && root.keyLabelMod3Override) return root.keyLabelMod3Override; - if (keyName === "Mod5" && root.keyLabelMod5Override) return root.keyLabelMod5Override; - return keyName; - } - - // ===== Bind override helpers (shared keyed map in plugin settings) ===== - function _cloneOverrides() { - if (!pluginApi || !pluginApi.pluginSettings) return ({}); - var src = pluginApi.pluginSettings.bindOverrides || ({}); - try { return JSON.parse(JSON.stringify(src)); } catch (e) { return ({}); } - } - - function saveBindDescription(bindId, desc) { - if (!pluginApi || !bindId) return; - var o = _cloneOverrides(); - if (!o[bindId]) o[bindId] = ({}); - var trimmed = (desc || "").trim(); - if (trimmed.length === 0) { - delete o[bindId].desc; - if (Object.keys(o[bindId]).length === 0) delete o[bindId]; - } else { - o[bindId].desc = trimmed; - } - pluginApi.pluginSettings.bindOverrides = o; - pluginApi.saveSettings(); - pluginApi.mainInstance?.refresh(); - } - - function hideBind(bindId) { - if (!pluginApi || !bindId) return; - var o = _cloneOverrides(); - if (!o[bindId]) o[bindId] = ({}); - o[bindId].hidden = true; - pluginApi.pluginSettings.bindOverrides = o; - pluginApi.saveSettings(); - pluginApi.mainInstance?.refresh(); - } - - function buildColumnItems(categoryIndices) { - var result = []; - if (!categoryIndices) return result; - - for (var i = 0; i < categoryIndices.length; i++) { - var catIndex = categoryIndices[i]; - if (catIndex >= categories.length) continue; - - var cat = categories[catIndex]; - result.push({ type: "header", title: cat.title }); - var term = root.searchText.toLowerCase(); - for (var j = 0; j < cat.binds.length; j++) { - var bnd = cat.binds[j]; - var isUndesc = bnd.undescribed === true; - if (!term || (bnd.desc && bnd.desc.toLowerCase().indexOf(term) !== -1) || (isUndesc && bnd.keys.toLowerCase().indexOf(term) !== -1)) { - result.push({ - type: "bind", - keys: bnd.keys, - desc: bnd.desc, - bindId: bnd.bindId || "", - undescribed: isUndesc - }); - } - } - if (i < categoryIndices.length - 1) { - result.push({ type: "spacer" }); - } - } - return result; - } - - function processCategories(cats) { - if (!cats || cats.length === 0) return []; - if (!root.splitWorkspaces) return cats; - - var result = []; - for (var i = 0; i < cats.length; i++) { - var cat = cats[i]; - if (!cat.binds || cat.binds.length <= root.workspaceSplitThreshold) { - result.push(cat); - continue; - } - - // Detect a category dominated by workspace verbs. - // Hyprland verbs: workspace, movetoworkspace, movetoworkspacesilent, movecurrentworkspacetomonitor - // Niri verbs: focus-workspace, move-window-to-workspace, move-column-to-workspace - var workspaceCount = 0; - for (var k = 0; k < cat.binds.length; k++) { - var v = cat.binds[k]._verb || ""; - if (v.indexOf("workspace") !== -1) workspaceCount++; - } - var workspaceDominated = workspaceCount >= Math.ceil(cat.binds.length * 0.6); - if (!workspaceDominated) { - result.push(cat); - continue; - } - - var switching = [], moving = [], mouse = []; - for (var j = 0; j < cat.binds.length; j++) { - var bind = cat.binds[j]; - var verb = bind._verb || ""; - var isMouse = (bind._mainKey || "").indexOf("MOUSE") !== -1 || - (bind.keys || "").indexOf("MOUSE") !== -1 || - (bind.keys || "").indexOf("Wheel") !== -1; - - if (isMouse) { - mouse.push(bind); - } else if (verb.indexOf("move") !== -1 || verb.indexOf("send") !== -1) { - // Hyprland: movetoworkspace*, Niri: move-window-to-workspace, move-column-to-workspace - moving.push(bind); - } else { - // Hyprland: workspace, Niri: focus-workspace - switching.push(bind); - } - } - - if (switching.length > 0) result.push({ title: pluginApi?.tr("panel.workspace-switching"), binds: switching }); - if (moving.length > 0) result.push({ title: pluginApi?.tr("panel.workspace-moving"), binds: moving }); - if (mouse.length > 0) result.push({ title: pluginApi?.tr("panel.workspace-mouse"), binds: mouse }); - } - return result; - } - - function distributeCategories() { - var numCols = root.columnCount; - - // Calculate weights for each category - var catData = []; - for (var i = 0; i < categories.length; i++) { - var weight = 1 + categories[i].binds.length + 1; // header + binds + spacer - catData.push({ index: i, weight: weight }); - } - - // Sort by weight descending (largest categories first for better distribution) - catData.sort(function(a, b) { return b.weight - a.weight; }); - - var columns = []; - var columnWeights = []; - for (var c = 0; c < numCols; c++) { - columns.push([]); - columnWeights.push(0); - } - - // Assign each category to the column with smallest current weight - for (var i = 0; i < catData.length; i++) { - var minCol = 0; - for (var c = 1; c < numCols; c++) { - if (columnWeights[c] < columnWeights[minCol]) { - minCol = c; - } - } - columns[minCol].push(catData[i].index); - columnWeights[minCol] += catData[i].weight; - } - - // Sort categories within each column by original order for consistent display - for (var c = 0; c < numCols; c++) { - columns[c].sort(function(a, b) { return a - b; }); - } - - return columns; - } - -} -- cgit v1.2.3