noctalia-shell/Services/ScalingService.qml

136 lines
3.6 KiB
QML

pragma Singleton
import QtQuick
import Quickshell
import qs.Commons
Singleton {
id: root
// Cache for current scales - updated via signals
property var currentScales: ({})
// Signal emitted when scale changes
signal scaleChanged(string screenName, real scale)
Component.onCompleted: {
Logger.log("Scaling", "Service started")
}
Connections {
target: Settings
function onSettingsLoaded() {
// Initialize cache from Settings once they are loaded on startup
var monitors = Settings.data.ui.monitorsScaling || []
for (var i = 0; i < monitors.length; i++) {
if (monitors[i].name && monitors[i].scale !== undefined) {
currentScales[monitors[i].name] = monitors[i].scale
root.scaleChanged(monitors[i].name, monitors[i].scale)
Logger.log("Scaling", "Caching scaling for", monitors[i].name, ":", monitors[i].scale)
}
}
}
}
// -------------------------------------------
// Manual scaling via Settings
function getScreenScale(aScreen) {
try {
if (aScreen !== undefined && aScreen.name !== undefined) {
return getScreenScaleByName(aScreen.name)
}
} catch (e) {
//Logger.warn(e)
}
return 1.0
}
// -------------------------------------------
// Get scale from cache for better performance
function getScreenScaleByName(aScreenName) {
try {
var scale = currentScales[aScreenName]
if ((scale !== undefined) && (scale != null)) {
return scale
}
} catch (e) {
//Logger.warn(e)
}
return 1.0
}
// -------------------------------------------
function setScreenScale(aScreen, scale) {
try {
if (aScreen !== undefined && aScreen.name !== undefined) {
return setScreenScaleByName(aScreen.name, scale)
}
} catch (e) {
//Logger.warn(e)
}
}
// -------------------------------------------
function setScreenScaleByName(aScreenName, scale) {
try {
// Check if scale actually changed
var oldScale = currentScales[aScreenName] || 1.0
if (oldScale === scale) {
return
// No change needed
}
// Update cache directly
currentScales[aScreenName] = scale
// Update Settings with immutable update for proper persistence
var monitors = Settings.data.ui.monitorsScaling || []
var found = false
var newMonitors = monitors.map(function (monitor) {
if (monitor.name === aScreenName) {
found = true
return {
"name": aScreenName,
"scale": scale
}
}
return monitor
})
if (!found) {
newMonitors.push({
"name": aScreenName,
"scale": scale
})
}
// Use slice() to ensure Settings detects the change
Settings.data.ui.monitorsScaling = newMonitors.slice()
// Emit signal for components to react
root.scaleChanged(aScreenName, scale)
Logger.log("Scaling", "Scale changed for", aScreenName, "to", scale)
} catch (e) {
Logger.warn("Scaling", "Error setting scale:", e)
}
}
// -------------------------------------------
// Dynamic scaling based on resolution
// Design reference resolution (for scale = 1.0)
readonly property int designScreenWidth: 2560
readonly property int designScreenHeight: 1440
function dynamicScale(aScreen) {
if (aScreen != null) {
var ratioW = aScreen.width / designScreenWidth
var ratioH = aScreen.height / designScreenHeight
return Math.min(ratioW, ratioH)
}
return 1.0
}
}