1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-06-13 06:33:30 -04:00

refactor(Hyprland): Update Lua migration and keybind writes

- emit native hl.dsp.* dispatchers for generated Lua keybinds
- keep legacy hyprland.conf installs read-only but preserved until dms setup migration
This commit is contained in:
purian23
2026-05-30 23:07:06 -04:00
parent 389fffaf64
commit a265625851
20 changed files with 1056 additions and 109 deletions
@@ -21,8 +21,11 @@ Singleton {
property var includeStatus: ({
"exists": false,
"included": false
"included": false,
"configFormat": "",
"readOnly": false
})
readonly property bool readOnly: CompositorService.isHyprland && includeStatus.readOnly === true
property bool checkingInclude: false
property bool fixingInclude: false
@@ -481,6 +484,15 @@ Singleton {
// Write compositor config from a neutral config entry and optionally reload
function applyConfigEntry(configEntry, configId, profileName, isManual) {
if (CompositorService.isHyprland && readOnly) {
if (isManual) {
profilesLoading = false;
manualActivation = false;
profileError(I18n.tr("Hyprland conf mode is read-only in Settings"));
}
showHyprlandReadOnlyWarning();
return;
}
ensureEnabledOutput(configEntry);
// Capture the entry being applied so disabled-output settings fields can read
// scale/position/transform back even when wlr reports no logical viewport.
@@ -1372,7 +1384,9 @@ Singleton {
if (compositor !== "niri" && compositor !== "hyprland" && compositor !== "dwl") {
includeStatus = {
"exists": false,
"included": false
"included": false,
"configFormat": "",
"readOnly": false
};
return;
}
@@ -1386,7 +1400,9 @@ Singleton {
if (exitCode !== 0) {
includeStatus = {
"exists": false,
"included": false
"included": false,
"configFormat": "",
"readOnly": false
};
return;
}
@@ -1395,13 +1411,19 @@ Singleton {
} catch (e) {
includeStatus = {
"exists": false,
"included": false
"included": false,
"configFormat": "",
"readOnly": false
};
}
});
}
function fixOutputsInclude() {
if (readOnly) {
showHyprlandReadOnlyWarning();
return;
}
const paths = getConfigPaths();
if (!paths)
return;
@@ -1426,6 +1448,10 @@ Singleton {
});
}
function showHyprlandReadOnlyWarning() {
ToastService.showWarning(I18n.tr("Hyprland conf mode"), I18n.tr("This install is still using hyprland.conf. Run dms setup to migrate before editing display settings."), "dms setup", "display-config");
}
function buildOutputsMap() {
const map = {};
for (const output of wlrOutputs) {
@@ -1514,6 +1540,10 @@ Singleton {
NiriService.generateOutputsConfig(outputsData);
break;
case "hyprland":
if (readOnly) {
showHyprlandReadOnlyWarning();
return false;
}
HyprlandService.generateOutputsConfig(outputsData, buildMergedHyprlandSettings());
break;
case "dwl":
@@ -1523,6 +1553,7 @@ Singleton {
WlrOutputService.applyOutputsConfig(outputsData, outputs);
break;
}
return true;
}
function normalizeOutputPositions(outputsData) {
@@ -1830,6 +1861,10 @@ Singleton {
function applyChanges() {
if (!hasPendingChanges)
return;
if (CompositorService.isHyprland && readOnly) {
showHyprlandReadOnlyWarning();
return;
}
const changeDescriptions = [];
if (formatChanged) {
@@ -12,13 +12,14 @@ StyledRect {
height: warningContent.implicitHeight + Theme.spacingL * 2
radius: Theme.cornerRadius
readonly property bool showError: DisplayConfigState.includeStatus.exists && !DisplayConfigState.includeStatus.included
readonly property bool showSetup: !DisplayConfigState.includeStatus.exists && !DisplayConfigState.includeStatus.included
readonly property bool showLegacy: DisplayConfigState.readOnly
readonly property bool showError: !showLegacy && DisplayConfigState.includeStatus.exists && !DisplayConfigState.includeStatus.included
readonly property bool showSetup: !showLegacy && !DisplayConfigState.includeStatus.exists && !DisplayConfigState.includeStatus.included
color: (showError || showSetup) ? Theme.withAlpha(Theme.primary, 0.15) : "transparent"
border.color: (showError || showSetup) ? Theme.withAlpha(Theme.primary, 0.3) : "transparent"
color: (showLegacy || showError || showSetup) ? Theme.withAlpha(Theme.primary, 0.15) : "transparent"
border.color: (showLegacy || showError || showSetup) ? Theme.withAlpha(Theme.primary, 0.3) : "transparent"
border.width: 1
visible: (showError || showSetup) && DisplayConfigState.hasOutputBackend && !DisplayConfigState.checkingInclude
visible: (showLegacy || showError || showSetup) && DisplayConfigState.hasOutputBackend && !DisplayConfigState.checkingInclude
Column {
id: warningContent
@@ -44,6 +45,8 @@ StyledRect {
StyledText {
text: {
if (root.showLegacy)
return I18n.tr("Hyprland conf mode");
if (root.showSetup)
return I18n.tr("First Time Setup");
if (root.showError)
@@ -59,6 +62,8 @@ StyledRect {
StyledText {
text: {
if (root.showLegacy)
return I18n.tr("This install is still using hyprland.conf. Run dms setup to migrate before editing display settings.");
if (root.showSetup)
return I18n.tr("Click 'Setup' to create the outputs config and add include to your compositor config.");
if (root.showError)
@@ -75,7 +80,7 @@ StyledRect {
DankButton {
id: fixButton
visible: root.showError || root.showSetup
visible: !root.showLegacy && (root.showError || root.showSetup)
text: {
if (DisplayConfigState.fixingInclude)
return I18n.tr("Fixing...");
+20 -9
View File
@@ -84,6 +84,10 @@ Item {
}
function startNewBind() {
if (KeybindsService.readOnly) {
KeybindsService.showHyprlandReadOnlyWarning();
return;
}
showingNewBind = true;
expandedKey = "";
}
@@ -292,7 +296,7 @@ Item {
StyledText {
readonly property string bindsFile: KeybindsService.currentProvider === "niri" ? "dms/binds.kdl" : KeybindsService.currentProvider === "hyprland" ? "dms/binds-user.lua" : "dms/binds.conf"
text: I18n.tr("Click any shortcut to edit. Changes save to %1").arg(bindsFile)
text: KeybindsService.readOnly ? I18n.tr("Hyprland conf mode is read-only in Settings") : I18n.tr("Click any shortcut to edit. Changes save to %1").arg(bindsFile)
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
wrapMode: Text.WordWrap
@@ -326,7 +330,7 @@ Item {
iconSize: Theme.iconSize
iconColor: Theme.primary
anchors.verticalCenter: parent.verticalCenter
enabled: !keybindsTab.showingNewBind
enabled: !keybindsTab.showingNewBind && !KeybindsService.readOnly
opacity: enabled ? 1 : 0.5
onClicked: keybindsTab.startNewBind()
}
@@ -342,14 +346,15 @@ Item {
radius: Theme.cornerRadius
readonly property var status: KeybindsService.dmsStatus
readonly property bool showError: !status.included && status.exists
readonly property bool showWarning: status.included && status.overriddenBy > 0
readonly property bool showSetup: !status.exists
readonly property bool showLegacy: KeybindsService.readOnly
readonly property bool showError: !showLegacy && !status.included && status.exists
readonly property bool showWarning: !showLegacy && status.included && status.overriddenBy > 0
readonly property bool showSetup: !showLegacy && !status.exists
color: (showError || showWarning || showSetup) ? Theme.withAlpha(Theme.primary, 0.15) : "transparent"
border.color: (showError || showWarning || showSetup) ? Theme.withAlpha(Theme.primary, 0.3) : "transparent"
color: (showLegacy || showError || showWarning || showSetup) ? Theme.withAlpha(Theme.primary, 0.15) : "transparent"
border.color: (showLegacy || showError || showWarning || showSetup) ? Theme.withAlpha(Theme.primary, 0.3) : "transparent"
border.width: 1
visible: (showError || showWarning || showSetup) && !KeybindsService.loading
visible: (showLegacy || showError || showWarning || showSetup) && !KeybindsService.loading
Column {
id: warningSection
@@ -375,6 +380,8 @@ Item {
StyledText {
text: {
if (warningBox.showLegacy)
return I18n.tr("Hyprland conf mode");
if (warningBox.showSetup)
return I18n.tr("First Time Setup");
if (warningBox.showError)
@@ -391,6 +398,8 @@ Item {
StyledText {
readonly property string bindsFile: KeybindsService.currentProvider === "niri" ? "dms/binds.kdl" : KeybindsService.currentProvider === "hyprland" ? "dms/binds-user.lua" : "dms/binds.conf"
text: {
if (warningBox.showLegacy)
return I18n.tr("This install is still using hyprland.conf. Run dms setup to migrate before editing shortcuts in Settings.");
if (warningBox.showSetup)
return I18n.tr("Click 'Setup' to create %1 and add include to config.").arg(bindsFile);
if (warningBox.showError)
@@ -411,7 +420,7 @@ Item {
DankButton {
id: fixButton
visible: warningBox.showError || warningBox.showSetup
visible: !warningBox.showLegacy && (warningBox.showError || warningBox.showSetup)
text: {
if (KeybindsService.fixing)
return I18n.tr("Fixing...");
@@ -559,6 +568,7 @@ Item {
desc: ""
})
panelWindow: keybindsTab.parentModal
readOnly: KeybindsService.readOnly
onSaveBind: (originalKey, newData) => keybindsTab.saveNewBind(newData)
onCancelEdit: keybindsTab.cancelNewBind()
}
@@ -668,6 +678,7 @@ Item {
bindData: modelData
isExpanded: keybindsTab.expandedKey === modelData.action
panelWindow: keybindsTab.parentModal
readOnly: KeybindsService.readOnly
onToggleExpand: keybindsTab.toggleExpanded(modelData.action)
onSaveBind: (originalKey, newData) => {
KeybindsService.saveBind(originalKey, newData);
+17 -4
View File
@@ -23,8 +23,11 @@ Item {
property var cursorIncludeStatus: ({
"exists": false,
"included": false
"included": false,
"configFormat": "",
"readOnly": false
})
readonly property bool cursorReadOnly: CompositorService.isHyprland && cursorIncludeStatus.readOnly === true
property bool checkingCursorInclude: false
property bool fixingCursorInclude: false
@@ -62,7 +65,9 @@ Item {
if (compositor !== "niri" && compositor !== "hyprland" && compositor !== "dwl") {
cursorIncludeStatus = {
"exists": false,
"included": false
"included": false,
"configFormat": "",
"readOnly": false
};
return;
}
@@ -76,7 +81,9 @@ Item {
if (exitCode !== 0) {
cursorIncludeStatus = {
"exists": false,
"included": false
"included": false,
"configFormat": "",
"readOnly": false
};
return;
}
@@ -85,13 +92,19 @@ Item {
} catch (e) {
cursorIncludeStatus = {
"exists": false,
"included": false
"included": false,
"configFormat": "",
"readOnly": false
};
}
});
}
function fixCursorInclude() {
if (cursorReadOnly) {
ToastService.showWarning(I18n.tr("Hyprland conf mode"), I18n.tr("This install is still using hyprland.conf. Run dms setup to migrate before editing cursor settings."), "dms setup", "hyprland-migration");
return;
}
const paths = getCursorConfigPaths();
if (!paths)
return;
+59 -17
View File
@@ -19,8 +19,11 @@ Item {
property var parentModal: null
property var windowRulesIncludeStatus: ({
"exists": false,
"included": false
"included": false,
"configFormat": "",
"readOnly": false
})
readonly property bool readOnly: CompositorService.isHyprland && windowRulesIncludeStatus.readOnly === true
property bool checkingInclude: false
property bool fixingInclude: false
property var windowRules: []
@@ -84,7 +87,9 @@ Item {
if (result.dmsStatus) {
windowRulesIncludeStatus = {
"exists": result.dmsStatus.exists,
"included": result.dmsStatus.included
"included": result.dmsStatus.included,
"configFormat": result.dmsStatus.configFormat ?? "",
"readOnly": result.dmsStatus.readOnly === true
};
}
} catch (e) {
@@ -94,6 +99,10 @@ Item {
}
function removeRule(ruleId) {
if (readOnly) {
showHyprlandReadOnlyWarning();
return;
}
const compositor = CompositorService.compositor;
if (compositor !== "niri" && compositor !== "hyprland")
return;
@@ -107,6 +116,10 @@ Item {
}
function reorderRules(fromIndex, toIndex) {
if (readOnly) {
showHyprlandReadOnlyWarning();
return;
}
if (fromIndex === toIndex)
return;
@@ -131,7 +144,9 @@ Item {
if (compositor !== "niri" && compositor !== "hyprland") {
windowRulesIncludeStatus = {
"exists": false,
"included": false
"included": false,
"configFormat": "",
"readOnly": false
};
return;
}
@@ -143,7 +158,9 @@ Item {
if (exitCode !== 0) {
windowRulesIncludeStatus = {
"exists": false,
"included": false
"included": false,
"configFormat": "",
"readOnly": false
};
return;
}
@@ -152,13 +169,19 @@ Item {
} catch (e) {
windowRulesIncludeStatus = {
"exists": false,
"included": false
"included": false,
"configFormat": "",
"readOnly": false
};
}
});
}
function fixWindowRulesInclude() {
if (readOnly) {
showHyprlandReadOnlyWarning();
return;
}
const paths = getWindowRulesConfigPaths();
if (!paths)
return;
@@ -182,6 +205,10 @@ Item {
}
function openRuleModal(window) {
if (readOnly) {
showHyprlandReadOnlyWarning();
return;
}
if (!PopoutService.windowRuleModalLoader)
return;
PopoutService.windowRuleModalLoader.active = true;
@@ -192,6 +219,10 @@ Item {
}
function editRule(rule) {
if (readOnly) {
showHyprlandReadOnlyWarning();
return;
}
if (!PopoutService.windowRuleModalLoader)
return;
PopoutService.windowRuleModalLoader.active = true;
@@ -201,6 +232,10 @@ Item {
}
}
function showHyprlandReadOnlyWarning() {
ToastService.showWarning(I18n.tr("Hyprland conf mode"), I18n.tr("This install is still using hyprland.conf. Run dms setup to migrate before editing window rules in Settings."), "dms setup", "hyprland-migration");
}
Component.onCompleted: {
if (CompositorService.isNiri || CompositorService.isHyprland) {
checkWindowRulesIncludeStatus();
@@ -274,6 +309,8 @@ Item {
iconName: "add"
iconSize: Theme.iconSize
iconColor: Theme.primary
enabled: !root.readOnly
opacity: enabled ? 1 : 0.5
onClicked: root.openRuleModal()
}
}
@@ -322,13 +359,14 @@ Item {
anchors.horizontalCenter: parent.horizontalCenter
radius: Theme.cornerRadius
readonly property bool showError: root.windowRulesIncludeStatus.exists && !root.windowRulesIncludeStatus.included
readonly property bool showSetup: !root.windowRulesIncludeStatus.exists && !root.windowRulesIncludeStatus.included
readonly property bool showLegacy: root.readOnly
readonly property bool showError: !showLegacy && root.windowRulesIncludeStatus.exists && !root.windowRulesIncludeStatus.included
readonly property bool showSetup: !showLegacy && !root.windowRulesIncludeStatus.exists && !root.windowRulesIncludeStatus.included
color: (showError || showSetup) ? Theme.withAlpha(Theme.warning, 0.15) : "transparent"
border.color: (showError || showSetup) ? Theme.withAlpha(Theme.warning, 0.3) : "transparent"
color: (showLegacy || showError || showSetup) ? Theme.withAlpha(Theme.warning, 0.15) : "transparent"
border.color: (showLegacy || showError || showSetup) ? Theme.withAlpha(Theme.warning, 0.3) : "transparent"
border.width: 1
visible: (showError || showSetup) && !root.checkingInclude && (CompositorService.isNiri || CompositorService.isHyprland)
visible: (showLegacy || showError || showSetup) && !root.checkingInclude && (CompositorService.isNiri || CompositorService.isHyprland)
Row {
id: warningSection
@@ -349,7 +387,7 @@ Item {
anchors.verticalCenter: parent.verticalCenter
StyledText {
text: warningBox.showSetup ? I18n.tr("Window Rules Not Configured") : I18n.tr("Window Rules Include Missing")
text: warningBox.showLegacy ? I18n.tr("Hyprland conf mode") : (warningBox.showSetup ? I18n.tr("Window Rules Not Configured") : I18n.tr("Window Rules Include Missing"))
font.pixelSize: Theme.fontSizeMedium
font.weight: Font.Medium
color: Theme.warning
@@ -359,7 +397,7 @@ Item {
StyledText {
readonly property string rulesFile: CompositorService.isNiri ? "dms/windowrules.kdl" : "dms/windowrules.lua"
text: warningBox.showSetup ? I18n.tr("Click 'Setup' to create %1 and add include to your compositor config.").arg(rulesFile) : I18n.tr("%1 exists but is not included. Window rules won't apply.").arg(rulesFile)
text: warningBox.showLegacy ? I18n.tr("This install is still using hyprland.conf. Run dms setup to migrate before editing window rules in Settings.") : (warningBox.showSetup ? I18n.tr("Click 'Setup' to create %1 and add include to your compositor config.").arg(rulesFile) : I18n.tr("%1 exists but is not included. Window rules won't apply.").arg(rulesFile))
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
wrapMode: Text.WordWrap
@@ -370,7 +408,7 @@ Item {
DankButton {
id: fixButton
visible: warningBox.showError || warningBox.showSetup
visible: !warningBox.showLegacy && (warningBox.showError || warningBox.showSetup)
text: root.fixingInclude ? I18n.tr("Fixing...") : (warningBox.showSetup ? I18n.tr("Setup") : I18n.tr("Fix Now"))
backgroundColor: Theme.warning
textColor: Theme.background
@@ -611,6 +649,8 @@ Item {
iconSize: 16
backgroundColor: "transparent"
iconColor: Theme.surfaceVariantText
enabled: !root.readOnly
opacity: enabled ? 1 : 0.5
onClicked: root.editRule(ruleDelegateItem.liveRuleData)
}
@@ -621,12 +661,14 @@ Item {
iconSize: 16
backgroundColor: "transparent"
iconColor: deleteArea.containsMouse ? Theme.error : Theme.surfaceVariantText
enabled: !root.readOnly
opacity: enabled ? 1 : 0.5
MouseArea {
id: deleteArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
hoverEnabled: !root.readOnly
cursorShape: root.readOnly ? Qt.ArrowCursor : Qt.PointingHandCursor
onClicked: root.removeRule(ruleDelegateItem.ruleIdRef)
}
}
@@ -641,8 +683,8 @@ Item {
width: 40
height: ruleCard.height
hoverEnabled: true
cursorShape: Qt.SizeVerCursor
drag.target: ruleDelegateItem.held ? ruleDelegateItem : undefined
cursorShape: root.readOnly ? Qt.ArrowCursor : Qt.SizeVerCursor
drag.target: !root.readOnly && ruleDelegateItem.held ? ruleDelegateItem : undefined
drag.axis: Drag.YAxis
preventStealing: true
+26
View File
@@ -18,9 +18,17 @@ Singleton {
readonly property string layoutPath: hyprDmsDir + "/layout.lua"
readonly property string cursorPath: hyprDmsDir + "/cursor.lua"
readonly property string windowrulesPath: hyprDmsDir + "/windowrules.lua"
readonly property bool luaConfigActive: CompositorService.isHyprland && Hyprland.usingLua === true
property int _lastGapValue: -1
onLuaConfigActiveChanged: {
if (luaConfigActive) {
Qt.callLater(generateLayoutConfig);
Qt.callLater(ensureWindowrulesConfig);
}
}
Component.onCompleted: {
if (CompositorService.isHyprland) {
Qt.callLater(generateLayoutConfig);
@@ -29,6 +37,8 @@ Singleton {
}
function ensureWindowrulesConfig() {
if (!canWriteLuaConfig("windowrules"))
return;
Proc.runCommand("hypr-ensure-windowrules", ["sh", "-c", `mkdir -p "${hyprDmsDir}" && [ ! -f "${windowrulesPath}" ] && touch "${windowrulesPath}" || true`], (output, exitCode) => {
if (exitCode !== 0)
log.warn("Failed to ensure windowrules.lua:", output);
@@ -66,6 +76,13 @@ Singleton {
return JSON.stringify(String(str ?? ""));
}
function canWriteLuaConfig(name) {
if (luaConfigActive)
return true;
log.info("Skipping Hyprland", name || "config", "Lua write because the active Hyprland config is not Lua");
return false;
}
function forceFlagValue(value) {
if (value === true)
return 1;
@@ -75,6 +92,11 @@ Singleton {
}
function generateOutputsConfig(outputsData, hyprlandSettings, callback) {
if (!canWriteLuaConfig("outputs")) {
if (callback)
callback(false);
return;
}
if (!outputsData || Object.keys(outputsData).length === 0) {
if (callback)
callback(false);
@@ -172,6 +194,8 @@ Singleton {
function generateLayoutConfig() {
if (!CompositorService.isHyprland)
return;
if (!canWriteLuaConfig("layout"))
return;
const defaultRadius = typeof SettingsData !== "undefined" ? SettingsData.cornerRadius : 12;
const defaultGaps = typeof SettingsData !== "undefined" ? Math.max(4, (SettingsData.barConfigs[0]?.spacing ?? 4)) : 4;
@@ -254,6 +278,8 @@ hl.config({
function generateCursorConfig() {
if (!CompositorService.isHyprland)
return;
if (!canWriteLuaConfig("cursor"))
return;
const settings = typeof SettingsData !== "undefined" ? SettingsData.cursorSettings : null;
if (!settings) {
+33 -3
View File
@@ -52,7 +52,9 @@ Singleton {
"bindsAfterDms": 0,
"effective": true,
"overriddenBy": 0,
"statusMessage": ""
"statusMessage": "",
"configFormat": "",
"readOnly": false
})
property var _rawData: null
@@ -102,6 +104,7 @@ Singleton {
return "";
}
}
readonly property bool readOnly: currentProvider === "hyprland" && dmsStatus.readOnly === true
readonly property var actionTypes: Actions.getActionTypes()
readonly property var dmsActions: getDmsActions()
@@ -258,6 +261,10 @@ Singleton {
function fixDmsBindsInclude() {
if (fixing || dmsBindsIncluded || !compositorConfigDir)
return;
if (readOnly) {
showHyprlandReadOnlyWarning();
return;
}
fixing = true;
const timestamp = Math.floor(Date.now() / 1000);
const backupPath = `${mainConfigPath}.dmsbackup${timestamp}`;
@@ -343,7 +350,9 @@ Singleton {
"bindsAfterDms": status.bindsAfterDms ?? 0,
"effective": status.effective ?? true,
"overriddenBy": status.overriddenBy ?? 0,
"statusMessage": status.statusMessage ?? ""
"statusMessage": status.statusMessage ?? "",
"configFormat": status.configFormat ?? "",
"readOnly": status.readOnly === true
};
}
_maybeWarnHyprlandLegacyConf();
@@ -482,6 +491,10 @@ Singleton {
}
function saveBind(originalKey, bindData) {
if (readOnly) {
showHyprlandReadOnlyWarning();
return;
}
if (!bindData.key || !Actions.isValidAction(bindData.action))
return;
saving = true;
@@ -510,13 +523,26 @@ Singleton {
return;
if (currentProvider !== "hyprland")
return;
if (readOnly) {
_hyprlandLegacyWarnShown = true;
showHyprlandReadOnlyWarning();
return;
}
if (!dmsStatus.exists || dmsStatus.included)
return;
_hyprlandLegacyWarnShown = true;
ToastService.showWarning(I18n.tr("Hyprland config still uses hyprlang"), I18n.tr("DMS Settings now writes Lua. Edits won't apply until you migrate."), "dms setup", "hyprland-migration");
ToastService.showWarning(I18n.tr("Hyprland config include missing"), I18n.tr("DMS Settings writes Lua keybinds. Add the DMS include so edits apply."), "dms setup", "hyprland-migration");
}
function showHyprlandReadOnlyWarning() {
ToastService.showWarning(I18n.tr("Hyprland conf mode"), I18n.tr("This install is still using hyprland.conf. Run dms setup to migrate before editing shortcuts in Settings."), "dms setup", "hyprland-migration");
}
function removeBind(key) {
if (readOnly) {
showHyprlandReadOnlyWarning();
return;
}
if (!key)
return;
removeProcess.command = ["dms", "keybinds", "remove", currentProvider, key];
@@ -525,6 +551,10 @@ Singleton {
}
function resetBind(key) {
if (readOnly) {
showHyprlandReadOnlyWarning();
return;
}
if (!key)
return;
removeProcess.command = ["dms", "keybinds", "reset", currentProvider, key];
+37 -8
View File
@@ -21,6 +21,7 @@ Item {
property var panelWindow: null
property bool recording: false
property bool isNew: false
property bool readOnly: false
property string restoreKey: ""
property int editingKeyIndex: -1
@@ -160,6 +161,10 @@ Item {
}
function startAddingNewKey() {
if (readOnly) {
KeybindsService.showHyprlandReadOnlyWarning();
return;
}
addingNewKey = true;
editingKeyIndex = -1;
editKey = "";
@@ -181,6 +186,8 @@ Item {
}
function updateEdit(changes) {
if (readOnly)
return;
if (changes.key !== undefined)
editKey = changes.key;
if (changes.action !== undefined)
@@ -208,6 +215,8 @@ Item {
}
function canSave() {
if (readOnly)
return false;
if (!editKey)
return false;
if (!Actions.isValidAction(editAction))
@@ -216,6 +225,10 @@ Item {
}
function doSave() {
if (readOnly) {
KeybindsService.showHyprlandReadOnlyWarning();
return;
}
if (!canSave())
return;
const origKey = addingNewKey ? "" : _originalKey;
@@ -247,6 +260,10 @@ Item {
}
function startRecording() {
if (readOnly) {
KeybindsService.showHyprlandReadOnlyWarning();
return;
}
recording = true;
}
@@ -438,6 +455,7 @@ Item {
anchors.top: parent.top
anchors.margins: Theme.spacingL
spacing: Theme.spacingM
enabled: !root.readOnly
Rectangle {
Layout.fillWidth: true
@@ -554,7 +572,7 @@ Item {
height: root._chipHeight
radius: root._chipHeight / 4
color: root.addingNewKey ? Theme.primary : Theme.surfaceVariant
visible: !root.isNew
visible: !root.isNew && !root.readOnly
Rectangle {
anchors.fill: parent
@@ -644,6 +662,7 @@ Item {
iconName: root.recording ? "close" : "radio_button_checked"
iconSize: Theme.iconSizeSmall
iconColor: root.recording ? Theme.error : Theme.primary
enabled: !root.readOnly
onClicked: root.recording ? root.stopRecording() : root.startRecording()
}
}
@@ -746,7 +765,7 @@ Item {
Layout.preferredHeight: root._inputHeight
radius: Theme.cornerRadius
color: root.addingNewKey ? Theme.primary : Theme.surfaceVariant
visible: root.keys.length === 1 && !root.isNew
visible: root.keys.length === 1 && !root.isNew && !root.readOnly
Rectangle {
anchors.fill: parent
@@ -861,6 +880,8 @@ Item {
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
if (root.readOnly)
return;
switch (typeDelegate.modelData.id) {
case "dms":
root.updateEdit({
@@ -926,6 +947,8 @@ Item {
enableFuzzySearch: true
maxPopupHeight: 300
onValueChanged: value => {
if (root.readOnly)
return;
const actions = KeybindsService.getDmsActions();
for (const act of actions) {
if (act.label === value) {
@@ -1176,8 +1199,12 @@ Item {
id: customToggleArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: root.useCustomCompositor = true
cursorShape: root.readOnly ? Qt.ArrowCursor : Qt.PointingHandCursor
onClicked: {
if (root.readOnly)
return;
root.useCustomCompositor = true;
}
}
}
}
@@ -1418,8 +1445,10 @@ Item {
id: presetToggleArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
cursorShape: root.readOnly ? Qt.ArrowCursor : Qt.PointingHandCursor
onClicked: {
if (root.readOnly)
return;
root.useCustomCompositor = false;
root.updateEdit({
"action": "close-window",
@@ -1768,7 +1797,7 @@ Item {
iconName: "delete"
iconSize: Theme.iconSize - 4
iconColor: Theme.error
visible: root.editingKeyIndex >= 0 && root.editingKeyIndex < root.keys.length && (root.keys[root.editingKeyIndex].isDMSManaged || root.keys[root.editingKeyIndex].isOverride) && !root.isNew
visible: root.editingKeyIndex >= 0 && root.editingKeyIndex < root.keys.length && (root.keys[root.editingKeyIndex].isDMSManaged || root.keys[root.editingKeyIndex].isOverride) && !root.isNew && !root.readOnly
onClicked: root.removeBind(root._originalKey)
}
@@ -1777,7 +1806,7 @@ Item {
buttonHeight: root._buttonHeight
backgroundColor: Theme.surfaceContainer
textColor: Theme.primary
visible: root.editingKeyIndex >= 0 && root.editingKeyIndex < root.keys.length && root.keys[root.editingKeyIndex].isOverride === true && root.keys[root.editingKeyIndex].hasDefault === true && !root.isNew
visible: root.editingKeyIndex >= 0 && root.editingKeyIndex < root.keys.length && root.keys[root.editingKeyIndex].isOverride === true && root.keys[root.editingKeyIndex].hasDefault === true && !root.isNew && !root.readOnly
onClicked: root.resetBind(root._originalKey)
}
@@ -1786,7 +1815,7 @@ Item {
}
StyledText {
text: !root.canSave() ? I18n.tr("Set key and action to save") : (root.hasChanges ? I18n.tr("Unsaved changes") : I18n.tr("No changes"))
text: root.readOnly ? I18n.tr("Read-only legacy config") : (!root.canSave() ? I18n.tr("Set key and action to save") : (root.hasChanges ? I18n.tr("Unsaved changes") : I18n.tr("No changes")))
font.pixelSize: Theme.fontSizeSmall
color: root.hasChanges ? Theme.surfaceText : Theme.surfaceVariantText
visible: !root.isNew