From 9da310ade49de9a757e9cf095b533a7005dfee12 Mon Sep 17 00:00:00 2001 From: Corey Woodworth Date: Fri, 26 Sep 2025 11:01:38 -0400 Subject: [PATCH 01/42] Rounds the ends of NSliders to be more consistent with the rest of Noctalia's look --- Widgets/NSlider.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Widgets/NSlider.qml b/Widgets/NSlider.qml index 51b2f62f..323c9f43 100644 --- a/Widgets/NSlider.qml +++ b/Widgets/NSlider.qml @@ -25,7 +25,7 @@ Slider { implicitHeight: trackHeight width: root.availableWidth height: implicitHeight - radius: 0 + radius: height / 2 color: Qt.alpha(Color.mSurface, 0.5) border.color: Qt.alpha(Color.mOutline, 0.5) border.width: Math.max(1, Style.borderS * scaling) From d1a89387f9564c089517fd1d697a359707cbe16b Mon Sep 17 00:00:00 2001 From: Corey Woodworth Date: Fri, 26 Sep 2025 13:24:19 -0400 Subject: [PATCH 02/42] Fix: Make sure left side doesn't get squished --- Widgets/NSlider.qml | 85 ++++++++++++++++++++++++++------------------- 1 file changed, 50 insertions(+), 35 deletions(-) diff --git a/Widgets/NSlider.qml b/Widgets/NSlider.qml index 323c9f43..a2721beb 100644 --- a/Widgets/NSlider.qml +++ b/Widgets/NSlider.qml @@ -30,52 +30,67 @@ Slider { border.color: Qt.alpha(Color.mOutline, 0.5) border.width: Math.max(1, Style.borderS * scaling) - // Animated gradient active track - Rectangle { - id: activeTrack + // A container composite shape that puts a semicircle on the end + Item { + id: activeTrackContainer width: root.visualPosition * parent.width height: parent.height - radius: parent.radius + clip: true - // Animated gradient fill - gradient: Gradient { - orientation: Gradient.Horizontal - GradientStop { - position: 0.0 - color: Qt.darker(Color.mPrimary, 1.2) - Behavior on color { - ColorAnimation { - duration: 300 + // The rounded end cap made from a rounded rectangle + Rectangle { + width: parent.height + height: parent.height + radius: width / 2 + color: Qt.darker(Color.mPrimary, 1.2) //starting color of gradient + } + + // The main rectangle + Rectangle { + x: parent.height / 2 + width: parent.width - x // Fills the rest of the container + height: parent.height + radius: 0 + // Animated gradient fill + gradient: Gradient { + orientation: Gradient.Horizontal + GradientStop { + position: 0.0 + color: Qt.darker(Color.mPrimary, 1.2) + Behavior on color { + ColorAnimation { + duration: 300 + } } } - } - GradientStop { - position: 0.5 - color: Color.mPrimary - SequentialAnimation on position { - loops: Animation.Infinite - NumberAnimation { - from: 0.3 - to: 0.7 - duration: 2000 - easing.type: Easing.InOutSine - } - NumberAnimation { - from: 0.7 - to: 0.3 - duration: 2000 - easing.type: Easing.InOutSine + GradientStop { + position: 0.5 + color: Color.mPrimary + SequentialAnimation on position { + loops: Animation.Infinite + NumberAnimation { + from: 0.3 + to: 0.7 + duration: 2000 + easing.type: Easing.InOutSine + } + NumberAnimation { + from: 0.7 + to: 0.3 + duration: 2000 + easing.type: Easing.InOutSine + } } } - } - GradientStop { - position: 1.0 - color: Qt.lighter(Color.mPrimary, 1.2) + GradientStop { + position: 1.0 + color: Qt.lighter(Color.mPrimary, 1.2) + } } } } - // Circular cutout + // Circular cutout Rectangle { id: knobCutout width: knobDiameter + cutoutExtra From 74a0c9dbf471fb5029ff7644a0b9eebcc2cd55fc Mon Sep 17 00:00:00 2001 From: Corey Woodworth Date: Fri, 26 Sep 2025 14:22:22 -0400 Subject: [PATCH 03/42] Fix: Knob was getting clipped. --- Widgets/NSlider.qml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Widgets/NSlider.qml b/Widgets/NSlider.qml index a2721beb..29ea2dbb 100644 --- a/Widgets/NSlider.qml +++ b/Widgets/NSlider.qml @@ -15,6 +15,8 @@ Slider { readonly property real trackHeight: knobDiameter * 0.4 readonly property real cutoutExtra: Math.round(Style.baseWidgetSize * 0.1 * scaling) + padding: cutoutExtra / 2 + snapMode: snapAlways ? Slider.SnapAlways : Slider.SnapOnRelease implicitHeight: Math.max(trackHeight, knobDiameter) @@ -90,14 +92,14 @@ Slider { } } - // Circular cutout + // Circular cutout Rectangle { id: knobCutout width: knobDiameter + cutoutExtra height: knobDiameter + cutoutExtra radius: width / 2 color: root.cutoutColor !== undefined ? root.cutoutColor : Color.mSurface - x: root.leftPadding + root.visualPosition * (root.availableWidth - root.knobDiameter) - cutoutExtra / 2 + x: root.leftPadding + Math.round(root.visualPosition * (root.availableWidth - root.knobDiameter) - cutoutExtra) anchors.verticalCenter: parent.verticalCenter } } From aa1cea8d03ae39ff171db7b7fc295f9920b801ee Mon Sep 17 00:00:00 2001 From: Corey Woodworth Date: Fri, 26 Sep 2025 15:30:16 -0400 Subject: [PATCH 04/42] Fix: Fix the vertical alignment of circles --- Widgets/NSlider.qml | 8 ++++---- Widgets/NToggle.qml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Widgets/NSlider.qml b/Widgets/NSlider.qml index 29ea2dbb..37211022 100644 --- a/Widgets/NSlider.qml +++ b/Widgets/NSlider.qml @@ -105,16 +105,16 @@ Slider { } handle: Item { - width: knob.implicitWidth - height: knob.implicitHeight + width: knobDiameter + height: knobDiameter x: root.leftPadding + Math.round(root.visualPosition * (root.availableWidth - width)) - y: root.topPadding + root.availableHeight / 2 - height / 2 + y: root.topPadding + Math.round(root.availableHeight / 2 - height / 2) Rectangle { id: knob implicitWidth: knobDiameter implicitHeight: knobDiameter - radius: width * 0.5 + radius: width / 2 color: root.pressed ? Color.mTertiary : Color.mSurface border.color: Color.mPrimary border.width: Math.max(1, Style.borderL * scaling) diff --git a/Widgets/NToggle.qml b/Widgets/NToggle.qml index 1381213b..9be41576 100644 --- a/Widgets/NToggle.qml +++ b/Widgets/NToggle.qml @@ -48,7 +48,7 @@ RowLayout { Rectangle { implicitWidth: Math.round((root.baseSize * 0.8) * scaling) - implicitHeight: Math.round((root.baseSize * 0.8) * scaling) + height: parent.height - (2 * scaling * 2) radius: height * 0.5 color: root.checked ? Color.mOnPrimary : Color.mPrimary border.color: root.checked ? Color.mSurface : Color.mSurface From 4785e287baab22e937232627892d51fdf9b46575 Mon Sep 17 00:00:00 2001 From: Corey Woodworth Date: Fri, 26 Sep 2025 15:37:02 -0400 Subject: [PATCH 05/42] Fix: Small fix. 4* instead of 2*2* --- Widgets/NToggle.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Widgets/NToggle.qml b/Widgets/NToggle.qml index 9be41576..9b4afa35 100644 --- a/Widgets/NToggle.qml +++ b/Widgets/NToggle.qml @@ -48,7 +48,7 @@ RowLayout { Rectangle { implicitWidth: Math.round((root.baseSize * 0.8) * scaling) - height: parent.height - (2 * scaling * 2) + height: parent.height - (4 * scaling) radius: height * 0.5 color: root.checked ? Color.mOnPrimary : Color.mPrimary border.color: root.checked ? Color.mSurface : Color.mSurface From 9d30eac13aefe8d813288d16d292343771eca540 Mon Sep 17 00:00:00 2001 From: Corey Woodworth Date: Fri, 26 Sep 2025 16:01:25 -0400 Subject: [PATCH 06/42] Fix: Correct same issue with Radio Buttons too. --- Widgets/NRadioButton.qml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Widgets/NRadioButton.qml b/Widgets/NRadioButton.qml index 9c44f133..7f7e296f 100644 --- a/Widgets/NRadioButton.qml +++ b/Widgets/NRadioButton.qml @@ -19,9 +19,8 @@ RadioButton { anchors.verticalCenter: parent.verticalCenter Rectangle { - anchors.centerIn: parent - implicitWidth: Style.marginS * scaling - implicitHeight: Style.marginS * scaling + anchors.fill: parent + anchors.margins: parent.width * 0.3 radius: width * 0.5 color: Qt.alpha(Color.mPrimary, root.checked ? 1 : 0) From 7b9ecd048dee39d86eca059bab07915527de6c24 Mon Sep 17 00:00:00 2001 From: Kevin Diaz Date: Fri, 26 Sep 2025 19:18:16 -0400 Subject: [PATCH 07/42] CI: add GitHub Actions workflow to automate AUR package updates on release --- .github/workflows/update-aur-package.yml | 108 +++++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 .github/workflows/update-aur-package.yml diff --git a/.github/workflows/update-aur-package.yml b/.github/workflows/update-aur-package.yml new file mode 100644 index 00000000..e221d3b6 --- /dev/null +++ b/.github/workflows/update-aur-package.yml @@ -0,0 +1,108 @@ +name: Update AUR Package + +on: + push: + tags: + - 'v*' + workflow_dispatch: + +jobs: + aur-sync: + name: Sync PKGBUILD with release + runs-on: ubuntu-latest + container: + image: archlinux:latest + defaults: + run: + shell: bash + env: + AUR_REPO: ssh://aur@aur.archlinux.org/noctalia-shell.git + GIT_SSH_COMMAND: ssh -i /root/.ssh/id_aur -o StrictHostKeyChecking=yes -o IdentitiesOnly=yes + PKGNAME: noctalia-shell + AUR_LINK: https://aur.archlinux.org/packages/noctalia-shell + + steps: + - name: Install dependencies + run: | + set -euo pipefail + pacman -Syu --noconfirm git base-devel pacman-contrib openssh + + - name: Create build user + run: | + set -euo pipefail + useradd -m builduser + echo 'builduser ALL=(ALL) NOPASSWD: ALL' >> /etc/sudoers + + - name: Configure SSH + env: + AUR_SSH_PRIVATE_KEY: ${{ secrets.AUR_SSH_PRIVATE_KEY }} + run: | + set -euo pipefail + mkdir -p /root/.ssh + chmod 700 /root/.ssh + printf '%s\n' "$AUR_SSH_PRIVATE_KEY" > /root/.ssh/id_aur + chmod 600 /root/.ssh/id_aur + ssh-keyscan aur.archlinux.org >> /root/.ssh/known_hosts + chmod 600 /root/.ssh/known_hosts + + - name: Determine version + id: vars + env: + TAG_NAME: ${{ github.ref_name }} + run: | + set -euo pipefail + PKGVER="${TAG_NAME#v}" + echo "pkgver=$PKGVER" >> "$GITHUB_OUTPUT" + + - name: Clone AUR repository + run: | + set -euo pipefail + git clone "$AUR_REPO" "$GITHUB_WORKSPACE/aur" + + - name: Update PKGBUILD + env: + PKGVER: ${{ steps.vars.outputs.pkgver }} + run: | + set -euo pipefail + cd "$GITHUB_WORKSPACE/aur" + sed -i "s/^pkgver=.*/pkgver=${PKGVER}/" PKGBUILD + sed -i "s/^pkgrel=.*/pkgrel=1/" PKGBUILD + + - name: Refresh checksums and metadata + run: | + set -euo pipefail + AUR_DIR="$GITHUB_WORKSPACE/aur" + chown -R builduser:builduser "$AUR_DIR" + su - builduser -c "cd $AUR_DIR && updpkgsums" + su - builduser -c "cd $AUR_DIR && makepkg --printsrcinfo > .SRCINFO" + + - name: Commit and push changes + env: + PKGVER: ${{ steps.vars.outputs.pkgver }} + run: | + set -euo pipefail + cd "$GITHUB_WORKSPACE/aur" + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + if [[ -n "$(git status --porcelain)" ]]; then + git add PKGBUILD .SRCINFO + git commit -m "chore(package): release ${PKGVER}" + git push origin HEAD + else + echo "No updates necessary." + fi + + - name: Summarize update + env: + PKGNAME: noctalia-shell + PKGVER: ${{ steps.vars.outputs.pkgver }} + AUR_LINK: https://aur.archlinux.org/packages/noctalia-shell + run: | + set -euo pipefail + { + echo "## AUR Update" + echo "" + echo "- Package: ${PKGNAME}" + echo "- Updated version: ${PKGVER}" + echo "- AUR page: ${AUR_LINK}" + } >> "$GITHUB_STEP_SUMMARY" \ No newline at end of file From b7c99905f37ce4336519ee238a5e42388b93574b Mon Sep 17 00:00:00 2001 From: Kevin Diaz Date: Fri, 26 Sep 2025 20:00:56 -0400 Subject: [PATCH 08/42] fix(ci): not in a git directory --- .github/workflows/update-aur-package.yml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/update-aur-package.yml b/.github/workflows/update-aur-package.yml index e221d3b6..7978e188 100644 --- a/.github/workflows/update-aur-package.yml +++ b/.github/workflows/update-aur-package.yml @@ -62,16 +62,17 @@ jobs: - name: Update PKGBUILD env: PKGVER: ${{ steps.vars.outputs.pkgver }} + working-directory: ${{ github.workspace }}/aur run: | set -euo pipefail - cd "$GITHUB_WORKSPACE/aur" sed -i "s/^pkgver=.*/pkgver=${PKGVER}/" PKGBUILD sed -i "s/^pkgrel=.*/pkgrel=1/" PKGBUILD - name: Refresh checksums and metadata + env: + AUR_DIR: ${{ github.workspace }}/aur run: | set -euo pipefail - AUR_DIR="$GITHUB_WORKSPACE/aur" chown -R builduser:builduser "$AUR_DIR" su - builduser -c "cd $AUR_DIR && updpkgsums" su - builduser -c "cd $AUR_DIR && makepkg --printsrcinfo > .SRCINFO" @@ -79,9 +80,10 @@ jobs: - name: Commit and push changes env: PKGVER: ${{ steps.vars.outputs.pkgver }} + working-directory: ${{ github.workspace }}/aur run: | set -euo pipefail - cd "$GITHUB_WORKSPACE/aur" + git config --global --add safe.directory "$PWD" git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" if [[ -n "$(git status --porcelain)" ]]; then From 96d3051151c7fb17cde87208af97565561654019 Mon Sep 17 00:00:00 2001 From: ItsLemmy Date: Fri, 26 Sep 2025 23:18:35 -0400 Subject: [PATCH 09/42] Update service --- Services/UpdateService.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Services/UpdateService.qml b/Services/UpdateService.qml index 6918614e..977cc142 100644 --- a/Services/UpdateService.qml +++ b/Services/UpdateService.qml @@ -8,7 +8,7 @@ Singleton { id: root // Public properties - property string baseVersion: "2.14.1" + property string baseVersion: "2.14.2" property bool isDevelopment: true property string currentVersion: `v${!isDevelopment ? baseVersion : baseVersion + "-dev"}` From f8ee0bb8dfe392c189736d6c831fa6d7e6f4b09a Mon Sep 17 00:00:00 2001 From: ItsLemmy Date: Fri, 26 Sep 2025 23:18:59 -0400 Subject: [PATCH 10/42] FilePicker: debugging and improvements. --- Commons/TablerIcons.qml | 3 +- .../WidgetSettings/ControlCenterSettings.qml | 3 +- Modules/Settings/Tabs/GeneralTab.qml | 3 +- Modules/Settings/Tabs/ScreenRecorderTab.qml | 3 +- Modules/Settings/Tabs/WallpaperTab.qml | 6 +- Widgets/NFilePicker.qml | 120 +++++++++++------- 6 files changed, 83 insertions(+), 55 deletions(-) diff --git a/Commons/TablerIcons.qml b/Commons/TablerIcons.qml index 2044503a..13b60e33 100644 --- a/Commons/TablerIcons.qml +++ b/Commons/TablerIcons.qml @@ -148,7 +148,8 @@ Singleton { "filepicker-file": "file", "filepicker-text": "file-text", "filepicker-eye": "eye", - "filepicker-eye-off": "eye-off" + "filepicker-eye-off": "eye-off", + "filepicker-folder-current": "checks" } // Fonts Codepoints - do not change! diff --git a/Modules/Settings/Bar/WidgetSettings/ControlCenterSettings.qml b/Modules/Settings/Bar/WidgetSettings/ControlCenterSettings.qml index 75aacb97..78b54210 100644 --- a/Modules/Settings/Bar/WidgetSettings/ControlCenterSettings.qml +++ b/Modules/Settings/Bar/WidgetSettings/ControlCenterSettings.qml @@ -90,8 +90,7 @@ ColumnLayout { NFilePicker { id: imagePicker title: I18n.tr("bar.widget-settings.control-center.select-custom-icon") - selectFiles: true - selectFolders: false + selectionMode: "files" nameFilters: ["*.jpg", "*.jpeg", "*.png", "*.gif", "*.pnm", "*.bmp"] initialPath: Quickshell.env("HOME") onAccepted: paths => { diff --git a/Modules/Settings/Tabs/GeneralTab.qml b/Modules/Settings/Tabs/GeneralTab.qml index c2e08693..a4600281 100644 --- a/Modules/Settings/Tabs/GeneralTab.qml +++ b/Modules/Settings/Tabs/GeneralTab.qml @@ -49,8 +49,7 @@ ColumnLayout { NFilePicker { id: avatarPicker title: I18n.tr("settings.general.profile.select-avatar") - selectFiles: true - selectFolders: true + selectionMode: "files" initialPath: Settings.data.general.avatarImage.substr(0, Settings.data.general.avatarImage.lastIndexOf("/")) || Quickshell.env("HOME") nameFilters: ["*.jpg", "*.jpeg", "*.png", "*.gif", "*.pnm", "*.bmp"] onAccepted: paths => { diff --git a/Modules/Settings/Tabs/ScreenRecorderTab.qml b/Modules/Settings/Tabs/ScreenRecorderTab.qml index cdd2be99..957579bf 100644 --- a/Modules/Settings/Tabs/ScreenRecorderTab.qml +++ b/Modules/Settings/Tabs/ScreenRecorderTab.qml @@ -229,8 +229,7 @@ ColumnLayout { NFilePicker { id: folderPicker - selectFiles: false - selectFolders: true + selectionMode: "folders" title: I18n.tr("settings.screen-recorder.general.select-output-folder") initialPath: Settings.data.screenRecorder.directory || Quickshell.env("HOME") + "/Videos" onAccepted: paths => { diff --git a/Modules/Settings/Tabs/WallpaperTab.qml b/Modules/Settings/Tabs/WallpaperTab.qml index fa22c89a..742849b9 100644 --- a/Modules/Settings/Tabs/WallpaperTab.qml +++ b/Modules/Settings/Tabs/WallpaperTab.qml @@ -340,8 +340,7 @@ ColumnLayout { NFilePicker { id: mainFolderPicker - selectFiles: false - selectFolders: true + selectionMode: "folders" title: I18n.tr("settings.wallpaper.settings.select-folder") initialPath: Settings.data.wallpaper.directory || Quickshell.env("HOME") + "/Pictures" onAccepted: paths => { @@ -353,8 +352,7 @@ ColumnLayout { NFilePicker { id: monitorFolderPicker - selectFiles: false - selectFolders: true + selectionMode: "folders" title: I18n.tr("settings.wallpaper.settings.select-monitor-folder") initialPath: WallpaperService.getMonitorDirectory(specificFolderMonitorName) || Quickshell.env("HOME") + "/Pictures" onAccepted: paths => { diff --git a/Widgets/NFilePicker.qml b/Widgets/NFilePicker.qml index 87b0efb1..d2e2f766 100644 --- a/Widgets/NFilePicker.qml +++ b/Widgets/NFilePicker.qml @@ -15,8 +15,7 @@ Popup { // Properties property string title: "File Picker" property string initialPath: Quickshell.env("HOME") || "/home" - property bool selectFiles: true - property bool selectFolders: true + property string selectionMode: "files" // "files" or "folders" property var nameFilters: ["*"] property bool showDirs: true property bool showHiddenFiles: false @@ -117,8 +116,16 @@ Popup { const fileIsDir = folderModel.get(i, "fileIsDir") const fileSize = folderModel.get(i, "fileSize") - if (root.selectFolders && !root.selectFiles && !fileIsDir) + // Skip hidden items if showHiddenFiles is false + // This additional check ensures hidden files are properly filtered + if (!root.showHiddenFiles && fileName.startsWith(".")) { continue + } + + // In folder mode, hide files + if (root.selectionMode === "folders" && !fileIsDir) + continue + if (searchText === "" || fileName.toLowerCase().includes(searchText)) { filteredModel.append({ "fileName": fileName, @@ -193,10 +200,28 @@ Popup { color: Color.mPrimary Layout.fillWidth: true } + + // "Select Current" button only visible in folder selection mode + NButton { + text: "Select Current" + icon: "filepicker-folder-current" + visible: root.selectionMode === "folders" + onClicked: { + filePickerPanel.currentSelection = [root.currentPath] + root.confirmSelection() + } + } + NIconButton { icon: "filepicker-refresh" tooltipText: "Refresh" - onClicked: folderModel.refresh() + onClicked: { + // Force a proper refresh by resetting the folder + const currentFolder = folderModel.folder + folderModel.folder = "" + folderModel.folder = currentFolder + Qt.callLater(root.updateFilteredModel) + } } NIconButton { icon: "filepicker-close" @@ -301,7 +326,11 @@ Popup { baseSize: Style.baseWidgetSize * 0.8 onClicked: { root.showHiddenFiles = !root.showHiddenFiles - root.updateFilteredModel() + // Force model refresh by resetting the folder + const currentFolder = folderModel.folder + folderModel.folder = "" + folderModel.folder = currentFolder + Qt.callLater(root.updateFilteredModel) } } } @@ -375,15 +404,20 @@ Popup { FolderListModel { id: folderModel folder: "file://" + root.currentPath - nameFilters: root.nameFilters + // Use wildcard filters including hidden files when showHiddenFiles is true + nameFilters: root.showHiddenFiles ? ["*", ".*"] : root.nameFilters showDirs: root.showDirs - showHidden: root.showHiddenFiles + showHidden: true // Always true, we'll filter in updateFilteredModel + showDotAndDotDot: false sortField: FolderListModel.Name sortReversed: false + onFolderChanged: { root.currentPath = folder.toString().replace("file://", "") filePickerPanel.currentSelection = [] + Qt.callLater(root.updateFilteredModel) } + onStatusChanged: { if (status === FolderListModel.Error) { if (root.currentPath !== Quickshell.env("HOME")) { @@ -396,6 +430,14 @@ Popup { } } + // Update nameFilters when showHiddenFiles changes + Connections { + target: root + function onShowHiddenFilesChanged() { + folderModel.nameFilters = root.showHiddenFiles ? ["*", ".*"] : root.nameFilters + } + } + ListModel { id: filteredModel } @@ -618,15 +660,16 @@ Popup { onClicked: mouse => { if (mouse.button === Qt.LeftButton) { if (model.fileIsDir) { - if (root.selectFolders && !root.selectFiles) { + // In folder mode, single click selects the folder + if (root.selectionMode === "folders") { filePickerPanel.currentSelection = [model.filePath] - } else { - folderModel.folder = "file://" + model.filePath - root.currentPath = model.filePath } + // In file mode, single click on folder does nothing (must double-click to enter) } else { - if (root.selectFiles) - filePickerPanel.currentSelection = [model.filePath] + // Single click on file selects it (only in file mode) + if (root.selectionMode === "files") { + filePickerPanel.currentSelection = [model.filePath] + } } } } @@ -634,15 +677,12 @@ Popup { onDoubleClicked: mouse => { if (mouse.button === Qt.LeftButton) { if (model.fileIsDir) { - if (root.selectFolders && !root.selectFiles) { - filePickerPanel.currentSelection = [model.filePath] - root.confirmSelection() - } else { - folderModel.folder = "file://" + model.filePath - root.currentPath = model.filePath - } + // Double-click on folder always navigates into it + folderModel.folder = "file://" + model.filePath + root.currentPath = model.filePath } else { - if (root.selectFiles) { + // Double-click on file selects and confirms (only in file mode) + if (root.selectionMode === "files") { filePickerPanel.currentSelection = [model.filePath] root.confirmSelection() } @@ -719,15 +759,16 @@ Popup { onClicked: mouse => { if (mouse.button === Qt.LeftButton) { if (model.fileIsDir) { - if (root.selectFolders && !root.selectFiles) { + // In folder mode, single click selects the folder + if (root.selectionMode === "folders") { filePickerPanel.currentSelection = [model.filePath] - } else { - folderModel.folder = "file://" + model.filePath - root.currentPath = model.filePath } + // In file mode, single click on folder does nothing (must double-click to enter) } else { - if (root.selectFiles) - filePickerPanel.currentSelection = [model.filePath] + // Single click on file selects it (only in file mode) + if (root.selectionMode === "files") { + filePickerPanel.currentSelection = [model.filePath] + } } } } @@ -735,15 +776,12 @@ Popup { onDoubleClicked: mouse => { if (mouse.button === Qt.LeftButton) { if (model.fileIsDir) { - if (root.selectFolders && !root.selectFiles) { - filePickerPanel.currentSelection = [model.filePath] - root.confirmSelection() - } else { - folderModel.folder = "file://" + model.filePath - root.currentPath = model.filePath - } + // Double-click on folder always navigates into it + folderModel.folder = "file://" + model.filePath + root.currentPath = model.filePath } else { - if (root.selectFiles) { + // Double-click on file selects and confirms (only in file mode) + if (root.selectionMode === "files") { filePickerPanel.currentSelection = [model.filePath] root.confirmSelection() } @@ -765,7 +803,8 @@ Popup { if (filePickerPanel.searchText.length > 0) { return "Searching for: \"" + filePickerPanel.searchText + "\" (" + filteredModel.count + " matches)" } else if (filePickerPanel.currentSelection.length > 0) { - return filePickerPanel.currentSelection.length + " item(s) selected" + const selectedName = filePickerPanel.currentSelection[0].split('/').pop() + return "Selected: " + selectedName } else { return filteredModel.count + " items" } @@ -785,14 +824,7 @@ Popup { } NButton { - text: { - if (root.selectFolders && !root.selectFiles) - return "Select Folder" - else if (root.selectFiles && !root.selectFolders) - return "Select File" - else - return "Select" - } + text: root.selectionMode === "folders" ? "Select Folder" : "Select File" icon: "filepicker-check" enabled: filePickerPanel.currentSelection.length > 0 onClicked: root.confirmSelection() From e2f7012c5bf966fe72e28a4c676c6d175bb2e1e0 Mon Sep 17 00:00:00 2001 From: ItsLemmy Date: Fri, 26 Sep 2025 23:35:05 -0400 Subject: [PATCH 11/42] NScrollView: properly disable horizontal scrrol when setting proper horizontalPolicy --- Widgets/NScrollView.qml | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/Widgets/NScrollView.qml b/Widgets/NScrollView.qml index 1edca1c1..63e28819 100644 --- a/Widgets/NScrollView.qml +++ b/Widgets/NScrollView.qml @@ -14,10 +14,44 @@ T.ScrollView { property real handleRadius: Style.radiusM * scaling property int verticalPolicy: ScrollBar.AsNeeded property int horizontalPolicy: ScrollBar.AsNeeded + property bool preventHorizontalScroll: horizontalPolicy === ScrollBar.AlwaysOff + property int boundsBehavior: Flickable.StopAtBounds + property int flickableDirection: Flickable.VerticalFlick implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset, contentWidth + leftPadding + rightPadding) implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset, contentHeight + topPadding + bottomPadding) + // Configure the internal flickable when it becomes available + Component.onCompleted: { + configureFlickable() + } + + // Function to configure the underlying Flickable + function configureFlickable() { + // Find the internal Flickable (it's usually the first child) + for (var i = 0; i < children.length; i++) { + var child = children[i] + if (child.toString().indexOf("Flickable") !== -1) { + // Configure the flickable to prevent horizontal scrolling + child.boundsBehavior = root.boundsBehavior + + if (root.preventHorizontalScroll) { + child.flickableDirection = Flickable.VerticalFlick + child.contentWidth = Qt.binding(() => child.width) + } else { + child.flickableDirection = root.flickableDirection + } + break + } + } + } + + // Watch for changes in horizontalPolicy + onHorizontalPolicyChanged: { + preventHorizontalScroll = (horizontalPolicy === ScrollBar.AlwaysOff) + configureFlickable() + } + ScrollBar.vertical: ScrollBar { parent: root x: root.mirrored ? 0 : root.width - width From 83d82a825bea7b60666ac986e06b664a58621d57 Mon Sep 17 00:00:00 2001 From: ItsLemmy Date: Fri, 26 Sep 2025 23:46:25 -0400 Subject: [PATCH 12/42] v2.14.3 --- Services/UpdateService.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Services/UpdateService.qml b/Services/UpdateService.qml index 977cc142..d23879dd 100644 --- a/Services/UpdateService.qml +++ b/Services/UpdateService.qml @@ -8,8 +8,8 @@ Singleton { id: root // Public properties - property string baseVersion: "2.14.2" - property bool isDevelopment: true + property string baseVersion: "2.14.3" + property bool isDevelopment: false property string currentVersion: `v${!isDevelopment ? baseVersion : baseVersion + "-dev"}` From dd29a739f3e6128bfcbe311a21e1e3cd1c07b1d2 Mon Sep 17 00:00:00 2001 From: ItsLemmy Date: Fri, 26 Sep 2025 23:48:03 -0400 Subject: [PATCH 13/42] v2.14.3-dev --- Services/UpdateService.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Services/UpdateService.qml b/Services/UpdateService.qml index d23879dd..7782e03b 100644 --- a/Services/UpdateService.qml +++ b/Services/UpdateService.qml @@ -9,7 +9,7 @@ Singleton { // Public properties property string baseVersion: "2.14.3" - property bool isDevelopment: false + property bool isDevelopment: true property string currentVersion: `v${!isDevelopment ? baseVersion : baseVersion + "-dev"}` From 13e32dc11b4cf371afcada11cba421f2f38bc095 Mon Sep 17 00:00:00 2001 From: ItsLemmy Date: Sat, 27 Sep 2025 08:58:48 -0400 Subject: [PATCH 14/42] Notifications test with a lot of actions --- Bin/notifications-test.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Bin/notifications-test.sh b/Bin/notifications-test.sh index 32ecd9d2..789b036b 100755 --- a/Bin/notifications-test.sh +++ b/Bin/notifications-test.sh @@ -41,6 +41,6 @@ gdbus call --session \ "dialog-question" \ "Confirmation Required" \ "Do you want to proceed with the action?" \ - "['default', 'OK', 'cancel', 'Cancel']" \ + "['default', 'OK', 'cancel', 'Cancel', 'maybe', 'Maybe', 'undecided', 'Undecided']" \ "{}" \ - 5000 \ No newline at end of file + 5000 From fe2654268dd27c59b6124bd5e02c004ba4e9e81c Mon Sep 17 00:00:00 2001 From: Ly-sec Date: Sat, 27 Sep 2025 15:14:44 +0200 Subject: [PATCH 15/42] NightLight: check if wlsunset exists, else dont enable NightLight SystemMonitorSettings: If RAM usage is not toggled, don't show % option Settings: remove NightLight from default bar widgets --- Assets/settings-default.json | 5 +---- Commons/Settings.qml | 6 ++---- Modules/Bar/Widgets/NightLight.qml | 6 ++++++ .../Settings/Bar/WidgetSettings/SystemMonitorSettings.qml | 1 + Services/ProgramCheckerService.qml | 4 +++- 5 files changed, 13 insertions(+), 9 deletions(-) diff --git a/Assets/settings-default.json b/Assets/settings-default.json index 55a851c8..7ea8b3f7 100644 --- a/Assets/settings-default.json +++ b/Assets/settings-default.json @@ -1,5 +1,5 @@ { - "settingsVersion": 8, + "settingsVersion": 9, "bar": { "position": "top", "backgroundOpacity": 1, @@ -51,9 +51,6 @@ { "id": "Brightness" }, - { - "id": "NightLight" - }, { "id": "Clock" }, diff --git a/Commons/Settings.qml b/Commons/Settings.qml index 819f11be..77623d7b 100644 --- a/Commons/Settings.qml +++ b/Commons/Settings.qml @@ -114,7 +114,7 @@ Singleton { JsonAdapter { id: adapter - property int settingsVersion: 8 + property int settingsVersion: 9 // bar property JsonObject bar: JsonObject { @@ -156,10 +156,8 @@ Singleton { "id": "Battery" }, { "id": "Volume" - }, { + }, { "id": "Brightness" - }, { - "id": "NightLight" }, { "id": "Clock" }, { diff --git a/Modules/Bar/Widgets/NightLight.qml b/Modules/Bar/Widgets/NightLight.qml index 3be8cf53..605f572a 100644 --- a/Modules/Bar/Widgets/NightLight.qml +++ b/Modules/Bar/Widgets/NightLight.qml @@ -24,6 +24,12 @@ NIconButton { icon: Settings.data.nightLight.enabled ? (Settings.data.nightLight.forced ? "nightlight-forced" : "nightlight-on") : "nightlight-off" tooltipText: Settings.data.nightLight.enabled ? (Settings.data.nightLight.forced ? I18n.tr("tooltips.night-light-forced") : I18n.tr("tooltips.night-light-enabled")) : I18n.tr("tooltips.night-light-disabled") onClicked: { + // Check if wlsunset is available before enabling night light + if (!ProgramCheckerService.wlsunsetAvailable) { + ToastService.showWarning(I18n.tr("settings.display.night-light.section.label"), I18n.tr("toast.night-light.not-installed")) + return + } + if (!Settings.data.nightLight.enabled) { Settings.data.nightLight.enabled = true Settings.data.nightLight.forced = false diff --git a/Modules/Settings/Bar/WidgetSettings/SystemMonitorSettings.qml b/Modules/Settings/Bar/WidgetSettings/SystemMonitorSettings.qml index b7524c86..06ba806d 100644 --- a/Modules/Settings/Bar/WidgetSettings/SystemMonitorSettings.qml +++ b/Modules/Settings/Bar/WidgetSettings/SystemMonitorSettings.qml @@ -62,6 +62,7 @@ ColumnLayout { label: I18n.tr("bar.widget-settings.system-monitor.memory-percentage") checked: valueShowMemoryAsPercent onToggled: checked => valueShowMemoryAsPercent = checked + visible: valueShowMemoryUsage } NToggle { diff --git a/Services/ProgramCheckerService.qml b/Services/ProgramCheckerService.qml index f8e4fa98..a7d5f436 100644 --- a/Services/ProgramCheckerService.qml +++ b/Services/ProgramCheckerService.qml @@ -18,6 +18,7 @@ Singleton { property bool fuzzelAvailable: false property bool vesktopAvailable: false property bool gpuScreenRecorderAvailable: false + property bool wlsunsetAvailable: false // Signal emitted when all checks are complete signal checksCompleted @@ -31,7 +32,8 @@ Singleton { "footAvailable": ["which", "foot"], "fuzzelAvailable": ["which", "fuzzel"], "vesktopAvailable": ["which", "vesktop"], - "gpuScreenRecorderAvailable": ["sh", "-c", "command -v gpu-screen-recorder >/dev/null 2>&1 || (command -v flatpak >/dev/null 2>&1 && flatpak list --app | grep -q 'com.dec05eba.gpu_screen_recorder')"] + "gpuScreenRecorderAvailable": ["sh", "-c", "command -v gpu-screen-recorder >/dev/null 2>&1 || (command -v flatpak >/dev/null 2>&1 && flatpak list --app | grep -q 'com.dec05eba.gpu_screen_recorder')"], + "wlsunsetAvailable": ["which", "wlsunset"] }) // Internal tracking From 65cd95c62b2300810cb1780965b6e76046db3eb9 Mon Sep 17 00:00:00 2001 From: ItsLemmy Date: Sat, 27 Sep 2025 09:17:23 -0400 Subject: [PATCH 16/42] Notifications: properly handle large/many action buttons. Fix #379 --- Modules/Notification/Notification.qml | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/Modules/Notification/Notification.qml b/Modules/Notification/Notification.qml index 3cf73d3f..b7f48abe 100644 --- a/Modules/Notification/Notification.qml +++ b/Modules/Notification/Notification.qml @@ -341,11 +341,14 @@ Variants { } // Notification actions - RowLayout { + Flow { Layout.fillWidth: true spacing: Style.marginS * scaling Layout.topMargin: Style.marginM * scaling + flow: Flow.LeftToRight + layoutDirection: Qt.LeftToRight + // Store the notification ID for access in button delegates property string parentNotificationId: notificationId @@ -378,17 +381,12 @@ Variants { textColor: hovered ? Color.mOnTertiary : Color.mOnPrimary hoverColor: Color.mTertiary outlined: false - Layout.preferredHeight: 24 * scaling + implicitHeight: 24 * scaling onClicked: { NotificationService.invokeAction(parent.parentNotificationId, actionData.identifier) } } } - - // Spacer to push buttons to the left - Item { - Layout.fillWidth: true - } } } } From d802b6a2fa3b350fdff86d2b638b6455218ea32f Mon Sep 17 00:00:00 2001 From: Matan Bendix Shenhav Date: Sat, 27 Sep 2025 16:24:56 +0200 Subject: [PATCH 17/42] feat(flake): deep merge settings with defaults --- flake.nix | 34 ++++++++++++++++++++++------------ 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/flake.nix b/flake.nix index 490e35cc..9b869208 100644 --- a/flake.nix +++ b/flake.nix @@ -110,6 +110,16 @@ }: let cfg = config.programs.noctalia-shell; + defaultSettings = builtins.fromJSON (builtins.readFile ./Assets/settings-default.json); + + # Deep merge user settings with defaults + mergedSettings = + if cfg.settings == null then + defaultSettings + else if builtins.isAttrs cfg.settings then + lib.recursiveUpdate defaultSettings cfg.settings + else + cfg.settings; # Pass through strings/paths as-is in { options.programs.noctalia-shell = { @@ -144,6 +154,8 @@ description = '' Noctalia shell configuration settings as an attribute set, string or filepath, to be written to ~/.config/noctalia/settings.json. + When provided as an attribute set, it will be deep-merged with + the default settings. ''; }; @@ -189,18 +201,16 @@ in lib.mkIf cfg.enable { xdg.configFile = { - "noctalia/settings.json" = lib.mkIf (cfg.settings != null) ( - { - onChange = restart; - } - // ( - if builtins.isAttrs cfg.settings then - { text = builtins.toJSON cfg.settings; } - else if builtins.isString cfg.settings then - { text = cfg.settings; } - else - { source = cfg.settings; } - ) + "noctalia/settings.json" = { + onChange = restart; + } + // ( + if builtins.isAttrs mergedSettings then + { text = builtins.toJSON mergedSettings + "\n"; } + else if builtins.isString mergedSettings then + { text = mergedSettings; } + else + { source = mergedSettings; } ); "noctalia/colors.json" = lib.mkIf (cfg.colors != null) ( { From afce091473f35d37206bc97ce9e2bc57c46bfbc3 Mon Sep 17 00:00:00 2001 From: ItsLemmy Date: Sat, 27 Sep 2025 11:03:52 -0400 Subject: [PATCH 18/42] Bluetooth: simplify the way we handle adapter state vs settings value. --- Services/BluetoothService.qml | 58 +++++++++++++++++------------------ 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/Services/BluetoothService.qml b/Services/BluetoothService.qml index 43ac4dd8..ccfc0439 100644 --- a/Services/BluetoothService.qml +++ b/Services/BluetoothService.qml @@ -10,6 +10,7 @@ Singleton { readonly property BluetoothAdapter adapter: Bluetooth.defaultAdapter readonly property bool available: (adapter !== null) + readonly property bool enabled: adapter?.enabled ?? false readonly property bool discovering: (adapter && adapter.discovering) ?? false readonly property var devices: adapter ? adapter.devices : null readonly property var pairedDevices: { @@ -30,18 +31,28 @@ Singleton { } property bool lastAdapterState: false + property bool restoringState: false function init() { Logger.log("Bluetooth", "Service initialized") - syncStateTimer.running = true + // Try to restore saved state if needed + if (Settings.data.network.bluetoothEnabled !== undefined && adapter) { + restoringState = true + adapter.enabled = Settings.data.network.bluetoothEnabled + restoringState = false + } + + // Need to delay a bit for the adapter to be ready + initTimer.running = true } Timer { - id: syncStateTimer + id: initTimer interval: 1000 repeat: false onTriggered: { - lastAdapterState = Settings.data.network.bluetoothEnabled = adapter.enabled + lastAdapterState = adapter?.enabled ?? false + Logger.log("Bluetooth", "LastAdapterState:", lastAdapterState) } } @@ -52,27 +63,6 @@ Singleton { onTriggered: adapter.discovering = true } - Timer { - id: stateDebounceTimer - interval: 200 - repeat: false - onTriggered: { - if (!adapter) { - Logger.warn("Bluetooth", "State debouncer", "No adapter available") - return - } - if (lastAdapterState === adapter.enabled) { - return - } - lastAdapterState = adapter.enabled - if (adapter.enabled) { - ToastService.showNotice(I18n.tr("bluetooth.panel.title"), I18n.tr("toast.bluetooth.enabled")) - } else { - ToastService.showNotice(I18n.tr("bluetooth.panel.title"), I18n.tr("toast.bluetooth.disabled")) - } - } - } - Connections { target: adapter function onEnabledChanged() { @@ -82,11 +72,21 @@ Singleton { } Logger.log("Bluetooth", "onEnableChanged", adapter.enabled) - Settings.data.network.bluetoothEnabled = adapter.enabled - stateDebounceTimer.restart() - if (adapter.enabled) { - // Using a timer to give a little time so the adapter is really enabled - discoveryTimer.running = true + + // Only save to settings if this is a user-initiated change + if (!restoringState) { + Settings.data.network.bluetoothEnabled = adapter.enabled + } + + // Show toast only for actual state changes + if (lastAdapterState !== adapter.enabled) { + lastAdapterState = adapter.enabled + if (adapter.enabled) { + ToastService.showNotice(I18n.tr("bluetooth.panel.title"), I18n.tr("toast.bluetooth.enabled")) + discoveryTimer.running = true + } else { + ToastService.showNotice(I18n.tr("bluetooth.panel.title"), I18n.tr("toast.bluetooth.disabled")) + } } } } From 35bf30ef5ed93800c5b39a7dc1a4a32c9339d83d Mon Sep 17 00:00:00 2001 From: Ly-sec Date: Sat, 27 Sep 2025 17:16:00 +0200 Subject: [PATCH 19/42] ColorSchemeTab: add matugen type option --- Assets/Translations/de.json | 4 +++ Assets/Translations/en.json | 4 +++ Assets/Translations/es.json | 4 +++ Assets/Translations/fr.json | 4 +++ Assets/Translations/pt.json | 4 +++ Assets/Translations/zh.json | 4 +++ Commons/Settings.qml | 3 +- Modules/Settings/Tabs/ColorSchemeTab.qml | 43 ++++++++++++++++++++++++ Services/MatugenService.qml | 6 ++-- 9 files changed, 72 insertions(+), 4 deletions(-) diff --git a/Assets/Translations/de.json b/Assets/Translations/de.json index d3678692..a07cc9e8 100644 --- a/Assets/Translations/de.json +++ b/Assets/Translations/de.json @@ -478,6 +478,10 @@ "enable-matugen": { "label": "Matugen aktivieren", "description": "Automatisch Farben basierend auf Ihrem aktiven Hintergrundbild generieren." + }, + "matugen-scheme-type": { + "label": "Matugen-Schema-Typ", + "description": "Wähle einen Farbstil für Matugen aus." } }, "predefined": { diff --git a/Assets/Translations/en.json b/Assets/Translations/en.json index 18bb5ce1..4a903bc2 100644 --- a/Assets/Translations/en.json +++ b/Assets/Translations/en.json @@ -478,6 +478,10 @@ "enable-matugen": { "label": "Enable Matugen", "description": "Automatically generate colors based on your active wallpaper." + }, + "matugen-scheme-type": { + "label": "Matugen scheme type", + "description": "Choose the color scheme generation algorithm for Matugen." } }, "predefined": { diff --git a/Assets/Translations/es.json b/Assets/Translations/es.json index 574484ba..0c23929f 100644 --- a/Assets/Translations/es.json +++ b/Assets/Translations/es.json @@ -476,6 +476,10 @@ "enable-matugen": { "label": "Activar Matugen", "description": "Genera colores automáticamente basados en tu fondo de pantalla activo." + }, + "matugen-scheme-type": { + "label": "Tipo de esquema Matugen", + "description": "Elige el algoritmo de generación de esquema de colores para Matugen." } }, "predefined": { diff --git a/Assets/Translations/fr.json b/Assets/Translations/fr.json index 5c302a1d..5a77f950 100644 --- a/Assets/Translations/fr.json +++ b/Assets/Translations/fr.json @@ -476,6 +476,10 @@ "enable-matugen": { "label": "Activer Matugen", "description": "Générez automatiquement des couleurs en fonction de votre fond d'écran actif." + }, + "matugen-scheme-type": { + "label": "Type de schéma Matugen", + "description": "Choisissez l'algorithme de génération de schéma de couleurs pour Matugen." } }, "predefined": { diff --git a/Assets/Translations/pt.json b/Assets/Translations/pt.json index 93e83768..cc913f62 100644 --- a/Assets/Translations/pt.json +++ b/Assets/Translations/pt.json @@ -442,6 +442,10 @@ "enable-matugen": { "label": "Ativar Matugen", "description": "Gera cores automaticamente com base no seu papel de parede ativo." + }, + "matugen-scheme-type": { + "label": "Tipo de esquema Matugen", + "description": "Escolha o algoritmo de geração de esquema de cores para Matugen." } }, "predefined": { diff --git a/Assets/Translations/zh.json b/Assets/Translations/zh.json index 7ab9b8e7..f4d3dd0a 100644 --- a/Assets/Translations/zh.json +++ b/Assets/Translations/zh.json @@ -476,6 +476,10 @@ "enable-matugen": { "label": "启用 Matugen", "description": "根据您的活动壁纸自动生成颜色。" + }, + "matugen-scheme-type": { + "label": "Matugen 配色方案类型", + "description": "为 Matugen 选择配色方案生成算法。" } }, "predefined": { diff --git a/Commons/Settings.qml b/Commons/Settings.qml index 77623d7b..9ccf3c9f 100644 --- a/Commons/Settings.qml +++ b/Commons/Settings.qml @@ -156,7 +156,7 @@ Singleton { "id": "Battery" }, { "id": "Volume" - }, { + }, { "id": "Brightness" }, { "id": "Clock" @@ -291,6 +291,7 @@ Singleton { property bool useWallpaperColors: false property string predefinedScheme: "Noctalia (default)" property bool darkMode: true + property string matugenSchemeType: "scheme-fruit-salad" } // matugen templates toggles diff --git a/Modules/Settings/Tabs/ColorSchemeTab.qml b/Modules/Settings/Tabs/ColorSchemeTab.qml index 443d01be..1407235e 100644 --- a/Modules/Settings/Tabs/ColorSchemeTab.qml +++ b/Modules/Settings/Tabs/ColorSchemeTab.qml @@ -140,6 +140,49 @@ ColumnLayout { } } + // Matugen Scheme Type Selection + NComboBox { + label: I18n.tr("settings.color-scheme.color-source.matugen-scheme-type.label") + description: I18n.tr("settings.color-scheme.color-source.matugen-scheme-type.description") + enabled: Settings.data.colorSchemes.useWallpaperColors + opacity: Settings.data.colorSchemes.useWallpaperColors ? 1.0 : 0.6 + + model: [{ + "key": "scheme-content", + "name": "Content" + }, { + "key": "scheme-expressive", + "name": "Expressive" + }, { + "key": "scheme-fidelity", + "name": "Fidelity" + }, { + "key": "scheme-fruit-salad", + "name": "Fruit Salad" + }, { + "key": "scheme-monochrome", + "name": "Monochrome" + }, { + "key": "scheme-neutral", + "name": "Neutral" + }, { + "key": "scheme-rainbow", + "name": "Rainbow" + }, { + "key": "scheme-tonal-spot", + "name": "Tonal Spot" + }] + + currentKey: Settings.data.colorSchemes.matugenSchemeType + + onSelected: key => { + Settings.data.colorSchemes.matugenSchemeType = key + if (Settings.data.colorSchemes.useWallpaperColors) { + MatugenService.generateFromWallpaper() + } + } + } + NDivider { Layout.fillWidth: true Layout.topMargin: Style.marginXL * scaling diff --git a/Services/MatugenService.qml b/Services/MatugenService.qml index c2ebcd53..e3410d57 100644 --- a/Services/MatugenService.qml +++ b/Services/MatugenService.qml @@ -66,14 +66,14 @@ Singleton { var extraUser = (Settings.configDir + "matugen.d").replace(/'/g, "'\\''") // Build the main script - var script = "cat > '" + pathEsc + "' << 'EOF'\n" + content + "EOF\n" + "for d in '" + extraRepo + "' '" + extraUser + "'; do\n" + " if [ -d \"$d\" ]; then\n" + " for f in \"$d\"/*.toml; do\n" + " [ -f \"$f\" ] && { echo; echo \"# extra: $f\"; cat \"$f\"; } >> '" + pathEsc + "'\n" + " done\n" + " fi\n" + "done\n" + "matugen image '" - + wp + "' --config '" + pathEsc + "' --mode " + mode + var script = "cat > '" + pathEsc + "' << 'EOF'\n" + content + "EOF\n" + "for d in '" + extraRepo + "' '" + extraUser + "'; do\n" + " if [ -d \"$d\" ]; then\n" + " for f in \"$d\"/*.toml; do\n" + " [ -f \"$f\" ] && { echo; echo \"# extra: $f\"; cat \"$f\"; } >> '" + pathEsc + "'\n" + " done\n" + " fi\n" + + "done\n" + "matugen image '" + wp + "' --config '" + pathEsc + "' --mode " + mode + " --type " + Settings.data.colorSchemes.matugenSchemeType // Add user config execution if enabled if (Settings.data.matugen.enableUserTemplates) { var userConfigDir = (Quickshell.env("HOME") + "/.config/matugen/").replace(/'/g, "'\\''") script += "\n# Execute user config if it exists\nif [ -f '" + userConfigDir + "config.toml' ]; then\n" - script += " matugen image '" + wp + "' --config '" + userConfigDir + "config.toml' --mode " + mode + "\n" + script += " matugen image '" + wp + "' --config '" + userConfigDir + "config.toml' --mode " + mode + " --type " + Settings.data.colorSchemes.matugenSchemeType + "\n" script += "fi" } From 50e2a95f52ee9a0865204a6c1c260c2ee909cbe7 Mon Sep 17 00:00:00 2001 From: Ly-sec Date: Sat, 27 Sep 2025 17:16:37 +0200 Subject: [PATCH 20/42] Update settings-default --- Assets/settings-default.json | 5 +++-- Commons/Settings.qml | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Assets/settings-default.json b/Assets/settings-default.json index 7ea8b3f7..e4005902 100644 --- a/Assets/settings-default.json +++ b/Assets/settings-default.json @@ -1,5 +1,5 @@ { - "settingsVersion": 9, + "settingsVersion": 10, "bar": { "position": "top", "backgroundOpacity": 1, @@ -158,7 +158,8 @@ "colorSchemes": { "useWallpaperColors": false, "predefinedScheme": "Noctalia (default)", - "darkMode": true + "darkMode": true, + "matugenSchemeType": "scheme-fruit-salad" }, "matugen": { "gtk4": false, diff --git a/Commons/Settings.qml b/Commons/Settings.qml index 9ccf3c9f..dd9bb779 100644 --- a/Commons/Settings.qml +++ b/Commons/Settings.qml @@ -114,7 +114,7 @@ Singleton { JsonAdapter { id: adapter - property int settingsVersion: 9 + property int settingsVersion: 10 // bar property JsonObject bar: JsonObject { From d4dd3b17341c2dce3111eb6dd8ebb11efed7443b Mon Sep 17 00:00:00 2001 From: Ly-sec Date: Sat, 27 Sep 2025 17:20:47 +0200 Subject: [PATCH 21/42] ColorSchemeTab: hide matugen scheme type when Matugen is disabled --- Modules/Settings/Tabs/ColorSchemeTab.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/Modules/Settings/Tabs/ColorSchemeTab.qml b/Modules/Settings/Tabs/ColorSchemeTab.qml index 1407235e..be5ed002 100644 --- a/Modules/Settings/Tabs/ColorSchemeTab.qml +++ b/Modules/Settings/Tabs/ColorSchemeTab.qml @@ -146,6 +146,7 @@ ColumnLayout { description: I18n.tr("settings.color-scheme.color-source.matugen-scheme-type.description") enabled: Settings.data.colorSchemes.useWallpaperColors opacity: Settings.data.colorSchemes.useWallpaperColors ? 1.0 : 0.6 + visible: Settings.data.colorSchemes.useWallpaperColors model: [{ "key": "scheme-content", From 7dc8d2cd88b17b23dbe9080aebf927cf15c3da99 Mon Sep 17 00:00:00 2001 From: Corey Woodworth Date: Sat, 27 Sep 2025 12:34:58 -0400 Subject: [PATCH 22/42] Fix: Works regardless of scaling value --- Widgets/NSlider.qml | 20 ++++++++++---------- Widgets/NToggle.qml | 9 +++++---- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/Widgets/NSlider.qml b/Widgets/NSlider.qml index 37211022..1bd0fda4 100644 --- a/Widgets/NSlider.qml +++ b/Widgets/NSlider.qml @@ -11,9 +11,9 @@ Slider { property bool snapAlways: true property real heightRatio: 0.7 - readonly property real knobDiameter: Math.round(Style.baseWidgetSize * heightRatio * scaling) - readonly property real trackHeight: knobDiameter * 0.4 - readonly property real cutoutExtra: Math.round(Style.baseWidgetSize * 0.1 * scaling) + readonly property real knobDiameter: Math.round((Style.baseWidgetSize * heightRatio * scaling) / 2) * 2 + readonly property real trackHeight: Math.round((knobDiameter * 0.4) /2) * 2 + readonly property real cutoutExtra: Math.round((Style.baseWidgetSize * 0.1 * scaling) / 2) * 2 padding: cutoutExtra / 2 @@ -95,20 +95,20 @@ Slider { // Circular cutout Rectangle { id: knobCutout - width: knobDiameter + cutoutExtra - height: knobDiameter + cutoutExtra + implicitWidth: knobDiameter + cutoutExtra + implicitHeight: knobDiameter + cutoutExtra radius: width / 2 color: root.cutoutColor !== undefined ? root.cutoutColor : Color.mSurface - x: root.leftPadding + Math.round(root.visualPosition * (root.availableWidth - root.knobDiameter) - cutoutExtra) + x: root.leftPadding + root.visualPosition * (root.availableWidth - root.knobDiameter) - cutoutExtra anchors.verticalCenter: parent.verticalCenter } } handle: Item { - width: knobDiameter - height: knobDiameter - x: root.leftPadding + Math.round(root.visualPosition * (root.availableWidth - width)) - y: root.topPadding + Math.round(root.availableHeight / 2 - height / 2) + implicitWidth: knobDiameter + implicitHeight: knobDiameter + x: root.leftPadding + root.visualPosition * (root.availableWidth - width) + anchors.verticalCenter: parent.verticalCenter Rectangle { id: knob diff --git a/Widgets/NToggle.qml b/Widgets/NToggle.qml index 9b4afa35..3e464bb8 100644 --- a/Widgets/NToggle.qml +++ b/Widgets/NToggle.qml @@ -27,8 +27,8 @@ RowLayout { Rectangle { id: switcher - implicitWidth: Math.round(root.baseSize * 1.625 * scaling) - implicitHeight: Math.round(root.baseSize * scaling) + implicitWidth: Math.round(root.baseSize * .85 * scaling) * 2 + implicitHeight: Math.round(root.baseSize * scaling * .5) * 2 radius: height * 0.5 color: root.checked ? Color.mPrimary : Color.mSurface border.color: Color.mOutline @@ -47,8 +47,9 @@ RowLayout { } Rectangle { - implicitWidth: Math.round((root.baseSize * 0.8) * scaling) - height: parent.height - (4 * scaling) + + implicitWidth: Math.round(root.baseSize * 0.4 * scaling) * 2 + implicitHeight: Math.round(root.baseSize * 0.4 * scaling) * 2 radius: height * 0.5 color: root.checked ? Color.mOnPrimary : Color.mPrimary border.color: root.checked ? Color.mSurface : Color.mSurface From a4b4caa2ce4a6f4bc897a54d51d55cba61fc18ac Mon Sep 17 00:00:00 2001 From: ItsLemmy Date: Sat, 27 Sep 2025 13:38:56 -0400 Subject: [PATCH 23/42] Bar SysMonitor: Implemented different sizing strategy to avoid unwanted shifting of items inside and outside the component. --- Modules/Bar/Widgets/SystemMonitor.qml | 233 ++++++++++++++------------ Services/SystemStatService.qml | 12 +- Widgets/NSlider.qml | 2 +- 3 files changed, 139 insertions(+), 108 deletions(-) diff --git a/Modules/Bar/Widgets/SystemMonitor.qml b/Modules/Bar/Widgets/SystemMonitor.qml index 2c14b242..e621ca96 100644 --- a/Modules/Bar/Widgets/SystemMonitor.qml +++ b/Modules/Bar/Widgets/SystemMonitor.qml @@ -39,12 +39,39 @@ Rectangle { readonly property bool showNetworkStats: (widgetSettings.showNetworkStats !== undefined) ? widgetSettings.showNetworkStats : widgetMetadata.showNetworkStats readonly property bool showDiskUsage: (widgetSettings.showDiskUsage !== undefined) ? widgetSettings.showDiskUsage : widgetMetadata.showDiskUsage + readonly property real iconSize: textSize * 1.4 readonly property real textSize: { var base = isVertical ? width * 0.82 : height return Math.max(1, compact ? base * 0.43 : base * 0.33) } - readonly property real iconSize: textSize * 1.25 + readonly property int percentTextWidth: Math.ceil(percentMetrics.tightBoundingRect.width + 2) + readonly property int tempTextWidth: Math.ceil(tempMetrics.tightBoundingRect.width + 2) + readonly property int memTextWidth: Math.ceil(memMetrics.tightBoundingRect.width + 2) + + TextMetrics { + id: percentMetrics + font.family: Settings.data.ui.fontFixed + font.weight: Style.fontWeightMedium + font.pointSize: textSize + text: "99%" // Use the longest possible string for measurement + } + + TextMetrics { + id: tempMetrics + font.family: Settings.data.ui.fontFixed + font.weight: Style.fontWeightMedium + font.pointSize: textSize + text: "99°" // Use the longest possible string for measurement + } + + TextMetrics { + id: memMetrics + font.family: Settings.data.ui.fontFixed + font.weight: Style.fontWeightMedium + font.pointSize: textSize + text: "99.9K" // Longest value part of network speed + } anchors.centerIn: parent implicitWidth: isVertical ? Math.round(Style.capsuleHeight * scaling) : Math.round(mainGrid.implicitWidth + Style.marginM * 2 * scaling) @@ -55,18 +82,15 @@ Rectangle { GridLayout { id: mainGrid anchors.centerIn: parent - - // Dynamic layout based on bar orientation flow: isVertical ? GridLayout.TopToBottom : GridLayout.LeftToRight rows: isVertical ? -1 : 1 columns: isVertical ? 1 : -1 - - rowSpacing: isVertical ? (Style.marginS * scaling) : (Style.marginXS * scaling) - columnSpacing: isVertical ? (Style.marginXS * scaling) : (Style.marginXS * scaling) + rowSpacing: isVertical ? (Style.marginM * scaling) : 0 + columnSpacing: isVertical ? 0 : (Style.marginM * scaling) // CPU Usage Component Item { - Layout.preferredWidth: cpuUsageContent.implicitWidth + Layout.preferredWidth: isVertical ? root.width : iconSize + percentTextWidth + (Style.marginXXS * scaling) Layout.preferredHeight: Math.round(Style.capsuleHeight * scaling) Layout.alignment: isVertical ? Qt.AlignHCenter : Qt.AlignVCenter visible: showCpuUsage @@ -80,19 +104,6 @@ Rectangle { rowSpacing: Style.marginXXS * scaling columnSpacing: Style.marginXXS * scaling - NText { - text: isVertical ? `${Math.round(SystemStatService.cpuUsage)}%` : `${SystemStatService.cpuUsage}%` - font.family: Settings.data.ui.fontFixed - font.pointSize: textSize - font.weight: Style.fontWeightMedium - Layout.alignment: Qt.AlignCenter - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - color: Color.mPrimary - Layout.row: isVertical ? 0 : 0 - Layout.column: isVertical ? 0 : 1 - } - NIcon { icon: "cpu-usage" font.pointSize: iconSize @@ -100,12 +111,27 @@ Rectangle { Layout.row: isVertical ? 1 : 0 Layout.column: 0 } + + NText { + text: `${Math.round(SystemStatService.cpuUsage)}%` + font.family: Settings.data.ui.fontFixed + font.pointSize: textSize + font.weight: Style.fontWeightMedium + Layout.alignment: Qt.AlignCenter + Layout.preferredWidth: isVertical ? -1 : percentTextWidth + horizontalAlignment: isVertical ? Text.AlignHCenter : Text.AlignRight + verticalAlignment: Text.AlignVCenter + color: Color.mPrimary + Layout.row: isVertical ? 0 : 0 + Layout.column: isVertical ? 0 : 1 + scale: isVertical ? Math.min(1.0, root.width / implicitWidth) : 1.0 + } } } // CPU Temperature Component Item { - Layout.preferredWidth: cpuTempContent.implicitWidth + Layout.preferredWidth: isVertical ? root.width : (iconSize + tempTextWidth) + (Style.marginXXS * scaling) Layout.preferredHeight: Math.round(Style.capsuleHeight * scaling) Layout.alignment: isVertical ? Qt.AlignHCenter : Qt.AlignVCenter visible: showCpuTemp @@ -119,22 +145,6 @@ Rectangle { rowSpacing: Style.marginXXS * scaling columnSpacing: Style.marginXXS * scaling - NText { - text: I18n.tr("system.cpu-temperature", { - "temp": SystemStatService.cpuTemp - }) - font.family: Settings.data.ui.fontFixed - font.pointSize: textSize - font.weight: Style.fontWeightMedium - Layout.alignment: Qt.AlignCenter - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - color: Color.mPrimary - scale: isVertical ? Math.min(1.0, cpuTempContent.width / implicitWidth) : 1.0 - Layout.row: isVertical ? 0 : 0 - Layout.column: isVertical ? 0 : 1 - } - NIcon { icon: "cpu-temperature" font.pointSize: iconSize @@ -142,12 +152,27 @@ Rectangle { Layout.row: isVertical ? 1 : 0 Layout.column: 0 } + + NText { + text: `${Math.round(SystemStatService.cpuTemp)}°` + font.family: Settings.data.ui.fontFixed + font.pointSize: textSize + font.weight: Style.fontWeightMedium + Layout.alignment: Qt.AlignCenter + Layout.preferredWidth: isVertical ? -1 : tempTextWidth + horizontalAlignment: isVertical ? Text.AlignHCenter : Text.AlignRight + verticalAlignment: Text.AlignVCenter + color: Color.mPrimary + Layout.row: isVertical ? 0 : 0 + Layout.column: isVertical ? 0 : 1 + scale: isVertical ? Math.min(1.0, root.width / implicitWidth) : 1.0 + } } } // Memory Usage Component Item { - Layout.preferredWidth: memoryContent.implicitWidth + Layout.preferredWidth: isVertical ? root.width : iconSize + (showMemoryAsPercent ? percentTextWidth : memTextWidth) + (Style.marginXXS * scaling) Layout.preferredHeight: Math.round(Style.capsuleHeight * scaling) Layout.alignment: isVertical ? Qt.AlignHCenter : Qt.AlignVCenter visible: showMemoryUsage @@ -161,25 +186,6 @@ Rectangle { rowSpacing: Style.marginXXS * scaling columnSpacing: Style.marginXXS * scaling - NText { - text: { - if (showMemoryAsPercent) { - return `${SystemStatService.memPercent}%` - } else { - return isVertical ? `${Math.round(SystemStatService.memGb)}G` : `${SystemStatService.memGb}G` - } - } - font.family: Settings.data.ui.fontFixed - font.pointSize: textSize - font.weight: Style.fontWeightMedium - Layout.alignment: Qt.AlignCenter - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - color: Color.mPrimary - Layout.row: isVertical ? 0 : 0 - Layout.column: isVertical ? 0 : 1 - } - NIcon { icon: "memory" font.pointSize: iconSize @@ -187,12 +193,27 @@ Rectangle { Layout.row: isVertical ? 1 : 0 Layout.column: 0 } + + NText { + text: showMemoryAsPercent ? `${Math.round(SystemStatService.memPercent)}%` : `${SystemStatService.memGb.toFixed(1)}G` + font.family: Settings.data.ui.fontFixed + font.pointSize: textSize + font.weight: Style.fontWeightMedium + Layout.alignment: Qt.AlignCenter + Layout.preferredWidth: isVertical ? -1 : (showMemoryAsPercent ? percentTextWidth : memTextWidth) + horizontalAlignment: isVertical ? Text.AlignHCenter : Text.AlignRight + verticalAlignment: Text.AlignVCenter + color: Color.mPrimary + Layout.row: isVertical ? 0 : 0 + Layout.column: isVertical ? 0 : 1 + scale: isVertical ? Math.min(1.0, root.width / implicitWidth) : 1.0 + } } } // Network Download Speed Component Item { - Layout.preferredWidth: downloadContent.implicitWidth + Layout.preferredWidth: isVertical ? root.width : iconSize + memTextWidth + (Style.marginXXS * scaling) Layout.preferredHeight: Math.round(Style.capsuleHeight * scaling) Layout.alignment: isVertical ? Qt.AlignHCenter : Qt.AlignVCenter visible: showNetworkStats @@ -204,20 +225,7 @@ Rectangle { rows: isVertical ? 2 : 1 columns: isVertical ? 1 : 2 rowSpacing: Style.marginXXS * scaling - columnSpacing: isVertical ? (Style.marginXXS * scaling) : (Style.marginXS * scaling) - - NText { - text: isVertical ? SystemStatService.formatCompactSpeed(SystemStatService.rxSpeed) : SystemStatService.formatSpeed(SystemStatService.rxSpeed) - font.family: Settings.data.ui.fontFixed - font.pointSize: textSize - font.weight: Style.fontWeightMedium - Layout.alignment: Qt.AlignCenter - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - color: Color.mPrimary - Layout.row: isVertical ? 0 : 0 - Layout.column: isVertical ? 0 : 1 - } + columnSpacing: Style.marginXXS * scaling NIcon { icon: "download-speed" @@ -226,12 +234,27 @@ Rectangle { Layout.row: isVertical ? 1 : 0 Layout.column: 0 } + + NText { + text: isVertical ? SystemStatService.formatCompactSpeed(SystemStatService.rxSpeed) : SystemStatService.formatSpeed(SystemStatService.rxSpeed) + font.family: Settings.data.ui.fontFixed + font.pointSize: textSize + font.weight: Style.fontWeightMedium + Layout.alignment: Qt.AlignCenter + Layout.preferredWidth: isVertical ? -1 : memTextWidth + horizontalAlignment: isVertical ? Text.AlignHCenter : Text.AlignRight + verticalAlignment: Text.AlignVCenter + color: Color.mPrimary + Layout.row: isVertical ? 0 : 0 + Layout.column: isVertical ? 0 : 1 + scale: isVertical ? Math.min(1.0, root.width / implicitWidth) : 1.0 + } } } // Network Upload Speed Component Item { - Layout.preferredWidth: uploadContent.implicitWidth + Layout.preferredWidth: isVertical ? root.width : iconSize + memTextWidth + (Style.marginXXS * scaling) Layout.preferredHeight: Math.round(Style.capsuleHeight * scaling) Layout.alignment: isVertical ? Qt.AlignHCenter : Qt.AlignVCenter visible: showNetworkStats @@ -243,20 +266,7 @@ Rectangle { rows: isVertical ? 2 : 1 columns: isVertical ? 1 : 2 rowSpacing: Style.marginXXS * scaling - columnSpacing: isVertical ? (Style.marginXXS * scaling) : (Style.marginXS * scaling) - - NText { - text: isVertical ? SystemStatService.formatCompactSpeed(SystemStatService.txSpeed) : SystemStatService.formatSpeed(SystemStatService.txSpeed) - font.family: Settings.data.ui.fontFixed - font.pointSize: textSize - font.weight: Style.fontWeightMedium - Layout.alignment: Qt.AlignCenter - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - color: Color.mPrimary - Layout.row: isVertical ? 0 : 0 - Layout.column: isVertical ? 0 : 1 - } + columnSpacing: Style.marginXXS * scaling NIcon { icon: "upload-speed" @@ -265,12 +275,27 @@ Rectangle { Layout.row: isVertical ? 1 : 0 Layout.column: 0 } + + NText { + text: isVertical ? SystemStatService.formatCompactSpeed(SystemStatService.txSpeed) : SystemStatService.formatSpeed(SystemStatService.txSpeed) + font.family: Settings.data.ui.fontFixed + font.pointSize: textSize + font.weight: Style.fontWeightMedium + Layout.alignment: Qt.AlignCenter + Layout.preferredWidth: isVertical ? -1 : memTextWidth + horizontalAlignment: isVertical ? Text.AlignHCenter : Text.AlignRight + verticalAlignment: Text.AlignVCenter + color: Color.mPrimary + Layout.row: isVertical ? 0 : 0 + Layout.column: isVertical ? 0 : 1 + scale: isVertical ? Math.min(1.0, root.width / implicitWidth) : 1.0 + } } } // Disk Usage Component (primary drive) Item { - Layout.preferredWidth: diskContent.implicitWidth + Layout.preferredWidth: isVertical ? root.width : iconSize + percentTextWidth + (Style.marginXXS * scaling) Layout.preferredHeight: Math.round(Style.capsuleHeight * scaling) Layout.alignment: isVertical ? Qt.AlignHCenter : Qt.AlignVCenter visible: showDiskUsage @@ -282,22 +307,7 @@ Rectangle { rows: isVertical ? 2 : 1 columns: isVertical ? 1 : 2 rowSpacing: Style.marginXXS * scaling - columnSpacing: isVertical ? (Style.marginXXS * scaling) : (Style.marginXS * scaling) - - NText { - text: I18n.tr("system.disk-usage", { - "percent": SystemStatService.diskPercent - }) - font.family: Settings.data.ui.fontFixed - font.pointSize: textSize - font.weight: Style.fontWeightMedium - Layout.alignment: Qt.AlignCenter - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - color: Color.mPrimary - Layout.row: isVertical ? 0 : 0 - Layout.column: isVertical ? 0 : 1 - } + columnSpacing: Style.marginXXS * scaling NIcon { icon: "storage" @@ -306,6 +316,21 @@ Rectangle { Layout.row: isVertical ? 1 : 0 Layout.column: 0 } + + NText { + text: `${SystemStatService.diskPercent}%` + font.family: Settings.data.ui.fontFixed + font.pointSize: textSize + font.weight: Style.fontWeightMedium + Layout.alignment: Qt.AlignCenter + Layout.preferredWidth: isVertical ? undefine : percentTextWidth + horizontalAlignment: isVertical ? Text.AlignHCenter : Text.AlignRight + verticalAlignment: Text.AlignVCenter + color: Color.mPrimary + Layout.row: isVertical ? 0 : 0 + Layout.column: isVertical ? 0 : 1 + scale: isVertical ? Math.min(1.0, root.width / implicitWidth) : 1.0 + } } } } diff --git a/Services/SystemStatService.qml b/Services/SystemStatService.qml index 7a87e5c9..9692c1ee 100644 --- a/Services/SystemStatService.qml +++ b/Services/SystemStatService.qml @@ -325,14 +325,20 @@ Singleton { // Helper function to format network speeds function formatSpeed(bytesPerSecond) { if (bytesPerSecond < 1024 * 1024) { - return (bytesPerSecond / 1024).toFixed(1) + "KB/s" + const kb = bytesPerSecond / 1024 + if (kb < 10) { + return kb.toFixed(1) + "KB" + } else { + return Math.round(kb) + "KB" + } } else if (bytesPerSecond < 1024 * 1024 * 1024) { - return (bytesPerSecond / (1024 * 1024)).toFixed(1) + "MB/s" + return (bytesPerSecond / (1024 * 1024)).toFixed(1) + "MB" } else { - return (bytesPerSecond / (1024 * 1024 * 1024)).toFixed(1) + "GB/s" + return (bytesPerSecond / (1024 * 1024 * 1024)).toFixed(1) + "GB" } } + // ------------------------------------------------------- // Compact speed formatter for vertical bar display function formatCompactSpeed(bytesPerSecond) { if (!bytesPerSecond || bytesPerSecond <= 0) diff --git a/Widgets/NSlider.qml b/Widgets/NSlider.qml index 1bd0fda4..eb6d007c 100644 --- a/Widgets/NSlider.qml +++ b/Widgets/NSlider.qml @@ -12,7 +12,7 @@ Slider { property real heightRatio: 0.7 readonly property real knobDiameter: Math.round((Style.baseWidgetSize * heightRatio * scaling) / 2) * 2 - readonly property real trackHeight: Math.round((knobDiameter * 0.4) /2) * 2 + readonly property real trackHeight: Math.round((knobDiameter * 0.4) / 2) * 2 readonly property real cutoutExtra: Math.round((Style.baseWidgetSize * 0.1 * scaling) / 2) * 2 padding: cutoutExtra / 2 From b5b8b62cf0f440809be84ce03ad5db5ab724ae4c Mon Sep 17 00:00:00 2001 From: ItsLemmy Date: Sat, 27 Sep 2025 14:03:54 -0400 Subject: [PATCH 24/42] Animation speed: allow 500% speed for quasi instant. --- Modules/Settings/Tabs/GeneralTab.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/Settings/Tabs/GeneralTab.qml b/Modules/Settings/Tabs/GeneralTab.qml index a4600281..33c3e0e5 100644 --- a/Modules/Settings/Tabs/GeneralTab.qml +++ b/Modules/Settings/Tabs/GeneralTab.qml @@ -115,7 +115,7 @@ ColumnLayout { NValueSlider { Layout.fillWidth: true from: 0.1 - to: 2.0 + to: 5.0 stepSize: 0.01 value: Settings.data.general.animationSpeed onMoved: value => Settings.data.general.animationSpeed = value From 4c516200dcbfe40cb578cd948bc7f739585b5c2a Mon Sep 17 00:00:00 2001 From: ItsLemmy Date: Sat, 27 Sep 2025 14:19:26 -0400 Subject: [PATCH 25/42] SystemMonitor: syntax error --- Modules/Bar/Widgets/SystemMonitor.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/Bar/Widgets/SystemMonitor.qml b/Modules/Bar/Widgets/SystemMonitor.qml index e621ca96..2376af86 100644 --- a/Modules/Bar/Widgets/SystemMonitor.qml +++ b/Modules/Bar/Widgets/SystemMonitor.qml @@ -323,7 +323,7 @@ Rectangle { font.pointSize: textSize font.weight: Style.fontWeightMedium Layout.alignment: Qt.AlignCenter - Layout.preferredWidth: isVertical ? undefine : percentTextWidth + Layout.preferredWidth: isVertical ? -1 : percentTextWidth horizontalAlignment: isVertical ? Text.AlignHCenter : Text.AlignRight verticalAlignment: Text.AlignVCenter color: Color.mPrimary From 8e248f6795aeae1eb4f711063490e5e5ebaeb8ca Mon Sep 17 00:00:00 2001 From: ItsLemmy Date: Sat, 27 Sep 2025 14:57:11 -0400 Subject: [PATCH 26/42] Tooltip: removed auto-positionning relative to the bar. as many tooltips are used in panels - still a few edge cases to work on --- Modules/Bar/Extras/BarPillVertical.qml | 1 - Modules/Bar/Widgets/Clock.qml | 2 ++ Modules/Bar/Widgets/ControlCenter.qml | 3 +++ Modules/Bar/Widgets/DarkMode.qml | 3 +++ Modules/Bar/Widgets/KeepAwake.qml | 3 +++ Modules/Bar/Widgets/NightLight.qml | 3 +++ Modules/Bar/Widgets/NotificationHistory.qml | 3 +++ Modules/Bar/Widgets/PowerProfile.qml | 3 +++ Modules/Bar/Widgets/ScreenRecorder.qml | 3 +++ Modules/Bar/Widgets/SessionMenu.qml | 3 +++ Modules/Bar/Widgets/Taskbar.qml | 4 +++- Modules/Bar/Widgets/WallpaperSelector.qml | 3 +++ Modules/Bar/Widgets/WiFi.qml | 3 +++ Widgets/NIconButton.qml | 7 ++++++- Widgets/NTooltip.qml | 23 +++------------------ 15 files changed, 44 insertions(+), 23 deletions(-) diff --git a/Modules/Bar/Extras/BarPillVertical.qml b/Modules/Bar/Extras/BarPillVertical.qml index 494083e4..deb304be 100644 --- a/Modules/Bar/Extras/BarPillVertical.qml +++ b/Modules/Bar/Extras/BarPillVertical.qml @@ -242,7 +242,6 @@ Item { text: root.tooltipText positionLeft: barPosition === "right" positionRight: barPosition === "left" - positionAbove: Settings.data.bar.position === "bottom" delay: Style.tooltipDelayLong } diff --git a/Modules/Bar/Widgets/Clock.qml b/Modules/Bar/Widgets/Clock.qml index 4a21bb10..0b314ffd 100644 --- a/Modules/Bar/Widgets/Clock.qml +++ b/Modules/Bar/Widgets/Clock.qml @@ -111,6 +111,8 @@ Rectangle { text: I18n.tr("clock.tooltip") target: clockContainer positionAbove: Settings.data.bar.position === "bottom" + positionLeft: barPosition === "right" + positionRight: barPosition === "left" } MouseArea { diff --git a/Modules/Bar/Widgets/ControlCenter.qml b/Modules/Bar/Widgets/ControlCenter.qml index a81199de..b730152a 100644 --- a/Modules/Bar/Widgets/ControlCenter.qml +++ b/Modules/Bar/Widgets/ControlCenter.qml @@ -36,6 +36,9 @@ NIconButton { // If we have a custom path or distro logo, don't use the theme icon. icon: (customIconPath === "" && !useDistroLogo) ? customIcon : "" tooltipText: I18n.tr("tooltips.open-control-center") + tooltipPositionAbove: Settings.data.bar.position === "bottom" + tooltipPositionLeft: Settings.data.bar.position === "right" + tooltipPositionRight: Settings.data.bar.position === "left" baseSize: Style.capsuleHeight compact: (Settings.data.bar.density === "compact") colorBg: (Settings.data.bar.showCapsule ? Color.mSurfaceVariant : Color.transparent) diff --git a/Modules/Bar/Widgets/DarkMode.qml b/Modules/Bar/Widgets/DarkMode.qml index 1043b225..333b3a9b 100644 --- a/Modules/Bar/Widgets/DarkMode.qml +++ b/Modules/Bar/Widgets/DarkMode.qml @@ -11,6 +11,9 @@ NIconButton { icon: "dark-mode" tooltipText: Settings.data.colorSchemes.darkMode ? I18n.tr("tooltips.switch-to-light-mode") : I18n.tr("tooltips.switch-to-dark-mode") + tooltipPositionAbove: Settings.data.bar.position === "bottom" + tooltipPositionLeft: Settings.data.bar.position === "right" + tooltipPositionRight: Settings.data.bar.position === "left" compact: (Settings.data.bar.density === "compact") baseSize: Style.capsuleHeight colorBg: Settings.data.colorSchemes.darkMode ? (Settings.data.bar.showCapsule ? Color.mSurfaceVariant : Color.transparent) : Color.mPrimary diff --git a/Modules/Bar/Widgets/KeepAwake.qml b/Modules/Bar/Widgets/KeepAwake.qml index 91ca6af2..72321b7c 100644 --- a/Modules/Bar/Widgets/KeepAwake.qml +++ b/Modules/Bar/Widgets/KeepAwake.qml @@ -15,6 +15,9 @@ NIconButton { compact: (Settings.data.bar.density === "compact") icon: IdleInhibitorService.isInhibited ? "keep-awake-on" : "keep-awake-off" tooltipText: IdleInhibitorService.isInhibited ? I18n.tr("tooltips.disable-keep-awake") : I18n.tr("tooltips.enable-keep-awake") + tooltipPositionAbove: Settings.data.bar.position === "bottom" + tooltipPositionLeft: Settings.data.bar.position === "right" + tooltipPositionRight: Settings.data.bar.position === "left" colorBg: IdleInhibitorService.isInhibited ? Color.mPrimary : (Settings.data.bar.showCapsule ? Color.mSurfaceVariant : Color.transparent) colorFg: IdleInhibitorService.isInhibited ? Color.mOnPrimary : Color.mOnSurface colorBorder: Color.transparent diff --git a/Modules/Bar/Widgets/NightLight.qml b/Modules/Bar/Widgets/NightLight.qml index 605f572a..72f44867 100644 --- a/Modules/Bar/Widgets/NightLight.qml +++ b/Modules/Bar/Widgets/NightLight.qml @@ -23,6 +23,9 @@ NIconButton { icon: Settings.data.nightLight.enabled ? (Settings.data.nightLight.forced ? "nightlight-forced" : "nightlight-on") : "nightlight-off" tooltipText: Settings.data.nightLight.enabled ? (Settings.data.nightLight.forced ? I18n.tr("tooltips.night-light-forced") : I18n.tr("tooltips.night-light-enabled")) : I18n.tr("tooltips.night-light-disabled") + tooltipPositionAbove: Settings.data.bar.position === "bottom" + tooltipPositionLeft: Settings.data.bar.position === "right" + tooltipPositionRight: Settings.data.bar.position === "left" onClicked: { // Check if wlsunset is available before enabling night light if (!ProgramCheckerService.wlsunsetAvailable) { diff --git a/Modules/Bar/Widgets/NotificationHistory.qml b/Modules/Bar/Widgets/NotificationHistory.qml index d33f3116..c5a9f47b 100644 --- a/Modules/Bar/Widgets/NotificationHistory.qml +++ b/Modules/Bar/Widgets/NotificationHistory.qml @@ -53,6 +53,9 @@ NIconButton { compact: (Settings.data.bar.density === "compact") icon: Settings.data.notifications.doNotDisturb ? "bell-off" : "bell" tooltipText: Settings.data.notifications.doNotDisturb ? I18n.tr("tooltips.open-notification-history-disable-dnd") : I18n.tr("tooltips.open-notification-history-enable-dnd") + tooltipPositionAbove: Settings.data.bar.position === "bottom" + tooltipPositionLeft: Settings.data.bar.position === "right" + tooltipPositionRight: Settings.data.bar.position === "left" colorBg: (Settings.data.bar.showCapsule ? Color.mSurfaceVariant : Color.transparent) colorFg: Color.mOnSurface colorBorder: Color.transparent diff --git a/Modules/Bar/Widgets/PowerProfile.qml b/Modules/Bar/Widgets/PowerProfile.qml index 691c72ca..0158c024 100644 --- a/Modules/Bar/Widgets/PowerProfile.qml +++ b/Modules/Bar/Widgets/PowerProfile.qml @@ -19,6 +19,9 @@ NIconButton { tooltipText: I18n.tr("tooltips.power-profile", { "profile": PowerProfileService.getName() }) + tooltipPositionAbove: Settings.data.bar.position === "bottom" + tooltipPositionLeft: Settings.data.bar.position === "right" + tooltipPositionRight: Settings.data.bar.position === "left" compact: (Settings.data.bar.density === "compact") colorBg: (PowerProfileService.profile === PowerProfile.Balanced) ? (Settings.data.bar.showCapsule ? Color.mSurfaceVariant : Color.transparent) : Color.mPrimary colorFg: (PowerProfileService.profile === PowerProfile.Balanced) ? Color.mOnSurface : Color.mOnPrimary diff --git a/Modules/Bar/Widgets/ScreenRecorder.qml b/Modules/Bar/Widgets/ScreenRecorder.qml index 28b5e7c2..01971e25 100644 --- a/Modules/Bar/Widgets/ScreenRecorder.qml +++ b/Modules/Bar/Widgets/ScreenRecorder.qml @@ -12,6 +12,9 @@ NIconButton { icon: "camera-video" tooltipText: ScreenRecorderService.isRecording ? I18n.tr("tooltips.click-to-stop-recording") : I18n.tr("tooltips.click-to-start-recording") + tooltipPositionAbove: Settings.data.bar.position === "bottom" + tooltipPositionLeft: Settings.data.bar.position === "right" + tooltipPositionRight: Settings.data.bar.position === "left" compact: (Settings.data.bar.density === "compact") baseSize: Style.capsuleHeight colorBg: ScreenRecorderService.isRecording ? Color.mPrimary : (Settings.data.bar.showCapsule ? Color.mSurfaceVariant : Color.transparent) diff --git a/Modules/Bar/Widgets/SessionMenu.qml b/Modules/Bar/Widgets/SessionMenu.qml index 8aea7785..fb309d29 100644 --- a/Modules/Bar/Widgets/SessionMenu.qml +++ b/Modules/Bar/Widgets/SessionMenu.qml @@ -15,6 +15,9 @@ NIconButton { baseSize: Style.capsuleHeight icon: "power" tooltipText: I18n.tr("tooltips.session-menu") + tooltipPositionAbove: Settings.data.bar.position === "bottom" + tooltipPositionLeft: Settings.data.bar.position === "right" + tooltipPositionRight: Settings.data.bar.position === "left" colorBg: (Settings.data.bar.showCapsule ? Color.mSurfaceVariant : Color.transparent) colorFg: Color.mError colorBorder: Color.transparent diff --git a/Modules/Bar/Widgets/Taskbar.qml b/Modules/Bar/Widgets/Taskbar.qml index 63e7326d..e9aadf4f 100644 --- a/Modules/Bar/Widgets/Taskbar.qml +++ b/Modules/Bar/Widgets/Taskbar.qml @@ -106,7 +106,9 @@ Rectangle { id: taskbarTooltip text: taskbarItem.modelData.title || taskbarItem.modelData.appId || "Unknown app." target: taskbarItem - positionAbove: Settings.data.bar.position === "bottom" + positionAbove: barPosition === "bottom" + positionLeft: barPosition === "right" + positionRight: barPosition === "left" } } } diff --git a/Modules/Bar/Widgets/WallpaperSelector.qml b/Modules/Bar/Widgets/WallpaperSelector.qml index 341df6df..79e794ea 100644 --- a/Modules/Bar/Widgets/WallpaperSelector.qml +++ b/Modules/Bar/Widgets/WallpaperSelector.qml @@ -15,6 +15,9 @@ NIconButton { compact: (Settings.data.bar.density === "compact") icon: "wallpaper-selector" tooltipText: I18n.tr("tooltips.open-wallpaper-selector") + tooltipPositionAbove: Settings.data.bar.position === "bottom" + tooltipPositionLeft: Settings.data.bar.position === "right" + tooltipPositionRight: Settings.data.bar.position === "left" colorBg: (Settings.data.bar.showCapsule ? Color.mSurfaceVariant : Color.transparent) colorFg: Color.mOnSurface colorBorder: Color.transparent diff --git a/Modules/Bar/Widgets/WiFi.qml b/Modules/Bar/Widgets/WiFi.qml index 0e8d3f9b..610a0351 100644 --- a/Modules/Bar/Widgets/WiFi.qml +++ b/Modules/Bar/Widgets/WiFi.qml @@ -41,6 +41,9 @@ NIconButton { } } tooltipText: I18n.tr("tooltips.manage-wifi") + tooltipPositionAbove: Settings.data.bar.position === "bottom" + tooltipPositionLeft: Settings.data.bar.position === "right" + tooltipPositionRight: Settings.data.bar.position === "left" onClicked: PanelService.getPanel("wifiPanel")?.toggle(this) onRightClicked: PanelService.getPanel("wifiPanel")?.toggle(this) } diff --git a/Widgets/NIconButton.qml b/Widgets/NIconButton.qml index 59891903..0e6c2c8e 100644 --- a/Widgets/NIconButton.qml +++ b/Widgets/NIconButton.qml @@ -11,6 +11,9 @@ Rectangle { property string icon property string tooltipText + property bool tooltipPositionAbove: false + property bool tooltipPositionLeft: false + property bool tooltipPositionRight: false property bool enabled: true property bool allowClickWhenDisabled: false property bool hovering: false @@ -65,7 +68,9 @@ Rectangle { NTooltip { id: tooltip target: root - positionAbove: Settings.data.bar.position === "bottom" + positionAbove: root.tooltipPositionAbove + positionLeft: root.tooltipPositionLeft + positionRight: root.tooltipPositionRight text: root.tooltipText } diff --git a/Widgets/NTooltip.qml b/Widgets/NTooltip.qml index 67cf6169..f0cd55df 100644 --- a/Widgets/NTooltip.qml +++ b/Widgets/NTooltip.qml @@ -48,34 +48,17 @@ Window { return } - // Auto-detect positioning based on bar position if not explicitly set - var shouldPositionLeft = positionLeft - var shouldPositionRight = positionRight - var shouldPositionAbove = positionAbove - - // If no explicit positioning is set, auto-detect based on bar position - if (!positionLeft && !positionRight && !positionAbove) { - if (barPosition === "left") { - shouldPositionRight = true - } else if (barPosition === "right") { - shouldPositionLeft = true - } else if (barPosition === "bottom") { - shouldPositionAbove = true - } - // For "top" bar, default to below (no change needed) - } - - if (shouldPositionLeft) { + if (positionLeft) { // Position tooltip to the left of the target var pos = target.mapToGlobal(0, 0) x = pos.x - width - 12 // 12 px margin to the left y = pos.y - height / 2 + target.height / 2 - } else if (shouldPositionRight) { + } else if (positionRight) { // Position tooltip to the right of the target var pos = target.mapToGlobal(target.width, 0) x = pos.x + 12 // 12 px margin to the right y = pos.y - height / 2 + target.height / 2 - } else if (shouldPositionAbove) { + } else if (positionAbove) { // Position tooltip above the target var pos = target.mapToGlobal(0, 0) x = pos.x - width / 2 + target.width / 2 From 348c1e8f9f4eb616552f0ed73da8d9a6f361378e Mon Sep 17 00:00:00 2001 From: ItsLemmy Date: Sat, 27 Sep 2025 15:01:40 -0400 Subject: [PATCH 27/42] General: Animation speed max back to 200% --- Modules/Settings/Tabs/GeneralTab.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/Settings/Tabs/GeneralTab.qml b/Modules/Settings/Tabs/GeneralTab.qml index 33c3e0e5..a4600281 100644 --- a/Modules/Settings/Tabs/GeneralTab.qml +++ b/Modules/Settings/Tabs/GeneralTab.qml @@ -115,7 +115,7 @@ ColumnLayout { NValueSlider { Layout.fillWidth: true from: 0.1 - to: 5.0 + to: 2.0 stepSize: 0.01 value: Settings.data.general.animationSpeed onMoved: value => Settings.data.general.animationSpeed = value From 2112f675c0d3f1d0582241f153a34ca5468bf841 Mon Sep 17 00:00:00 2001 From: ItsLemmy Date: Sat, 27 Sep 2025 16:12:11 -0400 Subject: [PATCH 28/42] Taskbar: fix warning due to non existing property. --- Modules/Bar/Widgets/Taskbar.qml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Modules/Bar/Widgets/Taskbar.qml b/Modules/Bar/Widgets/Taskbar.qml index e9aadf4f..30057eb7 100644 --- a/Modules/Bar/Widgets/Taskbar.qml +++ b/Modules/Bar/Widgets/Taskbar.qml @@ -106,9 +106,9 @@ Rectangle { id: taskbarTooltip text: taskbarItem.modelData.title || taskbarItem.modelData.appId || "Unknown app." target: taskbarItem - positionAbove: barPosition === "bottom" - positionLeft: barPosition === "right" - positionRight: barPosition === "left" + positionAbove: Settings.data.bar.position === "bottom" + positionLeft: Settings.data.bar.position === "right" + positionRight: Settings.data.bar.position === "left" } } } From 8b89e95b13764b0f0de0e57d77ec91705fe1e2f2 Mon Sep 17 00:00:00 2001 From: ItsLemmy Date: Sat, 27 Sep 2025 16:12:28 -0400 Subject: [PATCH 29/42] New setting to disable all UI animations --- Assets/Translations/en.json | 4 ++++ Assets/settings-default.json | 5 ++-- Commons/Settings.qml | 5 ++-- Commons/Style.qml | 8 +++---- Modules/Settings/Tabs/GeneralTab.qml | 35 +++++++++++++++++++--------- 5 files changed, 38 insertions(+), 19 deletions(-) diff --git a/Assets/Translations/en.json b/Assets/Translations/en.json index 4a903bc2..ab808c11 100644 --- a/Assets/Translations/en.json +++ b/Assets/Translations/en.json @@ -31,6 +31,10 @@ "animation-speed": { "label": "Animation speed", "description": "Adjust global animation speed." + }, + "animation-disable": { + "label": "Disable UI Animations", + "description": "Disable all animations for a faster, more responsive experience." } }, "screen-corners": { diff --git a/Assets/settings-default.json b/Assets/settings-default.json index e4005902..08d0199a 100644 --- a/Assets/settings-default.json +++ b/Assets/settings-default.json @@ -1,5 +1,5 @@ { - "settingsVersion": 10, + "settingsVersion": 11, "bar": { "position": "top", "backgroundOpacity": 1, @@ -67,7 +67,8 @@ "forceBlackScreenCorners": false, "radiusRatio": 1, "screenRadiusRatio": 1, - "animationSpeed": 1 + "animationSpeed": 1, + "animationDisabled": false }, "location": { "name": "Tokyo", diff --git a/Commons/Settings.qml b/Commons/Settings.qml index dd9bb779..b5fc1751 100644 --- a/Commons/Settings.qml +++ b/Commons/Settings.qml @@ -54,7 +54,7 @@ Singleton { // Then it should be commented out again, regular users don't need to generate // default settings on every start // TODO: automate this someday! - //generateDefaultSettings() + // generateDefaultSettings() // Patch-in the local default, resolved to user's home adapter.general.avatarImage = defaultAvatar @@ -114,7 +114,7 @@ Singleton { JsonAdapter { id: adapter - property int settingsVersion: 10 + property int settingsVersion: 11 // bar property JsonObject bar: JsonObject { @@ -175,6 +175,7 @@ Singleton { property real radiusRatio: 1.0 property real screenRadiusRatio: 1.0 property real animationSpeed: 1.0 + property bool animationDisabled: false } // location diff --git a/Commons/Style.qml b/Commons/Style.qml index c8607f79..ab09232f 100644 --- a/Commons/Style.qml +++ b/Commons/Style.qml @@ -60,10 +60,10 @@ Singleton { property real opacityFull: 1.0 // Animation duration (ms) - property int animationFast: Math.round(150 / Settings.data.general.animationSpeed) - property int animationNormal: Math.round(300 / Settings.data.general.animationSpeed) - property int animationSlow: Math.round(450 / Settings.data.general.animationSpeed) - property int animationSlowest: Math.round(750 / Settings.data.general.animationSpeed) + property int animationFast: Settings.data.general.animationDisabled ? 0 : Math.round(150 / Settings.data.general.animationSpeed) + property int animationNormal: Settings.data.general.animationDisabled ? 0 : Math.round(300 / Settings.data.general.animationSpeed) + property int animationSlow: Settings.data.general.animationDisabled ? 0 : Math.round(450 / Settings.data.general.animationSpeed) + property int animationSlowest: Settings.data.general.animationDisabled ? 0 : Math.round(750 / Settings.data.general.animationSpeed) // Delays property int tooltipDelay: 300 diff --git a/Modules/Settings/Tabs/GeneralTab.qml b/Modules/Settings/Tabs/GeneralTab.qml index a4600281..93cd9c49 100644 --- a/Modules/Settings/Tabs/GeneralTab.qml +++ b/Modules/Settings/Tabs/GeneralTab.qml @@ -104,22 +104,35 @@ ColumnLayout { // Animation Speed ColumnLayout { - spacing: Style.marginXXS * scaling + spacing: Style.marginL * scaling Layout.fillWidth: true - NLabel { - label: I18n.tr("settings.general.ui.animation-speed.label") - description: I18n.tr("settings.general.ui.animation-speed.description") + NToggle { + label: I18n.tr("settings.general.ui.animation-disable.label") + description: I18n.tr("settings.general.ui.animation-disable.description") + checked: Settings.data.general.animationDisabled + onToggled: checked => Settings.data.general.animationDisabled = checked } - NValueSlider { + ColumnLayout { + spacing: Style.marginXXS * scaling Layout.fillWidth: true - from: 0.1 - to: 2.0 - stepSize: 0.01 - value: Settings.data.general.animationSpeed - onMoved: value => Settings.data.general.animationSpeed = value - text: Math.round(Settings.data.general.animationSpeed * 100) + "%" + visible: !Settings.data.general.animationDisabled + + NLabel { + label: I18n.tr("settings.general.ui.animation-speed.label") + description: I18n.tr("settings.general.ui.animation-speed.description") + } + + NValueSlider { + Layout.fillWidth: true + from: 0.1 + to: 2.0 + stepSize: 0.01 + value: Settings.data.general.animationSpeed + onMoved: value => Settings.data.general.animationSpeed = value + text: Math.round(Settings.data.general.animationSpeed * 100) + "%" + } } } } From e73d85de040e528126530499ffe406eba7db260f Mon Sep 17 00:00:00 2001 From: ItsLemmy Date: Sat, 27 Sep 2025 17:33:09 -0400 Subject: [PATCH 30/42] Bluetooth: Removed the copy of the adapter's state in Settings, makes code much simpler and robust by always relying on the actual adapter's state. --- Assets/settings-default.json | 5 +-- Commons/Settings.qml | 3 +- Modules/Bar/Bluetooth/BluetoothPanel.qml | 4 +- Modules/Bar/Widgets/Bluetooth.qml | 2 +- Modules/Settings/Tabs/NetworkTab.qml | 2 +- Services/BluetoothService.qml | 47 +++--------------------- 6 files changed, 13 insertions(+), 50 deletions(-) diff --git a/Assets/settings-default.json b/Assets/settings-default.json index 08d0199a..3fddf7cd 100644 --- a/Assets/settings-default.json +++ b/Assets/settings-default.json @@ -1,5 +1,5 @@ { - "settingsVersion": 11, + "settingsVersion": 12, "bar": { "position": "top", "backgroundOpacity": 1, @@ -118,8 +118,7 @@ "pinnedApps": [] }, "network": { - "wifiEnabled": true, - "bluetoothEnabled": true + "wifiEnabled": true }, "notifications": { "doNotDisturb": false, diff --git a/Commons/Settings.qml b/Commons/Settings.qml index b5fc1751..bed06a98 100644 --- a/Commons/Settings.qml +++ b/Commons/Settings.qml @@ -114,7 +114,7 @@ Singleton { JsonAdapter { id: adapter - property int settingsVersion: 11 + property int settingsVersion: 12 // bar property JsonObject bar: JsonObject { @@ -240,7 +240,6 @@ Singleton { // network property JsonObject network: JsonObject { property bool wifiEnabled: true - property bool bluetoothEnabled: true } // notifications diff --git a/Modules/Bar/Bluetooth/BluetoothPanel.qml b/Modules/Bar/Bluetooth/BluetoothPanel.qml index 3f4773ab..c625f1f1 100644 --- a/Modules/Bar/Bluetooth/BluetoothPanel.qml +++ b/Modules/Bar/Bluetooth/BluetoothPanel.qml @@ -44,13 +44,13 @@ NPanel { NToggle { id: bluetoothSwitch - checked: Settings.data.network.bluetoothEnabled + checked: BluetoothService.enabled onToggled: checked => BluetoothService.setBluetoothEnabled(checked) baseSize: Style.baseWidgetSize * 0.65 * scaling } NIconButton { - enabled: Settings.data.network.bluetoothEnabled + enabled: BluetoothService.enabled icon: BluetoothService.adapter && BluetoothService.adapter.discovering ? "stop" : "refresh" tooltipText: I18n.tr("tooltips.refresh-devices") baseSize: Style.baseWidgetSize * 0.8 diff --git a/Modules/Bar/Widgets/Bluetooth.qml b/Modules/Bar/Widgets/Bluetooth.qml index 04f2757d..ce374bd4 100644 --- a/Modules/Bar/Widgets/Bluetooth.qml +++ b/Modules/Bar/Widgets/Bluetooth.qml @@ -20,7 +20,7 @@ NIconButton { colorBorder: Color.transparent colorBorderHover: Color.transparent - icon: Settings.data.network.bluetoothEnabled ? "bluetooth" : "bluetooth-off" + icon: BluetoothService.enabled ? "bluetooth" : "bluetooth-off" tooltipText: I18n.tr("tooltips.bluetooth-devices") onClicked: PanelService.getPanel("bluetoothPanel")?.toggle(this) onRightClicked: PanelService.getPanel("bluetoothPanel")?.toggle(this) diff --git a/Modules/Settings/Tabs/NetworkTab.qml b/Modules/Settings/Tabs/NetworkTab.qml index 104ac149..b19a0819 100644 --- a/Modules/Settings/Tabs/NetworkTab.qml +++ b/Modules/Settings/Tabs/NetworkTab.qml @@ -23,7 +23,7 @@ ColumnLayout { NToggle { label: I18n.tr("settings.network.bluetooth.label") - checked: Settings.data.network.bluetoothEnabled + checked: BluetoothService.enabled onToggled: checked => BluetoothService.setBluetoothEnabled(checked) } diff --git a/Services/BluetoothService.qml b/Services/BluetoothService.qml index ccfc0439..8820592c 100644 --- a/Services/BluetoothService.qml +++ b/Services/BluetoothService.qml @@ -21,6 +21,7 @@ Singleton { return dev && (dev.paired || dev.trusted) }) } + readonly property var allDevicesWithBattery: { if (!adapter || !adapter.devices) { return [] @@ -30,30 +31,8 @@ Singleton { }) } - property bool lastAdapterState: false - property bool restoringState: false - function init() { Logger.log("Bluetooth", "Service initialized") - // Try to restore saved state if needed - if (Settings.data.network.bluetoothEnabled !== undefined && adapter) { - restoringState = true - adapter.enabled = Settings.data.network.bluetoothEnabled - restoringState = false - } - - // Need to delay a bit for the adapter to be ready - initTimer.running = true - } - - Timer { - id: initTimer - interval: 1000 - repeat: false - onTriggered: { - lastAdapterState = adapter?.enabled ?? false - Logger.log("Bluetooth", "LastAdapterState:", lastAdapterState) - } } Timer { @@ -72,21 +51,11 @@ Singleton { } Logger.log("Bluetooth", "onEnableChanged", adapter.enabled) - - // Only save to settings if this is a user-initiated change - if (!restoringState) { - Settings.data.network.bluetoothEnabled = adapter.enabled - } - - // Show toast only for actual state changes - if (lastAdapterState !== adapter.enabled) { - lastAdapterState = adapter.enabled - if (adapter.enabled) { - ToastService.showNotice(I18n.tr("bluetooth.panel.title"), I18n.tr("toast.bluetooth.enabled")) - discoveryTimer.running = true - } else { - ToastService.showNotice(I18n.tr("bluetooth.panel.title"), I18n.tr("toast.bluetooth.disabled")) - } + if (adapter.enabled) { + ToastService.showNotice(I18n.tr("bluetooth.panel.title"), I18n.tr("toast.bluetooth.enabled")) + discoveryTimer.running = true + } else { + ToastService.showNotice(I18n.tr("bluetooth.panel.title"), I18n.tr("toast.bluetooth.disabled")) } } } @@ -146,14 +115,10 @@ Singleton { if (!device) return false - /* Paired - Means you’ve successfully exchanged keys with the device. - The devices remember each other and can authenticate without repeating the pairing process. - Example: once your headphones are paired, you don’t need to type a PIN every time. Hence, instead of !device.paired, should be device.connected */ From c1c91edb6c485007072658a0aadae48887546fa3 Mon Sep 17 00:00:00 2001 From: ItsLemmy Date: Sat, 27 Sep 2025 17:51:52 -0400 Subject: [PATCH 31/42] NButton: no bar position is allowed in Widgets/ - Only exception is NPanel. --- Widgets/NButton.qml | 1 - 1 file changed, 1 deletion(-) diff --git a/Widgets/NButton.qml b/Widgets/NButton.qml index e80b6b31..f05247ae 100644 --- a/Widgets/NButton.qml +++ b/Widgets/NButton.qml @@ -129,7 +129,6 @@ Rectangle { NTooltip { id: tooltip target: root - positionAbove: Settings.data.bar.position === "bottom" text: root.tooltipText } From 92460fc5c32163fd7534ec38c793cb2dc03bd729 Mon Sep 17 00:00:00 2001 From: ItsLemmy Date: Sat, 27 Sep 2025 18:22:57 -0400 Subject: [PATCH 32/42] IPC call to enable/disable/toggle wallpaper random automation. Fix #378 --- Services/BluetoothService.qml | 1 + Services/IPCService.qml | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/Services/BluetoothService.qml b/Services/BluetoothService.qml index 8820592c..c5e17d62 100644 --- a/Services/BluetoothService.qml +++ b/Services/BluetoothService.qml @@ -115,6 +115,7 @@ Singleton { if (!device) return false + /* Paired Means you’ve successfully exchanged keys with the device. diff --git a/Services/IPCService.qml b/Services/IPCService.qml index d2241c38..0095705c 100644 --- a/Services/IPCService.qml +++ b/Services/IPCService.qml @@ -172,5 +172,15 @@ Item { } WallpaperService.changeWallpaper(path, screen) } + + function toggleAutomation() { + Settings.data.wallpaper.randomEnabled = !Settings.data.wallpaper.randomEnabled + } + function disableAutomation() { + Settings.data.wallpaper.randomEnabled = false + } + function enableAutomation() { + Settings.data.wallpaper.randomEnabled = true + } } } From 7e9f7f40ef9b79878dc3836140ebb715cbae4541 Mon Sep 17 00:00:00 2001 From: ItsLemmy Date: Sat, 27 Sep 2025 20:40:15 -0400 Subject: [PATCH 33/42] v2.14.4 --- Services/UpdateService.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Services/UpdateService.qml b/Services/UpdateService.qml index 7782e03b..f668b177 100644 --- a/Services/UpdateService.qml +++ b/Services/UpdateService.qml @@ -8,8 +8,8 @@ Singleton { id: root // Public properties - property string baseVersion: "2.14.3" - property bool isDevelopment: true + property string baseVersion: "2.14.4" + property bool isDevelopment: false property string currentVersion: `v${!isDevelopment ? baseVersion : baseVersion + "-dev"}` From fbf80ab57798825adcc2030d8837dac36a6c7d64 Mon Sep 17 00:00:00 2001 From: ItsLemmy Date: Sat, 27 Sep 2025 20:40:48 -0400 Subject: [PATCH 34/42] v2.14.4-dev --- Services/UpdateService.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Services/UpdateService.qml b/Services/UpdateService.qml index f668b177..2b24e7b8 100644 --- a/Services/UpdateService.qml +++ b/Services/UpdateService.qml @@ -9,7 +9,7 @@ Singleton { // Public properties property string baseVersion: "2.14.4" - property bool isDevelopment: false + property bool isDevelopment: true property string currentVersion: `v${!isDevelopment ? baseVersion : baseVersion + "-dev"}` From 0593543d7a93e3f6d81d9dfa1c294d190accb0a0 Mon Sep 17 00:00:00 2001 From: ItsLemmy Date: Sat, 27 Sep 2025 21:12:39 -0400 Subject: [PATCH 35/42] Tooltip: Refactoring in a single global tooltip. --- Modules/Bar/Extras/BarPillHorizontal.qml | 21 +- Modules/Bar/Extras/BarPillVertical.qml | 22 +- Modules/Bar/Widgets/ActiveWindow.qml | 18 +- Modules/Bar/Widgets/Bluetooth.qml | 4 +- Modules/Bar/Widgets/Clock.qml | 14 +- Modules/Bar/Widgets/ControlCenter.qml | 4 +- Modules/Bar/Widgets/DarkMode.qml | 4 +- Modules/Bar/Widgets/KeepAwake.qml | 4 +- Modules/Bar/Widgets/MediaMini.qml | 46 +- Modules/Bar/Widgets/NightLight.qml | 4 +- Modules/Bar/Widgets/NotificationHistory.qml | 4 +- Modules/Bar/Widgets/PowerProfile.qml | 4 +- Modules/Bar/Widgets/ScreenRecorder.qml | 4 +- Modules/Bar/Widgets/SessionMenu.qml | 4 +- Modules/Bar/Widgets/Taskbar.qml | 13 +- Modules/Bar/Widgets/Tray.qml | 11 +- Modules/Bar/Widgets/WallpaperSelector.qml | 4 +- Modules/Bar/Widgets/WiFi.qml | 7 +- Modules/Dock/Dock.qml | 16 +- Modules/OSD/OSD.qml | 18 +- .../Settings/Bar/BarWidgetSettingsDialog.qml | 1 + Modules/Toast/ToastScreen.qml | 10 +- Modules/Tooltip/Tooltip.qml | 414 ++++++++++++++++++ Services/BarService.qml | 13 + Services/PanelService.qml | 3 + Widgets/NButton.qml | 15 +- Widgets/NIconButton.qml | 19 +- Widgets/NSearchableComboBox.qml | 10 +- Widgets/NTooltip.qml | 197 --------- shell.qml | 6 + 30 files changed, 532 insertions(+), 382 deletions(-) create mode 100644 Modules/Tooltip/Tooltip.qml delete mode 100644 Widgets/NTooltip.qml diff --git a/Modules/Bar/Extras/BarPillHorizontal.qml b/Modules/Bar/Extras/BarPillHorizontal.qml index f32bfc48..4962153c 100644 --- a/Modules/Bar/Extras/BarPillHorizontal.qml +++ b/Modules/Bar/Extras/BarPillHorizontal.qml @@ -46,6 +46,15 @@ Item { width: pillHeight + Math.max(0, pill.width - pillOverlap) height: pillHeight + Connections { + target: root + function onTooltipTextChanged() { + if (PanelService.tooltip.visible) { + PanelService.tooltip.updateText(root.tooltipText) + } + } + } + Rectangle { id: pill width: revealed ? pillMaxWidth : 1 @@ -195,14 +204,6 @@ Item { } } - NTooltip { - id: tooltip - positionAbove: Settings.data.bar.position === "bottom" - target: pill - delay: Style.tooltipDelayLong - text: root.tooltipText - } - Timer { id: showTimer interval: Style.pillDelay @@ -220,7 +221,7 @@ Item { onEntered: { hovered = true root.entered() - tooltip.show() + PanelService.tooltip.show(pill, root.tooltipText, BarService.getTooltipDirection(), Style.tooltipDelayLong) if (disableOpen || forceClose) { return } @@ -234,7 +235,7 @@ Item { if (!forceOpen && !forceClose) { hide() } - tooltip.hide() + PanelService.tooltip.hide() } onClicked: function (mouse) { if (mouse.button === Qt.LeftButton) { diff --git a/Modules/Bar/Extras/BarPillVertical.qml b/Modules/Bar/Extras/BarPillVertical.qml index deb304be..f24ff086 100644 --- a/Modules/Bar/Extras/BarPillVertical.qml +++ b/Modules/Bar/Extras/BarPillVertical.qml @@ -58,6 +58,15 @@ Item { width: buttonSize height: revealed ? (buttonSize + maxPillHeight - pillOverlap) : buttonSize + Connections { + target: root + function onTooltipTextChanged() { + if (PanelService.tooltip.visible) { + PanelService.tooltip.updateText(root.tooltipText) + } + } + } + Rectangle { id: pill width: revealed ? maxPillWidth : 1 @@ -236,15 +245,6 @@ Item { } } - NTooltip { - id: tooltip - target: pill - text: root.tooltipText - positionLeft: barPosition === "right" - positionRight: barPosition === "left" - delay: Style.tooltipDelayLong - } - Timer { id: showTimer interval: Style.pillDelay @@ -262,7 +262,7 @@ Item { onEntered: { hovered = true root.entered() - tooltip.show() + PanelService.tooltip.show(pill, root.tooltipText, BarService.getTooltipDirection(), Style.tooltipDelayLong) if (disableOpen || forceClose) { return } @@ -276,7 +276,7 @@ Item { if (!forceOpen && !forceClose) { hide() } - tooltip.hide() + PanelService.tooltip.hide() } onClicked: function (mouse) { if (mouse.button === Qt.LeftButton) { diff --git a/Modules/Bar/Widgets/ActiveWindow.qml b/Modules/Bar/Widgets/ActiveWindow.qml index 8a43014f..537c53bd 100644 --- a/Modules/Bar/Widgets/ActiveWindow.qml +++ b/Modules/Bar/Widgets/ActiveWindow.qml @@ -328,29 +328,17 @@ Item { cursorShape: Qt.PointingHandCursor acceptedButtons: Qt.LeftButton onEntered: { - if (barPosition === "left" || barPosition === "right") { - tooltip.show() - } else if ((tooltip.text !== "") && (scrollingMode === "never")) { - tooltip.show() + if ((windowTitle !== "") && (barPosition === "left" || barPosition === "right") || (scrollingMode === "never")) { + PanelService.tooltip.show(root, windowTitle, BarService.getTooltipDirection()) } } onExited: { - tooltip.hide() + PanelService.tooltip.hide() } } } } - NTooltip { - id: tooltip - text: windowTitle - target: (barPosition === "left" || barPosition === "right") ? verticalLayout : windowActiveRect - positionLeft: barPosition === "right" - positionRight: barPosition === "left" - positionAbove: Settings.data.bar.position === "bottom" - delay: Style.tooltipDelay - } - Connections { target: CompositorService function onActiveWindowChanged() { diff --git a/Modules/Bar/Widgets/Bluetooth.qml b/Modules/Bar/Widgets/Bluetooth.qml index ce374bd4..e76fa234 100644 --- a/Modules/Bar/Widgets/Bluetooth.qml +++ b/Modules/Bar/Widgets/Bluetooth.qml @@ -19,9 +19,9 @@ NIconButton { colorFg: Color.mOnSurface colorBorder: Color.transparent colorBorderHover: Color.transparent - - icon: BluetoothService.enabled ? "bluetooth" : "bluetooth-off" tooltipText: I18n.tr("tooltips.bluetooth-devices") + tooltipDirection: BarService.getTooltipDirection() + icon: BluetoothService.enabled ? "bluetooth" : "bluetooth-off" onClicked: PanelService.getPanel("bluetoothPanel")?.toggle(this) onRightClicked: PanelService.getPanel("bluetoothPanel")?.toggle(this) } diff --git a/Modules/Bar/Widgets/Clock.qml b/Modules/Bar/Widgets/Clock.qml index 0b314ffd..29bce8c6 100644 --- a/Modules/Bar/Widgets/Clock.qml +++ b/Modules/Bar/Widgets/Clock.qml @@ -106,14 +106,6 @@ Rectangle { } } } - NTooltip { - id: tooltip - text: I18n.tr("clock.tooltip") - target: clockContainer - positionAbove: Settings.data.bar.position === "bottom" - positionLeft: barPosition === "right" - positionRight: barPosition === "left" - } MouseArea { id: clockMouseArea @@ -122,14 +114,14 @@ Rectangle { hoverEnabled: true onEntered: { if (!PanelService.getPanel("calendarPanel")?.active) { - tooltip.show() + PanelService.tooltip.show(root, I18n.tr("clock.tooltip"), BarService.getTooltipDirection()) } } onExited: { - tooltip.hide() + PanelService.tooltip.hide() } onClicked: { - tooltip.hide() + PanelService.tooltip.hide() PanelService.getPanel("calendarPanel")?.toggle(this) } } diff --git a/Modules/Bar/Widgets/ControlCenter.qml b/Modules/Bar/Widgets/ControlCenter.qml index b730152a..69c1b39b 100644 --- a/Modules/Bar/Widgets/ControlCenter.qml +++ b/Modules/Bar/Widgets/ControlCenter.qml @@ -36,9 +36,7 @@ NIconButton { // If we have a custom path or distro logo, don't use the theme icon. icon: (customIconPath === "" && !useDistroLogo) ? customIcon : "" tooltipText: I18n.tr("tooltips.open-control-center") - tooltipPositionAbove: Settings.data.bar.position === "bottom" - tooltipPositionLeft: Settings.data.bar.position === "right" - tooltipPositionRight: Settings.data.bar.position === "left" + tooltipDirection: BarService.getTooltipDirection() baseSize: Style.capsuleHeight compact: (Settings.data.bar.density === "compact") colorBg: (Settings.data.bar.showCapsule ? Color.mSurfaceVariant : Color.transparent) diff --git a/Modules/Bar/Widgets/DarkMode.qml b/Modules/Bar/Widgets/DarkMode.qml index 333b3a9b..b35dd236 100644 --- a/Modules/Bar/Widgets/DarkMode.qml +++ b/Modules/Bar/Widgets/DarkMode.qml @@ -11,9 +11,7 @@ NIconButton { icon: "dark-mode" tooltipText: Settings.data.colorSchemes.darkMode ? I18n.tr("tooltips.switch-to-light-mode") : I18n.tr("tooltips.switch-to-dark-mode") - tooltipPositionAbove: Settings.data.bar.position === "bottom" - tooltipPositionLeft: Settings.data.bar.position === "right" - tooltipPositionRight: Settings.data.bar.position === "left" + tooltipDirection: BarService.getTooltipDirection() compact: (Settings.data.bar.density === "compact") baseSize: Style.capsuleHeight colorBg: Settings.data.colorSchemes.darkMode ? (Settings.data.bar.showCapsule ? Color.mSurfaceVariant : Color.transparent) : Color.mPrimary diff --git a/Modules/Bar/Widgets/KeepAwake.qml b/Modules/Bar/Widgets/KeepAwake.qml index 72321b7c..ba09d92a 100644 --- a/Modules/Bar/Widgets/KeepAwake.qml +++ b/Modules/Bar/Widgets/KeepAwake.qml @@ -15,9 +15,7 @@ NIconButton { compact: (Settings.data.bar.density === "compact") icon: IdleInhibitorService.isInhibited ? "keep-awake-on" : "keep-awake-off" tooltipText: IdleInhibitorService.isInhibited ? I18n.tr("tooltips.disable-keep-awake") : I18n.tr("tooltips.enable-keep-awake") - tooltipPositionAbove: Settings.data.bar.position === "bottom" - tooltipPositionLeft: Settings.data.bar.position === "right" - tooltipPositionRight: Settings.data.bar.position === "left" + tooltipDirection: BarService.getTooltipDirection() colorBg: IdleInhibitorService.isInhibited ? Color.mPrimary : (Settings.data.bar.showCapsule ? Color.mSurfaceVariant : Color.transparent) colorFg: IdleInhibitorService.isInhibited ? Color.mOnPrimary : Color.mOnSurface colorBorder: Color.transparent diff --git a/Modules/Bar/Widgets/MediaMini.qml b/Modules/Bar/Widgets/MediaMini.qml index 002fc67c..53914fcb 100644 --- a/Modules/Bar/Widgets/MediaMini.qml +++ b/Modules/Bar/Widgets/MediaMini.qml @@ -42,6 +42,21 @@ Item { // Fixed width - no expansion readonly property real widgetWidth: Math.max(1, screen.width * 0.06) + readonly property string tooltipText: { + var title = getTitle() + var controls = "" + if (MediaService.canGoNext) { + controls += "Right click for next.\n" + } + if (MediaService.canGoPrevious) { + controls += "Middle click for previous." + } + if (controls !== "") { + return title + "\n\n" + controls + } + return title + } + implicitHeight: visible ? ((barPosition === "left" || barPosition === "right") ? calculatedVerticalHeight() : Math.round(Style.barHeight * scaling)) : 0 implicitWidth: visible ? ((barPosition === "left" || barPosition === "right") ? Math.round(Style.baseWidgetSize * 0.8 * scaling) : (widgetWidth * scaling)) : 0 @@ -360,39 +375,14 @@ Item { } onEntered: { - if ((tooltip.text !== "") && (barPosition === "left" || barPosition === "right")) { - tooltip.show() - } else if ((tooltip.text !== "") && (scrollingMode === "never")) { - tooltip.show() + if ((tooltipText !== "") && (barPosition === "left" || barPosition === "right") || (scrollingMode === "never")) { + PanelService.tooltip.show(root, tooltipText, BarService.getTooltipDirection()) } } onExited: { - tooltip.hide() + PanelService.tooltip.hide() } } } } - - NTooltip { - id: tooltip - text: { - var title = getTitle() - var controls = "" - if (MediaService.canGoNext) { - controls += "Right click for next.\n" - } - if (MediaService.canGoPrevious) { - controls += "Middle click for previous." - } - if (controls !== "") { - return title + "\n\n" + controls - } - return title - } - target: (barPosition === "left" || barPosition === "right") ? verticalLayout : mediaMini - positionLeft: barPosition === "right" - positionRight: barPosition === "left" - positionAbove: Settings.data.bar.position === "bottom" - delay: Style.tooltipDelay - } } diff --git a/Modules/Bar/Widgets/NightLight.qml b/Modules/Bar/Widgets/NightLight.qml index 72f44867..b972d37d 100644 --- a/Modules/Bar/Widgets/NightLight.qml +++ b/Modules/Bar/Widgets/NightLight.qml @@ -23,9 +23,7 @@ NIconButton { icon: Settings.data.nightLight.enabled ? (Settings.data.nightLight.forced ? "nightlight-forced" : "nightlight-on") : "nightlight-off" tooltipText: Settings.data.nightLight.enabled ? (Settings.data.nightLight.forced ? I18n.tr("tooltips.night-light-forced") : I18n.tr("tooltips.night-light-enabled")) : I18n.tr("tooltips.night-light-disabled") - tooltipPositionAbove: Settings.data.bar.position === "bottom" - tooltipPositionLeft: Settings.data.bar.position === "right" - tooltipPositionRight: Settings.data.bar.position === "left" + tooltipDirection: BarService.getTooltipDirection() onClicked: { // Check if wlsunset is available before enabling night light if (!ProgramCheckerService.wlsunsetAvailable) { diff --git a/Modules/Bar/Widgets/NotificationHistory.qml b/Modules/Bar/Widgets/NotificationHistory.qml index c5a9f47b..2fee32ae 100644 --- a/Modules/Bar/Widgets/NotificationHistory.qml +++ b/Modules/Bar/Widgets/NotificationHistory.qml @@ -53,9 +53,7 @@ NIconButton { compact: (Settings.data.bar.density === "compact") icon: Settings.data.notifications.doNotDisturb ? "bell-off" : "bell" tooltipText: Settings.data.notifications.doNotDisturb ? I18n.tr("tooltips.open-notification-history-disable-dnd") : I18n.tr("tooltips.open-notification-history-enable-dnd") - tooltipPositionAbove: Settings.data.bar.position === "bottom" - tooltipPositionLeft: Settings.data.bar.position === "right" - tooltipPositionRight: Settings.data.bar.position === "left" + tooltipDirection: BarService.getTooltipDirection() colorBg: (Settings.data.bar.showCapsule ? Color.mSurfaceVariant : Color.transparent) colorFg: Color.mOnSurface colorBorder: Color.transparent diff --git a/Modules/Bar/Widgets/PowerProfile.qml b/Modules/Bar/Widgets/PowerProfile.qml index 0158c024..fbd2a719 100644 --- a/Modules/Bar/Widgets/PowerProfile.qml +++ b/Modules/Bar/Widgets/PowerProfile.qml @@ -19,9 +19,7 @@ NIconButton { tooltipText: I18n.tr("tooltips.power-profile", { "profile": PowerProfileService.getName() }) - tooltipPositionAbove: Settings.data.bar.position === "bottom" - tooltipPositionLeft: Settings.data.bar.position === "right" - tooltipPositionRight: Settings.data.bar.position === "left" + tooltipDirection: BarService.getTooltipDirection() compact: (Settings.data.bar.density === "compact") colorBg: (PowerProfileService.profile === PowerProfile.Balanced) ? (Settings.data.bar.showCapsule ? Color.mSurfaceVariant : Color.transparent) : Color.mPrimary colorFg: (PowerProfileService.profile === PowerProfile.Balanced) ? Color.mOnSurface : Color.mOnPrimary diff --git a/Modules/Bar/Widgets/ScreenRecorder.qml b/Modules/Bar/Widgets/ScreenRecorder.qml index 01971e25..5149a3a6 100644 --- a/Modules/Bar/Widgets/ScreenRecorder.qml +++ b/Modules/Bar/Widgets/ScreenRecorder.qml @@ -12,9 +12,7 @@ NIconButton { icon: "camera-video" tooltipText: ScreenRecorderService.isRecording ? I18n.tr("tooltips.click-to-stop-recording") : I18n.tr("tooltips.click-to-start-recording") - tooltipPositionAbove: Settings.data.bar.position === "bottom" - tooltipPositionLeft: Settings.data.bar.position === "right" - tooltipPositionRight: Settings.data.bar.position === "left" + tooltipDirection: BarService.getTooltipDirection() compact: (Settings.data.bar.density === "compact") baseSize: Style.capsuleHeight colorBg: ScreenRecorderService.isRecording ? Color.mPrimary : (Settings.data.bar.showCapsule ? Color.mSurfaceVariant : Color.transparent) diff --git a/Modules/Bar/Widgets/SessionMenu.qml b/Modules/Bar/Widgets/SessionMenu.qml index fb309d29..f8f09929 100644 --- a/Modules/Bar/Widgets/SessionMenu.qml +++ b/Modules/Bar/Widgets/SessionMenu.qml @@ -15,9 +15,7 @@ NIconButton { baseSize: Style.capsuleHeight icon: "power" tooltipText: I18n.tr("tooltips.session-menu") - tooltipPositionAbove: Settings.data.bar.position === "bottom" - tooltipPositionLeft: Settings.data.bar.position === "right" - tooltipPositionRight: Settings.data.bar.position === "left" + tooltipDirection: BarService.getTooltipDirection() colorBg: (Settings.data.bar.showCapsule ? Color.mSurfaceVariant : Color.transparent) colorFg: Color.mError colorBorder: Color.transparent diff --git a/Modules/Bar/Widgets/Taskbar.qml b/Modules/Bar/Widgets/Taskbar.qml index 30057eb7..5f7d7ed3 100644 --- a/Modules/Bar/Widgets/Taskbar.qml +++ b/Modules/Bar/Widgets/Taskbar.qml @@ -98,17 +98,8 @@ Rectangle { } } } - onEntered: taskbarTooltip.show() - onExited: taskbarTooltip.hide() - } - - NTooltip { - id: taskbarTooltip - text: taskbarItem.modelData.title || taskbarItem.modelData.appId || "Unknown app." - target: taskbarItem - positionAbove: Settings.data.bar.position === "bottom" - positionLeft: Settings.data.bar.position === "right" - positionRight: Settings.data.bar.position === "left" + onEntered: PanelService.tooltip.show(taskbarItem, taskbarItem.modelData.title || taskbarItem.modelData.appId || "Unknown app.", BarService.getTooltipDirection()) + onExited: PanelService.tooltip.hide() } } } diff --git a/Modules/Bar/Widgets/Tray.qml b/Modules/Bar/Widgets/Tray.qml index 3904a9cf..927ffae9 100644 --- a/Modules/Bar/Widgets/Tray.qml +++ b/Modules/Bar/Widgets/Tray.qml @@ -135,15 +135,8 @@ Rectangle { } } } - onEntered: trayTooltip.show() - onExited: trayTooltip.hide() - } - - NTooltip { - id: trayTooltip - target: trayIcon - text: modelData.tooltipTitle || modelData.name || modelData.id || "Tray Item" - positionAbove: Settings.data.bar.position === "bottom" + onEntered: PanelService.tooltip.show(trayIcon, modelData.tooltipTitle || modelData.name || modelData.id || "Tray Item", BarService.getTooltipDirection()) + onExited: PanelService.tooltip.hide() } } } diff --git a/Modules/Bar/Widgets/WallpaperSelector.qml b/Modules/Bar/Widgets/WallpaperSelector.qml index 79e794ea..4be3e9a1 100644 --- a/Modules/Bar/Widgets/WallpaperSelector.qml +++ b/Modules/Bar/Widgets/WallpaperSelector.qml @@ -15,9 +15,7 @@ NIconButton { compact: (Settings.data.bar.density === "compact") icon: "wallpaper-selector" tooltipText: I18n.tr("tooltips.open-wallpaper-selector") - tooltipPositionAbove: Settings.data.bar.position === "bottom" - tooltipPositionLeft: Settings.data.bar.position === "right" - tooltipPositionRight: Settings.data.bar.position === "left" + tooltipDirection: BarService.getTooltipDirection() colorBg: (Settings.data.bar.showCapsule ? Color.mSurfaceVariant : Color.transparent) colorFg: Color.mOnSurface colorBorder: Color.transparent diff --git a/Modules/Bar/Widgets/WiFi.qml b/Modules/Bar/Widgets/WiFi.qml index 610a0351..00fefab6 100644 --- a/Modules/Bar/Widgets/WiFi.qml +++ b/Modules/Bar/Widgets/WiFi.qml @@ -19,7 +19,8 @@ NIconButton { colorFg: Color.mOnSurface colorBorder: Color.transparent colorBorderHover: Color.transparent - + tooltipText: I18n.tr("tooltips.manage-wifi") + tooltipDirection: BarService.getTooltipDirection() icon: { try { if (NetworkService.ethernetConnected) { @@ -40,10 +41,6 @@ NIconButton { return "signal_wifi_bad" } } - tooltipText: I18n.tr("tooltips.manage-wifi") - tooltipPositionAbove: Settings.data.bar.position === "bottom" - tooltipPositionLeft: Settings.data.bar.position === "right" - tooltipPositionRight: Settings.data.bar.position === "left" onClicked: PanelService.getPanel("wifiPanel")?.toggle(this) onRightClicked: PanelService.getPanel("wifiPanel")?.toggle(this) } diff --git a/Modules/Dock/Dock.qml b/Modules/Dock/Dock.qml index f6135c37..6b8d7484 100644 --- a/Modules/Dock/Dock.qml +++ b/Modules/Dock/Dock.qml @@ -373,14 +373,6 @@ Variants { } } - // Individual tooltip for this app - NTooltip { - id: appTooltip - target: appButton - positionAbove: true - visible: false - } - Image { id: appIcon width: iconSize @@ -481,8 +473,8 @@ Variants { onEntered: { anyAppHovered = true const appName = appButton.appTitle || appButton.appId || "Unknown" - appTooltip.text = appName.length > 40 ? appName.substring(0, 37) + "..." : appName - appTooltip.isVisible = true + const tooltipText = appName.length > 40 ? appName.substring(0, 37) + "..." : appName + PanelService.tooltip.show(appButton, tooltipText, "top") if (autoHide) { showTimer.stop() hideTimer.stop() @@ -492,7 +484,7 @@ Variants { onExited: { anyAppHovered = false - appTooltip.hide() + PanelService.tooltip.hide() if (autoHide && !dockHovered && !peekHovered && !menuHovered) { hideTimer.restart() } @@ -508,7 +500,7 @@ Variants { // Close any other existing context menu first root.closeAllContextMenus() // Hide tooltip when showing context menu - appTooltip.hide() + PanelService.tooltip.hide() contextMenu.show(appButton, modelData.toplevel || modelData) return } diff --git a/Modules/OSD/OSD.qml b/Modules/OSD/OSD.qml index 1ad395a7..059dcb5d 100644 --- a/Modules/OSD/OSD.qml +++ b/Modules/OSD/OSD.qml @@ -410,10 +410,10 @@ Variants { // Make visible and animate in osdItem.visible = true // Use Qt.callLater to ensure the visible change is processed before animation - Qt.callLater(function () { - osdItem.opacity = 1 - osdItem.scale = 1.0 - }) + Qt.callLater(() => { + osdItem.opacity = 1 + osdItem.scale = 1.0 + }) // Start the auto-hide timer hideTimer.start() @@ -524,11 +524,11 @@ Variants { root.item.showOSD() } else { // If item not ready yet, wait for it - Qt.callLater(function () { - if (root.item) { - root.item.showOSD() - } - }) + Qt.callLater(() => { + if (root.item) { + root.item.showOSD() + } + }) } } diff --git a/Modules/Settings/Bar/BarWidgetSettingsDialog.qml b/Modules/Settings/Bar/BarWidgetSettingsDialog.qml index 68310021..a198b1e3 100644 --- a/Modules/Settings/Bar/BarWidgetSettingsDialog.qml +++ b/Modules/Settings/Bar/BarWidgetSettingsDialog.qml @@ -69,6 +69,7 @@ Popup { NIconButton { icon: "close" + tooltipText: "Close" onClicked: widgetSettings.close() } } diff --git a/Modules/Toast/ToastScreen.qml b/Modules/Toast/ToastScreen.qml index a701c898..6a635aea 100644 --- a/Modules/Toast/ToastScreen.qml +++ b/Modules/Toast/ToastScreen.qml @@ -93,11 +93,11 @@ Item { // Activate the loader and show toast windowLoader.active = true // Need a small delay to ensure the window is created - Qt.callLater(function () { - if (windowLoader.item) { - windowLoader.item.showToast(data.message, data.description, data.type, data.duration) - } - }) + Qt.callLater(() => { + if (windowLoader.item) { + windowLoader.item.showToast(data.message, data.description, data.type, data.duration) + } + }) } function onToastHidden() { diff --git a/Modules/Tooltip/Tooltip.qml b/Modules/Tooltip/Tooltip.qml new file mode 100644 index 00000000..171971f6 --- /dev/null +++ b/Modules/Tooltip/Tooltip.qml @@ -0,0 +1,414 @@ +import QtQuick +import QtQuick.Layouts +import Quickshell +import qs.Commons +import qs.Services +import qs.Widgets + +PopupWindow { + id: root + + property string text: "" + property string direction: "auto" // "auto", "left", "right", "top", "bottom" + property int margin: Style.marginXS // distance from target + property int padding: Style.marginM + property int delay: 0 + property int hideDelay: 0 + property int maxWidth: 340 + property real scaling: 1.0 + property int animationDuration: Style.animationFast + property real animationScale: 0.85 + + // Internal properties + property var targetItem: null + property real anchorX: 0 + property real anchorY: 0 + property bool isPositioned: false + property bool pendingShow: false + property bool animatingOut: false + + visible: false + color: Color.transparent + + anchor.item: targetItem + anchor.rect.x: anchorX + anchor.rect.y: anchorY + + implicitWidth: Math.min(tooltipText.implicitWidth + padding * 2 * scaling, maxWidth * scaling) + implicitHeight: tooltipText.implicitHeight + padding * 2 * scaling + + // Timer for showing tooltip after delay + Timer { + id: showTimer + interval: root.delay + repeat: false + onTriggered: { + root.positionAndShow() + } + } + + // Timer for hiding tooltip after delay + Timer { + id: hideTimer + interval: root.hideDelay + repeat: false + onTriggered: { + root.startHideAnimation() + } + } + + // Show animation + ParallelAnimation { + id: showAnimation + + PropertyAnimation { + target: tooltipContainer + property: "opacity" + from: 0.0 + to: 1.0 + duration: root.animationDuration + easing.type: Easing.OutCubic + } + + PropertyAnimation { + target: tooltipContainer + property: "scale" + from: root.animationScale + to: 1.0 + duration: root.animationDuration + easing.type: Easing.OutBack + easing.overshoot: 1.2 + } + } + + // Hide animation + ParallelAnimation { + id: hideAnimation + + PropertyAnimation { + target: tooltipContainer + property: "opacity" + from: 1.0 + to: 0.0 + duration: root.animationDuration * 0.75 // Slightly faster hide + easing.type: Easing.InCubic + } + + PropertyAnimation { + target: tooltipContainer + property: "scale" + from: 1.0 + to: root.animationScale + duration: root.animationDuration * 0.75 + easing.type: Easing.InCubic + } + + onFinished: { + root.completeHide() + } + } + + // Function to show tooltip + function show(target, tipText, customDirection, showDelay) { + if (!target || !tipText || tipText === "") + return + + if (showDelay !== undefined) { + delay = showDelay + } else { + delay = Style.tooltipDelay + } + + // Stop any running timers and animations + hideTimer.stop() + showTimer.stop() + hideAnimation.stop() + animatingOut = false + + // If we're already showing for a different target, hide immediately + if (visible && targetItem !== target) { + hideImmediately() + } + + // Set properties + targetItem = target + text = tipText + pendingShow = true + + if (customDirection !== undefined) { + direction = customDirection + } else { + direction = "auto" + } + + // Start show timer + showTimer.start() + } + + // Function to position and display the tooltip + function positionAndShow() { + if (!targetItem || !pendingShow) + return + + // Get screen dimensions - try multiple methods + var screenWidth = Screen.width + var screenHeight = Screen.height + + // Try to get screen from target item + if (targetItem) { + if (targetItem.screen) { + screenWidth = targetItem.screen.width + screenHeight = targetItem.screen.height + scaling = ScalingService.getScreenScale(targetItem.screen) + } + } + + // Calculate tooltip dimensions + const tipWidth = Math.min(tooltipText.implicitWidth + padding * 2 * scaling, maxWidth * scaling) + const tipHeight = tooltipText.implicitHeight + padding * 2 * scaling + + // Get target's global position + const targetGlobal = targetItem.mapToGlobal(0, 0) + const targetWidth = targetItem.width + const targetHeight = targetItem.height + + var newAnchorX = 0 + var newAnchorY = 0 + + if (direction === "auto") { + // Calculate available space in each direction + const spaceLeft = targetGlobal.x + const spaceRight = screenWidth - (targetGlobal.x + targetWidth) + const spaceTop = targetGlobal.y + const spaceBottom = screenHeight - (targetGlobal.y + targetHeight) + + // Try positions in order of available space + const positions = [{ + "dir": "bottom", + "space": spaceBottom, + "x": (targetWidth - tipWidth) / 2, + "y": targetHeight + margin * scaling, + "fits": spaceBottom >= tipHeight + margin * scaling + }, { + "dir": "top", + "space": spaceTop, + "x": (targetWidth - tipWidth) / 2, + "y": -tipHeight - margin * scaling, + "fits": spaceTop >= tipHeight + margin * scaling + }, { + "dir": "right", + "space": spaceRight, + "x": targetWidth + margin * scaling, + "y": (targetHeight - tipHeight) / 2, + "fits": spaceRight >= tipWidth + margin * scaling + }, { + "dir": "left", + "space": spaceLeft, + "x": -tipWidth - margin * scaling, + "y": (targetHeight - tipHeight) / 2, + "fits": spaceLeft >= tipWidth + margin * scaling + }] + + // Find first position that fits + var selectedPosition = null + for (var i = 0; i < positions.length; i++) { + if (positions[i].fits) { + selectedPosition = positions[i] + break + } + } + + // If none fit perfectly + if (!selectedPosition) { + // Sort by available space and use position with most space + positions.sort(function (a, b) { + return b.space - a.space + }) + selectedPosition = positions[0] + } + + newAnchorX = selectedPosition.x + newAnchorY = selectedPosition.y + + // Adjust horizontal position to keep tooltip on screen + if (direction === "auto") { + const globalX = targetGlobal.x + newAnchorX + if (globalX < 0) { + newAnchorX = -targetGlobal.x + margin * scaling + } else if (globalX + tipWidth > screenWidth) { + newAnchorX = screenWidth - targetGlobal.x - tipWidth - margin * scaling + } + } + } else { + // Manual direction positioning + switch (direction) { + case "left": + newAnchorX = -tipWidth - margin * scaling + newAnchorY = (targetHeight - tipHeight) / 2 + break + case "right": + newAnchorX = targetWidth + margin * scaling + newAnchorY = (targetHeight - tipHeight) / 2 + break + case "top": + newAnchorX = (targetWidth - tipWidth) / 2 + newAnchorY = -tipHeight - margin * scaling + break + case "bottom": + newAnchorX = (targetWidth - tipWidth) / 2 + newAnchorY = targetHeight + margin * scaling + break + } + } + + // Apply position + anchorX = newAnchorX + anchorY = newAnchorY + isPositioned = true + pendingShow = false + + // Show tooltip and start animation + visible = true + + // Initialize animation state + tooltipContainer.opacity = 0.0 + tooltipContainer.scale = animationScale + + // Start show animation + showAnimation.start() + + // Force anchor update after showing + Qt.callLater(() => { + if (root.anchor && root.visible) { + root.anchor.updateAnchor() + } + }) + } + + // Function to hide tooltip + function hide() { + // Stop show timer if it's running + showTimer.stop() + pendingShow = false + + // Stop hide timer if it's running + hideTimer.stop() + + if (hideDelay > 0 && visible && !animatingOut) { + hideTimer.start() + } else { + startHideAnimation() + } + } + + function startHideAnimation() { + if (!visible || animatingOut) + return + + animatingOut = true + showAnimation.stop() // Stop show animation if running + hideAnimation.start() + } + + function completeHide() { + // Hide the popup window + visible = false + animatingOut = false + pendingShow = false + + // Clear the text and reset state + text = "" + isPositioned = false + + // Reset container state + tooltipContainer.opacity = 1.0 + tooltipContainer.scale = 1.0 + } + + // Quick hide without delay or animation + function hideImmediately() { + showTimer.stop() + hideTimer.stop() + showAnimation.stop() + hideAnimation.stop() + pendingShow = false + animatingOut = false + completeHide() + } + + // Update text function for binding support + function updateText(newText) { + if (visible && targetItem) { + text = newText + positionAndShow() + } + } + + // Reset function to clean up state + function reset() { + // Stop all timers and animations + showTimer.stop() + hideTimer.stop() + showAnimation.stop() + hideAnimation.stop() + + // Clear all state + visible = false + pendingShow = false + animatingOut = false + text = "" + isPositioned = false + + // Reset to defaults + direction = "auto" + delay = 0 + hideDelay = 0 + + // Reset container state + tooltipContainer.opacity = 1.0 + tooltipContainer.scale = 1.0 + } + + // Tooltip content container for animations + Item { + id: tooltipContainer + anchors.fill: parent + + // Animation properties + opacity: 1.0 + scale: 1.0 + transformOrigin: Item.Center + + Rectangle { + anchors.fill: parent + color: Color.mSurface + border.color: Color.mOutline + border.width: Math.max(1, Style.borderS * scaling) + radius: Style.radiusM * scaling + + // Only show content when we have text + visible: root.text !== "" + + NText { + id: tooltipText + anchors.centerIn: parent + anchors.margins: root.padding * root.scaling + text: root.text + font.pointSize: Style.fontSizeS * scaling + color: Color.mOnSurfaceVariant + wrapMode: Text.WordWrap + width: Math.min(implicitWidth, root.maxWidth * root.scaling - root.padding * 2 * root.scaling) + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } + } + } + + Component.onCompleted: { + reset() + } + + Component.onDestruction: { + reset() + } +} diff --git a/Services/BarService.qml b/Services/BarService.qml index 9f8611ed..63e607cc 100644 --- a/Services/BarService.qml +++ b/Services/BarService.qml @@ -202,4 +202,17 @@ Singleton { } return false } + + function getTooltipDirection() { + switch (Settings.data.bar.position) { + case "right": + return "left" + case "left": + return "right" + case "bottom": + return "top" + default: + return "bottom" + } + } } diff --git a/Services/PanelService.qml b/Services/PanelService.qml index 5d33aeb7..59db74b4 100644 --- a/Services/PanelService.qml +++ b/Services/PanelService.qml @@ -10,6 +10,9 @@ Singleton { // This is not a panel... property var lockScreen: null + // A ref. to our global tooltip + property var tooltip: null + // Panels property var registeredPanels: ({}) property var openedPanel: null diff --git a/Widgets/NButton.qml b/Widgets/NButton.qml index f05247ae..89e71c12 100644 --- a/Widgets/NButton.qml +++ b/Widgets/NButton.qml @@ -2,6 +2,7 @@ import QtQuick import QtQuick.Controls import QtQuick.Layouts import qs.Commons +import qs.Services Rectangle { id: root @@ -126,12 +127,6 @@ Rectangle { } } - NTooltip { - id: tooltip - target: root - text: root.tooltipText - } - // Mouse interaction MouseArea { id: mouseArea @@ -144,18 +139,18 @@ Rectangle { onEntered: { root.hovered = true if (tooltipText) { - tooltip.show() + PanelService.tooltip.show(root, root.tooltipText) } } onExited: { root.hovered = false if (tooltipText) { - tooltip.hide() + PanelService.tooltip.hide() } } onPressed: mouse => { if (tooltipText) { - tooltip.hide() + PanelService.tooltip.hide() } if (mouse.button === Qt.LeftButton) { root.clicked() @@ -169,7 +164,7 @@ Rectangle { onCanceled: { root.hovered = false if (tooltipText) { - tooltip.hide() + PanelService.tooltip.hide() } } } diff --git a/Widgets/NIconButton.qml b/Widgets/NIconButton.qml index 0e6c2c8e..5c82dfb3 100644 --- a/Widgets/NIconButton.qml +++ b/Widgets/NIconButton.qml @@ -11,9 +11,7 @@ Rectangle { property string icon property string tooltipText - property bool tooltipPositionAbove: false - property bool tooltipPositionLeft: false - property bool tooltipPositionRight: false + property string tooltipDirection: "auto" property bool enabled: true property bool allowClickWhenDisabled: false property bool hovering: false @@ -65,15 +63,6 @@ Rectangle { } } - NTooltip { - id: tooltip - target: root - positionAbove: root.tooltipPositionAbove - positionLeft: root.tooltipPositionLeft - positionRight: root.tooltipPositionRight - text: root.tooltipText - } - MouseArea { // Always enabled to allow hover/tooltip even when the button is disabled enabled: true @@ -84,20 +73,20 @@ Rectangle { onEntered: { hovering = root.enabled ? true : false if (tooltipText) { - tooltip.show() + PanelService.tooltip.show(parent, tooltipText, tooltipDirection) } root.entered() } onExited: { hovering = false if (tooltipText) { - tooltip.hide() + PanelService.tooltip.hide() } root.exited() } onClicked: function (mouse) { if (tooltipText) { - tooltip.hide() + PanelService.tooltip.hide() } if (!root.enabled && !allowClickWhenDisabled) { return diff --git a/Widgets/NSearchableComboBox.qml b/Widgets/NSearchableComboBox.qml index ed2fe374..e949405d 100644 --- a/Widgets/NSearchableComboBox.qml +++ b/Widgets/NSearchableComboBox.qml @@ -255,11 +255,11 @@ RowLayout { // Ensure the model is filtered when popup opens filterModel() // Small delay to ensure the popup is fully rendered - Qt.callLater(function () { - if (searchInput && searchInput.inputItem) { - searchInput.inputItem.forceActiveFocus() - } - }) + Qt.callLater(() => { + if (searchInput && searchInput.inputItem) { + searchInput.inputItem.forceActiveFocus() + } + }) } } } diff --git a/Widgets/NTooltip.qml b/Widgets/NTooltip.qml deleted file mode 100644 index f0cd55df..00000000 --- a/Widgets/NTooltip.qml +++ /dev/null @@ -1,197 +0,0 @@ -import QtQuick -import qs.Commons -import qs.Services - -Window { - id: root - - property bool isVisible: false - property string text: I18n.tr("widgets.tooltip.placeholder") - property Item target: null - property int delay: Style.tooltipDelay - property bool positionAbove: false - property bool positionLeft: false - property bool positionRight: false - - readonly property string barPosition: Settings.data.bar.position - - flags: Qt.ToolTip | Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint - color: Color.transparent - visible: false - - onIsVisibleChanged: { - if (isVisible) { - if (delay > 0) { - timerShow.running = true - } else { - _showNow() - } - } else { - _hideNow() - } - } - - function show() { - isVisible = true - } - function hide() { - isVisible = false - timerShow.running = false - } - - function _showNow() { - // Compute new size everytime we show the tooltip - width = Math.max(50 * scaling, tooltipText.implicitWidth + Style.marginL * 2 * scaling) - height = Math.max(40 * scaling, tooltipText.implicitHeight + Style.marginM * 2 * scaling) - - if (!target) { - return - } - - if (positionLeft) { - // Position tooltip to the left of the target - var pos = target.mapToGlobal(0, 0) - x = pos.x - width - 12 // 12 px margin to the left - y = pos.y - height / 2 + target.height / 2 - } else if (positionRight) { - // Position tooltip to the right of the target - var pos = target.mapToGlobal(target.width, 0) - x = pos.x + 12 // 12 px margin to the right - y = pos.y - height / 2 + target.height / 2 - } else if (positionAbove) { - // Position tooltip above the target - var pos = target.mapToGlobal(0, 0) - x = pos.x - width / 2 + target.width / 2 - y = pos.y - height - 12 // 12 px margin above - } else { - // Position tooltip below the target - var pos = target.mapToGlobal(0, target.height) - x = pos.x - width / 2 + target.width / 2 - y = pos.y + 12 // 12 px margin below - } - - // Start with animation values - tooltipRect.scaleValue = 0.8 - tooltipRect.opacityValue = 0.0 - visible = true - - // Use a timer to trigger the animation after the component is visible - showTimer.start() - } - - function _hideNow() { - // Start hide animation - tooltipRect.scaleValue = 0.8 - tooltipRect.opacityValue = 0.0 - - // Hide after animation completes - hideTimer.start() - } - - Connections { - target: root.target - function onXChanged() { - if (root.visible) { - root._showNow() - } - } - function onYChanged() { - if (root.visible) { - root._showNow() - } - } - function onWidthChanged() { - if (root.visible) { - root._showNow() - } - } - function onHeightChanged() { - if (root.visible) { - root._showNow() - } - } - } - Connections { - target: root - function onTextChanged() { - if (root.visible) { - root._showNow() - } - } - } - - Timer { - id: timerShow - interval: delay - running: false - repeat: false - onTriggered: { - _showNow() - running = false - } - } - - // Timer to hide tooltip after animation - Timer { - id: hideTimer - interval: Style.animationNormal - repeat: false - onTriggered: { - visible = false - } - } - - // Timer to trigger show animation - Timer { - id: showTimer - interval: Style.animationFast / 15 // Very short delay to ensure component is visible - repeat: false - onTriggered: { - // Animate to final values - tooltipRect.scaleValue = 1.0 - tooltipRect.opacityValue = 1.0 - } - } - - Rectangle { - id: tooltipRect - anchors.fill: parent - radius: Style.radiusM * scaling - color: Color.mSurface - border.color: Color.mOutline - border.width: Math.max(1, Style.borderS * scaling) - z: 1 - - // Animation properties - property real scaleValue: 1.0 - property real opacityValue: 1.0 - - scale: scaleValue - opacity: opacityValue - - // Animation behaviors - Behavior on scale { - NumberAnimation { - duration: Style.animationNormal - easing.type: Easing.OutExpo - } - } - - Behavior on opacity { - NumberAnimation { - duration: Style.animationNormal - easing.type: Easing.OutQuad - } - } - - NText { - id: tooltipText - anchors.centerIn: parent - text: root.text - font.pointSize: Style.fontSizeM * scaling - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - wrapMode: Text.Wrap - } - } -} diff --git a/shell.qml b/shell.qml index cd1689bc..e333472c 100644 --- a/shell.qml +++ b/shell.qml @@ -39,6 +39,7 @@ import qs.Modules.Notification import qs.Modules.OSD import qs.Modules.Settings import qs.Modules.Toast +import qs.Modules.Tooltip import qs.Modules.Wallpaper ShellRoot { @@ -50,6 +51,10 @@ ShellRoot { Bar {} Dock {} + Tooltip { + id: globalTooltip + } + Notification { id: notification } @@ -115,6 +120,7 @@ ShellRoot { Component.onCompleted: { // Save a ref. to our lockScreen so we can access it easily PanelService.lockScreen = lockScreen + PanelService.tooltip = globalTooltip BarWidgetRegistry.init() } From 1a0ea3893c33dd52ed0c734f3e1b79f1e89d5f6a Mon Sep 17 00:00:00 2001 From: Aiser Date: Sun, 28 Sep 2025 15:52:54 +0800 Subject: [PATCH 36/42] i18n: Optimize Chinese translation --- Assets/Translations/zh.json | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/Assets/Translations/zh.json b/Assets/Translations/zh.json index f4d3dd0a..da1b6111 100644 --- a/Assets/Translations/zh.json +++ b/Assets/Translations/zh.json @@ -29,7 +29,11 @@ "animation-speed": { "label": "动画速度", "description": "调整全局动画速度。" - } + }, + "animation-disable": { + "label": "关闭动画", + "description": "禁用所有动画效果,以获得更快速、更灵敏的体验。" + } }, "screen-corners": { "section": { @@ -226,7 +230,7 @@ }, "floating": { "label": "浮动状态栏", - "description": "将状态栏显示为浮动'药丸'。注意:这会将屏幕边角移动到边缘。" + "description": "将状态栏显示为浮动样式(类似于一个药丸)。注意:这会将屏幕边角移动到边缘。" }, "margins": { "label": "边距", @@ -1318,4 +1322,4 @@ "charging": "正在充电。", "discharging": "正在放电。" } - } \ No newline at end of file + } From c41fa1aef773579e76ba76572811d3e8633d3d8d Mon Sep 17 00:00:00 2001 From: Aiser <2912778691@qq.com> Date: Sun, 28 Sep 2025 16:03:59 +0800 Subject: [PATCH 37/42] i18n: Optimize Chinese translation --- Assets/Translations/zh.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Assets/Translations/zh.json b/Assets/Translations/zh.json index da1b6111..a0710bd5 100644 --- a/Assets/Translations/zh.json +++ b/Assets/Translations/zh.json @@ -265,7 +265,7 @@ }, "exclusive-zone": { "label": "独占区域", - "description": "防止窗口重叠。" + "description": "可以防止窗口重叠。" }, "background-opacity": { "label": "背景不透明度", From 25eb31747a9c83578d8d3692f6b57012cff5ce02 Mon Sep 17 00:00:00 2001 From: Ly-sec Date: Sun, 28 Sep 2025 10:43:02 +0200 Subject: [PATCH 38/42] ColorSchemeTab: hide predefined colorschemes when matugen is enabled --- Assets/Translations/de.json | 2 +- Assets/Translations/en.json | 2 +- Assets/Translations/es.json | 2 +- Assets/Translations/fr.json | 2 +- Assets/Translations/pt.json | 2 +- Assets/Translations/zh.json | 2 +- Modules/Settings/Tabs/ColorSchemeTab.qml | 2 ++ 7 files changed, 8 insertions(+), 6 deletions(-) diff --git a/Assets/Translations/de.json b/Assets/Translations/de.json index a07cc9e8..a6732da6 100644 --- a/Assets/Translations/de.json +++ b/Assets/Translations/de.json @@ -487,7 +487,7 @@ "predefined": { "section": { "label": "Vordefinierte Farbschemata", - "description": "Um diese Farbschemata zu verwenden, müssen Sie Matugen ausschalten. Mit aktiviertem Matugen werden Farben automatisch aus Ihrem Hintergrundbild generiert." + "description": "Wählen Sie aus einer Sammlung vordefinierter Farbschemata." } }, "matugen": { diff --git a/Assets/Translations/en.json b/Assets/Translations/en.json index ab808c11..24c6326f 100644 --- a/Assets/Translations/en.json +++ b/Assets/Translations/en.json @@ -491,7 +491,7 @@ "predefined": { "section": { "label": "Predefined color schemes", - "description": "To use these color schemes, you must turn off Matugen. With Matugen enabled, colors are automatically generated from your wallpaper." + "description": "Choose from a collection of predefined color schemes." } }, "matugen": { diff --git a/Assets/Translations/es.json b/Assets/Translations/es.json index 0c23929f..ee2ac979 100644 --- a/Assets/Translations/es.json +++ b/Assets/Translations/es.json @@ -485,7 +485,7 @@ "predefined": { "section": { "label": "Esquemas de colores predefinidos", - "description": "Para usar estos esquemas de colores, debes desactivar Matugen. Con Matugen activado, los colores se generan automáticamente a partir de tu fondo de pantalla." + "description": "Elige entre una colección de esquemas de colores predefinidos." } }, "matugen": { diff --git a/Assets/Translations/fr.json b/Assets/Translations/fr.json index 5a77f950..c30b0590 100644 --- a/Assets/Translations/fr.json +++ b/Assets/Translations/fr.json @@ -485,7 +485,7 @@ "predefined": { "section": { "label": "Jeux de couleurs prédéfinis", - "description": "Pour utiliser ces jeux de couleurs, vous devez désactiver Matugen. Avec Matugen activé, les couleurs sont générées automatiquement à partir de votre fond d'écran." + "description": "Choisissez parmi une collection de jeux de couleurs prédéfinis." } }, "matugen": { diff --git a/Assets/Translations/pt.json b/Assets/Translations/pt.json index cc913f62..7beb64d9 100644 --- a/Assets/Translations/pt.json +++ b/Assets/Translations/pt.json @@ -451,7 +451,7 @@ "predefined": { "section": { "label": "Esquemas de cores predefinidos", - "description": "Para usar esses esquemas de cores, você deve desativar o Matugen. Com o Matugen ativado, as cores são geradas automaticamente a partir do seu papel de parede." + "description": "Escolha entre uma coleção de esquemas de cores predefinidos." } }, "matugen": { diff --git a/Assets/Translations/zh.json b/Assets/Translations/zh.json index a0710bd5..b66d5ebf 100644 --- a/Assets/Translations/zh.json +++ b/Assets/Translations/zh.json @@ -489,7 +489,7 @@ "predefined": { "section": { "label": "预定义配色方案", - "description": "要使用这些配色方案,您必须关闭 Matugen。启用 Matugen 后,颜色会根据您的壁纸自动生成。" + "description": "从预定义配色方案集合中选择。" } }, "matugen": { diff --git a/Modules/Settings/Tabs/ColorSchemeTab.qml b/Modules/Settings/Tabs/ColorSchemeTab.qml index be5ed002..57f3d399 100644 --- a/Modules/Settings/Tabs/ColorSchemeTab.qml +++ b/Modules/Settings/Tabs/ColorSchemeTab.qml @@ -188,12 +188,14 @@ ColumnLayout { Layout.fillWidth: true Layout.topMargin: Style.marginXL * scaling Layout.bottomMargin: Style.marginXL * scaling + visible: !Settings.data.colorSchemes.useWallpaperColors } // Predefined Color Schemes ColumnLayout { spacing: Style.marginM * scaling Layout.fillWidth: true + visible: !Settings.data.colorSchemes.useWallpaperColors NHeader { label: I18n.tr("settings.color-scheme.predefined.section.label") From 388824bf370c73b4a9f60227a0260dbb284a1734 Mon Sep 17 00:00:00 2001 From: Ly-sec Date: Sun, 28 Sep 2025 11:16:26 +0200 Subject: [PATCH 39/42] i18n: add description to all Bar widget settings --- Assets/Translations/de.json | 109 +- Assets/Translations/en.json | 105 +- Assets/Translations/es.json | 109 +- Assets/Translations/fr.json | 109 +- Assets/Translations/pt.json | 127 +- Assets/Translations/zh.json | 2551 +++++++++-------- .../WidgetSettings/ActiveWindowSettings.qml | 9 +- .../WidgetSettings/ControlCenterSettings.qml | 3 +- .../WidgetSettings/CustomButtonSettings.qml | 9 +- .../Bar/WidgetSettings/MediaMiniSettings.qml | 15 +- .../NotificationHistorySettings.qml | 6 +- .../WidgetSettings/SystemMonitorSettings.qml | 18 +- .../Bar/WidgetSettings/WorkspaceSettings.qml | 3 +- 13 files changed, 1787 insertions(+), 1386 deletions(-) diff --git a/Assets/Translations/de.json b/Assets/Translations/de.json index a6732da6..9137fef8 100644 --- a/Assets/Translations/de.json +++ b/Assets/Translations/de.json @@ -31,6 +31,10 @@ "animation-speed": { "label": "Animationsgeschwindigkeit", "description": "Globale Animationsgeschwindigkeit anpassen." + }, + "animation-disable": { + "label": "UI-Animationen deaktivieren", + "description": "Alle Animationen für eine schnellere, reaktionsschnellere Erfahrung deaktivieren." } }, "screen-corners": { @@ -829,21 +833,54 @@ "search-placeholder": "Widgets suchen..." }, "active-window": { - "auto-hide": "Automatisch ausblenden", - "scrolling-mode": "Scrollmodus", - "show-app-icon": "App-Symbol anzeigen" + "auto-hide": { + "label": "Automatisch ausblenden", + "description": "Widget automatisch ausblenden, wenn kein Fenster aktiv ist." + }, + "show-app-icon": { + "label": "App-Symbol anzeigen", + "description": "Anwendungssymbol neben dem Fenstertitel anzeigen." + }, + "scrolling-mode": { + "label": "Scrollmodus", + "description": "Steuern, wann Textscrolling für lange Fenstertitel aktiviert ist." + } }, "system-monitor": { - "cpu-usage": "CPU-Auslastung", - "cpu-temperature": "CPU-Temperatur", - "memory-usage": "Speicherverbrauch", - "memory-percentage": "Speicher als Prozentsatz", - "network-traffic": "Netzwerkverkehr", - "storage-usage": "Speichernutzung" + "cpu-usage": { + "label": "CPU-Auslastung", + "description": "Aktuelle CPU-Auslastung in Prozent anzeigen." + }, + "cpu-temperature": { + "label": "CPU-Temperatur", + "description": "CPU-Temperaturwerte anzeigen, falls verfügbar." + }, + "memory-usage": { + "label": "Speicherverbrauch", + "description": "Aktuelle RAM-Nutzungsinformationen anzeigen." + }, + "memory-percentage": { + "label": "Speicher als Prozentsatz", + "description": "Speicherverbrauch als Prozentsatz statt absolute Werte anzeigen." + }, + "network-traffic": { + "label": "Netzwerkverkehr", + "description": "Upload- und Download-Geschwindigkeiten anzeigen." + }, + "storage-usage": { + "label": "Speichernutzung", + "description": "Festplattenspeicher-Nutzungsinformationen anzeigen." + } }, "notification-history": { - "show-unread-badge": "Badge für ungelesene Nachrichten anzeigen", - "hide-badge-when-zero": "Badge ausblenden, wenn null" + "show-unread-badge": { + "label": "Badge für ungelesene Nachrichten anzeigen", + "description": "Badge mit der Anzahl ungelesener Benachrichtigungen anzeigen." + }, + "hide-badge-when-zero": { + "label": "Badge ausblenden, wenn null", + "description": "Benachrichtigungs-Badge ausblenden, wenn keine ungelesenen Benachrichtigungen vorhanden sind." + } }, "battery": { "display-mode": { @@ -856,7 +893,10 @@ } }, "control-center": { - "use-distro-logo": "Distro-Logo anstelle von Symbol verwenden", + "use-distro-logo": { + "label": "Distro-Logo anstelle von Symbol verwenden", + "description": "Logo Ihrer Distribution anstelle eines benutzerdefinierten Symbols verwenden." + }, "icon": { "label": "Symbol", "description": "Symbol aus der Bibliothek oder eine benutzerdefinierte Datei auswählen." @@ -878,7 +918,10 @@ } }, "workspace": { - "label-mode": "Beschriftungsmodus", + "label-mode": { + "label": "Beschriftungsmodus", + "description": "Wählen Sie, wie Arbeitsbereichs-Beschriftungen angezeigt werden." + }, "hide-unoccupied": { "label": "Unbesetzte ausblenden", "description": "Arbeitsbereiche ohne Fenster nicht anzeigen." @@ -908,9 +951,18 @@ "description": "Symbol aus der Bibliothek auswählen." }, "browse": "Durchsuchen", - "left-click": "Linksklick", - "right-click": "Rechtsklick", - "middle-click": "Mittelklick", + "left-click": { + "label": "Linksklick", + "description": "Befehl, der ausgeführt wird, wenn die Schaltfläche links angeklickt wird." + }, + "right-click": { + "label": "Rechtsklick", + "description": "Befehl, der ausgeführt wird, wenn die Schaltfläche rechts angeklickt wird." + }, + "middle-click": { + "label": "Mittelklick", + "description": "Befehl, der ausgeführt wird, wenn die Schaltfläche mit der mittleren Maustaste angeklickt wird." + }, "dynamic-text": "Dynamischer Text", "display-command-output": { "label": "Befehlsausgabe anzeigen", @@ -922,11 +974,26 @@ } }, "media-mini": { - "auto-hide": "Automatisch ausblenden", - "show-album-art": "Albumcover anzeigen", - "show-visualizer": "Visualizer anzeigen", - "visualizer-type": "Visualizer-Typ", - "scrolling-mode": "Scrollmodus" + "auto-hide": { + "label": "Automatisch ausblenden", + "description": "Widget automatisch ausblenden, wenn keine Medien abgespielt werden." + }, + "show-album-art": { + "label": "Albumcover anzeigen", + "description": "Albumcover des aktuell abgespielten Tracks anzeigen." + }, + "show-visualizer": { + "label": "Visualizer anzeigen", + "description": "Audio-Visualizer anzeigen, wenn Musik abgespielt wird." + }, + "visualizer-type": { + "label": "Visualizer-Typ", + "description": "Stil des Audio-Visualizers auswählen." + }, + "scrolling-mode": { + "label": "Scrollmodus", + "description": "Steuern, wann Textscrolling für lange Track-Titel aktiviert ist." + } }, "clock": { "use-primary-color": { diff --git a/Assets/Translations/en.json b/Assets/Translations/en.json index 24c6326f..5f2e156e 100644 --- a/Assets/Translations/en.json +++ b/Assets/Translations/en.json @@ -816,21 +816,54 @@ "search-placeholder": "Search widget..." }, "active-window": { - "auto-hide": "Hide automatically", - "show-app-icon": "Show app icon", - "scrolling-mode": "Scrolling mode" + "auto-hide": { + "label": "Hide automatically", + "description": "Automatically hide the widget when no window is active." + }, + "show-app-icon": { + "label": "Show app icon", + "description": "Display the application icon next to the window title." + }, + "scrolling-mode": { + "label": "Scrolling mode", + "description": "Control when text scrolling is enabled for long window titles." + } }, "system-monitor": { - "cpu-usage": "CPU usage", - "cpu-temperature": "CPU temperature", - "memory-usage": "Memory usage", - "memory-percentage": "Memory as percentage", - "network-traffic": "Network traffic", - "storage-usage": "Storage usage" + "cpu-usage": { + "label": "CPU usage", + "description": "Display current CPU usage percentage." + }, + "cpu-temperature": { + "label": "CPU temperature", + "description": "Show CPU temperature readings if available." + }, + "memory-usage": { + "label": "Memory usage", + "description": "Display current RAM usage information." + }, + "memory-percentage": { + "label": "Memory as percentage", + "description": "Show memory usage as a percentage instead of absolute values." + }, + "network-traffic": { + "label": "Network traffic", + "description": "Display network upload and download speeds." + }, + "storage-usage": { + "label": "Storage usage", + "description": "Show disk space usage information." + } }, "notification-history": { - "show-unread-badge": "Show unread badge", - "hide-badge-when-zero": "Hide badge when zero" + "show-unread-badge": { + "label": "Show unread badge", + "description": "Display a badge showing the number of unread notifications." + }, + "hide-badge-when-zero": { + "label": "Hide badge when zero", + "description": "Hide the notification badge when there are no unread notifications." + } }, "battery": { "display-mode": { @@ -843,7 +876,10 @@ } }, "control-center": { - "use-distro-logo": "Use distro logo instead of icon", + "use-distro-logo": { + "label": "Use distro logo instead of icon", + "description": "Use your distribution's logo instead of a custom icon." + }, "icon": { "label": "Icon", "description": "Select an icon from the library or a custom file." @@ -865,7 +901,10 @@ } }, "workspace": { - "label-mode": "Label Mode", + "label-mode": { + "label": "Label Mode", + "description": "Choose how workspace labels are displayed." + }, "hide-unoccupied": { "label": "Hide unoccupied", "description": "Don't display workspaces without windows." @@ -895,9 +934,18 @@ "description": "Select an icon from the library." }, "browse": "Browse", - "left-click": "Left click", - "right-click": "Right click", - "middle-click": "Middle click", + "left-click": { + "label": "Left click", + "description": "Command to execute when the button is left-clicked." + }, + "right-click": { + "label": "Right click", + "description": "Command to execute when the button is right-clicked." + }, + "middle-click": { + "label": "Middle click", + "description": "Command to execute when the button is middle-clicked." + }, "dynamic-text": "Dynamic text", "display-command-output": { "label": "Display Command Output", @@ -909,11 +957,26 @@ } }, "media-mini": { - "auto-hide": "Hide automatically", - "show-album-art": "Show album art", - "show-visualizer": "Show visualizer", - "visualizer-type": "Visualizer type", - "scrolling-mode": "Scrolling mode" + "auto-hide": { + "label": "Hide automatically", + "description": "Automatically hide the widget when no media is playing." + }, + "show-album-art": { + "label": "Show album art", + "description": "Display the album artwork for the currently playing track." + }, + "show-visualizer": { + "label": "Show visualizer", + "description": "Display an audio visualizer when music is playing." + }, + "visualizer-type": { + "label": "Visualizer type", + "description": "Choose the style of audio visualizer to display." + }, + "scrolling-mode": { + "label": "Scrolling mode", + "description": "Control when text scrolling is enabled for long track titles." + } }, "clock": { "use-primary-color": { diff --git a/Assets/Translations/es.json b/Assets/Translations/es.json index ee2ac979..e5afc18c 100644 --- a/Assets/Translations/es.json +++ b/Assets/Translations/es.json @@ -29,6 +29,10 @@ "animation-speed": { "label": "Velocidad de la animación", "description": "Ajusta la velocidad de la animación global." + }, + "animation-disable": { + "label": "Desactivar animaciones de UI", + "description": "Desactivar todas las animaciones para una experiencia más rápida y responsiva." } }, "screen-corners": { @@ -810,21 +814,54 @@ "search-placeholder": "Buscar widgets..." }, "active-window": { - "auto-hide": "Ocultar automáticamente", - "show-app-icon": "Mostrar icono de la aplicación", - "scrolling-mode": "Modo de desplazamiento" + "auto-hide": { + "label": "Ocultar automáticamente", + "description": "Ocultar automáticamente el widget cuando no hay ventana activa." + }, + "show-app-icon": { + "label": "Mostrar icono de la aplicación", + "description": "Mostrar el icono de la aplicación junto al título de la ventana." + }, + "scrolling-mode": { + "label": "Modo de desplazamiento", + "description": "Controlar cuándo está habilitado el desplazamiento de texto para títulos de ventana largos." + } }, "system-monitor": { - "cpu-usage": "Uso de CPU", - "cpu-temperature": "Temperatura de la CPU", - "memory-usage": "Uso de memoria", - "memory-percentage": "Memoria como porcentaje", - "network-traffic": "Tráfico de red", - "storage-usage": "Uso de almacenamiento" + "cpu-usage": { + "label": "Uso de CPU", + "description": "Mostrar el porcentaje actual de uso de CPU." + }, + "cpu-temperature": { + "label": "Temperatura de la CPU", + "description": "Mostrar lecturas de temperatura de CPU si están disponibles." + }, + "memory-usage": { + "label": "Uso de memoria", + "description": "Mostrar información actual del uso de RAM." + }, + "memory-percentage": { + "label": "Memoria como porcentaje", + "description": "Mostrar el uso de memoria como porcentaje en lugar de valores absolutos." + }, + "network-traffic": { + "label": "Tráfico de red", + "description": "Mostrar velocidades de carga y descarga de la red." + }, + "storage-usage": { + "label": "Uso de almacenamiento", + "description": "Mostrar información del uso del espacio en disco." + } }, "notification-history": { - "show-unread-badge": "Mostrar insignia de no leídos", - "hide-badge-when-zero": "Ocultar insignia cuando es cero" + "show-unread-badge": { + "label": "Mostrar insignia de no leídos", + "description": "Mostrar una insignia con el número de notificaciones no leídas." + }, + "hide-badge-when-zero": { + "label": "Ocultar insignia cuando es cero", + "description": "Ocultar la insignia de notificaciones cuando no hay notificaciones no leídas." + } }, "battery": { "display-mode": { @@ -837,7 +874,10 @@ } }, "control-center": { - "use-distro-logo": "Usar logo de la distro en lugar del icono", + "use-distro-logo": { + "label": "Usar logo de la distribución en lugar de icono", + "description": "Usar el logo de tu distribución en lugar de un icono personalizado." + }, "icon": { "label": "Icono", "description": "Selecciona un icono de la biblioteca o un archivo personalizado." @@ -859,7 +899,10 @@ } }, "workspace": { - "label-mode": "Modo de etiqueta", + "label-mode": { + "label": "Modo de etiqueta", + "description": "Elegir cómo se muestran las etiquetas de los espacios de trabajo." + }, "hide-unoccupied": { "label": "Ocultar desocupados", "description": "No mostrar espacios de trabajo sin ventanas." @@ -889,9 +932,18 @@ "description": "Selecciona un icono de la biblioteca." }, "browse": "Explorar", - "left-click": "Clic izquierdo", - "right-click": "Clic derecho", - "middle-click": "Clic central", + "left-click": { + "label": "Clic izquierdo", + "description": "Comando a ejecutar cuando se hace clic izquierdo en el botón." + }, + "right-click": { + "label": "Clic derecho", + "description": "Comando a ejecutar cuando se hace clic derecho en el botón." + }, + "middle-click": { + "label": "Clic medio", + "description": "Comando a ejecutar cuando se hace clic medio en el botón." + }, "dynamic-text": "Texto dinámico", "display-command-output": { "label": "Mostrar salida de comando", @@ -903,11 +955,26 @@ } }, "media-mini": { - "auto-hide": "Ocultar automáticamente", - "show-album-art": "Mostrar carátula del álbum", - "show-visualizer": "Mostrar visualizador", - "visualizer-type": "Tipo de visualizador", - "scrolling-mode": "Modo de desplazamiento" + "auto-hide": { + "label": "Ocultar automáticamente", + "description": "Ocultar automáticamente el widget cuando no se está reproduciendo ningún medio." + }, + "show-album-art": { + "label": "Mostrar arte del álbum", + "description": "Mostrar la portada del álbum de la pista que se está reproduciendo actualmente." + }, + "show-visualizer": { + "label": "Mostrar visualizador", + "description": "Mostrar un visualizador de audio cuando se reproduce música." + }, + "visualizer-type": { + "label": "Tipo de visualizador", + "description": "Elegir el estilo de visualizador de audio a mostrar." + }, + "scrolling-mode": { + "label": "Modo de desplazamiento", + "description": "Controlar cuándo está habilitado el desplazamiento de texto para títulos de pista largos." + } }, "clock": { "use-primary-color": { diff --git a/Assets/Translations/fr.json b/Assets/Translations/fr.json index c30b0590..7e5d1ae5 100644 --- a/Assets/Translations/fr.json +++ b/Assets/Translations/fr.json @@ -29,6 +29,10 @@ "animation-speed": { "label": "Vitesse d'animation", "description": "Ajustez la vitesse globale des animations." + }, + "animation-disable": { + "label": "Désactiver les animations de l'interface", + "description": "Désactiver toutes les animations pour une expérience plus rapide et réactive." } }, "screen-corners": { @@ -810,21 +814,54 @@ "search-placeholder": "Rechercher des widgets..." }, "active-window": { - "show-app-icon": "Afficher l'icône de l'application", - "scrolling-mode": "Mode de défilement", - "auto-hide": "Masquer automatiquement" + "show-app-icon": { + "label": "Afficher l'icône de l'application", + "description": "Afficher l'icône de l'application à côté du titre de la fenêtre." + }, + "scrolling-mode": { + "label": "Mode de défilement", + "description": "Contrôler quand le défilement de texte est activé pour les titres de fenêtre longs." + }, + "auto-hide": { + "label": "Masquer automatiquement", + "description": "Masquer automatiquement le widget quand aucune fenêtre n'est active." + } }, "system-monitor": { - "cpu-usage": "Utilisation du CPU", - "cpu-temperature": "Température du CPU", - "memory-usage": "Utilisation de la mémoire", - "memory-percentage": "Mémoire en pourcentage", - "network-traffic": "Trafic réseau", - "storage-usage": "Utilisation du stockage" + "cpu-usage": { + "label": "Utilisation du CPU", + "description": "Afficher le pourcentage d'utilisation actuel du CPU." + }, + "cpu-temperature": { + "label": "Température du CPU", + "description": "Afficher les lectures de température du CPU si disponibles." + }, + "memory-usage": { + "label": "Utilisation de la mémoire", + "description": "Afficher les informations actuelles d'utilisation de la RAM." + }, + "memory-percentage": { + "label": "Mémoire en pourcentage", + "description": "Afficher l'utilisation de la mémoire en pourcentage au lieu de valeurs absolues." + }, + "network-traffic": { + "label": "Trafic réseau", + "description": "Afficher les vitesses de téléchargement et de téléversement du réseau." + }, + "storage-usage": { + "label": "Utilisation du stockage", + "description": "Afficher les informations d'utilisation de l'espace disque." + } }, "notification-history": { - "show-unread-badge": "Afficher le badge non lu", - "hide-badge-when-zero": "Masquer le badge si zéro" + "show-unread-badge": { + "label": "Afficher le badge non lu", + "description": "Afficher un badge montrant le nombre de notifications non lues." + }, + "hide-badge-when-zero": { + "label": "Masquer le badge quand zéro", + "description": "Masquer le badge de notification quand il n'y a pas de notifications non lues." + } }, "battery": { "display-mode": { @@ -837,7 +874,10 @@ } }, "control-center": { - "use-distro-logo": "Utiliser le logo de la distribution au lieu de l'icône", + "use-distro-logo": { + "label": "Utiliser le logo de la distribution au lieu de l'icône", + "description": "Utiliser le logo de votre distribution au lieu d'une icône personnalisée." + }, "icon": { "label": "Icône", "description": "Sélectionnez une icône de la bibliothèque ou un fichier personnalisé." @@ -859,7 +899,10 @@ } }, "workspace": { - "label-mode": "Mode d'étiquette", + "label-mode": { + "label": "Mode d'étiquette", + "description": "Choisir comment les étiquettes d'espace de travail sont affichées." + }, "hide-unoccupied": { "label": "Masquer les inoccupés", "description": "Ne pas afficher les espaces de travail sans fenêtres." @@ -889,9 +932,18 @@ "description": "Sélectionnez une icône dans la bibliothèque." }, "browse": "Parcourir", - "left-click": "Clic gauche", - "right-click": "Clic droit", - "middle-click": "Clic du milieu", + "left-click": { + "label": "Clic gauche", + "description": "Commande à exécuter quand le bouton est cliqué à gauche." + }, + "right-click": { + "label": "Clic droit", + "description": "Commande à exécuter quand le bouton est cliqué à droite." + }, + "middle-click": { + "label": "Clic milieu", + "description": "Commande à exécuter quand le bouton est cliqué au milieu." + }, "dynamic-text": "Texte dynamique", "display-command-output": { "label": "Afficher la sortie de la commande", @@ -903,11 +955,26 @@ } }, "media-mini": { - "show-album-art": "Afficher la pochette de l'album", - "show-visualizer": "Afficher le visualiseur", - "visualizer-type": "Type de visualiseur", - "scrolling-mode": "Mode de défilement", - "auto-hide": "Masquer automatiquement" + "show-album-art": { + "label": "Afficher la pochette d'album", + "description": "Afficher la pochette d'album de la piste en cours de lecture." + }, + "show-visualizer": { + "label": "Afficher le visualiseur", + "description": "Afficher un visualiseur audio quand la musique est en cours de lecture." + }, + "visualizer-type": { + "label": "Type de visualiseur", + "description": "Choisir le style de visualiseur audio à afficher." + }, + "scrolling-mode": { + "label": "Mode de défilement", + "description": "Contrôler quand le défilement de texte est activé pour les titres de piste longs." + }, + "auto-hide": { + "label": "Masquer automatiquement", + "description": "Masquer automatiquement le widget quand aucun média n'est en cours de lecture." + } }, "clock": { "use-primary-color": { diff --git a/Assets/Translations/pt.json b/Assets/Translations/pt.json index 7beb64d9..ce3e2ec1 100644 --- a/Assets/Translations/pt.json +++ b/Assets/Translations/pt.json @@ -29,6 +29,10 @@ "animation-speed": { "label": "Velocidade da animação", "description": "Ajuste a velocidade global da animação." + }, + "animation-disable": { + "label": "Desativar animações da interface", + "description": "Desativar todas as animações para uma experiência mais rápida e responsiva." } }, "screen-corners": { @@ -713,7 +717,6 @@ } } }, - "widgets": { "tooltip": { "placeholder": "Espaço reservado" @@ -811,21 +814,54 @@ "search-placeholder": "Pesquisar widgets..." }, "active-window": { - "auto-hide": "Ocultar automaticamente", - "show-app-icon": "Mostrar ícone do aplicativo", - "scrolling-mode": "Modo de Rolagem" + "auto-hide": { + "label": "Ocultar automaticamente", + "description": "Ocultar automaticamente o widget quando nenhuma janela está ativa." + }, + "show-app-icon": { + "label": "Mostrar ícone do aplicativo", + "description": "Exibir o ícone do aplicativo ao lado do título da janela." + }, + "scrolling-mode": { + "label": "Modo de rolagem", + "description": "Controlar quando a rolagem de texto está habilitada para títulos de janela longos." + } }, "system-monitor": { - "cpu-usage": "Uso de CPU", - "cpu-temperature": "Temperatura da CPU", - "memory-usage": "Uso de memória", - "memory-percentage": "Memória em porcentagem", - "network-traffic": "Tráfego de rede", - "storage-usage": "Uso de armazenamento" + "cpu-usage": { + "label": "Uso da CPU", + "description": "Exibir o percentual atual de uso da CPU." + }, + "cpu-temperature": { + "label": "Temperatura da CPU", + "description": "Mostrar leituras de temperatura da CPU se disponíveis." + }, + "memory-usage": { + "label": "Uso de memória", + "description": "Exibir informações atuais de uso da RAM." + }, + "memory-percentage": { + "label": "Memória como percentual", + "description": "Mostrar o uso de memória como percentual em vez de valores absolutos." + }, + "network-traffic": { + "label": "Tráfego de rede", + "description": "Exibir velocidades de upload e download da rede." + }, + "storage-usage": { + "label": "Uso de armazenamento", + "description": "Mostrar informações de uso do espaço em disco." + } }, "notification-history": { - "show-unread-badge": "Mostrar selo de não lidas", - "hide-badge-when-zero": "Ocultar selo quando for zero" + "show-unread-badge": { + "label": "Mostrar distintivo de não lidos", + "description": "Exibir um distintivo mostrando o número de notificações não lidas." + }, + "hide-badge-when-zero": { + "label": "Ocultar distintivo quando zero", + "description": "Ocultar o distintivo de notificação quando não há notificações não lidas." + } }, "battery": { "display-mode": { @@ -838,7 +874,10 @@ } }, "control-center": { - "use-distro-logo": "Usar logo da distro em vez do ícone", + "use-distro-logo": { + "label": "Usar logo da distribuição em vez de ícone", + "description": "Usar o logo da sua distribuição em vez de um ícone personalizado." + }, "icon": { "label": "Ícone", "description": "Selecione um ícone da biblioteca ou um arquivo personalizado." @@ -860,7 +899,10 @@ } }, "workspace": { - "label-mode": "Modo de Rótulo", + "label-mode": { + "label": "Modo de rótulo", + "description": "Escolher como os rótulos de espaço de trabalho são exibidos." + }, "hide-unoccupied": { "label": "Ocultar desocupados", "description": "Não exibir áreas de trabalho sem janelas." @@ -890,9 +932,18 @@ "description": "Selecione um ícone da biblioteca." }, "browse": "Navegar", - "left-click": "Clique esquerdo", - "right-click": "Clique direito", - "middle-click": "Clique do meio", + "left-click": { + "label": "Clique esquerdo", + "description": "Comando a executar quando o botão é clicado com o botão esquerdo." + }, + "right-click": { + "label": "Clique direito", + "description": "Comando a executar quando o botão é clicado com o botão direito." + }, + "middle-click": { + "label": "Clique do meio", + "description": "Comando a executar quando o botão é clicado com o botão do meio." + }, "dynamic-text": "Texto dinâmico", "display-command-output": { "label": "Exibir Saída de Comando", @@ -904,11 +955,26 @@ } }, "media-mini": { - "auto-hide": "Ocultar automaticamente", - "show-album-art": "Mostrar arte do álbum", - "show-visualizer": "Mostrar visualizador", - "visualizer-type": "Tipo de visualizador", - "scrolling-mode": "Modo de rolagem" + "auto-hide": { + "label": "Ocultar automaticamente", + "description": "Ocultar automaticamente o widget quando nenhuma mídia está sendo reproduzida." + }, + "show-album-art": { + "label": "Mostrar arte do álbum", + "description": "Exibir a arte do álbum da faixa atualmente sendo reproduzida." + }, + "show-visualizer": { + "label": "Mostrar visualizador", + "description": "Exibir um visualizador de áudio quando música está sendo reproduzida." + }, + "visualizer-type": { + "label": "Tipo de visualizador", + "description": "Escolher o estilo de visualizador de áudio a exibir." + }, + "scrolling-mode": { + "label": "Modo de rolagem", + "description": "Controlar quando a rolagem de texto está habilitada para títulos de faixa longos." + } }, "clock": { "use-primary-color": { @@ -1105,8 +1171,8 @@ "bottom_right": "Inferior direito", "bottom_center": "Centro inferior", "top_center": "Centro superior", - "center_left": "Esquerda central", - "center_right": "Direita central" + "center_left": "Centro à esquerda", + "center_right": "Centro à direita" } }, "bar": { @@ -1122,19 +1188,6 @@ "comfortable": "Confortável" } }, - "launcher": { - "position": { - "center": "Centro (padrão)", - "top_left": "Superior esquerdo", - "top_right": "Superior direito", - "bottom_left": "Inferior esquerdo", - "bottom_right": "Inferior direito", - "bottom_center": "Centro inferior", - "top_center": "Centro superior", - "center_left": "Centro à esquerda", - "center_right": "Centro à direita" - } - }, "display-mode": { "on-hover": "Ao passar o mouse", "always-show": "Sempre mostrar", diff --git a/Assets/Translations/zh.json b/Assets/Translations/zh.json index b66d5ebf..6578e8f8 100644 --- a/Assets/Translations/zh.json +++ b/Assets/Translations/zh.json @@ -1,1325 +1,1388 @@ { - "settings": { - "general": { - "title": "通用", - "profile": { - "section": { - "label": "个人资料", - "description": "编辑您的用户详细信息和头像。" - }, - "picture": { - "label": "{user} 的个人资料图片", - "description": "在整个界面中显示您的个人资料图片。" - }, - "select-avatar": "选择头像图片" - }, - "ui": { - "section": { - "label": "用户界面", - "description": "自定义界面的外观、风格和行为。" - }, - "dim-desktop": { - "label": "调暗桌面", - "description": "当面板或菜单打开时调暗桌面。" - }, - "border-radius": { - "label": "边框圆角", - "description": "控制窗口、按钮及其他元素的边角圆度。" - }, - "animation-speed": { - "label": "动画速度", - "description": "调整全局动画速度。" - }, - "animation-disable": { - "label": "关闭动画", - "description": "禁用所有动画效果,以获得更快速、更灵敏的体验。" - } - }, - "screen-corners": { - "section": { - "label": "屏幕边角", - "description": "自定义屏幕边角的圆度和视觉效果。" - }, - "show-corners": { - "label": "显示屏幕圆角", - "description": "在屏幕边缘显示圆角。" - }, - "solid-black": { - "label": "实心黑色边角", - "description": "使用实心黑色而非状态栏背景色。" - }, - "radius": { - "label": "屏幕边角半径", - "description": "调整屏幕圆角的弧度。" - } - }, - "fonts": { - "section": { - "label": "字体", - "description": "选择整个界面中使用的字体。" - }, - "default": { - "label": "默认字体", - "description": "整个界面使用的主要字体。", - "placeholder": "选择默认字体...", - "search-placeholder": "搜索字体..." - }, - "monospace": { - "label": "等宽字体", - "description": "用于数字和统计信息显示的等宽字体。", - "placeholder": "选择等宽字体...", - "search-placeholder": "搜索等宽字体..." - }, - "accent": { - "label": "强调字体", - "description": "用于突出显示内容的大字体。", - "placeholder": "选择标题字体...", - "search-placeholder": "搜索标题字体..." - } - } - }, - "audio": { - "title": "音频", - "volumes": { - "section": { - "label": "音量", - "description": "调整音量控制和音频级别。" - }, - "output-volume": { - "label": "输出音量", - "description": "系统全局音量级别。" - }, - "mute-output": { - "label": "静音输出", - "description": "静音系统主音频输出。" - }, - "input-volume": { - "label": "输入音量", - "description": "麦克风输入音量级别。" - }, - "mute-input": { - "label": "静音输入", - "description": "静音默认音频输入(麦克风)。" - }, - "step-size": { - "label": "音量步长", - "description": "调整音量变化的步长(滚轮、键盘快捷键)。" - }, - "volume-overdrive": { - "label": "允许音量过载", - "description": "允许将音量提高到100%以上。可能不被所有硬件支持。" - } - }, - "devices": { - "section": { - "label": "音频设备", - "description": "配置可用的音频输入和输出设备。" - }, - "output-device": { - "label": "输出设备", - "description": "选择所需的音频输出设备。" - }, - "input-device": { - "label": "输入设备", - "description": "选择所需的音频输入设备。" - } - }, - "media": { - "section": { - "label": "媒体播放器", - "description": "设置您首选和要忽略的媒体应用程序。" - }, - "primary-player": { - "label": "主要播放器", - "description": "输入关键词以识别您的主要播放器。", - "placeholder": "例如:spotify, vlc, mpv" - }, - "excluded-player": { - "label": "排除的播放器", - "description": "添加您希望系统忽略的播放器关键词。每个关键词应单独占一行。", - "placeholder": "输入关键词并按 + 添加" - }, - "visualizer-type": { - "label": "可视化类型", - "description": "选择媒体播放的可视化类型" - }, - "frame-rate": { - "label": "帧率", - "description": "帧率越高越流畅,但会占用更多资源。" - }, - "scrolling-title": { - "label": "标题滚动", - "description": "为长媒体标题启用连续滚动" - }, - "scrolling-speed": { - "label": "滚动速度", - "description": "标题从头到尾滚动所需的时间(秒)" - } - } - }, - "display": { - "title": "显示", - "monitors": { - "section": { - "label": "每显示器设置", - "description": "调整每个显示器的缩放比例和亮度。" - }, - "scale": "缩放比例", - "brightness": "亮度", - "reset-scaling": "重置缩放", - "brightness-step": { - "label": "亮度步长", - "description": "调整亮度变化的步长(滚轮和键盘快捷键)。" - } - }, - "night-light": { - "section": { - "label": "夜灯", - "description": "减少蓝光发射,帮助您更好地睡眠并减轻眼睛疲劳。" - }, - "enable": { - "label": "启用夜灯", - "description": "应用暖色滤镜以减少蓝光发射。" - }, - "temperature": { - "label": "色温", - "description": "设置夜间和白天的颜色温暖度。", - "night": "夜间", - "day": "白天" - }, - "auto-schedule": { - "label": "自动调度", - "description": "基于 {location} 的日落和日出时间 - 推荐。" - }, - "manual-schedule": { - "label": "手动调度", - "description": "为日出和日落设置自定义时间。", - "sunrise": "日出时间", - "sunset": "日落时间", - "select-start": "选择开始时间", - "select-stop": "选择停止时间" - }, - "force-activation": { - "label": "强制激活", - "description": "忽略调度并立即应用夜间滤镜。" - } - } - }, - "bar": { - "title": "状态栏", - "appearance": { - "section": { - "label": "外观", - "description": "自定义状态栏的外观和位置。" - }, - "position": { - "label": "状态栏位置", - "description": "选择在屏幕上的位置。" - }, - "density": { - "label": "状态栏密度", - "description": "调整状态栏的内边距以获得紧凑或宽松的外观。" - }, - "background-opacity": { - "label": "背景不透明度", - "description": "调整状态栏的背景不透明度。" - }, - "show-capsule": { - "label": "显示胶囊", - "description": "显示小部件背景。" - }, - "floating": { - "label": "浮动状态栏", - "description": "将状态栏显示为浮动样式(类似于一个药丸)。注意:这会将屏幕边角移动到边缘。" - }, - "margins": { - "label": "边距", - "description": "调整浮动状态栏周围的边距。", - "vertical": "垂直", - "horizontal": "水平" - } - }, - "widgets": { - "section": { - "label": "小部件定位", - "description": "拖放小部件以在每个部分内重新排序,或使用添加/删除按钮管理小部件。" - } - }, - "monitors": { - "section": { - "label": "显示器显示", - "description": "在特定显示器上显示状态栏。如果未选择,则默认为全部。" - } - } - }, - "dock": { - "title": "Dock", - "appearance": { - "section": { - "label": "外观", - "description": "自定义 Dock 的行为和外观。" - }, - "auto-hide": { - "label": "自动隐藏", - "description": "不使用时自动隐藏。" - }, - "exclusive-zone": { - "label": "独占区域", - "description": "可以防止窗口重叠。" - }, - "background-opacity": { - "label": "背景不透明度", - "description": "调整 Dock 的背景不透明度。" - }, - "floating-distance": { - "label": "Dock 浮动距离", - "description": "调整距离屏幕边缘的浮动距离。" - } - }, - "monitors": { - "section": { - "label": "显示器显示", - "description": "选择在哪个显示器上显示 Dock。" - } - } - }, - "launcher": { - "title": "启动器", - "settings": { - "section": { - "label": "外观", - "description": "自定义启动器的行为和外观。" - }, - "position": { - "label": "位置", - "description": "选择启动器面板出现的位置。" - }, - "background-opacity": { - "label": "背景不透明度", - "description": "调整启动器的背景不透明度。" - }, - "clipboard-history": { - "label": "启用剪贴板历史记录", - "description": "从启动器访问之前复制的项目。" - }, - "sort-by-usage": { - "label": "按使用频率排序", - "description": "启用后,经常启动的应用程序将显示在列表首位。" - }, - "use-app2unit": { - "label": "使用 App2Unit 启动应用程序", - "description": "使用替代启动方法以更好地管理应用程序进程并防止问题。" - } - } - }, - "notifications": { - "title": "通知", - "settings": { - "section": { - "label": "外观", - "description": "配置通知的外观和行为。" - }, - "do-not-disturb": { - "label": "勿扰模式", - "description": "启用后禁用所有通知弹出窗口。" - }, - "enable-osd": { - "label": "启用屏幕显示", - "description": "实时显示音量和亮度变化。" - }, - "location": { - "label": "位置", - "description": "通知在屏幕上出现的位置。" - }, - "always-on-top": { - "label": "始终置顶", - "description": "在全屏窗口和其他图层上方显示通知。" - } - }, - "duration": { - "section": { - "label": "通知持续时间", - "description": "根据紧急级别配置通知保持可见的时间。" - }, - "respect-expire": { - "label": "尊重过期超时", - "description": "使用通知中设置的过期超时。" - }, - "low-urgency": { - "label": "低紧急度", - "description": "低优先级通知保持可见的时间。" - }, - "normal-urgency": { - "label": "正常紧急度", - "description": "正常优先级通知保持可见的时间。" - }, - "critical-urgency": { - "label": "高紧急度", - "description": "高优先级通知保持可见的时间。" - } - }, - "monitors": { - "section": { - "label": "显示器显示", - "description": "在特定显示器上显示通知。如果未选择,则默认为全部。" - } - } - }, - "osd": { - "title": "屏幕显示", - "description": "配置屏幕叠加指示器,例如音量和亮度。", + "settings": { + "general": { + "title": "通用", + "profile": { "section": { - "general": { - "label": "常规", - "description": "配置屏幕显示(OSD)的可见性与行为。" - } + "label": "个人资料", + "description": "编辑您的用户详细信息和头像。" }, - "enabled": { - "label": "启用屏幕显示", - "description": "实时显示音量与亮度变化。" + "picture": { + "label": "{user} 的个人资料图片", + "description": "在整个界面中显示您的个人资料图片。" }, - "location": { - "label": "位置", - "description": "屏幕显示出现的位置。" - }, - "duration": { - "section": { - "label": "自动隐藏超时", - "description": "屏幕显示自动隐藏前保持可见的时长。" - }, - "auto-hide": { - "label": "在此之后隐藏", - "description": "调整屏幕显示消失前的时间。" - } - }, - "monitors": { - "section": { - "label": "显示器显示", - "description": "在特定显示器上显示屏幕显示。若未选择,则默认全部。" - } - } + "select-avatar": "选择头像图片" }, - "wallpaper": { - "title": "壁纸", - "settings": { - "section": { - "label": "壁纸设置", - "description": "控制壁纸的管理和显示方式。" - }, - "enable-management": { - "label": "启用壁纸管理", - "description": "使用 Noctalia 管理壁纸。如果您更喜欢使用其他应用程序,请取消选中。" - }, - "folder": { - "label": "壁纸文件夹", - "description": "您的主壁纸文件夹路径。", - "tooltip": "浏览壁纸文件夹" - }, - "monitor-specific": { - "label": "显示器特定目录", - "description": "为每个显示器设置不同的壁纸文件夹。", - "tooltip": "浏览壁纸文件夹" - }, - "select-folder": "选择壁纸文件夹", - "select-monitor-folder": "选择显示器壁纸文件夹" - }, - "look-feel": { - "section": { - "label": "外观和感觉" - }, - "fill-mode": { - "label": "填充模式", - "description": "选择图像应如何缩放以匹配显示器的分辨率。" - }, - "fill-color": { - "label": "填充颜色", - "description": "选择可能出现在壁纸后面的填充颜色。" - }, - "transition-type": { - "label": "过渡类型", - "description": "切换壁纸时的动画类型。" - }, - "transition-duration": { - "label": "过渡持续时间", - "description": "过渡动画的持续时间(秒)。" - }, - "edge-smoothness": { - "label": "软化过渡边缘", - "description": "对过渡边缘应用柔和、羽化的效果。" - } - }, - "automation": { - "section": { - "label": "自动化" - }, - "random-wallpaper": { - "label": "随机壁纸", - "description": "按固定间隔调度随机壁纸更改。" - }, - "interval": { - "label": "壁纸间隔", - "description": "自动更改壁纸的频率。" - }, - "custom-interval": { - "label": "自定义间隔", - "description": "以 HH:MM 格式输入时间(例如:01:30)。" - } - } - }, - "color-scheme": { - "title": "配色方案", - "color-source": { - "section": { - "label": "颜色来源", - "description": "Noctalia 颜色的主要设置。" - }, - "dark-mode": { - "label": "深色模式", - "description": "切换到更暗的主题,便于夜间观看。" - }, - "enable-matugen": { - "label": "启用 Matugen", - "description": "根据您的活动壁纸自动生成颜色。" - }, - "matugen-scheme-type": { - "label": "Matugen 配色方案类型", - "description": "为 Matugen 选择配色方案生成算法。" - } - }, - "predefined": { - "section": { - "label": "预定义配色方案", - "description": "从预定义配色方案集合中选择。" - } - }, - "matugen": { - "section": { - "label": "Matugen 模板", - "description": "将颜色应用于外部应用程序。" - }, - "ui": { - "label": "用户界面", - "description": "桌面环境和 UI 工具包主题。", - "gtk4": { - "description": "写入 {filepath}" - }, - "gtk3": { - "description": "写入 {filepath}" - }, - "qt6": { - "description": "写入 {filepath}" - }, - "qt5": { - "description": "写入 {filepath}" - } - }, - "terminal": { - "label": "终端", - "description": "终端模拟器主题。", - "kitty": { - "description": "写入 {filepath} 并重新加载", - "description-missing": "需要安装 {app}" - }, - "ghostty": { - "description": "写入 {filepath} 并重新加载", - "description-missing": "需要安装 {app}" - }, - "foot": { - "description": "写入 {filepath} 并重新加载", - "description-missing": "需要安装 {app}" - } - }, - "programs": { - "label": "程序", - "description": "应用程序特定主题。", - "fuzzel": { - "description": "写入 {filepath} 并重新加载", - "description-missing": "需要安装 {app}" - }, - "vesktop": { - "description": "写入 {filepath}", - "description-missing": "需要安装 {app}" - }, - "pywalfox": { - "description": "写入 {filepath} 并运行 pywalfox update", - "description-missing": "需要安装 {app}" - } - }, - "misc": { - "label": "杂项", - "description": "其他配置选项。", - "user-templates": { - "label": "用户模板", - "description": "启用来自 ~/.config/matugen/config.toml 的用户定义 Matugen 配置" - } - } - } - }, - "location": { - "title": "位置", - "location": { - "section": { - "label": "您的位置", - "description": "通过设置您的位置获取准确的天气和夜灯调度。" - }, - "search": { - "label": "搜索位置", - "description": "例如:多伦多, 安大略省", - "placeholder": "输入位置名称" - } - }, - "weather": { - "section": { - "label": "天气", - "description": "选择您喜欢的温度单位。" - }, - "fahrenheit": { - "label": "以华氏度显示温度 (°F)", - "description": "以华氏度而非摄氏度显示温度。" - } - }, - "date-time": { - "section": { - "label": "日期和时间", - "description": "自定义日期和时间的显示方式。" - }, - "12hour-format": { - "label": "在锁屏上使用 12 小时制", - "description": "开启为 AM/PM 格式(例如:8:00 PM),关闭为 24 小时制(例如:20:00)。" - }, - "week-numbers": { - "label": "显示周数", - "description": "在日历中显示一年中的第几周(例如:第 38 周)。" - } - } - }, - "network": { - "title": "网络", + "ui": { "section": { - "description": "管理 Wi-Fi 和蓝牙连接。" + "label": "用户界面", + "description": "自定义界面的外观、风格和行为。" }, - "wifi": { - "label": "启用 Wi-Fi" + "dim-desktop": { + "label": "调暗桌面", + "description": "当面板或菜单打开时调暗桌面。" }, - "bluetooth": { - "label": "启用蓝牙" + "border-radius": { + "label": "边框圆角", + "description": "控制窗口、按钮及其他元素的边角圆度。" + }, + "animation-speed": { + "label": "动画速度", + "description": "调整全局动画速度。" + }, + "animation-disable": { + "label": "关闭动画", + "description": "禁用所有动画效果,以获得更快速、更灵敏的体验。" } }, - "screen-recorder": { - "title": "屏幕录制", - "general": { - "section": { - "label": "常规设置", - "description": "管理屏幕录制输出和内容。" - }, - "output-folder": { - "label": "输出文件夹", - "description": "屏幕录制将保存的文件夹。", - "tooltip": "浏览输出文件夹" - }, - "show-cursor": { - "label": "显示光标", - "description": "在视频中录制鼠标光标。" - }, - "select-output-folder": "选择输出文件夹" + "screen-corners": { + "section": { + "label": "屏幕边角", + "description": "自定义屏幕边角的圆度和视觉效果。" }, - "video": { - "section": { - "label": "视频设置", - "description": "配置视频录制选项。" - }, - "video-source": { - "label": "视频源", - "description": "推荐使用 Portal,如果出现伪影请尝试 Screen。" - }, - "frame-rate": { - "label": "帧率", - "description": "屏幕录制的目标帧率。" - }, - "video-quality": { - "label": "视频质量", - "description": "更高的质量会导致更大的文件大小。" - }, - "video-codec": { - "label": "视频编解码器", - "description": "h264 是最常见的编解码器。" - }, - "color-range": { - "label": "颜色范围", - "description": "推荐使用 Limited 以获得更好的兼容性。" - } + "show-corners": { + "label": "显示屏幕圆角", + "description": "在屏幕边缘显示圆角。" }, - "audio": { - "section": { - "label": "音频设置", - "description": "配置音频录制选项。" - }, - "audio-source": { - "label": "音频源", - "description": "录制期间要捕获的音频源。" - }, - "audio-codec": { - "label": "音频编解码器", - "description": "推荐使用 Opus 以获得最佳性能和最小的音频大小。" - } + "solid-black": { + "label": "实心黑色边角", + "description": "使用实心黑色而非状态栏背景色。" + }, + "radius": { + "label": "屏幕边角半径", + "description": "调整屏幕圆角的弧度。" } }, - "about": { - "title": "关于", - "noctalia": { - "section": { - "label": "Noctalia shell", - "description": "一款为 Wayland 精心打造的时尚简约桌面 shell,基于 Quickshell 构建。" - }, - "latest-version": "最新版本:", - "installed-version": "已安装版本:", - "download-latest": "下载最新版本" + "fonts": { + "section": { + "label": "字体", + "description": "选择整个界面中使用的字体。" }, - "contributors": { - "section": { - "label": "贡献者", - "description": "向我们 {count} 位超棒的贡献者致敬!", - "description_plural": "向我们 {count} 位超棒的贡献者致敬!" - } - } - }, - "hooks": { - "title": "钩子", - "system-hooks": { - "section": { - "label": "系统钩子", - "description": "配置在系统事件发生时执行的命令。" - }, - "enable": { - "label": "启用钩子", - "description": "启用或禁用所有钩子命令。" - } + "default": { + "label": "默认字体", + "description": "整个界面使用的主要字体。", + "placeholder": "选择默认字体...", + "search-placeholder": "搜索字体..." }, - "wallpaper-changed": { - "label": "壁纸已更改", - "description": "壁纸更改时执行的命令。", - "placeholder": "例如:notify-send \"壁纸\" \"已更改\"" + "monospace": { + "label": "等宽字体", + "description": "用于数字和统计信息显示的等宽字体。", + "placeholder": "选择等宽字体...", + "search-placeholder": "搜索等宽字体..." }, - "theme-changed": { - "label": "主题已更改", - "description": "主题在深色和浅色模式之间切换时执行的命令。", - "placeholder": "例如:notify-send \"主题\" \"已切换\"" - }, - "info": { - "command-info": { - "label": "钩子命令信息", - "description": "• 命令通过 shell 执行 (sh -c)\n• 命令在后台运行(分离)\n• 测试按钮使用当前值执行" - }, - "parameters": { - "label": "可用参数", - "description": "• 壁纸钩子: $1 = 壁纸路径, $2 = 屏幕名称\n• 主题切换钩子: $1 = true/false (深色模式状态)" - } + "accent": { + "label": "强调字体", + "description": "用于突出显示内容的大字体。", + "placeholder": "选择标题字体...", + "search-placeholder": "搜索标题字体..." } } }, - "widgets": { - "tooltip": { - "placeholder": "占位符" - }, - "file-picker": { - "select-folder": "选择文件夹", - "select-file": "选择文件" - }, - "datetime-tokens": { - "common": { - "12hour-time-minutes": "12小时制带分钟", - "24hour-time-minutes": "24小时制带分钟", - "24hour-time-seconds": "24小时制带秒", - "weekday-month-day": "星期、月份和日期", - "iso-date": "ISO 日期格式", - "us-date": "美国日期格式", - "european-date": "欧洲日期格式", - "weekday-date": "星期和日期" + "audio": { + "title": "音频", + "volumes": { + "section": { + "label": "音量", + "description": "调整音量控制和音频级别。" }, - "hour": { - "no-leading-zero": "小时无前导零 (0-23) - 24小时制", - "leading-zero": "小时有前导零 (00-23) - 24小时制" + "output-volume": { + "label": "输出音量", + "description": "系统全局音量级别。" }, - "minute": { - "no-leading-zero": "分钟无前导零 (0-59)", - "leading-zero": "分钟有前导零 (00-59)" + "mute-output": { + "label": "静音输出", + "description": "静音系统主音频输出。" }, - "second": { - "no-leading-zero": "秒无前导零 (0-59)", - "leading-zero": "秒有前导零 (00-59)" + "input-volume": { + "label": "输入音量", + "description": "麦克风输入音量级别。" }, - "ampm": { - "uppercase": "AM/PM 大写", - "lowercase": "am/pm 小写" + "mute-input": { + "label": "静音输入", + "description": "静音默认音频输入(麦克风)。" }, - "timezone": { - "abbreviation": "时区缩写" + "step-size": { + "label": "音量步长", + "description": "调整音量变化的步长(滚轮、键盘快捷键)。" }, - "year": { - "two-digit": "年份为两位数 (00-99)", - "four-digit": "年份为四位数" - }, - "month": { - "number-no-zero": "月份数字无前导零 (1-12)", - "number-leading-zero": "月份数字有前导零 (01-12)", - "abbreviated": "月份名称缩写", - "full": "月份名称全称" - }, - "day": { - "no-leading-zero": "日期无前导零 (1-31)", - "leading-zero": "日期有前导零 (01-31)", - "abbreviated": "星期名称缩写", - "full": "星期名称全称" + "volume-overdrive": { + "label": "允许音量过载", + "description": "允许将音量提高到100%以上。可能不被所有硬件支持。" } }, - "icon-picker": { - "title": "图标选择器", - "search": { - "label": "搜索" + "devices": { + "section": { + "label": "音频设备", + "description": "配置可用的音频输入和输出设备。" }, - "cancel": "取消", - "apply": "应用" + "output-device": { + "label": "输出设备", + "description": "选择所需的音频输出设备。" + }, + "input-device": { + "label": "输入设备", + "description": "选择所需的音频输入设备。" + } }, - "color-picker": { - "title": "颜色选择器", - "hex": { - "label": "十六进制颜色", - "description": "输入十六进制颜色代码。" + "media": { + "section": { + "label": "媒体播放器", + "description": "设置您首选和要忽略的媒体应用程序。" }, - "rgb": { - "label": "RGB 值", - "description": "调整红、绿、蓝和亮度值。" + "primary-player": { + "label": "主要播放器", + "description": "输入关键词以识别您的主要播放器。", + "placeholder": "例如:spotify, vlc, mpv" }, + "excluded-player": { + "label": "排除的播放器", + "description": "添加您希望系统忽略的播放器关键词。每个关键词应单独占一行。", + "placeholder": "输入关键词并按 + 添加" + }, + "visualizer-type": { + "label": "可视化类型", + "description": "选择媒体播放的可视化类型" + }, + "frame-rate": { + "label": "帧率", + "description": "帧率越高越流畅,但会占用更多资源。" + }, + "scrolling-title": { + "label": "标题滚动", + "description": "为长媒体标题启用连续滚动" + }, + "scrolling-speed": { + "label": "滚动速度", + "description": "标题从头到尾滚动所需的时间(秒)" + } + } + }, + "display": { + "title": "显示", + "monitors": { + "section": { + "label": "每显示器设置", + "description": "调整每个显示器的缩放比例和亮度。" + }, + "scale": "缩放比例", "brightness": "亮度", - "theme-colors": { - "label": "主题颜色", - "description": "快速访问您的主题调色板。" + "reset-scaling": "重置缩放", + "brightness-step": { + "label": "亮度步长", + "description": "调整亮度变化的步长(滚轮和键盘快捷键)。" + } + }, + "night-light": { + "section": { + "label": "夜灯", + "description": "减少蓝光发射,帮助您更好地睡眠并减轻眼睛疲劳。" }, - "palette": { - "label": "调色板", - "description": "从各种预定义颜色中选择。" + "enable": { + "label": "启用夜灯", + "description": "应用暖色滤镜以减少蓝光发射。" }, - "cancel": "取消", - "apply": "应用" + "temperature": { + "label": "色温", + "description": "设置夜间和白天的颜色温暖度。", + "night": "夜间", + "day": "白天" + }, + "auto-schedule": { + "label": "自动调度", + "description": "基于 {location} 的日落和日出时间 - 推荐。" + }, + "manual-schedule": { + "label": "手动调度", + "description": "为日出和日落设置自定义时间。", + "sunrise": "日出时间", + "sunset": "日落时间", + "select-start": "选择开始时间", + "select-stop": "选择停止时间" + }, + "force-activation": { + "label": "强制激活", + "description": "忽略调度并立即应用夜间滤镜。" + } } }, "bar": { - "widget-settings": { - "dialog": { - "cancel": "取消", - "apply": "应用" + "title": "状态栏", + "appearance": { + "section": { + "label": "外观", + "description": "自定义状态栏的外观和位置。" }, - "section-editor": { - "placeholder": "选择要添加的小部件...", - "search-placeholder": "搜索小部件..." + "position": { + "label": "状态栏位置", + "description": "选择在屏幕上的位置。" }, - "active-window": { - "auto-hide": "自动隐藏", - "show-app-icon": "显示应用图标", - "scrolling-mode": "滚动模式" + "density": { + "label": "状态栏密度", + "description": "调整状态栏的内边距以获得紧凑或宽松的外观。" }, - "system-monitor": { - "cpu-usage": "CPU 使用率", - "cpu-temperature": "CPU 温度", - "memory-usage": "内存使用率", - "memory-percentage": "内存百分比", - "network-traffic": "网络流量", - "storage-usage": "存储使用率" + "background-opacity": { + "label": "背景不透明度", + "description": "调整状态栏的背景不透明度。" }, - "notification-history": { - "show-unread-badge": "显示未读标记", - "hide-badge-when-zero": "为零时隐藏标记" + "show-capsule": { + "label": "显示胶囊", + "description": "显示小部件背景。" }, - "battery": { - "display-mode": { - "label": "显示模式", - "description": "选择您希望此值显示的方式。" - }, - "low-battery-threshold": { - "label": "低电量警告阈值", - "description": "当电量低于此百分比时显示警告。" - } + "floating": { + "label": "浮动状态栏", + "description": "将状态栏显示为浮动样式(类似于一个药丸)。注意:这会将屏幕边角移动到边缘。" }, - "control-center": { - "use-distro-logo": "使用发行版徽标代替图标", - "icon": { - "label": "图标", - "description": "从库中选择图标或自定义文件。" - }, - "browse-library": "浏览库", - "browse-file": "浏览文件", - "select-custom-icon": "选择自定义图标" + "margins": { + "label": "边距", + "description": "调整浮动状态栏周围的边距。", + "vertical": "垂直", + "horizontal": "水平" + } + }, + "widgets": { + "section": { + "label": "小部件定位", + "description": "拖放小部件以在每个部分内重新排序,或使用添加/删除按钮管理小部件。" + } + }, + "monitors": { + "section": { + "label": "显示器显示", + "description": "在特定显示器上显示状态栏。如果未选择,则默认为全部。" + } + } + }, + "dock": { + "title": "Dock", + "appearance": { + "section": { + "label": "外观", + "description": "自定义 Dock 的行为和外观。" }, - "keyboard-layout": { - "display-mode": { - "label": "显示模式", - "description": "选择您希望此值显示的方式。" - } + "auto-hide": { + "label": "自动隐藏", + "description": "不使用时自动隐藏。" }, - "volume": { - "display-mode": { - "label": "显示模式", - "description": "选择您希望此值显示的方式。" - } + "exclusive-zone": { + "label": "独占区域", + "description": "可以防止窗口重叠。" }, - "workspace": { - "label-mode": "标签模式", - "hide-unoccupied": { - "label": "隐藏未占用", - "description": "不显示没有窗口的工作区。" - } + "background-opacity": { + "label": "背景不透明度", + "description": "调整 Dock 的背景不透明度。" }, - "microphone": { - "display-mode": { - "label": "显示模式", - "description": "选择您希望此值显示的方式。" - } + "floating-distance": { + "label": "Dock 浮动距离", + "description": "调整距离屏幕边缘的浮动距离。" + } + }, + "monitors": { + "section": { + "label": "显示器显示", + "description": "选择在哪个显示器上显示 Dock。" + } + } + }, + "launcher": { + "title": "启动器", + "settings": { + "section": { + "label": "外观", + "description": "自定义启动器的行为和外观。" }, - "brightness": { - "display-mode": { - "label": "显示模式", - "description": "选择您希望此值显示的方式。" - } + "position": { + "label": "位置", + "description": "选择启动器面板出现的位置。" }, - "spacer": { - "width": { - "label": "宽度", - "description": "间距宽度(像素)" - } + "background-opacity": { + "label": "背景不透明度", + "description": "调整启动器的背景不透明度。" }, - "custom-button": { - "icon": { - "label": "图标", - "description": "从库中选择图标。" - }, - "browse": "浏览", - "left-click": "左键点击", - "right-click": "右键点击", - "middle-click": "中键点击", - "dynamic-text": "动态文本", - "display-command-output": { - "label": "显示命令输出", - "description": "输入一个定期运行的命令。其输出的第一行将显示为文本。" - }, - "refresh-interval": { - "label": "刷新间隔", - "description": "间隔时间(毫秒)。" - } + "clipboard-history": { + "label": "启用剪贴板历史记录", + "description": "从启动器访问之前复制的项目。" }, - "media-mini": { - "auto-hide": "自动隐藏", - "show-album-art": "显示专辑封面", - "show-visualizer": "显示可视化器", - "visualizer-type": "可视化器类型", - "scrolling-mode": "滚动模式" + "sort-by-usage": { + "label": "按使用频率排序", + "description": "启用后,经常启动的应用程序将显示在列表首位。" }, - "clock": { - "use-primary-color": { - "label": "使用主颜色", - "description": "启用后,将应用主颜色进行强调。" - }, - "use-monospaced-font": { - "label": "使用等宽字体", - "description": "启用后,时钟将使用等宽字体。" - }, - "clock-display": { - "label": "时钟显示", - "description": "通过从下面的列表添加标记来自定义时钟显示。要使用 12 小时制,必须包含 'AP' 标记。" - }, - "horizontal-bar": { - "label": "水平栏", - "description": "提示:使用 \\n 创建换行。" - }, - "vertical-bar": { - "label": "垂直栏", - "description": "使用空格将每个部分分隔到新行。" - }, - "preview": "预览" + "use-app2unit": { + "label": "使用 App2Unit 启动应用程序", + "description": "使用替代启动方法以更好地管理应用程序进程并防止问题。" } } }, "notifications": { - "panel": { - "title": "通知", - "no-notifications": "无通知", - "description": "您的通知将在到达时显示在此处。" + "title": "通知", + "settings": { + "section": { + "label": "外观", + "description": "配置通知的外观和行为。" + }, + "do-not-disturb": { + "label": "勿扰模式", + "description": "启用后禁用所有通知弹出窗口。" + }, + "enable-osd": { + "label": "启用屏幕显示", + "description": "实时显示音量和亮度变化。" + }, + "location": { + "label": "位置", + "description": "通知在屏幕上出现的位置。" + }, + "always-on-top": { + "label": "始终置顶", + "description": "在全屏窗口和其他图层上方显示通知。" + } + }, + "duration": { + "section": { + "label": "通知持续时间", + "description": "根据紧急级别配置通知保持可见的时间。" + }, + "respect-expire": { + "label": "尊重过期超时", + "description": "使用通知中设置的过期超时。" + }, + "low-urgency": { + "label": "低紧急度", + "description": "低优先级通知保持可见的时间。" + }, + "normal-urgency": { + "label": "正常紧急度", + "description": "正常优先级通知保持可见的时间。" + }, + "critical-urgency": { + "label": "高紧急度", + "description": "高优先级通知保持可见的时间。" + } + }, + "monitors": { + "section": { + "label": "显示器显示", + "description": "在特定显示器上显示通知。如果未选择,则默认为全部。" + } + } + }, + "osd": { + "title": "屏幕显示", + "description": "配置屏幕叠加指示器,例如音量和亮度。", + "section": { + "general": { + "label": "常规", + "description": "配置屏幕显示(OSD)的可见性与行为。" + } + }, + "enabled": { + "label": "启用屏幕显示", + "description": "实时显示音量与亮度变化。" + }, + "location": { + "label": "位置", + "description": "屏幕显示出现的位置。" + }, + "duration": { + "section": { + "label": "自动隐藏超时", + "description": "屏幕显示自动隐藏前保持可见的时长。" + }, + "auto-hide": { + "label": "在此之后隐藏", + "description": "调整屏幕显示消失前的时间。" + } + }, + "monitors": { + "section": { + "label": "显示器显示", + "description": "在特定显示器上显示屏幕显示。若未选择,则默认全部。" + } } }, "wallpaper": { - "panel": { - "title": "壁纸选择器", - "apply-all-monitors": { - "label": "应用到所有显示器", - "description": "一次性将选定的壁纸应用到所有显示器。" + "title": "壁纸", + "settings": { + "section": { + "label": "壁纸设置", + "description": "控制壁纸的管理和显示方式。" }, - "search": "搜索:" - }, - "transitions": { - "none": "无", - "random": "随机", - "fade": "淡入淡出", - "disc": "圆盘", - "stripes": "条纹", - "wipe": "擦除" - }, - "fill-modes": { - "center": "居中", - "crop": "裁剪(填充)", - "fit": "适应(包含)", - "stretch": "拉伸" - }, - "no-match": "未找到匹配项。", - "no-wallpaper": "未找到壁纸。", - "try-different-search": "尝试不同的搜索查询。", - "configure-directory": "使用图像配置您的壁纸目录。" - }, - "bluetooth": { - "panel": { - "title": "蓝牙", - "disabled": "蓝牙已禁用", - "enable-message": "启用蓝牙以查看可用设备。", - "connected-devices": "已连接设备", - "known-devices": "已知设备", - "available-devices": "可用设备", - "scanning": "正在扫描设备...", - "pairing-mode": "确保您的设备处于配对模式。" - } - }, - "wifi": { - "panel": { - "title": "Wi-Fi", - "disabled": "Wi-Fi 已禁用", - "enable-message": "启用 Wi-Fi 以查看可用网络。", - "searching": "正在搜索附近网络...", - "connected": "已连接", - "disconnecting": "正在断开连接...", - "forgetting": "正在忘记...", - "saved": "已保存", - "disconnect": "断开连接", - "enter-password": "输入密码...", - "connect": "连接", - "password": "密码", - "forget-network": "忘记此网络?", - "forget": "忘记", - "no-networks": "未找到网络", - "scan-again": "重新扫描" - } - }, - "calendar": { - "panel": { - "week": "周" - } - }, - "tooltips": { - "refresh": "刷新", - "close": "关闭", - "refresh-wallpaper-list": "刷新壁纸列表", - "refresh-devices": "刷新设备", - "forget-network": "忘记网络", - "clear-history": "清除历史记录", - "delete-notification": "删除通知", - "previous-month": "上个月", - "next-month": "下个月", - "add-widget": "添加小部件", - "widget-settings": "小部件设置", - "remove-widget": "移除小部件", - "move-to-left-section": "移动到左侧部分", - "move-to-center-section": "移动到中央部分", - "move-to-right-section": "移动到右侧部分", - "open-settings": "打开设置", - "session-menu": "会话菜单", - "cancel-timer": "取消计时器", - "start-screen-recording": "开始屏幕录制", - "stop-screen-recording": "停止屏幕录制", - "screen-recorder-not-installed": "屏幕录制器未安装", - "keep-awake": "保持唤醒", - "enable-keep-awake": "启用保持唤醒", - "disable-keep-awake": "禁用保持唤醒", - "wallpaper-selector": "左键点击:打开壁纸选择器。\n右键点击:设置随机壁纸。", - "do-not-disturb-enabled": "'勿扰模式'已启用", - "do-not-disturb-disabled": "'勿扰模式'已禁用", - "connect-disconnect-devices": "左键点击连接。右键点击忘记。", - "set-power-profile": "设置\"{profile}\"电源模式", - "switch-to-light-mode": "切换到浅色模式", - "switch-to-dark-mode": "切换到深色模式", - "night-light-disabled": "夜灯已禁用。\n左键点击循环模式。\n右键点击访问设置。", - "night-light-enabled": "夜灯已启用。\n左键点击循环模式。\n右键点击访问设置。", - "night-light-forced": "夜灯已强制启用。\n左键点击循环模式。\n右键点击访问设置。", - "click-to-start-recording": "点击开始录制", - "click-to-stop-recording": "点击停止录制", - "open-control-center": "打开控制中心", - "volume-at": "音量 {volume}%\n左键点击切换静音。右键点击进入设置。\n滚动调整音量。", - "microphone-volume-at": "麦克风音量 {volume}%\n左键点击切换静音。右键点击进入设置。\n滚动调整音量。", - "manage-wifi": "管理 Wi-Fi", - "bluetooth-devices": "蓝牙设备", - "open-notification-history-enable-dnd": "打开通知历史记录\n右键点击启用\"勿扰模式\"。", - "open-notification-history-disable-dnd": "打开通知历史记录\n右键点击禁用\"勿扰模式\"。", - "open-wallpaper-selector": "打开壁纸选择器", - "previous-media": "上一媒体", - "pause": "暂停", - "play": "播放", - "next-media": "下一媒体", - "power-profile": "'{profile}' 电源模式", - "keyboard-layout": "{layout} 键盘布局" - }, - "clock": { - "tooltip": "打开日历" - }, - "dock": { - "menu": { - "focus": "聚焦", - "pin": "固定", - "unpin": "取消固定", - "close": "关闭" - } - }, - "placeholders": { - "search-icons": "例如:noctalia, niri, battery, cloud", - "profile-picture-path": "/home/user/.face", - "enter-width-pixels": "输入宽度(像素)", - "enter-command": "输入要执行的命令(应用程序或自定义脚本)", - "command-example": "echo \"Hello World\"", - "search-wallpapers": "输入以筛选壁纸...", - "search-launcher": "搜索条目...或使用 > 执行命令", - "search": "搜索...", - "select": "选择", - "cancel": "取消", - "test": "测试" - }, - "options": { - "bar": { - "position": { - "top": "顶部", - "bottom": "底部", - "left": "左侧", - "right": "右侧" + "enable-management": { + "label": "启用壁纸管理", + "description": "使用 Noctalia 管理壁纸。如果您更喜欢使用其他应用程序,请取消选中。" }, - "density": { - "compact": "紧凑", - "default": "默认", - "comfortable": "舒适" + "folder": { + "label": "壁纸文件夹", + "description": "您的主壁纸文件夹路径。", + "tooltip": "浏览壁纸文件夹" + }, + "monitor-specific": { + "label": "显示器特定目录", + "description": "为每个显示器设置不同的壁纸文件夹。", + "tooltip": "浏览壁纸文件夹" + }, + "select-folder": "选择壁纸文件夹", + "select-monitor-folder": "选择显示器壁纸文件夹" + }, + "look-feel": { + "section": { + "label": "外观和感觉" + }, + "fill-mode": { + "label": "填充模式", + "description": "选择图像应如何缩放以匹配显示器的分辨率。" + }, + "fill-color": { + "label": "填充颜色", + "description": "选择可能出现在壁纸后面的填充颜色。" + }, + "transition-type": { + "label": "过渡类型", + "description": "切换壁纸时的动画类型。" + }, + "transition-duration": { + "label": "过渡持续时间", + "description": "过渡动画的持续时间(秒)。" + }, + "edge-smoothness": { + "label": "软化过渡边缘", + "description": "对过渡边缘应用柔和、羽化的效果。" } }, - "launcher": { - "position": { - "center": "居中(默认)", - "top_left": "左上角", - "top_right": "右上角", - "bottom_left": "左下角", - "bottom_right": "右下角", - "bottom_center": "底部居中", - "top_center": "顶部居中", - "center_left": "左侧居中", - "center_right": "右侧居中" - } - }, - "osd": { - "position": { - "top_left": "左上角", - "top_right": "右上角", - "top_center": "顶部居中", - "bottom_left": "左下角", - "bottom_right": "右下角", - "bottom_center": "底部居中", - "center_left": "左侧居中", - "center_right": "右侧居中" - } - }, - "display-mode": { - "on-hover": "悬停时显示", - "always-show": "始终显示", - "always-hide": "始终隐藏", - "force-open": "强制打开" - }, - "workspace-labels": { - "none": "无", - "index": "索引", - "name": "名称" - }, - "visualizer-types": { - "none": "无", - "linear": "线性", - "mirrored": "镜像", - "wave": "波形" - }, - "scrolling-modes": { - "always": "始终滚动", - "hover": "悬停时滚动", - "never": "从不滚动" - }, - "frame-rates": { - "fps": "{fps} FPS" - }, - "screen-recording": { - "sources": { - "portal": "Portal", - "screen": "屏幕" + "automation": { + "section": { + "label": "自动化" }, - "quality": { - "medium": "中等", - "high": "高", - "very-high": "很高", - "ultra": "超高" + "random-wallpaper": { + "label": "随机壁纸", + "description": "按固定间隔调度随机壁纸更改。" }, - "color-range": { - "limited": "有限", - "full": "完全" + "interval": { + "label": "壁纸间隔", + "description": "自动更改壁纸的频率。" }, - "audio-sources": { - "system-output": "系统输出", - "microphone-input": "麦克风输入", - "both": "系统输出 + 麦克风输入" + "custom-interval": { + "label": "自定义间隔", + "description": "以 HH:MM 格式输入时间(例如:01:30)。" } } }, - "session-menu": { - "title": "会话菜单", - "click-again": "再次点击立即执行", - "action-in-seconds": "{seconds} 秒后{action}...", - "lock-subtitle": "锁定您的会话", - "end-subtitle": "结束您的会话", - "lock": "锁定", - "suspend": "挂起", - "reboot": "重启", - "logout": "注销", - "shutdown": "关机" - }, - "plugins": { - "applications": "应用程序", - "clipboard": "剪贴板历史记录", - "calculator": "计算器", - "clipboard-search-description": "搜索剪贴板历史记录", - "clipboard-clear-description": "清除所有剪贴板历史记录", - "clipboard-history-disabled": "剪贴板历史记录已禁用", - "clipboard-history-disabled-description": "在设置中启用剪贴板历史记录或安装 cliphist", - "clipboard-clear-history": "清除剪贴板历史记录", - "clipboard-clear-description-full": "从剪贴板历史记录中移除所有项目", - "clipboard-loading": "正在加载剪贴板历史记录...", - "clipboard-loading-description": "请稍候", - "calculator-description": "计算器 - 计算数学表达式", - "calculator-name": "计算器", - "calculator-enter-expression": "输入数学表达式", - "calculator-error": "错误" - }, - "system": { - "uptime": "系统运行时间:{uptime}", - "welcome-back": "欢迎回来,{user}!", - "monitor-description": "{model} ({width}x{height})", - "scaling-percentage": "{percentage}%", - "location-display": "{name} ({coordinates})", - "signal-strength": "{signal}%", - "cpu-temperature": "{temp}°C", - "disk-usage": "{percent}%", - "widget-settings-title": "{widget} 设置", - "unknown-app": "未知应用", - "no-media-player-detected": "未检测到媒体播放器", - "user-requested": "用户请求", - "unknown": "未知", - "unknown-version": "未知", - "unknown-layout": "未知" - }, - "lock-screen": { - "secure-terminal": "安全终端", - "unlock-command": "sudo unlock-session", - "password": "密码:", - "shut-down": "关机", - "restart": "重启", - "suspend": "挂起" - }, - "toast": { - "night-light": { - "enabled": "已启用", - "disabled": "已禁用", - "not-installed": "未安装 wlsunset", - "forced": "强制激活", - "normal": "正常模式" + "color-scheme": { + "title": "配色方案", + "color-source": { + "section": { + "label": "颜色来源", + "description": "Noctalia 颜色的主要设置。" + }, + "dark-mode": { + "label": "深色模式", + "description": "切换到更暗的主题,便于夜间观看。" + }, + "enable-matugen": { + "label": "启用 Matugen", + "description": "根据您的活动壁纸自动生成颜色。" + }, + "matugen-scheme-type": { + "label": "Matugen 配色方案类型", + "description": "为 Matugen 选择配色方案生成算法。" + } }, - "keep-awake": { - "enabled": "已启用", - "disabled": "已禁用" + "predefined": { + "section": { + "label": "预定义配色方案", + "description": "从预定义配色方案集合中选择。" + } }, "matugen": { - "enabled": "已启用", - "disabled": "已禁用", - "not-installed": "未安装" - }, - "recording": { - "stopping": "正在停止录制…", - "started": "录制已开始", - "saved": "录制已保存", - "failed-start": "开始录制失败", - "failed-gpu": "gpu-screen-recorder 意外退出。", - "failed-general": "录制器因错误退出。", - "no-portals": "桌面门户未运行", - "no-portals-desc": "请启动 xdg-desktop-portal 和一个合成器门户 (wlr/hyprland/gnome/kde)。" - }, - "clipboard": { - "unavailable": "剪贴板历史记录不可用", - "unavailable-desc": "未安装 'cliphist' 应用程序。请安装它以使用剪贴板历史记录功能。" - }, - "ipc": { - "powerpanel-deprecated": "PowerPanel 已重命名为 SessionMenu,此 IPC 调用将很快弃用。请改用 \"ipc call sessionMenu toggle\"。", - "sidepanel-deprecated": "SidePanel 已重命名为 ControlCenter,此 IPC 调用将很快弃用。请改用 \"ipc call controlCenter toggle\"。" - }, - "wifi": { - "enabled": "已启用", - "disabled": "已禁用", - "connected": "已连接到 '{ssid}'", - "disconnected": "已断开与 '{ssid}' 的连接" - }, - "bluetooth": { - "enabled": "已启用", - "disabled": "已禁用" - }, - "do-not-disturb": { - "enabled": "'勿扰模式'已启用", - "disabled": "'勿扰模式'已禁用", - "enabled-desc": "您可以在历史记录中找到这些通知。", - "disabled-desc": "显示所有通知。" - }, - "power-profile": { - "changed": "电源模式已更改", - "profile-name": "\"{profile}\"" - }, - "audio": { - "muted": "已静音", - "unmuted": "已取消静音" - }, - "battery": { - "low": "电量低", - "low-desc": "电量为 {percent}%。请连接充电器。" - }, - "missing-control-center": { - "label": "最后一个控制中心小部件已移除", - "description": "控制中心小部件已从状态栏中移除。要再次从状态栏访问它,您需要重新添加小部件。" + "section": { + "label": "Matugen 模板", + "description": "将颜色应用于外部应用程序。" + }, + "ui": { + "label": "用户界面", + "description": "桌面环境和 UI 工具包主题。", + "gtk4": { + "description": "写入 {filepath}" + }, + "gtk3": { + "description": "写入 {filepath}" + }, + "qt6": { + "description": "写入 {filepath}" + }, + "qt5": { + "description": "写入 {filepath}" + } + }, + "terminal": { + "label": "终端", + "description": "终端模拟器主题。", + "kitty": { + "description": "写入 {filepath} 并重新加载", + "description-missing": "需要安装 {app}" + }, + "ghostty": { + "description": "写入 {filepath} 并重新加载", + "description-missing": "需要安装 {app}" + }, + "foot": { + "description": "写入 {filepath} 并重新加载", + "description-missing": "需要安装 {app}" + } + }, + "programs": { + "label": "程序", + "description": "应用程序特定主题。", + "fuzzel": { + "description": "写入 {filepath} 并重新加载", + "description-missing": "需要安装 {app}" + }, + "vesktop": { + "description": "写入 {filepath}", + "description-missing": "需要安装 {app}" + }, + "pywalfox": { + "description": "写入 {filepath} 并运行 pywalfox update", + "description-missing": "需要安装 {app}" + } + }, + "misc": { + "label": "杂项", + "description": "其他配置选项。", + "user-templates": { + "label": "用户模板", + "description": "启用来自 ~/.config/matugen/config.toml 的用户定义 Matugen 配置" + } + } } }, - "weather": { - "clear-sky": "晴朗", - "mainly-clear": "基本晴朗", - "partly-cloudy": "局部多云", - "overcast": "阴天", - "fog": "雾", - "drizzle": "毛毛雨", - "snow": "雪", - "rain-showers": "阵雨", - "thunderstorm": "雷暴", - "unknown": "未知" + "location": { + "title": "位置", + "location": { + "section": { + "label": "您的位置", + "description": "通过设置您的位置获取准确的天气和夜灯调度。" + }, + "search": { + "label": "搜索位置", + "description": "例如:多伦多, 安大略省", + "placeholder": "输入位置名称" + } + }, + "weather": { + "section": { + "label": "天气", + "description": "选择您喜欢的温度单位。" + }, + "fahrenheit": { + "label": "以华氏度显示温度 (°F)", + "description": "以华氏度而非摄氏度显示温度。" + } + }, + "date-time": { + "section": { + "label": "日期和时间", + "description": "自定义日期和时间的显示方式。" + }, + "12hour-format": { + "label": "在锁屏上使用 12 小时制", + "description": "开启为 AM/PM 格式(例如:8:00 PM),关闭为 24 小时制(例如:20:00)。" + }, + "week-numbers": { + "label": "显示周数", + "description": "在日历中显示一年中的第几周(例如:第 38 周)。" + } + } }, - "authentication": { - "failed": "认证失败", - "error": "认证错误" + "network": { + "title": "网络", + "section": { + "description": "管理 Wi-Fi 和蓝牙连接。" + }, + "wifi": { + "label": "启用 Wi-Fi" + }, + "bluetooth": { + "label": "启用蓝牙" + } }, - "general": { - "no-results": "无结果", - "no-summary": "无摘要", - "unknown": "未知" + "screen-recorder": { + "title": "屏幕录制", + "general": { + "section": { + "label": "常规设置", + "description": "管理屏幕录制输出和内容。" + }, + "output-folder": { + "label": "输出文件夹", + "description": "屏幕录制将保存的文件夹。", + "tooltip": "浏览输出文件夹" + }, + "show-cursor": { + "label": "显示光标", + "description": "在视频中录制鼠标光标。" + }, + "select-output-folder": "选择输出文件夹" + }, + "video": { + "section": { + "label": "视频设置", + "description": "配置视频录制选项。" + }, + "video-source": { + "label": "视频源", + "description": "推荐使用 Portal,如果出现伪影请尝试 Screen。" + }, + "frame-rate": { + "label": "帧率", + "description": "屏幕录制的目标帧率。" + }, + "video-quality": { + "label": "视频质量", + "description": "更高的质量会导致更大的文件大小。" + }, + "video-codec": { + "label": "视频编解码器", + "description": "h264 是最常见的编解码器。" + }, + "color-range": { + "label": "颜色范围", + "description": "推荐使用 Limited 以获得更好的兼容性。" + } + }, + "audio": { + "section": { + "label": "音频设置", + "description": "配置音频录制选项。" + }, + "audio-source": { + "label": "音频源", + "description": "录制期间要捕获的音频源。" + }, + "audio-codec": { + "label": "音频编解码器", + "description": "推荐使用 Opus 以获得最佳性能和最小的音频大小。" + } + } + }, + "about": { + "title": "关于", + "noctalia": { + "section": { + "label": "Noctalia shell", + "description": "一款为 Wayland 精心打造的时尚简约桌面 shell,基于 Quickshell 构建。" + }, + "latest-version": "最新版本:", + "installed-version": "已安装版本:", + "download-latest": "下载最新版本" + }, + "contributors": { + "section": { + "label": "贡献者", + "description": "向我们 {count} 位超棒的贡献者致敬!", + "description_plural": "向我们 {count} 位超棒的贡献者致敬!" + } + } + }, + "hooks": { + "title": "钩子", + "system-hooks": { + "section": { + "label": "系统钩子", + "description": "配置在系统事件发生时执行的命令。" + }, + "enable": { + "label": "启用钩子", + "description": "启用或禁用所有钩子命令。" + } + }, + "wallpaper-changed": { + "label": "壁纸已更改", + "description": "壁纸更改时执行的命令。", + "placeholder": "例如:notify-send \"壁纸\" \"已更改\"" + }, + "theme-changed": { + "label": "主题已更改", + "description": "主题在深色和浅色模式之间切换时执行的命令。", + "placeholder": "例如:notify-send \"主题\" \"已切换\"" + }, + "info": { + "command-info": { + "label": "钩子命令信息", + "description": "• 命令通过 shell 执行 (sh -c)\n• 命令在后台运行(分离)\n• 测试按钮使用当前值执行" + }, + "parameters": { + "label": "可用参数", + "description": "• 壁纸钩子: $1 = 壁纸路径, $2 = 屏幕名称\n• 主题切换钩子: $1 = true/false (深色模式状态)" + } + } + } + }, + "widgets": { + "tooltip": { + "placeholder": "占位符" + }, + "file-picker": { + "select-folder": "选择文件夹", + "select-file": "选择文件" + }, + "datetime-tokens": { + "common": { + "12hour-time-minutes": "12小时制带分钟", + "24hour-time-minutes": "24小时制带分钟", + "24hour-time-seconds": "24小时制带秒", + "weekday-month-day": "星期、月份和日期", + "iso-date": "ISO 日期格式", + "us-date": "美国日期格式", + "european-date": "欧洲日期格式", + "weekday-date": "星期和日期" + }, + "hour": { + "no-leading-zero": "小时无前导零 (0-23) - 24小时制", + "leading-zero": "小时有前导零 (00-23) - 24小时制" + }, + "minute": { + "no-leading-zero": "分钟无前导零 (0-59)", + "leading-zero": "分钟有前导零 (00-59)" + }, + "second": { + "no-leading-zero": "秒无前导零 (0-59)", + "leading-zero": "秒有前导零 (00-59)" + }, + "ampm": { + "uppercase": "AM/PM 大写", + "lowercase": "am/pm 小写" + }, + "timezone": { + "abbreviation": "时区缩写" + }, + "year": { + "two-digit": "年份为两位数 (00-99)", + "four-digit": "年份为四位数" + }, + "month": { + "number-no-zero": "月份数字无前导零 (1-12)", + "number-leading-zero": "月份数字有前导零 (01-12)", + "abbreviated": "月份名称缩写", + "full": "月份名称全称" + }, + "day": { + "no-leading-zero": "日期无前导零 (1-31)", + "leading-zero": "日期有前导零 (01-31)", + "abbreviated": "星期名称缩写", + "full": "星期名称全称" + } + }, + "icon-picker": { + "title": "图标选择器", + "search": { + "label": "搜索" + }, + "cancel": "取消", + "apply": "应用" + }, + "color-picker": { + "title": "颜色选择器", + "hex": { + "label": "十六进制颜色", + "description": "输入十六进制颜色代码。" + }, + "rgb": { + "label": "RGB 值", + "description": "调整红、绿、蓝和亮度值。" + }, + "brightness": "亮度", + "theme-colors": { + "label": "主题颜色", + "description": "快速访问您的主题调色板。" + }, + "palette": { + "label": "调色板", + "description": "从各种预定义颜色中选择。" + }, + "cancel": "取消", + "apply": "应用" + } + }, + "bar": { + "widget-settings": { + "dialog": { + "cancel": "取消", + "apply": "应用" + }, + "section-editor": { + "placeholder": "选择要添加的小部件...", + "search-placeholder": "搜索小部件..." + }, + "active-window": { + "auto-hide": { + "label": "自动隐藏", + "description": "当没有活动窗口时自动隐藏小部件。" + }, + "show-app-icon": { + "label": "显示应用图标", + "description": "在窗口标题旁边显示应用程序图标。" + }, + "scrolling-mode": { + "label": "滚动模式", + "description": "控制何时为长窗口标题启用文本滚动。" + } + }, + "system-monitor": { + "cpu-usage": { + "label": "CPU使用率", + "description": "显示当前CPU使用百分比。" + }, + "cpu-temperature": { + "label": "CPU温度", + "description": "如果可用,显示CPU温度读数。" + }, + "memory-usage": { + "label": "内存使用率", + "description": "显示当前RAM使用信息。" + }, + "memory-percentage": { + "label": "内存百分比", + "description": "以百分比而不是绝对值显示内存使用情况。" + }, + "network-traffic": { + "label": "网络流量", + "description": "显示网络上传和下载速度。" + }, + "storage-usage": { + "label": "存储使用率", + "description": "显示磁盘空间使用信息。" + } + }, + "notification-history": { + "show-unread-badge": { + "label": "显示未读徽章", + "description": "显示显示未读通知数量的徽章。" + }, + "hide-badge-when-zero": { + "label": "零时隐藏徽章", + "description": "当没有未读通知时隐藏通知徽章。" + } + }, + "battery": { + "display-mode": { + "label": "显示模式", + "description": "选择您希望此值显示的方式。" + }, + "low-battery-threshold": { + "label": "低电量警告阈值", + "description": "当电量低于此百分比时显示警告。" + } + }, + "control-center": { + "use-distro-logo": { + "label": "使用发行版徽标而不是图标", + "description": "使用您的发行版徽标而不是自定义图标。" + }, + "icon": { + "label": "图标", + "description": "从库中选择图标或自定义文件。" + }, + "browse-library": "浏览库", + "browse-file": "浏览文件", + "select-custom-icon": "选择自定义图标" + }, + "keyboard-layout": { + "display-mode": { + "label": "显示模式", + "description": "选择您希望此值显示的方式。" + } + }, + "volume": { + "display-mode": { + "label": "显示模式", + "description": "选择您希望此值显示的方式。" + } + }, + "workspace": { + "label-mode": { + "label": "标签模式", + "description": "选择工作区标签的显示方式。" + }, + "hide-unoccupied": { + "label": "隐藏未占用", + "description": "不显示没有窗口的工作区。" + } + }, + "microphone": { + "display-mode": { + "label": "显示模式", + "description": "选择您希望此值显示的方式。" + } + }, + "brightness": { + "display-mode": { + "label": "显示模式", + "description": "选择您希望此值显示的方式。" + } + }, + "spacer": { + "width": { + "label": "宽度", + "description": "间距宽度(像素)" + } + }, + "custom-button": { + "icon": { + "label": "图标", + "description": "从库中选择图标。" + }, + "browse": "浏览", + "left-click": { + "label": "左键点击", + "description": "左键点击按钮时执行的命令。" + }, + "right-click": { + "label": "右键点击", + "description": "右键点击按钮时执行的命令。" + }, + "middle-click": { + "label": "中键点击", + "description": "中键点击按钮时执行的命令。" + }, + "dynamic-text": "动态文本", + "display-command-output": { + "label": "显示命令输出", + "description": "输入一个定期运行的命令。其输出的第一行将显示为文本。" + }, + "refresh-interval": { + "label": "刷新间隔", + "description": "间隔时间(毫秒)。" + } + }, + "media-mini": { + "auto-hide": { + "label": "自动隐藏", + "description": "当没有媒体播放时自动隐藏小部件。" + }, + "show-album-art": { + "label": "显示专辑封面", + "description": "显示当前播放曲目的专辑封面。" + }, + "show-visualizer": { + "label": "显示可视化器", + "description": "播放音乐时显示音频可视化器。" + }, + "visualizer-type": { + "label": "可视化器类型", + "description": "选择要显示的音频可视化器样式。" + }, + "scrolling-mode": { + "label": "滚动模式", + "description": "控制何时为长曲目标题启用文本滚动。" + } + }, + "clock": { + "use-primary-color": { + "label": "使用主颜色", + "description": "启用后,将应用主颜色进行强调。" + }, + "use-monospaced-font": { + "label": "使用等宽字体", + "description": "启用后,时钟将使用等宽字体。" + }, + "clock-display": { + "label": "时钟显示", + "description": "通过从下面的列表添加标记来自定义时钟显示。要使用 12 小时制,必须包含 'AP' 标记。" + }, + "horizontal-bar": { + "label": "水平栏", + "description": "提示:使用 \\n 创建换行。" + }, + "vertical-bar": { + "label": "垂直栏", + "description": "使用空格将每个部分分隔到新行。" + }, + "preview": "预览" + } + } + }, + "notifications": { + "panel": { + "title": "通知", + "no-notifications": "无通知", + "description": "您的通知将在到达时显示在此处。" + } + }, + "wallpaper": { + "panel": { + "title": "壁纸选择器", + "apply-all-monitors": { + "label": "应用到所有显示器", + "description": "一次性将选定的壁纸应用到所有显示器。" + }, + "search": "搜索:" + }, + "transitions": { + "none": "无", + "random": "随机", + "fade": "淡入淡出", + "disc": "圆盘", + "stripes": "条纹", + "wipe": "擦除" + }, + "fill-modes": { + "center": "居中", + "crop": "裁剪(填充)", + "fit": "适应(包含)", + "stretch": "拉伸" + }, + "no-match": "未找到匹配项。", + "no-wallpaper": "未找到壁纸。", + "try-different-search": "尝试不同的搜索查询。", + "configure-directory": "使用图像配置您的壁纸目录。" + }, + "bluetooth": { + "panel": { + "title": "蓝牙", + "disabled": "蓝牙已禁用", + "enable-message": "启用蓝牙以查看可用设备。", + "connected-devices": "已连接设备", + "known-devices": "已知设备", + "available-devices": "可用设备", + "scanning": "正在扫描设备...", + "pairing-mode": "确保您的设备处于配对模式。" + } + }, + "wifi": { + "panel": { + "title": "Wi-Fi", + "disabled": "Wi-Fi 已禁用", + "enable-message": "启用 Wi-Fi 以查看可用网络。", + "searching": "正在搜索附近网络...", + "connected": "已连接", + "disconnecting": "正在断开连接...", + "forgetting": "正在忘记...", + "saved": "已保存", + "disconnect": "断开连接", + "enter-password": "输入密码...", + "connect": "连接", + "password": "密码", + "forget-network": "忘记此网络?", + "forget": "忘记", + "no-networks": "未找到网络", + "scan-again": "重新扫描" + } + }, + "calendar": { + "panel": { + "week": "周" + } + }, + "tooltips": { + "refresh": "刷新", + "close": "关闭", + "refresh-wallpaper-list": "刷新壁纸列表", + "refresh-devices": "刷新设备", + "forget-network": "忘记网络", + "clear-history": "清除历史记录", + "delete-notification": "删除通知", + "previous-month": "上个月", + "next-month": "下个月", + "add-widget": "添加小部件", + "widget-settings": "小部件设置", + "remove-widget": "移除小部件", + "move-to-left-section": "移动到左侧部分", + "move-to-center-section": "移动到中央部分", + "move-to-right-section": "移动到右侧部分", + "open-settings": "打开设置", + "session-menu": "会话菜单", + "cancel-timer": "取消计时器", + "start-screen-recording": "开始屏幕录制", + "stop-screen-recording": "停止屏幕录制", + "screen-recorder-not-installed": "屏幕录制器未安装", + "keep-awake": "保持唤醒", + "enable-keep-awake": "启用保持唤醒", + "disable-keep-awake": "禁用保持唤醒", + "wallpaper-selector": "左键点击:打开壁纸选择器。\n右键点击:设置随机壁纸。", + "do-not-disturb-enabled": "'勿扰模式'已启用", + "do-not-disturb-disabled": "'勿扰模式'已禁用", + "connect-disconnect-devices": "左键点击连接。右键点击忘记。", + "set-power-profile": "设置\"{profile}\"电源模式", + "switch-to-light-mode": "切换到浅色模式", + "switch-to-dark-mode": "切换到深色模式", + "night-light-disabled": "夜灯已禁用。\n左键点击循环模式。\n右键点击访问设置。", + "night-light-enabled": "夜灯已启用。\n左键点击循环模式。\n右键点击访问设置。", + "night-light-forced": "夜灯已强制启用。\n左键点击循环模式。\n右键点击访问设置。", + "click-to-start-recording": "点击开始录制", + "click-to-stop-recording": "点击停止录制", + "open-control-center": "打开控制中心", + "volume-at": "音量 {volume}%\n左键点击切换静音。右键点击进入设置。\n滚动调整音量。", + "microphone-volume-at": "麦克风音量 {volume}%\n左键点击切换静音。右键点击进入设置。\n滚动调整音量。", + "manage-wifi": "管理 Wi-Fi", + "bluetooth-devices": "蓝牙设备", + "open-notification-history-enable-dnd": "打开通知历史记录\n右键点击启用\"勿扰模式\"。", + "open-notification-history-disable-dnd": "打开通知历史记录\n右键点击禁用\"勿扰模式\"。", + "open-wallpaper-selector": "打开壁纸选择器", + "previous-media": "上一媒体", + "pause": "暂停", + "play": "播放", + "next-media": "下一媒体", + "power-profile": "'{profile}' 电源模式", + "keyboard-layout": "{layout} 键盘布局" + }, + "clock": { + "tooltip": "打开日历" + }, + "dock": { + "menu": { + "focus": "聚焦", + "pin": "固定", + "unpin": "取消固定", + "close": "关闭" + } + }, + "placeholders": { + "search-icons": "例如:noctalia, niri, battery, cloud", + "profile-picture-path": "/home/user/.face", + "enter-width-pixels": "输入宽度(像素)", + "enter-command": "输入要执行的命令(应用程序或自定义脚本)", + "command-example": "echo \"Hello World\"", + "search-wallpapers": "输入以筛选壁纸...", + "search-launcher": "搜索条目...或使用 > 执行命令", + "search": "搜索...", + "select": "选择", + "cancel": "取消", + "test": "测试" + }, + "options": { + "bar": { + "position": { + "top": "顶部", + "bottom": "底部", + "left": "左侧", + "right": "右侧" + }, + "density": { + "compact": "紧凑", + "default": "默认", + "comfortable": "舒适" + } + }, + "launcher": { + "position": { + "center": "居中(默认)", + "top_left": "左上角", + "top_right": "右上角", + "bottom_left": "左下角", + "bottom_right": "右下角", + "bottom_center": "底部居中", + "top_center": "顶部居中", + "center_left": "左侧居中", + "center_right": "右侧居中" + } + }, + "osd": { + "position": { + "top_left": "左上角", + "top_right": "右上角", + "top_center": "顶部居中", + "bottom_left": "左下角", + "bottom_right": "右下角", + "bottom_center": "底部居中", + "center_left": "左侧居中", + "center_right": "右侧居中" + } + }, + "display-mode": { + "on-hover": "悬停时显示", + "always-show": "始终显示", + "always-hide": "始终隐藏", + "force-open": "强制打开" + }, + "workspace-labels": { + "none": "无", + "index": "索引", + "name": "名称" + }, + "visualizer-types": { + "none": "无", + "linear": "线性", + "mirrored": "镜像", + "wave": "波形" + }, + "scrolling-modes": { + "always": "始终滚动", + "hover": "悬停时滚动", + "never": "从不滚动" + }, + "frame-rates": { + "fps": "{fps} FPS" + }, + "screen-recording": { + "sources": { + "portal": "Portal", + "screen": "屏幕" + }, + "quality": { + "medium": "中等", + "high": "高", + "very-high": "很高", + "ultra": "超高" + }, + "color-range": { + "limited": "有限", + "full": "完全" + }, + "audio-sources": { + "system-output": "系统输出", + "microphone-input": "麦克风输入", + "both": "系统输出 + 麦克风输入" + } + } + }, + "session-menu": { + "title": "会话菜单", + "click-again": "再次点击立即执行", + "action-in-seconds": "{seconds} 秒后{action}...", + "lock-subtitle": "锁定您的会话", + "end-subtitle": "结束您的会话", + "lock": "锁定", + "suspend": "挂起", + "reboot": "重启", + "logout": "注销", + "shutdown": "关机" + }, + "plugins": { + "applications": "应用程序", + "clipboard": "剪贴板历史记录", + "calculator": "计算器", + "clipboard-search-description": "搜索剪贴板历史记录", + "clipboard-clear-description": "清除所有剪贴板历史记录", + "clipboard-history-disabled": "剪贴板历史记录已禁用", + "clipboard-history-disabled-description": "在设置中启用剪贴板历史记录或安装 cliphist", + "clipboard-clear-history": "清除剪贴板历史记录", + "clipboard-clear-description-full": "从剪贴板历史记录中移除所有项目", + "clipboard-loading": "正在加载剪贴板历史记录...", + "clipboard-loading-description": "请稍候", + "calculator-description": "计算器 - 计算数学表达式", + "calculator-name": "计算器", + "calculator-enter-expression": "输入数学表达式", + "calculator-error": "错误" + }, + "system": { + "uptime": "系统运行时间:{uptime}", + "welcome-back": "欢迎回来,{user}!", + "monitor-description": "{model} ({width}x{height})", + "scaling-percentage": "{percentage}%", + "location-display": "{name} ({coordinates})", + "signal-strength": "{signal}%", + "cpu-temperature": "{temp}°C", + "disk-usage": "{percent}%", + "widget-settings-title": "{widget} 设置", + "unknown-app": "未知应用", + "no-media-player-detected": "未检测到媒体播放器", + "user-requested": "用户请求", + "unknown": "未知", + "unknown-version": "未知", + "unknown-layout": "未知" + }, + "lock-screen": { + "secure-terminal": "安全终端", + "unlock-command": "sudo unlock-session", + "password": "密码:", + "shut-down": "关机", + "restart": "重启", + "suspend": "挂起" + }, + "toast": { + "night-light": { + "enabled": "已启用", + "disabled": "已禁用", + "not-installed": "未安装 wlsunset", + "forced": "强制激活", + "normal": "正常模式" + }, + "keep-awake": { + "enabled": "已启用", + "disabled": "已禁用" + }, + "matugen": { + "enabled": "已启用", + "disabled": "已禁用", + "not-installed": "未安装" + }, + "recording": { + "stopping": "正在停止录制…", + "started": "录制已开始", + "saved": "录制已保存", + "failed-start": "开始录制失败", + "failed-gpu": "gpu-screen-recorder 意外退出。", + "failed-general": "录制器因错误退出。", + "no-portals": "桌面门户未运行", + "no-portals-desc": "请启动 xdg-desktop-portal 和一个合成器门户 (wlr/hyprland/gnome/kde)。" + }, + "clipboard": { + "unavailable": "剪贴板历史记录不可用", + "unavailable-desc": "未安装 'cliphist' 应用程序。请安装它以使用剪贴板历史记录功能。" + }, + "ipc": { + "powerpanel-deprecated": "PowerPanel 已重命名为 SessionMenu,此 IPC 调用将很快弃用。请改用 \"ipc call sessionMenu toggle\"。", + "sidepanel-deprecated": "SidePanel 已重命名为 ControlCenter,此 IPC 调用将很快弃用。请改用 \"ipc call controlCenter toggle\"。" + }, + "wifi": { + "enabled": "已启用", + "disabled": "已禁用", + "connected": "已连接到 '{ssid}'", + "disconnected": "已断开与 '{ssid}' 的连接" + }, + "bluetooth": { + "enabled": "已启用", + "disabled": "已禁用" + }, + "do-not-disturb": { + "enabled": "'勿扰模式'已启用", + "disabled": "'勿扰模式'已禁用", + "enabled-desc": "您可以在历史记录中找到这些通知。", + "disabled-desc": "显示所有通知。" + }, + "power-profile": { + "changed": "电源模式已更改", + "profile-name": "\"{profile}\"" + }, + "audio": { + "muted": "已静音", + "unmuted": "已取消静音" }, "battery": { - "no-battery-detected": "未检测到电池。", - "charging-rate": "充电速率:{rate} W。", - "discharging-rate": "放电速率:{rate} W。", - "charging": "正在充电。", - "discharging": "正在放电。" + "low": "电量低", + "low-desc": "电量为 {percent}%。请连接充电器。" + }, + "missing-control-center": { + "label": "最后一个控制中心小部件已移除", + "description": "控制中心小部件已从状态栏中移除。要再次从状态栏访问它,您需要重新添加小部件。" } + }, + "weather": { + "clear-sky": "晴朗", + "mainly-clear": "基本晴朗", + "partly-cloudy": "局部多云", + "overcast": "阴天", + "fog": "雾", + "drizzle": "毛毛雨", + "snow": "雪", + "rain-showers": "阵雨", + "thunderstorm": "雷暴", + "unknown": "未知" + }, + "authentication": { + "failed": "认证失败", + "error": "认证错误" + }, + "general": { + "no-results": "无结果", + "no-summary": "无摘要", + "unknown": "未知" + }, + "battery": { + "no-battery-detected": "未检测到电池。", + "charging-rate": "充电速率:{rate} W。", + "discharging-rate": "放电速率:{rate} W。", + "charging": "正在充电。", + "discharging": "正在放电。" } +} \ No newline at end of file diff --git a/Modules/Settings/Bar/WidgetSettings/ActiveWindowSettings.qml b/Modules/Settings/Bar/WidgetSettings/ActiveWindowSettings.qml index c2ef8983..f28c4bac 100644 --- a/Modules/Settings/Bar/WidgetSettings/ActiveWindowSettings.qml +++ b/Modules/Settings/Bar/WidgetSettings/ActiveWindowSettings.qml @@ -29,20 +29,23 @@ ColumnLayout { NToggle { Layout.fillWidth: true - label: I18n.tr("bar.widget-settings.active-window.auto-hide") + label: I18n.tr("bar.widget-settings.active-window.auto-hide.label") + description: I18n.tr("bar.widget-settings.active-window.auto-hide.description") checked: root.valueAutoHide onToggled: checked => root.valueAutoHide = checked } NToggle { Layout.fillWidth: true - label: I18n.tr("bar.widget-settings.active-window.show-app-icon") + label: I18n.tr("bar.widget-settings.active-window.show-app-icon.label") + description: I18n.tr("bar.widget-settings.active-window.show-app-icon.description") checked: root.valueShowIcon onToggled: checked => root.valueShowIcon = checked } NComboBox { - label: I18n.tr("bar.widget-settings.active-window.scrolling-mode") + label: I18n.tr("bar.widget-settings.active-window.scrolling-mode.label") + description: I18n.tr("bar.widget-settings.active-window.scrolling-mode.description") model: [{ "key": "always", "name": I18n.tr("options.scrolling-modes.always") diff --git a/Modules/Settings/Bar/WidgetSettings/ControlCenterSettings.qml b/Modules/Settings/Bar/WidgetSettings/ControlCenterSettings.qml index 78b54210..cd44b941 100644 --- a/Modules/Settings/Bar/WidgetSettings/ControlCenterSettings.qml +++ b/Modules/Settings/Bar/WidgetSettings/ControlCenterSettings.qml @@ -28,7 +28,8 @@ ColumnLayout { } NToggle { - label: I18n.tr("bar.widget-settings.control-center.use-distro-logo") + label: I18n.tr("bar.widget-settings.control-center.use-distro-logo.label") + description: I18n.tr("bar.widget-settings.control-center.use-distro-logo.description") checked: valueUseDistroLogo onToggled: { valueUseDistroLogo = checked diff --git a/Modules/Settings/Bar/WidgetSettings/CustomButtonSettings.qml b/Modules/Settings/Bar/WidgetSettings/CustomButtonSettings.qml index 0733091a..9c7aaed0 100644 --- a/Modules/Settings/Bar/WidgetSettings/CustomButtonSettings.qml +++ b/Modules/Settings/Bar/WidgetSettings/CustomButtonSettings.qml @@ -58,7 +58,8 @@ ColumnLayout { NTextInput { id: leftClickExecInput Layout.fillWidth: true - label: I18n.tr("bar.widget-settings.custom-button.left-click") + label: I18n.tr("bar.widget-settings.custom-button.left-click.label") + description: I18n.tr("bar.widget-settings.custom-button.left-click.description") placeholderText: I18n.tr("placeholders.enter-command") text: widgetData?.leftClickExec || widgetMetadata.leftClickExec } @@ -66,7 +67,8 @@ ColumnLayout { NTextInput { id: rightClickExecInput Layout.fillWidth: true - label: I18n.tr("bar.widget-settings.custom-button.right-click") + label: I18n.tr("bar.widget-settings.custom-button.right-click.label") + description: I18n.tr("bar.widget-settings.custom-button.right-click.description") placeholderText: I18n.tr("placeholders.enter-command") text: widgetData?.rightClickExec || widgetMetadata.rightClickExec } @@ -74,7 +76,8 @@ ColumnLayout { NTextInput { id: middleClickExecInput Layout.fillWidth: true - label: I18n.tr("bar.widget-settings.custom-button.middle-click") + label: I18n.tr("bar.widget-settings.custom-button.middle-click.label") + description: I18n.tr("bar.widget-settings.custom-button.middle-click.description") placeholderText: I18n.tr("placeholders.enter-command") text: widgetData.middleClickExec || widgetMetadata.middleClickExec } diff --git a/Modules/Settings/Bar/WidgetSettings/MediaMiniSettings.qml b/Modules/Settings/Bar/WidgetSettings/MediaMiniSettings.qml index d2391434..184a37e5 100644 --- a/Modules/Settings/Bar/WidgetSettings/MediaMiniSettings.qml +++ b/Modules/Settings/Bar/WidgetSettings/MediaMiniSettings.qml @@ -32,26 +32,30 @@ ColumnLayout { NToggle { Layout.fillWidth: true - label: I18n.tr("bar.widget-settings.media-mini.auto-hide") + label: I18n.tr("bar.widget-settings.media-mini.auto-hide.label") + description: I18n.tr("bar.widget-settings.media-mini.auto-hide.description") checked: root.valueAutoHide onToggled: checked => root.valueAutoHide = checked } NToggle { - label: I18n.tr("bar.widget-settings.media-mini.show-album-art") + label: I18n.tr("bar.widget-settings.media-mini.show-album-art.label") + description: I18n.tr("bar.widget-settings.media-mini.show-album-art.description") checked: valueShowAlbumArt onToggled: checked => valueShowAlbumArt = checked } NToggle { - label: I18n.tr("bar.widget-settings.media-mini.show-visualizer") + label: I18n.tr("bar.widget-settings.media-mini.show-visualizer.label") + description: I18n.tr("bar.widget-settings.media-mini.show-visualizer.description") checked: valueShowVisualizer onToggled: checked => valueShowVisualizer = checked } NComboBox { visible: valueShowVisualizer - label: I18n.tr("bar.widget-settings.media-mini.visualizer-type") + label: I18n.tr("bar.widget-settings.media-mini.visualizer-type.label") + description: I18n.tr("bar.widget-settings.media-mini.visualizer-type.description") model: [{ "key": "linear", "name": I18n.tr("options.visualizer-types.linear") @@ -68,7 +72,8 @@ ColumnLayout { } NComboBox { - label: I18n.tr("bar.widget-settings.media-mini.scrolling-mode") + label: I18n.tr("bar.widget-settings.media-mini.scrolling-mode.label") + description: I18n.tr("bar.widget-settings.media-mini.scrolling-mode.description") model: [{ "key": "always", "name": I18n.tr("options.scrolling-modes.always") diff --git a/Modules/Settings/Bar/WidgetSettings/NotificationHistorySettings.qml b/Modules/Settings/Bar/WidgetSettings/NotificationHistorySettings.qml index 206c2797..4510a605 100644 --- a/Modules/Settings/Bar/WidgetSettings/NotificationHistorySettings.qml +++ b/Modules/Settings/Bar/WidgetSettings/NotificationHistorySettings.qml @@ -25,13 +25,15 @@ ColumnLayout { } NToggle { - label: I18n.tr("bar.widget-settings.notification-history.show-unread-badge") + label: I18n.tr("bar.widget-settings.notification-history.show-unread-badge.label") + description: I18n.tr("bar.widget-settings.notification-history.show-unread-badge.description") checked: valueShowUnreadBadge onToggled: checked => valueShowUnreadBadge = checked } NToggle { - label: I18n.tr("bar.widget-settings.notification-history.hide-badge-when-zero") + label: I18n.tr("bar.widget-settings.notification-history.hide-badge-when-zero.label") + description: I18n.tr("bar.widget-settings.notification-history.hide-badge-when-zero.description") checked: valueHideWhenZero onToggled: checked => valueHideWhenZero = checked } diff --git a/Modules/Settings/Bar/WidgetSettings/SystemMonitorSettings.qml b/Modules/Settings/Bar/WidgetSettings/SystemMonitorSettings.qml index 06ba806d..852ea8d8 100644 --- a/Modules/Settings/Bar/WidgetSettings/SystemMonitorSettings.qml +++ b/Modules/Settings/Bar/WidgetSettings/SystemMonitorSettings.qml @@ -35,7 +35,8 @@ ColumnLayout { NToggle { id: showCpuUsage Layout.fillWidth: true - label: I18n.tr("bar.widget-settings.system-monitor.cpu-usage") + label: I18n.tr("bar.widget-settings.system-monitor.cpu-usage.label") + description: I18n.tr("bar.widget-settings.system-monitor.cpu-usage.description") checked: valueShowCpuUsage onToggled: checked => valueShowCpuUsage = checked } @@ -43,7 +44,8 @@ ColumnLayout { NToggle { id: showCpuTemp Layout.fillWidth: true - label: I18n.tr("bar.widget-settings.system-monitor.cpu-temperature") + label: I18n.tr("bar.widget-settings.system-monitor.cpu-temperature.label") + description: I18n.tr("bar.widget-settings.system-monitor.cpu-temperature.description") checked: valueShowCpuTemp onToggled: checked => valueShowCpuTemp = checked } @@ -51,7 +53,8 @@ ColumnLayout { NToggle { id: showMemoryUsage Layout.fillWidth: true - label: I18n.tr("bar.widget-settings.system-monitor.memory-usage") + label: I18n.tr("bar.widget-settings.system-monitor.memory-usage.label") + description: I18n.tr("bar.widget-settings.system-monitor.memory-usage.description") checked: valueShowMemoryUsage onToggled: checked => valueShowMemoryUsage = checked } @@ -59,7 +62,8 @@ ColumnLayout { NToggle { id: showMemoryAsPercent Layout.fillWidth: true - label: I18n.tr("bar.widget-settings.system-monitor.memory-percentage") + label: I18n.tr("bar.widget-settings.system-monitor.memory-percentage.label") + description: I18n.tr("bar.widget-settings.system-monitor.memory-percentage.description") checked: valueShowMemoryAsPercent onToggled: checked => valueShowMemoryAsPercent = checked visible: valueShowMemoryUsage @@ -68,7 +72,8 @@ ColumnLayout { NToggle { id: showNetworkStats Layout.fillWidth: true - label: I18n.tr("bar.widget-settings.system-monitor.network-traffic") + label: I18n.tr("bar.widget-settings.system-monitor.network-traffic.label") + description: I18n.tr("bar.widget-settings.system-monitor.network-traffic.description") checked: valueShowNetworkStats onToggled: checked => valueShowNetworkStats = checked } @@ -76,7 +81,8 @@ ColumnLayout { NToggle { id: showDiskUsage Layout.fillWidth: true - label: I18n.tr("bar.widget-settings.system-monitor.storage-usage") + label: I18n.tr("bar.widget-settings.system-monitor.storage-usage.label") + description: I18n.tr("bar.widget-settings.system-monitor.storage-usage.description") checked: valueShowDiskUsage onToggled: checked => valueShowDiskUsage = checked } diff --git a/Modules/Settings/Bar/WidgetSettings/WorkspaceSettings.qml b/Modules/Settings/Bar/WidgetSettings/WorkspaceSettings.qml index 27c4b057..b44f16b9 100644 --- a/Modules/Settings/Bar/WidgetSettings/WorkspaceSettings.qml +++ b/Modules/Settings/Bar/WidgetSettings/WorkspaceSettings.qml @@ -23,7 +23,8 @@ ColumnLayout { NComboBox { id: labelModeCombo - label: I18n.tr("bar.widget-settings.workspace.label-mode") + label: I18n.tr("bar.widget-settings.workspace.label-mode.label") + description: I18n.tr("bar.widget-settings.workspace.label-mode.description") model: [{ "key": "none", "name": I18n.tr("options.workspace-labels.none") From b9b233a873c9f7a861e075b641f1cae345feb8ed Mon Sep 17 00:00:00 2001 From: Aiser <2912778691@qq.com> Date: Sun, 28 Sep 2025 17:38:43 +0800 Subject: [PATCH 40/42] i18n: Optimize Chinese translation --- Assets/Translations/zh.json | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Assets/Translations/zh.json b/Assets/Translations/zh.json index 6578e8f8..a478c547 100644 --- a/Assets/Translations/zh.json +++ b/Assets/Translations/zh.json @@ -174,11 +174,11 @@ }, "night-light": { "section": { - "label": "夜灯", + "label": "夜间模式", "description": "减少蓝光发射,帮助您更好地睡眠并减轻眼睛疲劳。" }, "enable": { - "label": "启用夜灯", + "label": "启用夜间模式", "description": "应用暖色滤镜以减少蓝光发射。" }, "temperature": { @@ -560,7 +560,7 @@ "location": { "section": { "label": "您的位置", - "description": "通过设置您的位置获取准确的天气和夜灯调度。" + "description": "通过设置您的位置获取准确的天气和调度夜间模式。" }, "search": { "label": "搜索位置", @@ -1105,9 +1105,9 @@ "set-power-profile": "设置\"{profile}\"电源模式", "switch-to-light-mode": "切换到浅色模式", "switch-to-dark-mode": "切换到深色模式", - "night-light-disabled": "夜灯已禁用。\n左键点击循环模式。\n右键点击访问设置。", - "night-light-enabled": "夜灯已启用。\n左键点击循环模式。\n右键点击访问设置。", - "night-light-forced": "夜灯已强制启用。\n左键点击循环模式。\n右键点击访问设置。", + "night-light-disabled": "夜间模式已禁用。\n左键点击循环模式。\n右键点击访问设置。", + "night-light-enabled": "夜间模式已启用。\n左键点击循环模式。\n右键点击访问设置。", + "night-light-forced": "夜间模式已强制启用。\n左键点击循环模式。\n右键点击访问设置。", "click-to-start-recording": "点击开始录制", "click-to-stop-recording": "点击停止录制", "open-control-center": "打开控制中心", @@ -1385,4 +1385,4 @@ "charging": "正在充电。", "discharging": "正在放电。" } -} \ No newline at end of file +} From 5b54be633dfc087a78eeb3fa36d1b0905cd21b03 Mon Sep 17 00:00:00 2001 From: Ly-sec Date: Sun, 28 Sep 2025 13:07:51 +0200 Subject: [PATCH 41/42] Aya: rename to ayu (probably a typo) --- Assets/ColorScheme/{Aya.json => Ayu.json} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename Assets/ColorScheme/{Aya.json => Ayu.json} (100%) diff --git a/Assets/ColorScheme/Aya.json b/Assets/ColorScheme/Ayu.json similarity index 100% rename from Assets/ColorScheme/Aya.json rename to Assets/ColorScheme/Ayu.json From 026d6027704b2c37dbbf4636ec5b8400f466b887 Mon Sep 17 00:00:00 2001 From: ItsLemmy Date: Sun, 28 Sep 2025 09:23:28 -0400 Subject: [PATCH 42/42] Tooltips: more robust tooltips after hot-reload --- Modules/Tooltip/Tooltip.qml | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/Modules/Tooltip/Tooltip.qml b/Modules/Tooltip/Tooltip.qml index 171971f6..495649e1 100644 --- a/Modules/Tooltip/Tooltip.qml +++ b/Modules/Tooltip/Tooltip.qml @@ -34,9 +34,6 @@ PopupWindow { anchor.rect.x: anchorX anchor.rect.y: anchorY - implicitWidth: Math.min(tooltipText.implicitWidth + padding * 2 * scaling, maxWidth * scaling) - implicitHeight: tooltipText.implicitHeight + padding * 2 * scaling - // Timer for showing tooltip after delay Timer { id: showTimer @@ -164,8 +161,11 @@ PopupWindow { } // Calculate tooltip dimensions - const tipWidth = Math.min(tooltipText.implicitWidth + padding * 2 * scaling, maxWidth * scaling) - const tipHeight = tooltipText.implicitHeight + padding * 2 * scaling + const tipWidth = Math.min(tooltipText.implicitWidth + (padding * 2 * scaling), maxWidth * scaling) + root.implicitWidth = tipWidth + + const tipHeight = tooltipText.implicitHeight + (padding * 2 * scaling) + root.implicitHeight = tipHeight // Get target's global position const targetGlobal = targetItem.mapToGlobal(0, 0) @@ -311,16 +311,11 @@ PopupWindow { } function completeHide() { - // Hide the popup window visible = false animatingOut = false pendingShow = false - - // Clear the text and reset state text = "" isPositioned = false - - // Reset container state tooltipContainer.opacity = 1.0 tooltipContainer.scale = 1.0 } @@ -384,7 +379,7 @@ PopupWindow { color: Color.mSurface border.color: Color.mOutline border.width: Math.max(1, Style.borderS * scaling) - radius: Style.radiusM * scaling + radius: Style.radiusS * scaling // Only show content when we have text visible: root.text !== "" @@ -396,8 +391,6 @@ PopupWindow { text: root.text font.pointSize: Style.fontSizeS * scaling color: Color.mOnSurfaceVariant - wrapMode: Text.WordWrap - width: Math.min(implicitWidth, root.maxWidth * root.scaling - root.padding * 2 * root.scaling) horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter }