mirror of
https://github.com/zoriya/noctalia-shell.git
synced 2026-08-15 18:43:59 +00:00
feat: Add OSD background opacity setting
- Add `OSD` background transparency - Add `OSD` background transparency translation
This commit is contained in:
+115
-114
@@ -49,83 +49,83 @@ Variants {
|
||||
function getIcon() {
|
||||
if (currentOSDType === "volume") {
|
||||
if (AudioService.muted) {
|
||||
return "volume-mute"
|
||||
return "volume-mute";
|
||||
}
|
||||
return (AudioService.volume <= Number.EPSILON) ? "volume-zero" : (AudioService.volume <= 0.5) ? "volume-low" : "volume-high"
|
||||
return (AudioService.volume <= Number.EPSILON) ? "volume-zero" : (AudioService.volume <= 0.5) ? "volume-low" : "volume-high";
|
||||
} else if (currentOSDType === "inputVolume") {
|
||||
if (AudioService.inputMuted) {
|
||||
return "microphone-off"
|
||||
return "microphone-off";
|
||||
}
|
||||
return "microphone"
|
||||
return "microphone";
|
||||
} else if (currentOSDType === "brightness") {
|
||||
return currentBrightness <= 0.5 ? "brightness-low" : "brightness-high"
|
||||
return currentBrightness <= 0.5 ? "brightness-low" : "brightness-high";
|
||||
}
|
||||
return ""
|
||||
return "";
|
||||
}
|
||||
|
||||
// Get current value (0-1 range)
|
||||
function getCurrentValue() {
|
||||
if (currentOSDType === "volume") {
|
||||
return isMuted ? 0 : currentVolume
|
||||
return isMuted ? 0 : currentVolume;
|
||||
} else if (currentOSDType === "inputVolume") {
|
||||
return isInputMuted ? 0 : currentInputVolume
|
||||
return isInputMuted ? 0 : currentInputVolume;
|
||||
} else if (currentOSDType === "brightness") {
|
||||
return currentBrightness
|
||||
return currentBrightness;
|
||||
}
|
||||
return 0
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Get maximum value for current OSD type
|
||||
function getMaxValue() {
|
||||
if (currentOSDType === "volume" || currentOSDType === "inputVolume") {
|
||||
return Settings.data.audio.volumeOverdrive ? 1.5 : 1.0
|
||||
return Settings.data.audio.volumeOverdrive ? 1.5 : 1.0;
|
||||
} else if (currentOSDType === "brightness") {
|
||||
return 1.0
|
||||
return 1.0;
|
||||
}
|
||||
return 1.0
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
// Get display percentage
|
||||
function getDisplayPercentage() {
|
||||
if (currentOSDType === "volume") {
|
||||
if (isMuted)
|
||||
return "0%"
|
||||
const max = getMaxValue()
|
||||
const pct = Math.round(Math.min(max, currentVolume) * 100)
|
||||
return pct + "%"
|
||||
return "0%";
|
||||
const max = getMaxValue();
|
||||
const pct = Math.round(Math.min(max, currentVolume) * 100);
|
||||
return pct + "%";
|
||||
} else if (currentOSDType === "inputVolume") {
|
||||
if (isInputMuted)
|
||||
return "0%"
|
||||
const max = getMaxValue()
|
||||
const pct = Math.round(Math.min(max, currentInputVolume) * 100)
|
||||
return pct + "%"
|
||||
return "0%";
|
||||
const max = getMaxValue();
|
||||
const pct = Math.round(Math.min(max, currentInputVolume) * 100);
|
||||
return pct + "%";
|
||||
} else if (currentOSDType === "brightness") {
|
||||
const pct = Math.round(Math.min(1.0, currentBrightness) * 100)
|
||||
return pct + "%"
|
||||
const pct = Math.round(Math.min(1.0, currentBrightness) * 100);
|
||||
return pct + "%";
|
||||
}
|
||||
return ""
|
||||
return "";
|
||||
}
|
||||
|
||||
// Get progress bar color
|
||||
function getProgressColor() {
|
||||
if (currentOSDType === "volume") {
|
||||
if (isMuted)
|
||||
return Color.mError
|
||||
return Color.mPrimary
|
||||
return Color.mError;
|
||||
return Color.mPrimary;
|
||||
} else if (currentOSDType === "inputVolume") {
|
||||
if (isInputMuted)
|
||||
return Color.mError
|
||||
return Color.mPrimary
|
||||
return Color.mError;
|
||||
return Color.mPrimary;
|
||||
}
|
||||
return Color.mPrimary
|
||||
return Color.mPrimary;
|
||||
}
|
||||
|
||||
// Get icon color
|
||||
function getIconColor() {
|
||||
if ((currentOSDType === "volume" && isMuted) || (currentOSDType === "inputVolume" && isInputMuted)) {
|
||||
return Color.mError
|
||||
return Color.mError;
|
||||
}
|
||||
return Color.mOnSurface
|
||||
return Color.mOnSurface;
|
||||
}
|
||||
|
||||
sourceComponent: PanelWindow {
|
||||
@@ -146,8 +146,8 @@ Variants {
|
||||
|
||||
// Ensure an even width to keep the vertical bar perfectly centered
|
||||
readonly property int barThickness: {
|
||||
const base = Math.max(8, Math.round(8 * Style.uiScaleRatio))
|
||||
return (base % 2 === 0) ? base : base + 1
|
||||
const base = Math.max(8, Math.round(8 * Style.uiScaleRatio));
|
||||
return (base % 2 === 0) ? base : base + 1;
|
||||
}
|
||||
|
||||
// Anchor selection based on location (window edges)
|
||||
@@ -159,46 +159,46 @@ Variants {
|
||||
// Margins depending on bar position and chosen location
|
||||
margins.top: {
|
||||
if (!(anchors.top))
|
||||
return 0
|
||||
var base = Style.marginM
|
||||
return 0;
|
||||
var base = Style.marginM;
|
||||
if (Settings.data.bar.position === "top") {
|
||||
var floatExtraV = Settings.data.bar.floating ? Settings.data.bar.marginVertical * Style.marginXL : 0
|
||||
return Style.barHeight + base + floatExtraV
|
||||
var floatExtraV = Settings.data.bar.floating ? Settings.data.bar.marginVertical * Style.marginXL : 0;
|
||||
return Style.barHeight + base + floatExtraV;
|
||||
}
|
||||
return base
|
||||
return base;
|
||||
}
|
||||
|
||||
margins.bottom: {
|
||||
if (!(anchors.bottom))
|
||||
return 0
|
||||
var base = Style.marginM
|
||||
return 0;
|
||||
var base = Style.marginM;
|
||||
if (Settings.data.bar.position === "bottom") {
|
||||
var floatExtraV = Settings.data.bar.floating ? Settings.data.bar.marginVertical * Style.marginXL : 0
|
||||
return Style.barHeight + base + floatExtraV
|
||||
var floatExtraV = Settings.data.bar.floating ? Settings.data.bar.marginVertical * Style.marginXL : 0;
|
||||
return Style.barHeight + base + floatExtraV;
|
||||
}
|
||||
return base
|
||||
return base;
|
||||
}
|
||||
|
||||
margins.left: {
|
||||
if (!(anchors.left))
|
||||
return 0
|
||||
var base = Style.marginM
|
||||
return 0;
|
||||
var base = Style.marginM;
|
||||
if (Settings.data.bar.position === "left") {
|
||||
var floatExtraH = Settings.data.bar.floating ? Settings.data.bar.marginHorizontal * Style.marginXL : 0
|
||||
return Style.barHeight + base + floatExtraH
|
||||
var floatExtraH = Settings.data.bar.floating ? Settings.data.bar.marginHorizontal * Style.marginXL : 0;
|
||||
return Style.barHeight + base + floatExtraH;
|
||||
}
|
||||
return base
|
||||
return base;
|
||||
}
|
||||
|
||||
margins.right: {
|
||||
if (!(anchors.right))
|
||||
return 0
|
||||
var base = Style.marginM
|
||||
return 0;
|
||||
var base = Style.marginM;
|
||||
if (Settings.data.bar.position === "right") {
|
||||
var floatExtraH = Settings.data.bar.floating ? Settings.data.bar.marginHorizontal * Style.marginXL : 0
|
||||
return Style.barHeight + base + floatExtraH
|
||||
var floatExtraH = Settings.data.bar.floating ? Settings.data.bar.marginHorizontal * Style.marginXL : 0;
|
||||
return Style.barHeight + base + floatExtraH;
|
||||
}
|
||||
return base
|
||||
return base;
|
||||
}
|
||||
|
||||
implicitWidth: verticalMode ? vWidth : hWidth
|
||||
@@ -206,6 +206,7 @@ Variants {
|
||||
|
||||
color: Color.transparent
|
||||
|
||||
WlrLayershell.namespace: "noctalia-osd-" + (screen?.name || "unknown")
|
||||
WlrLayershell.keyboardFocus: WlrKeyboardFocus.None
|
||||
WlrLayershell.layer: (Settings.data.osd && Settings.data.osd.overlayLayer) ? WlrLayer.Overlay : WlrLayer.Top
|
||||
exclusionMode: PanelWindow.ExclusionMode.Ignore
|
||||
@@ -248,10 +249,10 @@ Variants {
|
||||
id: visibilityTimer
|
||||
interval: Style.animationNormal + 50 // Add small buffer
|
||||
onTriggered: {
|
||||
osdItem.visible = false
|
||||
root.currentOSDType = ""
|
||||
osdItem.visible = false;
|
||||
root.currentOSDType = "";
|
||||
// Deactivate the loader when done
|
||||
root.active = false
|
||||
root.active = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -261,11 +262,11 @@ Variants {
|
||||
anchors.fill: parent
|
||||
anchors.margins: Style.marginM * 1.5
|
||||
radius: Style.radiusL
|
||||
color: Color.mSurface
|
||||
border.color: Color.mOutline
|
||||
color: Qt.alpha(Color.mSurface, Settings.data.osd.backgroundOpacity || 1.0)
|
||||
border.color: Qt.alpha(Color.mOutline, Settings.data.osd.backgroundOpacity || 1.0)
|
||||
border.width: {
|
||||
const bw = Math.max(2, Style.borderM)
|
||||
return (bw % 2 === 0) ? bw : bw + 1
|
||||
const bw = Math.max(2, Style.borderM);
|
||||
return (bw % 2 === 0) ? bw : bw + 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -364,9 +365,9 @@ Variants {
|
||||
ColumnLayout {
|
||||
// Ensure inner padding respects the rounded corners; avoid clipping the icon/text
|
||||
property int vMargin: {
|
||||
const styleMargin = Style.marginL
|
||||
const cornerGuard = Math.round(osdItem.radius)
|
||||
return Math.max(styleMargin, cornerGuard)
|
||||
const styleMargin = Style.marginL;
|
||||
const cornerGuard = Math.round(osdItem.radius);
|
||||
return Math.max(styleMargin, cornerGuard);
|
||||
}
|
||||
property int vMarginTop: Math.round(Math.max(osdItem.radius, Style.marginS))
|
||||
property int balanceDelta: Style.marginS
|
||||
@@ -448,46 +449,46 @@ Variants {
|
||||
|
||||
function show() {
|
||||
// Cancel any pending hide operations
|
||||
hideTimer.stop()
|
||||
visibilityTimer.stop()
|
||||
hideTimer.stop();
|
||||
visibilityTimer.stop();
|
||||
|
||||
// Make visible and animate in
|
||||
osdItem.visible = true
|
||||
osdItem.visible = true;
|
||||
// Use Qt.callLater to ensure the visible change is processed before animation
|
||||
Qt.callLater(() => {
|
||||
osdItem.opacity = 1
|
||||
osdItem.scale = 1.0
|
||||
})
|
||||
osdItem.opacity = 1;
|
||||
osdItem.scale = 1.0;
|
||||
});
|
||||
|
||||
// Start the auto-hide timer
|
||||
hideTimer.start()
|
||||
hideTimer.start();
|
||||
}
|
||||
|
||||
function hide() {
|
||||
hideTimer.stop()
|
||||
visibilityTimer.stop()
|
||||
hideTimer.stop();
|
||||
visibilityTimer.stop();
|
||||
|
||||
// Start fade out animation
|
||||
osdItem.opacity = 0
|
||||
osdItem.scale = 0.85 // Less dramatic scale change for smoother effect
|
||||
osdItem.opacity = 0;
|
||||
osdItem.scale = 0.85; // Less dramatic scale change for smoother effect
|
||||
|
||||
// Delay hiding the element until after animation completes
|
||||
visibilityTimer.start()
|
||||
visibilityTimer.start();
|
||||
}
|
||||
|
||||
function hideImmediately() {
|
||||
hideTimer.stop()
|
||||
visibilityTimer.stop()
|
||||
osdItem.opacity = 0
|
||||
osdItem.scale = 0.85
|
||||
osdItem.visible = false
|
||||
root.currentOSDType = ""
|
||||
root.active = false
|
||||
hideTimer.stop();
|
||||
visibilityTimer.stop();
|
||||
osdItem.opacity = 0;
|
||||
osdItem.scale = 0.85;
|
||||
osdItem.visible = false;
|
||||
root.currentOSDType = "";
|
||||
root.active = false;
|
||||
}
|
||||
}
|
||||
|
||||
function showOSD() {
|
||||
osdItem.show()
|
||||
osdItem.show();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -497,34 +498,34 @@ Variants {
|
||||
|
||||
function onVolumeChanged() {
|
||||
if (volumeInitialized) {
|
||||
showOSD("volume")
|
||||
showOSD("volume");
|
||||
}
|
||||
}
|
||||
|
||||
function onMutedChanged() {
|
||||
if (muteInitialized) {
|
||||
showOSD("volume")
|
||||
showOSD("volume");
|
||||
}
|
||||
}
|
||||
|
||||
function onInputVolumeChanged() {
|
||||
if (!inputAudioInitialized) {
|
||||
return
|
||||
return;
|
||||
}
|
||||
if (!AudioService.hasInput) {
|
||||
return
|
||||
return;
|
||||
}
|
||||
showOSD("inputVolume")
|
||||
showOSD("inputVolume");
|
||||
}
|
||||
|
||||
function onInputMutedChanged() {
|
||||
if (!inputAudioInitialized) {
|
||||
return
|
||||
return;
|
||||
}
|
||||
if (!AudioService.hasInput) {
|
||||
return
|
||||
return;
|
||||
}
|
||||
showOSD("inputVolume")
|
||||
showOSD("inputVolume");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -534,11 +535,11 @@ Variants {
|
||||
interval: 500
|
||||
running: true
|
||||
onTriggered: {
|
||||
volumeInitialized = true
|
||||
muteInitialized = true
|
||||
inputAudioInitialized = true
|
||||
volumeInitialized = true;
|
||||
muteInitialized = true;
|
||||
inputAudioInitialized = true;
|
||||
// Brightness initializes on first change to avoid showing OSD on startup
|
||||
connectBrightnessMonitors()
|
||||
connectBrightnessMonitors();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -547,65 +548,65 @@ Variants {
|
||||
target: BrightnessService
|
||||
|
||||
function onMonitorsChanged() {
|
||||
connectBrightnessMonitors()
|
||||
connectBrightnessMonitors();
|
||||
}
|
||||
}
|
||||
|
||||
function disconnectBrightnessMonitors() {
|
||||
for (var i = 0; i < BrightnessService.monitors.length; i++) {
|
||||
let monitor = BrightnessService.monitors[i]
|
||||
monitor.brightnessUpdated.disconnect(onBrightnessChanged)
|
||||
let monitor = BrightnessService.monitors[i];
|
||||
monitor.brightnessUpdated.disconnect(onBrightnessChanged);
|
||||
}
|
||||
}
|
||||
|
||||
function connectBrightnessMonitors() {
|
||||
for (var i = 0; i < BrightnessService.monitors.length; i++) {
|
||||
let monitor = BrightnessService.monitors[i]
|
||||
let monitor = BrightnessService.monitors[i];
|
||||
// Disconnect first to avoid duplicate connections
|
||||
monitor.brightnessUpdated.disconnect(onBrightnessChanged)
|
||||
monitor.brightnessUpdated.connect(onBrightnessChanged)
|
||||
monitor.brightnessUpdated.disconnect(onBrightnessChanged);
|
||||
monitor.brightnessUpdated.connect(onBrightnessChanged);
|
||||
}
|
||||
}
|
||||
|
||||
function onBrightnessChanged(newBrightness) {
|
||||
root.lastUpdatedBrightness = newBrightness
|
||||
root.lastUpdatedBrightness = newBrightness;
|
||||
|
||||
if (!brightnessInitialized) {
|
||||
brightnessInitialized = true
|
||||
return
|
||||
brightnessInitialized = true;
|
||||
return;
|
||||
}
|
||||
|
||||
showOSD("brightness")
|
||||
showOSD("brightness");
|
||||
}
|
||||
|
||||
function showOSD(type) {
|
||||
// Update the current OSD type
|
||||
currentOSDType = type
|
||||
currentOSDType = type;
|
||||
|
||||
// Activate the loader if not already active
|
||||
if (!root.active) {
|
||||
root.active = true
|
||||
root.active = true;
|
||||
}
|
||||
|
||||
// Show the OSD (may need to wait for loader to create the item)
|
||||
if (root.item) {
|
||||
root.item.showOSD()
|
||||
root.item.showOSD();
|
||||
} else {
|
||||
// If item not ready yet, wait for it
|
||||
Qt.callLater(() => {
|
||||
if (root.item) {
|
||||
root.item.showOSD()
|
||||
}
|
||||
})
|
||||
if (root.item) {
|
||||
root.item.showOSD();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function hideOSD() {
|
||||
if (root.item && root.item.osdItem) {
|
||||
root.item.osdItem.hideImmediately()
|
||||
root.item.osdItem.hideImmediately();
|
||||
} else if (root.active) {
|
||||
// If loader is active but item isn't ready, just deactivate
|
||||
root.active = false
|
||||
root.active = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,15 +11,15 @@ ColumnLayout {
|
||||
|
||||
// Helper functions to update arrays immutably
|
||||
function addMonitor(list, name) {
|
||||
const arr = (list || []).slice()
|
||||
const arr = (list || []).slice();
|
||||
if (!arr.includes(name))
|
||||
arr.push(name)
|
||||
return arr
|
||||
arr.push(name);
|
||||
return arr;
|
||||
}
|
||||
function removeMonitor(list, name) {
|
||||
return (list || []).filter(function (n) {
|
||||
return n !== name
|
||||
})
|
||||
return n !== name;
|
||||
});
|
||||
}
|
||||
|
||||
// Display
|
||||
@@ -30,31 +30,40 @@ ColumnLayout {
|
||||
NComboBox {
|
||||
label: I18n.tr("settings.osd.location.label")
|
||||
description: I18n.tr("settings.osd.location.description")
|
||||
model: [{
|
||||
model: [
|
||||
{
|
||||
"key": "top",
|
||||
"name": I18n.tr("options.osd.position.top_center")
|
||||
}, {
|
||||
},
|
||||
{
|
||||
"key": "top_left",
|
||||
"name": I18n.tr("options.osd.position.top_left")
|
||||
}, {
|
||||
},
|
||||
{
|
||||
"key": "top_right",
|
||||
"name": I18n.tr("options.osd.position.top_right")
|
||||
}, {
|
||||
},
|
||||
{
|
||||
"key": "bottom",
|
||||
"name": I18n.tr("options.osd.position.bottom_center")
|
||||
}, {
|
||||
},
|
||||
{
|
||||
"key": "bottom_left",
|
||||
"name": I18n.tr("options.osd.position.bottom_left")
|
||||
}, {
|
||||
},
|
||||
{
|
||||
"key": "bottom_right",
|
||||
"name": I18n.tr("options.osd.position.bottom_right")
|
||||
}, {
|
||||
},
|
||||
{
|
||||
"key": "left",
|
||||
"name": I18n.tr("options.osd.position.center_left")
|
||||
}, {
|
||||
},
|
||||
{
|
||||
"key": "right",
|
||||
"name": I18n.tr("options.osd.position.center_right")
|
||||
}]
|
||||
}
|
||||
]
|
||||
currentKey: Settings.data.osd.location || "top_right"
|
||||
onSelected: key => Settings.data.osd.location = key
|
||||
}
|
||||
@@ -90,6 +99,21 @@ ColumnLayout {
|
||||
onToggled: checked => Settings.data.osd.overlayLayer = checked
|
||||
}
|
||||
|
||||
NLabel {
|
||||
label: I18n.tr("settings.osd.background-opacity.label", "Background opacity")
|
||||
description: I18n.tr("settings.osd.background-opacity.description", "Controls the transparency of the OSD background.")
|
||||
}
|
||||
|
||||
NValueSlider {
|
||||
Layout.fillWidth: true
|
||||
from: 0
|
||||
to: 100
|
||||
stepSize: 1
|
||||
value: Settings.data.osd.backgroundOpacity * 100
|
||||
onMoved: value => Settings.data.osd.backgroundOpacity = value / 100
|
||||
text: Math.round(Settings.data.osd.backgroundOpacity * 100) + "%"
|
||||
}
|
||||
|
||||
NLabel {
|
||||
label: I18n.tr("settings.osd.duration.auto-hide.label")
|
||||
description: I18n.tr("settings.osd.duration.auto-hide.description")
|
||||
@@ -128,22 +152,22 @@ ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
label: modelData.name || I18n.tr("system.unknown")
|
||||
description: {
|
||||
const compositorScale = CompositorService.getDisplayScale(modelData.name)
|
||||
const compositorScale = CompositorService.getDisplayScale(modelData.name);
|
||||
I18n.tr("system.monitor-description", {
|
||||
"model": modelData.model,
|
||||
"width": modelData.width * compositorScale,
|
||||
"height": modelData.height * compositorScale,
|
||||
"scale": compositorScale
|
||||
})
|
||||
"model": modelData.model,
|
||||
"width": modelData.width * compositorScale,
|
||||
"height": modelData.height * compositorScale,
|
||||
"scale": compositorScale
|
||||
});
|
||||
}
|
||||
checked: (Settings.data.osd.monitors || []).indexOf(modelData.name) !== -1
|
||||
onToggled: checked => {
|
||||
if (checked) {
|
||||
Settings.data.osd.monitors = addMonitor(Settings.data.osd.monitors, modelData.name)
|
||||
} else {
|
||||
Settings.data.osd.monitors = removeMonitor(Settings.data.osd.monitors, modelData.name)
|
||||
}
|
||||
}
|
||||
if (checked) {
|
||||
Settings.data.osd.monitors = addMonitor(Settings.data.osd.monitors, modelData.name);
|
||||
} else {
|
||||
Settings.data.osd.monitors = removeMonitor(Settings.data.osd.monitors, modelData.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user