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