mirror of
https://github.com/noctalia-dev/noctalia-shell.git
synced 2026-05-11 17:08:27 +08:00
Merge branch 'main' into idle
This commit is contained in:
@@ -1006,6 +1006,9 @@
|
||||
"weather-show-background-description": "Zeige den Hintergrundcontainer für das Wetter-Widget an."
|
||||
},
|
||||
"display": {
|
||||
"monitors-backlight-device-auto-option": "Standard",
|
||||
"monitors-backlight-device-description": "Wähle ein Hintergrundbeleuchtungsgerät für diesen Ausgang.",
|
||||
"monitors-backlight-device-label": "Hintergrundbeleuchtungsgerät",
|
||||
"monitors-brightness-step-description": "Schrittgröße für Helligkeitsänderungen anpassen (Mausrad und Tastenkürzel).",
|
||||
"monitors-brightness-step-label": "Helligkeits-Schrittgröße",
|
||||
"monitors-brightness-unavailable-ddc-disabled": "Helligkeitssteuerung nicht verfügbar. Aktivieren Sie \"Externe Helligkeitsunterstützung\", um die Helligkeit dieses Displays zu steuern.",
|
||||
|
||||
@@ -1006,6 +1006,9 @@
|
||||
"weather-show-background-description": "Show the background container for the weather widget."
|
||||
},
|
||||
"display": {
|
||||
"monitors-backlight-device-auto-option": "Default",
|
||||
"monitors-backlight-device-description": "Select a backlight device for this output.",
|
||||
"monitors-backlight-device-label": "Backlight device",
|
||||
"monitors-brightness-step-description": "Adjust the step size for brightness changes (scroll wheel and keyboard shortcuts).",
|
||||
"monitors-brightness-step-label": "Brightness step size",
|
||||
"monitors-brightness-unavailable-ddc-disabled": "Brightness control unavailable. Enable \"External brightness support\" to control this display's brightness.",
|
||||
|
||||
@@ -1006,6 +1006,9 @@
|
||||
"weather-show-background-description": "Mostrar el contenedor de fondo para el widget del clima."
|
||||
},
|
||||
"display": {
|
||||
"monitors-backlight-device-auto-option": "Predeterminado",
|
||||
"monitors-backlight-device-description": "Selecciona un dispositivo de retroiluminación para esta salida.",
|
||||
"monitors-backlight-device-label": "Dispositivo de Retroiluminación",
|
||||
"monitors-brightness-step-description": "Ajusta el tamaño del paso para los cambios de brillo (rueda de desplazamiento y atajos de teclado).",
|
||||
"monitors-brightness-step-label": "Tamaño del paso de brillo",
|
||||
"monitors-brightness-unavailable-ddc-disabled": "Control de brillo no disponible. Habilita \"Soporte de brillo externo\" para controlar el brillo de esta pantalla.",
|
||||
|
||||
@@ -1006,6 +1006,9 @@
|
||||
"weather-show-background-description": "Afficher le conteneur d'arrière-plan pour le widget météo."
|
||||
},
|
||||
"display": {
|
||||
"monitors-backlight-device-auto-option": "Par défaut",
|
||||
"monitors-backlight-device-description": "Sélectionnez un appareil de rétroéclairage pour cette sortie.",
|
||||
"monitors-backlight-device-label": "Appareil de Rétroéclairage",
|
||||
"monitors-brightness-step-description": "Ajustez l'incrément pour les changements de luminosité (molette de la souris et raccourcis clavier).",
|
||||
"monitors-brightness-step-label": "Incrément de luminosité",
|
||||
"monitors-brightness-unavailable-ddc-disabled": "Contrôle de la luminosité indisponible. Activez \"Prise en charge de la luminosité externe\" pour contrôler la luminosité de cet écran.",
|
||||
|
||||
@@ -1006,6 +1006,9 @@
|
||||
"weather-show-background-description": "Jelenítse meg az időjárás widget háttérkonténerét."
|
||||
},
|
||||
"display": {
|
||||
"monitors-backlight-device-auto-option": "Alapértelmezett",
|
||||
"monitors-backlight-device-description": "Válasszon háttérvilágítási eszközt ehhez a kimenethez.",
|
||||
"monitors-backlight-device-label": "Háttérvilágítási Eszköz",
|
||||
"monitors-brightness-step-description": "Állítsa be a lépésméretet a fényerő változásokhoz (görgetőkerék és billentyűparancsok).",
|
||||
"monitors-brightness-step-label": "Fényerő lépésméret",
|
||||
"monitors-brightness-unavailable-ddc-disabled": "Fényerő vezérlés nem érhető el. Engedélyezze a \"Külső fényerő támogatás\" opciót a kijelző fényerejének szabályozásához.",
|
||||
|
||||
@@ -1006,6 +1006,9 @@
|
||||
"weather-show-background-description": "天気ウィジェットの背景コンテナを表示します。"
|
||||
},
|
||||
"display": {
|
||||
"monitors-backlight-device-auto-option": "デフォルト",
|
||||
"monitors-backlight-device-description": "この出力のバックライトデバイスを選択してください。",
|
||||
"monitors-backlight-device-label": "バックライトデバイス",
|
||||
"monitors-brightness-step-description": "明るさの変化量(スクロールホイールやショートカットキー)を調整します。",
|
||||
"monitors-brightness-step-label": "明るさの調整ステップ",
|
||||
"monitors-brightness-unavailable-ddc-disabled": "明るさ調整を利用できません。このディスプレイを操作するには「外部ディスプレイの明るさ制御」を有効にしてください。",
|
||||
|
||||
@@ -1006,6 +1006,9 @@
|
||||
"weather-show-background-description": "날씨 위젯의 배경 컨테이너를 표시합니다."
|
||||
},
|
||||
"display": {
|
||||
"monitors-backlight-device-auto-option": "기본값",
|
||||
"monitors-backlight-device-description": "이 출력에 대한 백라이트 장치를 선택하세요.",
|
||||
"monitors-backlight-device-label": "백라이트 장치",
|
||||
"monitors-brightness-step-description": "밝기 변경(스크롤 휠 및 키보드 단축키)의 단계 크기를 조정합니다.",
|
||||
"monitors-brightness-step-label": "밝기 단계 크기",
|
||||
"monitors-brightness-unavailable-ddc-disabled": "밝기 제어를 사용할 수 없습니다. 이 디스플레이의 밝기를 제어하려면 \"외부 밝기 지원\"을 활성화하세요.",
|
||||
|
||||
@@ -1006,6 +1006,9 @@
|
||||
"weather-show-background-description": "Toon de achtergrondcontainer voor de weerwidget."
|
||||
},
|
||||
"display": {
|
||||
"monitors-backlight-device-auto-option": "Standaard",
|
||||
"monitors-backlight-device-description": "Selecteer een achtergrondverlichtingsapparaat voor deze uitvoer.",
|
||||
"monitors-backlight-device-label": "Achtergrondverlichting Apparaat",
|
||||
"monitors-brightness-step-description": "Pas de stapgrootte voor helderheidswijzigingen aan (scrollwiel en sneltoetsen).",
|
||||
"monitors-brightness-step-label": "Stapgrootte helderheid",
|
||||
"monitors-brightness-unavailable-ddc-disabled": "Helderheidsregeling niet beschikbaar. Schakel \"Ondersteuning voor externe helderheid\" in om de helderheid van dit scherm te regelen.",
|
||||
|
||||
@@ -1006,6 +1006,9 @@
|
||||
"weather-show-background-description": "Pokaż tło dla widżetu pogody."
|
||||
},
|
||||
"display": {
|
||||
"monitors-backlight-device-auto-option": "Domyślny",
|
||||
"monitors-backlight-device-description": "Wybierz urządzenie podświetlenia dla tego wyjścia.",
|
||||
"monitors-backlight-device-label": "Urządzenie Podświetlenia",
|
||||
"monitors-brightness-step-description": "Dostosuj skok zmiany jasności (kółko myszy i skróty klawiszowe).",
|
||||
"monitors-brightness-step-label": "Skok jasności",
|
||||
"monitors-brightness-unavailable-ddc-disabled": "Sterowanie jasnością niedostępne. Włącz \"Obsługa zewnętrznej jasności\", aby sterować jasnością tego ekranu.",
|
||||
|
||||
@@ -1006,6 +1006,9 @@
|
||||
"weather-show-background-description": "Mostrar o contêiner de fundo para o widget de clima."
|
||||
},
|
||||
"display": {
|
||||
"monitors-backlight-device-auto-option": "Padrão",
|
||||
"monitors-backlight-device-description": "Selecione um dispositivo de retroiluminação para esta saída.",
|
||||
"monitors-backlight-device-label": "Dispositivo de Retroiluminação",
|
||||
"monitors-brightness-step-description": "Ajuste o tamanho do passo para alterações de brilho (roda do mouse e atalhos de teclado).",
|
||||
"monitors-brightness-step-label": "Tamanho do passo do brilho",
|
||||
"monitors-brightness-unavailable-ddc-disabled": "Controle de brilho indisponível. Ative \"Suporte de brilho externo\" para controlar o brilho desta tela.",
|
||||
|
||||
@@ -1006,6 +1006,9 @@
|
||||
"weather-show-background-description": "Показать фоновый контейнер для погодного виджета."
|
||||
},
|
||||
"display": {
|
||||
"monitors-backlight-device-auto-option": "По умолчанию",
|
||||
"monitors-backlight-device-description": "Выберите устройство подсветки для этого вывода.",
|
||||
"monitors-backlight-device-label": "Устройство Подсветки",
|
||||
"monitors-brightness-step-description": "Настройка шага изменения яркости (колесо прокрутки и сочетания клавиш).",
|
||||
"monitors-brightness-step-label": "Шаг изменения яркости",
|
||||
"monitors-brightness-unavailable-ddc-disabled": "Управление яркостью недоступно. Включите \"Поддержка внешней яркости\", чтобы управлять яркостью этого дисплея.",
|
||||
|
||||
@@ -1006,6 +1006,9 @@
|
||||
"weather-show-background-description": "Visa bakgrundsbehållaren för väderwidgeten."
|
||||
},
|
||||
"display": {
|
||||
"monitors-backlight-device-auto-option": "Standard",
|
||||
"monitors-backlight-device-description": "Välj en bakgrundsbelysningsenhet för denna utgång.",
|
||||
"monitors-backlight-device-label": "Bakgrundsbelysningsenhet",
|
||||
"monitors-brightness-step-description": "Justera stegstorleken för ljusstyrkeändringar (rullhjul och kortkommandon).",
|
||||
"monitors-brightness-step-label": "Ljusstyrkestegstorlek",
|
||||
"monitors-brightness-unavailable-ddc-disabled": "Ljusstyrkekontroll otillgänglig. Aktivera \"Extern ljusstyrkestöd\"för att kontrollera ljusstyrkan på denna skärm.",
|
||||
|
||||
@@ -1006,6 +1006,9 @@
|
||||
"weather-show-background-description": "Hava durumu widget'ı için arka plan konteynerini göster."
|
||||
},
|
||||
"display": {
|
||||
"monitors-backlight-device-auto-option": "Varsayılan",
|
||||
"monitors-backlight-device-description": "Bu çıkış için bir arka ışık cihazı seçin.",
|
||||
"monitors-backlight-device-label": "Arka Işık Cihazı",
|
||||
"monitors-brightness-step-description": "Parlaklık değişimleri için adım boyutunu ayarlayın (tekerlek ve klavye kısayolları).",
|
||||
"monitors-brightness-step-label": "Parlaklık adım boyutu",
|
||||
"monitors-brightness-unavailable-ddc-disabled": "Parlaklık kontrolü kullanılamıyor. Bu ekranın parlaklığını kontrol etmek için \"Harici parlaklık desteği\"ni etkinleştirin.",
|
||||
|
||||
@@ -1006,6 +1006,9 @@
|
||||
"weather-show-background-description": "Показати фоновий контейнер для погодного віджета."
|
||||
},
|
||||
"display": {
|
||||
"monitors-backlight-device-auto-option": "За замовчуванням",
|
||||
"monitors-backlight-device-description": "Виберіть пристрій підсвічування для цього виходу.",
|
||||
"monitors-backlight-device-label": "Пристрій Підсвічування",
|
||||
"monitors-brightness-step-description": "Налаштуйте крок зміни яскравості (колесо миші та гарячі клавіші).",
|
||||
"monitors-brightness-step-label": "Крок зміни яскравості",
|
||||
"monitors-brightness-unavailable-ddc-disabled": "Регулювання яскравості недоступне. Увімкніть \"Підтримку зовнішньої яскравості\", щоб керувати яскравістю цього дисплея.",
|
||||
|
||||
@@ -1006,6 +1006,9 @@
|
||||
"weather-show-background-description": "显示天气小部件的背景容器。"
|
||||
},
|
||||
"display": {
|
||||
"monitors-backlight-device-auto-option": "默认",
|
||||
"monitors-backlight-device-description": "为此输出选择一个背光设备。",
|
||||
"monitors-backlight-device-label": "背光设备",
|
||||
"monitors-brightness-step-description": "调整亮度变化的步长(滚轮和键盘快捷键)。",
|
||||
"monitors-brightness-step-label": "亮度步长",
|
||||
"monitors-brightness-unavailable-ddc-disabled": "亮度控制不可用。启用“外部亮度支持”以控制此显示器的亮度。",
|
||||
|
||||
@@ -1006,6 +1006,9 @@
|
||||
"weather-show-background-description": "顯示天氣小工具的填充背景"
|
||||
},
|
||||
"display": {
|
||||
"monitors-backlight-device-auto-option": "預設",
|
||||
"monitors-backlight-device-description": "為此輸出選擇一個背光裝置。",
|
||||
"monitors-backlight-device-label": "背光裝置",
|
||||
"monitors-brightness-step-description": "微調亮度調整一格的大小 (滾輪及鍵盤快捷鍵)",
|
||||
"monitors-brightness-step-label": "亮度步進大小",
|
||||
"monitors-brightness-unavailable-ddc-disabled": "無法使用亮度控制, 啟用 \"外部亮度調整支援\" 來控制這個顯示器的亮度",
|
||||
|
||||
@@ -460,7 +460,8 @@
|
||||
"brightness": {
|
||||
"brightnessStep": 5,
|
||||
"enforceMinimum": true,
|
||||
"enableDdcSupport": false
|
||||
"enableDdcSupport": false,
|
||||
"backlightDeviceMappings": []
|
||||
},
|
||||
"colorSchemes": {
|
||||
"useWallpaperColors": false,
|
||||
@@ -512,4 +513,4 @@
|
||||
"gridSnap": false,
|
||||
"monitorWidgets": []
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -519,6 +519,15 @@
|
||||
"tabLabel": "panels.desktop-widgets.title",
|
||||
"subTab": null
|
||||
},
|
||||
{
|
||||
"labelKey": "panels.display.monitors-backlight-device-label",
|
||||
"descriptionKey": "panels.display.monitors-backlight-device-description",
|
||||
"widget": "NComboBox",
|
||||
"tab": 14,
|
||||
"tabLabel": "panels.display.title",
|
||||
"subTab": 0,
|
||||
"subTabLabel": "common.brightness"
|
||||
},
|
||||
{
|
||||
"labelKey": "panels.display.monitors-brightness-step-label",
|
||||
"descriptionKey": "panels.display.monitors-brightness-step-description",
|
||||
|
||||
@@ -670,6 +670,8 @@ Singleton {
|
||||
property int brightnessStep: 5
|
||||
property bool enforceMinimum: true
|
||||
property bool enableDdcSupport: false
|
||||
property list<var> backlightDeviceMappings: []
|
||||
// Format: [{ "output": "eDP-1", "device": "/sys/class/backlight/intel_backlight" }]
|
||||
}
|
||||
|
||||
property JsonObject colorSchemes: JsonObject {
|
||||
|
||||
@@ -205,6 +205,7 @@ Item {
|
||||
rotateText: isVerticalBar && currentMaxTextLength > 0
|
||||
autoHide: false
|
||||
forceOpen: _pillForceOpen
|
||||
forceClose: !_pillForceOpen
|
||||
customTextIconColor: iconColor
|
||||
|
||||
// Helper function to build tooltip content
|
||||
|
||||
@@ -102,7 +102,7 @@ Loader {
|
||||
readonly property int barHeight: Style.getBarHeightForScreen(modelData?.name)
|
||||
readonly property int peekEdgeLength: {
|
||||
const edgeSize = isVertical ? Math.round(modelData?.height || maxHeight) : Math.round(modelData?.width || maxWidth);
|
||||
const minLength = Math.max(1, Math.round(edgeSize * 0.1));
|
||||
const minLength = Math.max(1, Math.round(edgeSize * ((isStaticMode && Settings.data.dock.showFrameIndicator && Settings.data.bar.barType === "framed" && hasBar) ? 0.1 : 0.25)));
|
||||
return Math.max(minLength, frameIndicatorLength);
|
||||
}
|
||||
readonly property int peekCenterOffsetX: {
|
||||
@@ -791,8 +791,9 @@ Loader {
|
||||
readonly property int extraLeft: (!isVertical && !exclusive && barOnLeft) ? barHeight : 0
|
||||
readonly property int extraRight: (!isVertical && !exclusive && barOnRight) ? barHeight : 0
|
||||
|
||||
width: dockContent.dockContainer.width + extraLeft + extraRight
|
||||
height: dockContent.dockContainer.height + extraTop + extraBottom
|
||||
// Add +2 buffer for fractional scaling issues
|
||||
width: dockContent.dockContainer.width + extraLeft + extraRight + (root.isVertical ? 2 : Style.margin2XL * 6)
|
||||
height: dockContent.dockContainer.height + extraTop + extraBottom + 2
|
||||
|
||||
anchors.horizontalCenter: isVertical ? undefined : parent.horizontalCenter
|
||||
anchors.verticalCenter: isVertical ? parent.verticalCenter : undefined
|
||||
@@ -802,6 +803,9 @@ Loader {
|
||||
anchors.left: dockPosition === "left" ? parent.left : undefined
|
||||
anchors.right: dockPosition === "right" ? parent.right : undefined
|
||||
|
||||
// Enable layer caching to reduce GPU usage from continuous animations
|
||||
layer.enabled: true
|
||||
|
||||
opacity: hidden ? 0 : 1
|
||||
scale: hidden ? 0.85 : 1
|
||||
|
||||
|
||||
@@ -41,9 +41,6 @@ Item {
|
||||
border.width: Style.borderS
|
||||
border.color: Qt.alpha(Color.mOutline, (isStaticMode ? 0 : Settings.data.dock.backgroundOpacity))
|
||||
|
||||
// Enable layer caching to reduce GPU usage from continuous animations
|
||||
layer.enabled: true
|
||||
|
||||
MouseArea {
|
||||
id: dockMouseArea
|
||||
anchors.fill: parent
|
||||
|
||||
@@ -37,7 +37,7 @@ PopupWindow {
|
||||
property real menuMinWidth: 120
|
||||
property real menuMaxWidth: 360
|
||||
property real menuMaxHeight: Math.max(180, Math.min(420, Math.round((targetScreen ? targetScreen.height : 600) * 0.3)))
|
||||
property int separatorCompactHeight: 8
|
||||
property int separatorCompactHeight: Style.borderS + Style.margin2S
|
||||
property string forcedGroupMenuMode: ""
|
||||
readonly property int separatorIndex: {
|
||||
for (let i = 0; i < root.items.length; i++) {
|
||||
@@ -49,7 +49,7 @@ PopupWindow {
|
||||
readonly property bool splitExtendedLayout: separatorIndex >= 0
|
||||
readonly property var scrollItems: splitExtendedLayout ? root.items.slice(0, separatorIndex) : root.items
|
||||
readonly property var fixedItems: splitExtendedLayout ? root.items.slice(separatorIndex + 1) : []
|
||||
readonly property real menuInnerHeight: Math.max(0, implicitHeight - Style.marginXL)
|
||||
readonly property real menuInnerHeight: Math.max(0, implicitHeight - Style.margin2M)
|
||||
readonly property real fixedActionsHeight: listHeight(fixedItems)
|
||||
readonly property real separatorBlockHeight: splitExtendedLayout ? separatorCompactHeight : 0
|
||||
readonly property real scrollAreaHeight: splitExtendedLayout ? Math.max(0, menuInnerHeight - fixedActionsHeight - separatorBlockHeight) : menuInnerHeight
|
||||
@@ -709,12 +709,14 @@ PopupWindow {
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: separator
|
||||
visible: root.splitExtendedLayout
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: menuFlick.bottom
|
||||
anchors.leftMargin: Style.marginS
|
||||
anchors.rightMargin: Style.marginS
|
||||
anchors.topMargin: Style.marginS
|
||||
height: Style.borderS
|
||||
color: Qt.alpha(Color.mOutline, 0.7)
|
||||
radius: Style.radiusXS
|
||||
@@ -728,15 +730,16 @@ PopupWindow {
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.leftMargin: Style.marginM
|
||||
anchors.rightMargin: Style.marginM
|
||||
anchors.topMargin: Style.marginS
|
||||
anchors.bottomMargin: Style.marginM
|
||||
anchors.top: menuFlick.bottom
|
||||
anchors.topMargin: root.separatorBlockHeight
|
||||
anchors.top: separator.bottom
|
||||
spacing: 0
|
||||
|
||||
Repeater {
|
||||
model: root.fixedItems
|
||||
|
||||
Rectangle {
|
||||
id: fixedItemRect
|
||||
readonly property int globalIndex: root.fixedItemGlobalIndex(index)
|
||||
width: fixedColumn.width
|
||||
height: root.rowHeightForItem(modelData)
|
||||
@@ -755,7 +758,7 @@ PopupWindow {
|
||||
NIcon {
|
||||
icon: modelData.icon
|
||||
pointSize: Style.fontSizeL
|
||||
color: root.hoveredItem === parent.globalIndex ? Color.mOnHover : Color.mOnSurfaceVariant
|
||||
color: root.hoveredItem === fixedItemRect.globalIndex ? Color.mOnHover : Color.mOnSurfaceVariant
|
||||
visible: icon !== ""
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
@@ -763,7 +766,7 @@ PopupWindow {
|
||||
NText {
|
||||
text: modelData.text
|
||||
pointSize: Style.fontSizeS
|
||||
color: root.hoveredItem === parent.globalIndex ? Color.mOnHover : Color.mOnSurfaceVariant
|
||||
color: root.hoveredItem === fixedItemRect.globalIndex ? Color.mOnHover : Color.mOnSurfaceVariant
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: fixedRowLayout.width - ((modelData.icon && modelData.icon !== "") ? (Style.fontSizeL + Style.marginS) : 0)
|
||||
elide: Text.ElideRight
|
||||
|
||||
@@ -201,7 +201,7 @@ NBox {
|
||||
color: Color.mError
|
||||
radius: height * 0.5
|
||||
width: Math.round(forgettingText.implicitWidth + Style.margin2S)
|
||||
height: math.round(forgettingText.implicitHeight + Style.margin2XXS)
|
||||
height: Math.round(forgettingText.implicitHeight + Style.margin2XXS)
|
||||
|
||||
NText {
|
||||
id: forgettingText
|
||||
|
||||
@@ -326,13 +326,29 @@ Item {
|
||||
highlightOverlay.opacity = 0;
|
||||
}
|
||||
|
||||
// Find and highlight a widget by its label key
|
||||
function isEffectivelyVisible(item) {
|
||||
var current = item;
|
||||
while (current) {
|
||||
if (current.visible === false)
|
||||
return false;
|
||||
if (current.opacity !== undefined && current.opacity <= 0)
|
||||
return false;
|
||||
current = current.parent;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Find and highlight a widget by its label key.
|
||||
function findAndHighlightWidget(item, labelKey) {
|
||||
if (!item)
|
||||
return null;
|
||||
|
||||
// Check if this item has a matching label
|
||||
if (item.hasOwnProperty("label") && item.label === I18n.tr(labelKey)) {
|
||||
// Skip hidden branches to avoid highlighting controls that are not on screen.
|
||||
if (!isEffectivelyVisible(item))
|
||||
return null;
|
||||
|
||||
// Check if this item has a matching label.
|
||||
if (item.hasOwnProperty("label") && item.label === I18n.tr(labelKey) && item.width > 0 && item.height > 0) {
|
||||
return item;
|
||||
}
|
||||
|
||||
|
||||
@@ -196,7 +196,7 @@ Item {
|
||||
id: connectedDevicesBox
|
||||
visible: root.connectedDevices.length > 0 && BluetoothService.enabled
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: connectedDevicesCol.implicitHeight + Style.marginXL
|
||||
Layout.preferredHeight: connectedDevicesCol.implicitHeight + Style.margin2M
|
||||
border.color: showOnlyLists ? Style.boxBorderColor : "transparent"
|
||||
|
||||
ColumnLayout {
|
||||
@@ -226,7 +226,7 @@ Item {
|
||||
id: pairedDevicesBox
|
||||
visible: root.pairedDevices.length > 0 && BluetoothService.enabled
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: pairedDevicesCol.implicitHeight + Style.marginXL
|
||||
Layout.preferredHeight: pairedDevicesCol.implicitHeight + Style.margin2M
|
||||
border.color: showOnlyLists ? Style.boxBorderColor : "transparent"
|
||||
|
||||
ColumnLayout {
|
||||
@@ -256,7 +256,7 @@ Item {
|
||||
id: availableDevicesBox
|
||||
visible: !root.showOnlyLists && root.unnamedAvailableDevices.length > 0 && BluetoothService.enabled
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: availableDevicesCol.implicitHeight + Style.marginXL
|
||||
Layout.preferredHeight: availableDevicesCol.implicitHeight + Style.margin2M
|
||||
border.color: "transparent"
|
||||
|
||||
ColumnLayout {
|
||||
|
||||
@@ -25,6 +25,32 @@ ColumnLayout {
|
||||
property var brightnessMonitor: BrightnessService.getMonitorForScreen(modelData)
|
||||
property real localBrightness: 0.5
|
||||
property bool localBrightnessChanging: false
|
||||
readonly property string automaticOptionLabel: {
|
||||
var baseLabel = I18n.tr("panels.display.monitors-backlight-device-auto-option");
|
||||
var autoDevicePath = (BrightnessService.availableBacklightDevices && BrightnessService.availableBacklightDevices.length > 0) ? BrightnessService.availableBacklightDevices[0] : "";
|
||||
if (autoDevicePath === "")
|
||||
return baseLabel;
|
||||
|
||||
var autoDeviceName = BrightnessService.getBacklightDeviceName(autoDevicePath) || autoDevicePath;
|
||||
return baseLabel + "(" + autoDeviceName + ")";
|
||||
}
|
||||
readonly property var backlightDeviceOptions: {
|
||||
var options = [{
|
||||
"key": "",
|
||||
"name": automaticOptionLabel
|
||||
}];
|
||||
|
||||
var devices = BrightnessService.availableBacklightDevices || [];
|
||||
for (var i = 0; i < devices.length; i++) {
|
||||
var devicePath = devices[i];
|
||||
var deviceName = BrightnessService.getBacklightDeviceName(devicePath) || devicePath;
|
||||
options.push({
|
||||
"key": devicePath,
|
||||
"name": deviceName
|
||||
});
|
||||
}
|
||||
return options;
|
||||
}
|
||||
|
||||
onBrightnessMonitorChanged: {
|
||||
if (brightnessMonitor && !localBrightnessChanging)
|
||||
@@ -160,13 +186,23 @@ ColumnLayout {
|
||||
}
|
||||
|
||||
NText {
|
||||
visible: brightnessMonitor && !brightnessMonitor.brightnessControlAvailable
|
||||
visible: brightnessMonitor && !brightnessMonitor.brightnessControlAvailable && !(brightnessMonitor.method === "internal" && brightnessMonitor.initInProgress)
|
||||
text: !Settings.data.brightness.enableDdcSupport ? I18n.tr("panels.display.monitors-brightness-unavailable-ddc-disabled") : I18n.tr("panels.display.monitors-brightness-unavailable-generic")
|
||||
pointSize: Style.fontSizeXS
|
||||
color: Color.mOnSurfaceVariant
|
||||
Layout.fillWidth: true
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
|
||||
NComboBox {
|
||||
Layout.fillWidth: true
|
||||
visible: brightnessMonitor && brightnessMonitor.method === "internal"
|
||||
label: I18n.tr("panels.display.monitors-backlight-device-label")
|
||||
description: I18n.tr("panels.display.monitors-backlight-device-description")
|
||||
model: backlightDeviceOptions
|
||||
currentKey: BrightnessService.getMappedBacklightDevice(modelData.name) || ""
|
||||
onSelected: key => BrightnessService.setMappedBacklightDevice(modelData.name, key)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+11
-15
@@ -260,19 +260,17 @@ PopupWindow {
|
||||
const tipWidth = Math.ceil(Math.min(contentWidth + (padding * 2), maxWidth));
|
||||
root.implicitWidth = tipWidth;
|
||||
|
||||
// Add +2 buffer for fractional scaling issues (especially with "top" direction)
|
||||
const tipHeight = Math.ceil(contentHeight + (padding * 2)) + 2;
|
||||
const tipHeight = Math.ceil(contentHeight + (padding * 2));
|
||||
root.implicitHeight = tipHeight;
|
||||
|
||||
// Get target's global position and convert to screen-relative
|
||||
// Round all values to avoid sub-pixel positioning issues with fractional scaling
|
||||
var targetGlobalAbs = targetItem.mapToGlobal(0, 0);
|
||||
var targetGlobal = {
|
||||
"x": Math.round(targetGlobalAbs.x - screenX),
|
||||
"y": Math.round(targetGlobalAbs.y - screenY)
|
||||
"x": targetGlobalAbs.x - screenX,
|
||||
"y": targetGlobalAbs.y - screenY
|
||||
};
|
||||
const targetWidth = Math.round(targetItem.width);
|
||||
const targetHeight = Math.round(targetItem.height);
|
||||
const targetWidth = targetItem.width;
|
||||
const targetHeight = targetItem.height;
|
||||
|
||||
var newAnchorX = 0;
|
||||
var newAnchorY = 0;
|
||||
@@ -427,7 +425,6 @@ PopupWindow {
|
||||
}
|
||||
|
||||
// Apply position first (before making visible)
|
||||
// Round to avoid sub-pixel positioning issues with fractional scaling
|
||||
// Use floor for negative values to push tooltip away from target
|
||||
anchorX = newAnchorX < 0 ? Math.floor(newAnchorX) : Math.round(newAnchorX);
|
||||
anchorY = newAnchorY < 0 ? Math.floor(newAnchorY) : Math.round(newAnchorY);
|
||||
@@ -519,19 +516,18 @@ PopupWindow {
|
||||
const tipWidth = Math.ceil(Math.min(contentWidth + (padding * 2), maxWidth));
|
||||
root.implicitWidth = tipWidth;
|
||||
|
||||
// Add +2 buffer for fractional scaling issues (especially with "top" direction)
|
||||
const tipHeight = Math.ceil(contentHeight + (padding * 2)) + 2;
|
||||
const tipHeight = Math.ceil(contentHeight + (padding * 2));
|
||||
root.implicitHeight = tipHeight;
|
||||
|
||||
// Reposition based on current direction (screen-relative)
|
||||
// Round all values to avoid sub-pixel positioning issues with fractional scaling
|
||||
var targetGlobalAbs = targetItem.mapToGlobal(0, 0);
|
||||
var targetGlobal = {
|
||||
"x": Math.round(targetGlobalAbs.x - screenX),
|
||||
"y": Math.round(targetGlobalAbs.y - screenY)
|
||||
"x": targetGlobalAbs.x - screenX,
|
||||
"y": targetGlobalAbs.y - screenY
|
||||
};
|
||||
const targetWidth = Math.round(targetItem.width);
|
||||
const targetHeight = Math.round(targetItem.height);
|
||||
const targetWidth = targetItem.width;
|
||||
const targetHeight = targetItem.height;
|
||||
|
||||
// Recalculate base anchor position (center on target for top/bottom, etc.)
|
||||
var newAnchorX = anchorX;
|
||||
@@ -608,7 +604,6 @@ PopupWindow {
|
||||
}
|
||||
|
||||
// Apply the new anchor positions
|
||||
// Round to avoid sub-pixel positioning issues with fractional scaling
|
||||
// Use floor for negative values to push tooltip away from target
|
||||
anchorX = newAnchorX < 0 ? Math.floor(newAnchorX) : Math.round(newAnchorX);
|
||||
anchorY = newAnchorY < 0 ? Math.floor(newAnchorY) : Math.round(newAnchorY);
|
||||
@@ -663,6 +658,7 @@ PopupWindow {
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
anchors.margins: border.width / 2
|
||||
color: Color.mSurface
|
||||
border.color: Color.mOutline
|
||||
border.width: Style.borderS
|
||||
|
||||
@@ -499,6 +499,10 @@ Singleton {
|
||||
});
|
||||
}
|
||||
|
||||
function lock() {
|
||||
CompositorService.lock();
|
||||
}
|
||||
|
||||
function lockAndSuspend() {
|
||||
CompositorService.lockAndSuspend();
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ Singleton {
|
||||
property list<var> ddcMonitors: []
|
||||
readonly property list<Monitor> monitors: variants.instances
|
||||
property bool appleDisplayPresent: false
|
||||
property list<var> availableBacklightDevices: []
|
||||
|
||||
function getMonitorForScreen(screen: ShellScreen): var {
|
||||
return monitors.find(m => m.modelData === screen);
|
||||
@@ -47,10 +48,109 @@ Singleton {
|
||||
return detectedDisplays;
|
||||
}
|
||||
|
||||
function normalizeBacklightDevicePath(devicePath): string {
|
||||
if (devicePath === undefined || devicePath === null)
|
||||
return "";
|
||||
|
||||
var normalized = String(devicePath).trim();
|
||||
if (normalized === "")
|
||||
return "";
|
||||
|
||||
if (normalized.startsWith("/sys/class/backlight/"))
|
||||
return normalized;
|
||||
|
||||
if (normalized.indexOf("/") === -1)
|
||||
return "/sys/class/backlight/" + normalized;
|
||||
|
||||
return normalized;
|
||||
}
|
||||
|
||||
function getBacklightDeviceName(devicePath): string {
|
||||
var normalized = normalizeBacklightDevicePath(devicePath);
|
||||
if (normalized === "")
|
||||
return "";
|
||||
|
||||
var parts = normalized.split("/");
|
||||
while (parts.length > 0 && parts[parts.length - 1] === "") {
|
||||
parts.pop();
|
||||
}
|
||||
return parts.length > 0 ? parts[parts.length - 1] : "";
|
||||
}
|
||||
|
||||
function getMappedBacklightDevice(outputName): string {
|
||||
var normalizedOutput = String(outputName || "").trim();
|
||||
if (normalizedOutput === "")
|
||||
return "";
|
||||
|
||||
var mappings = Settings.data.brightness.backlightDeviceMappings || [];
|
||||
for (var i = 0; i < mappings.length; i++) {
|
||||
var mapping = mappings[i];
|
||||
if (!mapping || typeof mapping !== "object")
|
||||
continue;
|
||||
|
||||
if (String(mapping.output || "").trim() === normalizedOutput)
|
||||
return normalizeBacklightDevicePath(mapping.device || "");
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
function setMappedBacklightDevice(outputName, devicePath): void {
|
||||
var normalizedOutput = String(outputName || "").trim();
|
||||
if (normalizedOutput === "")
|
||||
return;
|
||||
|
||||
var normalizedDevicePath = normalizeBacklightDevicePath(devicePath);
|
||||
var mappings = Settings.data.brightness.backlightDeviceMappings || [];
|
||||
var nextMappings = [];
|
||||
var replaced = false;
|
||||
|
||||
for (var i = 0; i < mappings.length; i++) {
|
||||
var mapping = mappings[i];
|
||||
if (!mapping || typeof mapping !== "object")
|
||||
continue;
|
||||
|
||||
var mappingOutput = String(mapping.output || "").trim();
|
||||
var mappingDevice = normalizeBacklightDevicePath(mapping.device || "");
|
||||
if (mappingOutput === "" || mappingDevice === "")
|
||||
continue;
|
||||
|
||||
if (mappingOutput === normalizedOutput) {
|
||||
if (!replaced && normalizedDevicePath !== "") {
|
||||
nextMappings.push({
|
||||
"output": normalizedOutput,
|
||||
"device": normalizedDevicePath
|
||||
});
|
||||
}
|
||||
replaced = true;
|
||||
} else {
|
||||
nextMappings.push({
|
||||
"output": mappingOutput,
|
||||
"device": mappingDevice
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (!replaced && normalizedDevicePath !== "") {
|
||||
nextMappings.push({
|
||||
"output": normalizedOutput,
|
||||
"device": normalizedDevicePath
|
||||
});
|
||||
}
|
||||
|
||||
Settings.data.brightness.backlightDeviceMappings = nextMappings;
|
||||
}
|
||||
|
||||
function scanBacklightDevices(): void {
|
||||
if (!scanBacklightProc.running)
|
||||
scanBacklightProc.running = true;
|
||||
}
|
||||
|
||||
reloadableId: "brightness"
|
||||
|
||||
Component.onCompleted: {
|
||||
Logger.i("Brightness", "Service started");
|
||||
scanBacklightDevices();
|
||||
if (Settings.data.brightness.enableDdcSupport) {
|
||||
ddcProc.running = true;
|
||||
}
|
||||
@@ -58,6 +158,7 @@ Singleton {
|
||||
|
||||
onMonitorsChanged: {
|
||||
ddcMonitors = [];
|
||||
scanBacklightDevices();
|
||||
if (Settings.data.brightness.enableDdcSupport) {
|
||||
ddcProc.running = true;
|
||||
}
|
||||
@@ -75,6 +176,14 @@ Singleton {
|
||||
ddcMonitors = [];
|
||||
}
|
||||
}
|
||||
function onBacklightDeviceMappingsChanged() {
|
||||
scanBacklightDevices();
|
||||
for (var i = 0; i < monitors.length; i++) {
|
||||
var m = monitors[i];
|
||||
if (m && !m.isDdc && !m.isAppleDisplay)
|
||||
m.initBrightness();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Variants {
|
||||
@@ -92,6 +201,34 @@ Singleton {
|
||||
}
|
||||
}
|
||||
|
||||
// Detect available internal backlight devices
|
||||
Process {
|
||||
id: scanBacklightProc
|
||||
command: ["sh", "-c", "for dev in /sys/class/backlight/*; do if [ -f \"$dev/brightness\" ] && [ -f \"$dev/max_brightness\" ]; then echo \"$dev\"; fi; done"]
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
var data = text.trim();
|
||||
if (data === "") {
|
||||
root.availableBacklightDevices = [];
|
||||
return;
|
||||
}
|
||||
|
||||
var lines = data.split("\n");
|
||||
var found = [];
|
||||
var seen = ({});
|
||||
for (var i = 0; i < lines.length; i++) {
|
||||
var path = root.normalizeBacklightDevicePath(lines[i]);
|
||||
if (path === "" || seen[path])
|
||||
continue;
|
||||
seen[path] = true;
|
||||
found.push(path);
|
||||
}
|
||||
|
||||
root.availableBacklightDevices = found;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Detect DDC monitors
|
||||
Process {
|
||||
id: ddcProc
|
||||
@@ -152,6 +289,7 @@ Singleton {
|
||||
property string maxBrightnessPath: ""
|
||||
property int maxBrightness: 100
|
||||
property bool ignoreNextChange: false
|
||||
property bool initInProgress: false
|
||||
|
||||
// Signal for brightness changes
|
||||
signal brightnessUpdated(real newBrightness)
|
||||
@@ -296,14 +434,22 @@ Singleton {
|
||||
Logger.d("Brightness", "Internal brightness:", current + "/" + max + " =", monitor.brightness);
|
||||
Logger.d("Brightness", "Using backlight device:", monitor.backlightDevice);
|
||||
}
|
||||
} else {
|
||||
monitor.backlightDevice = "";
|
||||
monitor.brightnessPath = "";
|
||||
monitor.maxBrightnessPath = "";
|
||||
}
|
||||
}
|
||||
|
||||
// Always update
|
||||
monitor.brightnessUpdated(monitor.brightness);
|
||||
root.monitorBrightnessChanged(monitor, monitor.brightness);
|
||||
monitor.initInProgress = false;
|
||||
}
|
||||
}
|
||||
onExited: (exitCode, exitStatus) => {
|
||||
monitor.initInProgress = false;
|
||||
}
|
||||
}
|
||||
|
||||
readonly property real stepSize: Settings.data.brightness.brightnessStep / 100.0
|
||||
@@ -379,12 +525,18 @@ Singleton {
|
||||
} else if (!isDdc) {
|
||||
monitor.commandRunning = true;
|
||||
monitor.ignoreNextChange = true;
|
||||
setBrightnessProc.command = ["brightnessctl", "s", rounded + "%"];
|
||||
var backlightDeviceName = root.getBacklightDeviceName(monitor.backlightDevice);
|
||||
if (backlightDeviceName !== "") {
|
||||
setBrightnessProc.command = ["brightnessctl", "-d", backlightDeviceName, "s", rounded + "%"];
|
||||
} else {
|
||||
setBrightnessProc.command = ["brightnessctl", "s", rounded + "%"];
|
||||
}
|
||||
setBrightnessProc.running = true;
|
||||
}
|
||||
}
|
||||
|
||||
function initBrightness(): void {
|
||||
monitor.initInProgress = true;
|
||||
if (isAppleDisplay) {
|
||||
initProc.command = ["asdbctl", "get"];
|
||||
initProc.running = true;
|
||||
@@ -392,16 +544,24 @@ Singleton {
|
||||
initProc.command = ["ddcutil", "-b", busNum, "--sleep-multiplier=0.05", "getvcp", "10", "--brief"];
|
||||
initProc.running = true;
|
||||
} else if (!isDdc) {
|
||||
// Internal backlight - find the first available backlight device and get its info
|
||||
// This now returns: device_path, current_brightness, max_brightness (on separate lines)
|
||||
initProc.command = ["sh", "-c", "for dev in /sys/class/backlight/*; do " + " if [ -f \"$dev/brightness\" ] && [ -f \"$dev/max_brightness\" ]; then " + " echo \"$dev\"; " + " cat \"$dev/brightness\"; " + " cat \"$dev/max_brightness\"; " + " break; " + " fi; " + "done"];
|
||||
// Internal backlight: first try explicit output mapping, then fall back to first available.
|
||||
var preferredDevicePath = root.getMappedBacklightDevice(modelData.name);
|
||||
var probeScript = [
|
||||
"preferred=\"$1\"",
|
||||
"if [ -n \"$preferred\" ] && [ ! -d \"$preferred\" ]; then preferred=\"/sys/class/backlight/$preferred\"; fi",
|
||||
"selected=\"\"",
|
||||
"if [ -n \"$preferred\" ] && [ -f \"$preferred/brightness\" ] && [ -f \"$preferred/max_brightness\" ]; then selected=\"$preferred\"; else for dev in /sys/class/backlight/*; do if [ -f \"$dev/brightness\" ] && [ -f \"$dev/max_brightness\" ]; then selected=\"$dev\"; break; fi; done; fi",
|
||||
"if [ -n \"$selected\" ]; then echo \"$selected\"; cat \"$selected/brightness\"; cat \"$selected/max_brightness\"; fi"
|
||||
].join("; ");
|
||||
initProc.command = ["sh", "-c", probeScript, "sh", preferredDevicePath];
|
||||
initProc.running = true;
|
||||
} else {
|
||||
monitor.initInProgress = false;
|
||||
}
|
||||
}
|
||||
|
||||
onBusNumChanged: initBrightness()
|
||||
onIsDdcChanged: if (isDdc)
|
||||
initBrightness()
|
||||
onIsDdcChanged: initBrightness()
|
||||
Component.onCompleted: initBrightness()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -192,16 +192,23 @@ Singleton {
|
||||
stderr: StdioCollector {}
|
||||
}
|
||||
|
||||
// Read /etc/hostname
|
||||
FileView {
|
||||
id: hostName
|
||||
path: "/etc/hostname"
|
||||
onLoaded: {
|
||||
const name = text().trim();
|
||||
if (name) {
|
||||
root.hostName = name;
|
||||
Logger.i("HostService", "resolved hostname", name);
|
||||
// Resolve hostname from distro-specific locations.
|
||||
// Prefer /etc/hostname, fallback to Gentoo's /etc/conf.d/hostname.
|
||||
Process {
|
||||
id: hostNameProcess
|
||||
command: ["sh", "-c",
|
||||
"if [ -r /etc/hostname ]; then sed -n '1p' /etc/hostname; exit 0; fi; if [ -r /etc/conf.d/hostname ]; then v=$(sed -n -E 's/^[[:space:]]*[Hh][Oo][Ss][Tt][Nn][Aa][Mm][Ee][[:space:]]*=[[:space:]]*//p' /etc/conf.d/hostname | sed -n '1p'); v=$(printf '%s' \"$v\" | sed -E 's/^[[:space:]]+//; s/[[:space:]]+$//; s/^\"//; s/\"$//; s/^\x27//; s/\x27$//'); printf '%s\n' \"$v\"; exit 0; fi; exit 0"]
|
||||
running: true
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
const name = String(text || "").trim();
|
||||
if (name.length > 0) {
|
||||
root.hostName = name;
|
||||
Logger.i("HostService", "resolved hostname", name);
|
||||
}
|
||||
}
|
||||
}
|
||||
stderr: StdioCollector {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -547,7 +547,15 @@ Singleton {
|
||||
|
||||
// Image handling
|
||||
function queueImage(path, appName, summary, notificationId) {
|
||||
if (!path || !path.startsWith("image://") || !notificationId)
|
||||
if (!path || !notificationId)
|
||||
return;
|
||||
|
||||
// Cache image:// URIs and temporary file paths (e.g. /tmp/ from Chromium)
|
||||
const filePath = path.startsWith("file://") ? path.substring(7) : path;
|
||||
const isImageUri = path.startsWith("image://");
|
||||
const isTempFile = (path.startsWith("/") || path.startsWith("file://")) && filePath.startsWith("/tmp/");
|
||||
|
||||
if (!isImageUri && !isTempFile)
|
||||
return;
|
||||
|
||||
ImageCacheService.getNotificationIcon(path, appName, summary, function (cachedPath, success) {
|
||||
|
||||
@@ -13,16 +13,19 @@ Singleton {
|
||||
|
||||
// When the wallpaper changes, regenerate theme if necessary
|
||||
function onWallpaperChanged(screenName, path) {
|
||||
if (!Settings.data.colorSchemes.useWallpaperColors)
|
||||
return;
|
||||
|
||||
var effectiveMonitor = Settings.data.colorSchemes.monitorForColors;
|
||||
if (effectiveMonitor === "" || effectiveMonitor === undefined) {
|
||||
effectiveMonitor = Screen.name;
|
||||
}
|
||||
|
||||
if (screenName === effectiveMonitor) {
|
||||
if (screenName !== effectiveMonitor)
|
||||
return;
|
||||
|
||||
if (Settings.data.colorSchemes.useWallpaperColors) {
|
||||
generateFromWallpaper();
|
||||
} else {
|
||||
// Re-run predefined scheme templates so {{image}} reflects the new wallpaper path
|
||||
ColorSchemeService.applyScheme(Settings.data.colorSchemes.predefinedScheme);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -77,6 +80,11 @@ Singleton {
|
||||
function generateFromPredefinedScheme(schemeData) {
|
||||
Logger.i("AppThemeService", "Generating templates from predefined color scheme");
|
||||
const mode = Settings.data.colorSchemes.darkMode ? "dark" : "light";
|
||||
TemplateProcessor.processPredefinedScheme(schemeData, mode);
|
||||
var effectiveMonitor = Settings.data.colorSchemes.monitorForColors;
|
||||
if (effectiveMonitor === "" || effectiveMonitor === undefined) {
|
||||
effectiveMonitor = Screen.name;
|
||||
}
|
||||
const wallpaperPath = WallpaperService.getWallpaper(effectiveMonitor) || "";
|
||||
TemplateProcessor.processPredefinedScheme(schemeData, mode, wallpaperPath);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,16 +118,17 @@ Singleton {
|
||||
* Uses --scheme flag to expand 14-color scheme to full 48-color palette
|
||||
* Uses debouncing to prevent spawning multiple processes when spamming scheme changes
|
||||
*/
|
||||
function processPredefinedScheme(schemeData, mode) {
|
||||
function processPredefinedScheme(schemeData, mode, wallpaperPath) {
|
||||
pendingPredefinedRequest = {
|
||||
schemeData: schemeData,
|
||||
mode: mode
|
||||
mode: mode,
|
||||
wallpaperPath: wallpaperPath || ""
|
||||
};
|
||||
pendingWallpaperRequest = null;
|
||||
debounceTimer.restart();
|
||||
}
|
||||
|
||||
function executePredefinedScheme(schemeData, mode) {
|
||||
function executePredefinedScheme(schemeData, mode, wallpaperPath) {
|
||||
// 1. Handle terminal themes (runtime generation or pre-rendered file copy)
|
||||
handleTerminalThemes(schemeData, mode);
|
||||
|
||||
@@ -161,10 +162,12 @@ Singleton {
|
||||
// Run Python template processor with --scheme flag
|
||||
// Don't pass --mode so templates get both dark and light colors (e.g., zed.json needs both)
|
||||
// Pass --default-mode so "default" in templates resolves to the current theme mode
|
||||
script += `python3 "${templateProcessorScript}" --scheme '${schemeJsonPathEsc}' --config '${configPathEsc}' --default-mode ${mode}\n`;
|
||||
// Pass wallpaper as positional arg so image_path is available in templates (no extraction occurs when --scheme is used)
|
||||
const wpArg = wallpaperPath ? `'${wallpaperPath.replace(/'/g, "'\\''")}'` : "";
|
||||
script += `python3 "${templateProcessorScript}" ${wpArg} --scheme '${schemeJsonPathEsc}' --config '${configPathEsc}' --default-mode ${mode}\n`;
|
||||
|
||||
// Add user templates if enabled
|
||||
script += buildUserTemplateCommandForPredefined(schemeData, mode);
|
||||
script += buildUserTemplateCommandForPredefined(schemeData, mode, wallpaperPath);
|
||||
|
||||
generateProcess.command = ["sh", "-c", script];
|
||||
generateProcess.running = true;
|
||||
@@ -515,7 +518,7 @@ Singleton {
|
||||
return script;
|
||||
}
|
||||
|
||||
function buildUserTemplateCommandForPredefined(schemeData, mode) {
|
||||
function buildUserTemplateCommandForPredefined(schemeData, mode, wallpaperPath) {
|
||||
if (!Settings.data.templates.enableUserTheming)
|
||||
return "";
|
||||
|
||||
@@ -523,13 +526,15 @@ Singleton {
|
||||
|
||||
// Reuse the scheme JSON already written by processPredefinedScheme()
|
||||
const schemeJsonPathEsc = schemeJsonPath.replace(/'/g, "'\\''");
|
||||
const wpArg = wallpaperPath ? `'${wallpaperPath.replace(/'/g, "'\\''")}'` : "";
|
||||
|
||||
let script = "\n# Execute user templates with predefined scheme colors\n";
|
||||
script += `if [ -f '${userConfigPath}' ]; then\n`;
|
||||
// Use --scheme flag with the already-written scheme JSON
|
||||
// Don't pass --mode so user templates get both dark and light colors
|
||||
// Pass --default-mode so "default" in templates resolves to the current theme mode
|
||||
script += ` python3 "${templateProcessorScript}" --scheme '${schemeJsonPathEsc}' --config '${userConfigPath}' --default-mode ${mode}\n`;
|
||||
// Pass wallpaper as positional arg so image_path is available in templates
|
||||
script += ` python3 "${templateProcessorScript}" ${wpArg} --scheme '${schemeJsonPathEsc}' --config '${userConfigPath}' --default-mode ${mode}\n`;
|
||||
script += "fi";
|
||||
|
||||
return script;
|
||||
@@ -551,7 +556,7 @@ Singleton {
|
||||
} else if (pendingPredefinedRequest) {
|
||||
const req = pendingPredefinedRequest;
|
||||
pendingPredefinedRequest = null;
|
||||
executePredefinedScheme(req.schemeData, req.mode);
|
||||
executePredefinedScheme(req.schemeData, req.mode, req.wallpaperPath);
|
||||
} else {
|
||||
Logger.d("TemplateProcessor", "executePendingRequest: no pending request");
|
||||
}
|
||||
|
||||
@@ -162,8 +162,11 @@ Singleton {
|
||||
return;
|
||||
}
|
||||
|
||||
// File paths are used directly, not cached
|
||||
if (imageUri.startsWith("/") || imageUri.startsWith("file://")) {
|
||||
// Resolve bare file path for temp check
|
||||
const filePath = imageUri.startsWith("file://") ? imageUri.substring(7) : imageUri;
|
||||
|
||||
// File paths in persistent locations are used directly, not cached
|
||||
if ((imageUri.startsWith("/") || imageUri.startsWith("file://")) && !isTemporaryPath(filePath)) {
|
||||
callback(imageUri, false);
|
||||
return;
|
||||
}
|
||||
@@ -171,12 +174,59 @@ Singleton {
|
||||
const cacheKey = generateNotificationKey(imageUri, appName, summary);
|
||||
const cachedPath = notificationsDir + cacheKey + ".png";
|
||||
|
||||
// Temporary file paths are copied to cache before the source is cleaned up
|
||||
if (imageUri.startsWith("/") || imageUri.startsWith("file://")) {
|
||||
processRequest(cacheKey, cachedPath, imageUri, callback, function () {
|
||||
copyTempFileToCache(filePath, cachedPath, cacheKey);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
processRequest(cacheKey, cachedPath, imageUri, callback, function () {
|
||||
// Notifications always use Qt fallback (image:// URIs can't be read by ImageMagick)
|
||||
queueFallbackProcessing(imageUri, cachedPath, cacheKey, 64);
|
||||
});
|
||||
}
|
||||
|
||||
// Check if a path is in a temporary directory that may be cleaned up
|
||||
function isTemporaryPath(path) {
|
||||
return path.startsWith("/tmp/");
|
||||
}
|
||||
|
||||
// Copy a temporary file to the cache directory
|
||||
function copyTempFileToCache(sourcePath, destPath, cacheKey) {
|
||||
const srcEsc = sourcePath.replace(/'/g, "'\\''");
|
||||
const dstEsc = destPath.replace(/'/g, "'\\''");
|
||||
|
||||
const processString = `
|
||||
import QtQuick
|
||||
import Quickshell.Io
|
||||
Process {
|
||||
command: ["cp", "--", "${srcEsc}", "${dstEsc}"]
|
||||
stdout: StdioCollector {}
|
||||
stderr: StdioCollector {}
|
||||
}
|
||||
`;
|
||||
|
||||
queueUtilityProcess({
|
||||
name: "CopyTempFile_" + cacheKey,
|
||||
processString: processString,
|
||||
onComplete: function (exitCode) {
|
||||
if (exitCode === 0) {
|
||||
Logger.d("ImageCache", "Temp file cached:", destPath);
|
||||
notifyCallbacks(cacheKey, destPath, true);
|
||||
} else {
|
||||
Logger.w("ImageCache", "Failed to cache temp file:", sourcePath);
|
||||
notifyCallbacks(cacheKey, "", false);
|
||||
}
|
||||
},
|
||||
onError: function () {
|
||||
Logger.e("ImageCache", "Error caching temp file:", sourcePath);
|
||||
notifyCallbacks(cacheKey, "", false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// -------------------------------------------------
|
||||
// Public API: Get Circular Avatar (256x256)
|
||||
// -------------------------------------------------
|
||||
|
||||
@@ -15,7 +15,7 @@ Item {
|
||||
property color borderColor: "transparent"
|
||||
property int imageFillMode: Image.PreserveAspectCrop
|
||||
|
||||
readonly property bool showFallback: (fallbackIcon !== undefined && fallbackIcon !== "") && (imagePath === undefined || imagePath === "")
|
||||
readonly property bool showFallback: (fallbackIcon !== undefined && fallbackIcon !== "") && (imagePath === undefined || imagePath === "" || imageSource.status === Image.Error)
|
||||
readonly property int status: imageSource.status
|
||||
|
||||
Rectangle {
|
||||
|
||||
Reference in New Issue
Block a user