Compare commits

...

126 Commits

Author SHA1 Message Date
Ly-sec
8c9396f325 Release v2.15.0 2025-10-01 15:51:51 +02:00
ItsLemmy
afccf048e7 Taskbar: inactive icon bumped from 0.5 to 0.6 opacity 2025-10-01 09:40:33 -04:00
ItsLemmy
f37625719d Clock: removed useMonospacedFont to keep things simple, + translations + cleanup 2025-10-01 09:20:14 -04:00
Lemmy
cad8fd671f Merge pull request #398 from DiscoCevapi/add-clock-font-setting
Add clock font setting for customizable clock displays
2025-10-01 09:13:18 -04:00
DiscoNiri
68e76abfc7 Move clock font settings to widget-specific configuration
- Moved clock font selection from general settings to clock widget settings
- Added custom font toggle and selection in ClockSettings.qml
- Updated BarWidgetRegistry.qml with new clock font metadata
- Removed global clockFont setting from Settings.qml and GeneralTab.qml
- Updated Clock.qml to use widget-specific custom font setting
- Added proper translation keys for new font options
- Maintained backward compatibility with existing font hierarchy
2025-10-01 20:26:13 +10:00
Lemmy
45c8fe7782 Merge pull request #358 from lonerOrz/fix/brightness
Fix brightness sync after external command changes
2025-09-30 22:49:41 -04:00
ItsLemmy
5ebf4b5377 i18n: launcher terminal-command 2025-09-30 22:45:00 -04:00
Lemmy
59fbe92fe4 Merge pull request #377 from lonerOrz/fix/launcher
fix: the launcher cannot run pure command-line (CLI) programs
2025-09-30 22:44:09 -04:00
ItsLemmy
b051e19f68 i18n: updated all translations via autotranslate! 2025-09-30 22:32:37 -04:00
ItsLemmy
6b9370ac85 i18n: added basic auto translation 2025-09-30 22:24:25 -04:00
lonerorz
9702a300ca Merge branch 'main' into fix/launcher 2025-10-01 10:11:12 +08:00
ItsLemmy
b043664617 Taskbar: Improved the look of the focused app. Made unfocused app icons semi transparent. 2025-09-30 21:33:06 -04:00
ItsLemmy
368e80daf2 .gitignore cleanup 2025-09-30 20:29:18 -04:00
ItsLemmy
056217bf43 Wallpaper: fix double wallpaper init. 2025-09-30 20:24:23 -04:00
ItsLemmy
c1abb3a7dc Default settings updated with Dock's: only same output. 2025-09-30 19:50:24 -04:00
ItsLemmy
52d2055699 MediaMini: fix another binding loop. 2025-09-30 18:20:28 -04:00
ItsLemmy
e324a33137 NiriService: added safe guards to avoid issue with wrong window indexes. 2025-09-30 18:16:35 -04:00
ItsLemmy
6f4aa1a1a1 MediaMini: fix binding loop + edge case where no icon would appear. Also set Autohide to false by default for ActiveWindow and MediaMini 2025-09-30 17:56:59 -04:00
Lemmy
f49462f999 Merge pull request #402 from luleyleo/output-filtered-dock
Per-monitor dock
2025-09-30 17:36:14 -04:00
Leopold Luley
4fb1e2de1e i18n: Add German translation for new dock settings. 2025-09-30 23:07:24 +02:00
Leopold Luley
6d05a20556 Dock: Reformat code. 2025-09-30 23:03:09 +02:00
Leopold Luley
ec2fbb53dc Dock: Allow showing the dock on outputs without a bar. 2025-09-30 23:02:13 +02:00
Leopold Luley
fdc61acfe4 Dock: Add option to filter by output. 2025-09-30 23:01:46 +02:00
Ly-sec
32712c7052 MediaMini: replace placeholder icon 2025-09-30 19:23:18 +02:00
Ly-sec
a0f6d14334 MediaMini: add no active player placeholder 2025-09-30 18:37:45 +02:00
Lysec
6ae8d8536e Merge pull request #400 from acdcbyl/main
i18n: Optimize Chinese translation
2025-09-30 15:35:15 +02:00
Aiser
650dcb8811 i18n: Optimize Chinese translation 2025-09-30 21:32:03 +08:00
ItsLemmy
970684e304 Niri: temp warning fix 2025-09-30 08:07:18 -04:00
Lemmy
e786946abf Merge pull request #394 from ixxie/feat/temp-settings
[NixOS] feat/temp settings
2025-09-30 07:55:14 -04:00
Lemmy
da046cade6 Merge pull request #396 from luleyleo/mouse-sorted-taskbar
NiriService: Keep windows sorted when moving them with the mouse
2025-09-30 07:51:37 -04:00
ItsLemmy
43dee793de More pointSize cleanup 2025-09-30 07:44:03 -04:00
Lysec
0a893f9c5f Merge pull request #399 from pugaizai/main
i18n: update zh-CN translations
2025-09-30 13:28:06 +02:00
Ly-sec
23887574cf NIcon: fix fontSize 2025-09-30 13:12:49 +02:00
pugaizai
2008ba85bc update sessionmenu translation 2025-09-30 19:07:49 +08:00
Ly-sec
773318191d NIcon: use textSize for font.pointSize 2025-09-30 13:02:56 +02:00
pugaizai
78cf0bc8a2 i18n: update zh-CN translations 2025-09-30 18:42:59 +08:00
DiscoNiri
8b0e0f6e0e Add clock font setting for customizable clock displays
This commit adds a new 'Clock Font' setting that allows users to customize
the font used specifically for clock displays in the bar and widgets,
independent of the default UI font.

Features:
- New clockFont property in Settings.data.ui (defaults to 'Roboto')
- Updated Bar Clock widget to use the custom font with fallback support
- Added searchable font dropdown in General Settings tab
- Backward compatible - uses default font if clockFont is not set
- Real-time updates - changes apply immediately

The font selection uses FontService.availableFonts and includes proper
fallback logic that respects the existing monospaced font setting.
2025-09-30 18:37:47 +10:00
Lysec
8c6b3a793f Merge pull request #397 from msdevpt/apply-theme
chore: refresh ghostty configuration
2025-09-30 09:37:42 +02:00
M.Silva
4c3eca80a4 chore: refresh ghostty configuration 2025-09-30 08:32:01 +01:00
Leopold Luley
f61f9a5809 NiriService: Keep windows sorted when moving them with the mouse. 2025-09-30 09:01:58 +02:00
ItsLemmy
518e90d910 SystemMonitor: apply fontScale to TextMetrics for smarted calculation 2025-09-29 21:46:10 -04:00
ItsLemmy
d2e5d0664a Font: added reset button for scaling 2025-09-29 21:42:47 -04:00
ItsLemmy
602d79c98e TrayMenu: fix icon size 2025-09-29 21:38:51 -04:00
ItsLemmy
4b13e89a64 Font: added per font family scaling. removed billboard font 2025-09-29 21:31:45 -04:00
ItsLemmy
1e8b122911 NiriService: syntax fix 2025-09-29 21:19:08 -04:00
Ly-sec
1f257ce847 ControlCenter: fix custom image 2025-09-30 01:33:09 +02:00
Matan Bendix Shenhav
df35589328 feat(flake): write settings to a fallback path 2025-09-30 00:11:03 +02:00
Matan Bendix Shenhav
c92478d27d feat(flake): restart systemd service on package update 2025-09-30 00:10:32 +02:00
Lemmy
ffe39e0ec9 Merge pull request #393 from luleyleo/sorted-taskbar
Sort windows in Taskbar by their scrolling position on Niri
2025-09-29 18:08:50 -04:00
ItsLemmy
b12cf345dc Background Wallpaper: attempt to free up memory earlier. 2025-09-29 16:53:59 -04:00
ItsLemmy
fc4418be0c Shader: fix "disc" shader (no disc at 0 progress) 2025-09-29 16:53:33 -04:00
Leopold Luley
82bfa346a7 NiriService: Fix stale focus state when opening a new window. 2025-09-29 22:16:46 +02:00
Leopold Luley
26ee5046f6 NiriService: Sort windows by their scrolling position. 2025-09-29 22:16:25 +02:00
ItsLemmy
51ed6ea2b0 Compositor: fix getFocusedWindow() 2025-09-29 15:10:44 -04:00
ItsLemmy
c53dd6fade Compositor: fix getFocusedWindowTitle. Since active workspace has been implemented.
+ autoformatting
2025-09-29 15:04:13 -04:00
Lemmy
bb24b6904d Merge pull request #386 from luleyleo/filtered-taskbar
Taskbar: Filter by screen and workspace
2025-09-29 15:02:31 -04:00
Ly-sec
d5857e3363 Merge branch 'main' of https://github.com/noctalia-dev/noctalia-shell 2025-09-29 16:32:00 +02:00
Ly-sec
559609be64 Launcher: add pin to dock button if dock is enabled 2025-09-29 16:31:53 +02:00
ItsLemmy
5cea61114b Scaling: fix scaling not properly applied on startup. 2025-09-29 10:20:19 -04:00
ItsLemmy
22794ea922 DateTime: proper locale usage. Fix #390
Replaced all Qt.formatDateTime() by Qt.locale().toString()
2025-09-29 10:07:58 -04:00
ItsLemmy
933ba54612 Init Sequence: minor reordering 2025-09-29 09:58:48 -04:00
ItsLemmy
0d0b9a21f2 Wallpaper Selector: added a shortcut to the wallpaper settings in the top bar. 2025-09-29 09:25:45 -04:00
ItsLemmy
9ed9231070 Init Sequence: removed a bunch of no longer necessary Settings.isLoaded 2025-09-29 09:11:37 -04:00
Ly-sec
b8b54825d5 SessionMenu: move lockAndSuspend to CompositorService 2025-09-29 14:20:15 +02:00
Ly-sec
250822e819 Revert "Matugen: add custom-colors.toml"
This reverts commit ece9789f6b.
2025-09-29 14:13:22 +02:00
Ly-sec
ece9789f6b Matugen: add custom-colors.toml 2025-09-29 13:43:37 +02:00
Ly-sec
f11d27bcf1 Background: "explicitly set currentWallpaper.source to nothing as an
attempt to fix the odd memory usage after a few hours"
2025-09-29 13:18:45 +02:00
Ly-sec
0e69256279 Background: fix short flash of default wallpaper before actual wallpaper shows 2025-09-29 13:13:21 +02:00
Leopold Luley
fa49d4aaa0 Taskbar: Add German translation for Taskbar settings. 2025-09-29 11:08:48 +02:00
Leopold Luley
b1f7ae5d9a Taskbar: Add settings. 2025-09-29 11:01:14 +02:00
Leopold Luley
e6b0be77e7 Taskbar: Filter by same output and active workspaces. 2025-09-29 11:01:14 +02:00
ItsLemmy
49961882dd Shell: changed init sequence so that i18n + Settings are fully loaded before any UI component spawn. 2025-09-28 23:39:34 -04:00
ItsLemmy
c1d2d82fa2 NSpinBox: fixes
- replaced row by rowlayount
- using proper Color.mOnTertiary for hover text/icon
- fixed binding break when entering value manually
2025-09-28 21:19:10 -04:00
ItsLemmy
c35f37c7d7 Use Color.transparent instead of "transparent" 2025-09-28 21:17:10 -04:00
Lemmy
e23cb90c5b Merge pull request #388 from MrDowntempo/Consistent-Hover
Nicer SpinBox with better mTertiary hover
2025-09-28 20:53:24 -04:00
ItsLemmy
b2688e9100 More conversion of Row/Column to Layout 2025-09-28 20:49:57 -04:00
ItsLemmy
7f3842ddbf Log cleanup (avoid super long string with path) 2025-09-28 20:39:28 -04:00
ItsLemmy
68b2c83be1 DockMenu: use RowLayout and ColumnLayout 2025-09-28 20:35:25 -04:00
Corey Woodworth
97fa2fb1b5 Back to Chevrons. +/- were inconsistent sizes. Better alignment 2025-09-28 20:20:02 -04:00
ItsLemmy
0ed8ed7fe5 Tooltips: fix clipping for tooltips with long sentences. 2025-09-28 19:45:37 -04:00
Corey Woodworth
a41be0b5d9 Removed gradient and redesigned buttons 2025-09-28 19:08:33 -04:00
ItsLemmy
072d80e2f3 Bar vs Dock: Dock are loaded only once the bar is fully loaded. This ensure the vertical bar use the full screen height if the dock is exclusive. 2025-09-28 16:39:23 -04:00
loner
1f898171e0 Merge remote-tracking branch 'upstream/main' into fix/launcher
# Conflicts:
#	Assets/Translations/zh-CN.json
2025-09-29 03:22:48 +08:00
loner
ef64395dd4 Resolve conflict 2025-09-29 03:09:30 +08:00
loner
a5c89fadb5 fix(services): emit brightnessUpdated signal in setBrightness 2025-09-29 02:40:01 +08:00
loner
cccf0e6017 fix: Fix brightness synchronization in multi-monitor setups 2025-09-29 02:34:42 +08:00
Ly-sec
5da474007e i18n: add lock-and-suspend to all languages 2025-09-28 19:53:20 +02:00
Ly-sec
ffd2cdaf74 SessionMenu: add lock & suspend option as requested in #301 2025-09-28 19:50:52 +02:00
MrDowntempo
5f3c088f22 Update NSpinBox.qml
I missed a line
2025-09-28 13:16:07 -04:00
MrDowntempo
382116e795 Merge branch 'main' into Consistent-Hover 2025-09-28 13:10:13 -04:00
Ly-sec
c7c49433f7 NotificationService: add flatpak name support 2025-09-28 19:08:04 +02:00
Corey Woodworth
0d2d0f1931 Nicer SpinBox with better mTertiary hover 2025-09-28 12:49:52 -04:00
Ly-sec
2e947edc5a Merge branch 'main' of https://github.com/noctalia-dev/noctalia-shell 2025-09-28 18:42:59 +02:00
Ly-sec
cdc32f3eac NSpinBox: add text input support 2025-09-28 18:42:53 +02:00
ItsLemmy
21736b3095 DockMenu: auto hides when not hovering the menu, simplified with a single mouse area. 2025-09-28 12:06:41 -04:00
ItsLemmy
48852a9ca4 Tray: close the menu on re-hovering the tooltip 2025-09-28 11:37:12 -04:00
ItsLemmy
65fab7b367 Tray: Fixing hiding tooltip 2025-09-28 11:17:02 -04:00
ItsLemmy
dc414df9bc NRadioButton: proper elipsis. Fix #385 2025-09-28 11:09:17 -04:00
ItsLemmy
69a6c052db LockScreen: adapted custom tooltips to the new lighter look. 2025-09-28 10:55:48 -04:00
ItsLemmy
c422435d3d Merge branch 'main' of github.com:noctalia-dev/noctalia-shell 2025-09-28 10:52:09 -04:00
ItsLemmy
fc1742e167 Tooltips: proper tooltip service 2025-09-28 10:51:56 -04:00
ItsLemmy
061e7f32da Tooltips: proper tooltip service 2025-09-28 10:40:15 -04:00
Lemmy
8dda007847 Merge pull request #371 from pugaizai/main
allow zh-CN like language code
2025-09-28 09:53:16 -04:00
pugaizai
1cdff28cca Merge from upstream 2025-09-28 21:43:50 +08:00
铺盖崽
f32a34e320 Rename zh.json to zh-CN.json 2025-09-28 21:34:02 +08:00
铺盖崽
0d0088bd52 allow zh-CN like language code 2025-09-28 21:34:02 +08:00
ItsLemmy
a7a7a96585 Merge branch 'tooltips' 2025-09-28 09:23:42 -04:00
ItsLemmy
026d602770 Tooltips: more robust tooltips after hot-reload 2025-09-28 09:23:28 -04:00
Ly-sec
5b54be633d Aya: rename to ayu (probably a typo) 2025-09-28 13:07:51 +02:00
Lysec
3bb10e9561 Merge pull request #383 from acdcbyl/main
i18n: Optimize Chinese translation
2025-09-28 11:44:04 +02:00
Aiser
b9b233a873 i18n: Optimize Chinese translation 2025-09-28 17:38:43 +08:00
Ly-sec
388824bf37 i18n: add description to all Bar widget settings 2025-09-28 11:16:26 +02:00
Ly-sec
25eb31747a ColorSchemeTab: hide predefined colorschemes when matugen is enabled 2025-09-28 10:43:02 +02:00
Lysec
f7109b0bf9 Merge pull request #382 from acdcbyl/main
i18n: Optimize Chinese translation
2025-09-28 10:08:48 +02:00
Aiser
c41fa1aef7 i18n: Optimize Chinese translation 2025-09-28 16:03:59 +08:00
Aiser
1a0ea3893c i18n: Optimize Chinese translation 2025-09-28 15:52:54 +08:00
ItsLemmy
0593543d7a Tooltip: Refactoring in a single global tooltip. 2025-09-28 00:15:43 -04:00
ItsLemmy
fbf80ab577 v2.14.4-dev 2025-09-27 20:40:48 -04:00
loner
b27728e5bf i18n(zh): add translation for terminal command 2025-09-27 12:12:31 +08:00
loner
2379ad134b i18n(pt): add translation for terminal command 2025-09-27 12:12:21 +08:00
loner
3ab9ffed78 i18n(fr): add translation for terminal command 2025-09-27 12:12:11 +08:00
loner
3182d1969b i18n(es): add translation for terminal command 2025-09-27 12:11:53 +08:00
loner
591d099255 i18n(de): add translation for terminal command 2025-09-27 12:11:43 +08:00
loner
256f9b4a76 feat(launcher): add configurable terminal command
The terminal command for launching applications was previously hardcoded to 'kitty', causing issues for users without it installed.

This change introduces a new setting, 'appLauncher.terminalCommand', allowing users to specify their preferred terminal emulator. The default value is set to 'xterm -e'.

The implementation includes:
- Defining the setting in 'Commons/Settings.qml'.
- Adding a text input in the launcher settings tab.
- Updating the application plugin to use the new setting.
2025-09-27 12:06:54 +08:00
loner
ff1509939a test kitty 2025-09-27 11:29:57 +08:00
loner
2c7038c504 Fix brightness sync after external command changes
Fix brightness sync after external command changes, improve brightness
module compatibility
2025-09-25 10:18:09 +08:00
124 changed files with 4791 additions and 2915 deletions

1
.gitignore vendored
View File

@@ -1 +0,0 @@
.qmlls.ini

View File

@@ -51,7 +51,7 @@ Singleton {
lines.push("\n[templates.ghostty]")
lines.push('input_path = "' + Quickshell.shellDir + '/Assets/Matugen/templates/ghostty.conf"')
lines.push('output_path = "~/.config/ghostty/themes/noctalia"')
lines.push("post_hook = \"grep -q '^theme *= *' ~/.config/ghostty/config; and sed -i 's/^theme *= *.*/theme = noctalia/' ~/.config/ghostty/config; or echo 'theme = noctalia' >> ~/.config/ghostty/config\"")
lines.push("post_hook = \"grep -q '^theme *= *' ~/.config/ghostty/config; and sed -i 's/^theme *= *.*/theme = noctalia/' ~/.config/ghostty/config; or echo 'theme = noctalia' >> ~/.config/ghostty/config; and pkill -SIGUSR2 ghostty\"")
}
if (Settings.data.matugen.foot) {
lines.push("\n[templates.foot]")

View File

@@ -2,7 +2,6 @@
"settings": {
"general": {
"title": "Allgemein",
"profile": {
"section": {
"label": "Profil",
@@ -14,7 +13,6 @@
},
"select-avatar": "Avatar-Bild auswählen"
},
"ui": {
"section": {
"label": "Benutzeroberfläche",
@@ -31,6 +29,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": {
@@ -60,20 +62,23 @@
"label": "Standard-Schriftart",
"description": "Hauptschriftart für die gesamte Benutzeroberfläche.",
"placeholder": "Standard-Schriftart auswählen...",
"search-placeholder": "Schriftarten suchen..."
"search-placeholder": "Schriftarten suchen...",
"scale": {
"description": "Vergrößern oder verkleinern Sie die Größe des Standardtextes.",
"label": "Standardmäßige Schriftgröße"
}
},
"monospace": {
"label": "Monospace-Schriftart",
"description": "Monospace-Schriftart für Zahlen und Statistik-Anzeigen.",
"placeholder": "Monospace-Schriftart auswählen...",
"search-placeholder": "Monospace-Schriftarten suchen..."
"search-placeholder": "Monospace-Schriftarten suchen...",
"scale": {
"description": "Die Größe des nichtproportionalen Textes vergrößern oder verkleinern.",
"label": "Schriftgröße mit fester Breite"
}
},
"accent": {
"label": "Akzent-Schriftart",
"description": "Große Schriftart für hervorgehobene Anzeigen.",
"placeholder": "Display-Schriftart auswählen...",
"search-placeholder": "Display-Schriftarten suchen..."
}
"reset-scaling": "Skalierung zurücksetzen"
}
},
"audio": {
@@ -247,6 +252,10 @@
"section": {
"label": "Monitor-Anzeige",
"description": "Statusleiste auf bestimmten Monitoren anzeigen. Standard ist alle, wenn keine ausgewählt sind."
},
"only-same-output": {
"label": "Nur Apps vom gleichen Bildschirm",
"description": "Zeige nur Apps vom dem Bildschirm an, wo sich das Dock befindet."
}
}
},
@@ -278,6 +287,10 @@
"section": {
"label": "Monitor-Anzeige",
"description": "Monitor auswählen, auf dem das Dock angezeigt werden soll."
},
"only-same-output": {
"description": "Zeige nur Apps aus der Ausgabe an, bei denen sich das Dock befindet.",
"label": "Nur Apps von derselben Ausgabe"
}
}
},
@@ -307,6 +320,10 @@
"use-app2unit": {
"label": "App2Unit zum Starten von Anwendungen verwenden",
"description": "Verwendet eine alternative Startmethode zur besseren Verwaltung von App-Prozessen und Problemvermeidung."
},
"terminal-command": {
"label": "Terminalbefehl",
"description": "Befehl zum Starten eines Terminals. Z.B. 'kitty -e' oder 'gnome-terminal --'."
}
}
},
@@ -487,7 +504,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": {
@@ -829,21 +846,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 +906,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 +931,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 +964,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 +987,27 @@
}
},
"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."
},
"no-active-player": "Kein aktiver Player"
},
"clock": {
"use-primary-color": {
@@ -949,7 +1030,27 @@
"label": "Vertikale Statusleiste",
"description": "Verwenden Sie ein Leerzeichen, um jeden Teil in eine neue Zeile zu trennen."
},
"preview": "Vorschau"
"preview": "Vorschau",
"custom-font": {
"description": "Wähle eine benutzerdefinierte Schriftart für die Uhrenanzeige aus.",
"label": "Benutzerdefinierte Schriftart",
"placeholder": "Benutzerdefinierte Schriftart auswählen...",
"search-placeholder": "Schriftarten suchen..."
},
"use-custom-font": {
"description": "Überschreibe die Standard-Schriftauswahl mit einer benutzerdefinierten Schriftart für die Uhr.",
"label": "Benutzerdefinierte Schriftart verwenden"
}
},
"taskbar": {
"only-active-workspaces": {
"label": "Nur von aktiven Arbeitsbereichen",
"description": "Zeige nur Apps von aktiven Arbeitsbereichen an."
},
"only-same-output": {
"label": "Nur vom gleichen Bildschirm",
"description": "Zeige nur Apps vom dem Bildschirm an, wo sich die Taskbar befindet."
}
}
}
},
@@ -1088,13 +1189,16 @@
"close": "Schließen"
}
},
"launcher": {
"pin": "An das Dock anheften",
"unpin": "Vom Dock lösen"
},
"placeholders": {
"search-icons": "z.B. noctalia, niri, battery, cloud",
"profile-picture-path": "/home/benutzer/.face",
"enter-width-pixels": "Breite in Pixeln eingeben",
"enter-command": "Befehl eingeben (App oder benutzerdefiniertes Skript)",
"command-example": "echo \"Hallo Welt\"",
"search-wallpapers": "Zum Filtern von Hintergrundbildern eingeben...",
"search-launcher": "Einträge suchen... oder > für Befehle verwenden",
"search": "Suchen...",
@@ -1166,7 +1270,6 @@
"frame-rates": {
"fps": "{fps} FPS"
},
"screen-recording": {
"sources": {
"portal": "Portal",
@@ -1193,13 +1296,18 @@
"title": "Sitzungsmenü",
"click-again": "Erneut klicken für sofortige Ausführung",
"action-in-seconds": "{action} in {seconds} Sekunden...",
"lock-subtitle": "Sitzung sperren",
"end-subtitle": "Sitzung beenden",
"lock": "Sperren",
"lock-subtitle": "Sitzung sperren",
"lock-and-suspend": "Sperren und Ruhezustand",
"lock-and-suspend-subtitle": "Sitzung sperren und System in den Ruhezustand versetzen",
"suspend": "Ruhezustand",
"reboot": "Neu starten",
"logout": "Abmelden",
"shutdown": "Herunterfahren"
"logout-subtitle": "Sitzung beenden",
"shutdown": "Herunterfahren",
"reboot-subtitle": "Starten Sie Ihren Computer neu.",
"shutdown-subtitle": "Schalte deinen Computer aus.",
"suspend-subtitle": "System in den Ruhezustand versetzen"
},
"plugins": {
"applications": "Anwendungen",
@@ -1322,4 +1430,4 @@
"description": "Das Control-Center-Widget wurde aus der Leiste entfernt. Um es erneut über die Leiste zu öffnen, fügen Sie das Widget wieder hinzu."
}
}
}
}

View File

@@ -2,7 +2,7 @@
"settings": {
"general": {
"title": "General",
"profile": {
"section": {
"label": "Profile",
@@ -56,6 +56,7 @@
}
},
"fonts": {
"reset-scaling": "Reset scaling",
"section": {
"label": "Fonts",
"description": "Choose the fonts used throughout the interface."
@@ -64,19 +65,21 @@
"label": "Default font",
"description": "Main font used throughout the interface.",
"placeholder": "Select default font...",
"search-placeholder": "Search fonts..."
"search-placeholder": "Search font...",
"scale": {
"label": "Default font size",
"description": "Increase or decrease the size of the standard text."
}
},
"monospace": {
"label": "Monospaced font",
"description": "Monospaced font used for numbers and stats display.",
"placeholder": "Select monospace font...",
"search-placeholder": "Search monospace fonts..."
},
"accent": {
"label": "Accent font",
"description": "Large font used for prominent displays.",
"placeholder": "Select display font...",
"search-placeholder": "Search display fonts..."
"search-placeholder": "Search monospace font...",
"scale": {
"label": "Monospaced font size",
"description": "Increase or decrease the size of the monospaced text."
}
}
}
},
@@ -282,6 +285,10 @@
"section": {
"label": "Monitor display",
"description": "Choose which monitor to display the dock on."
},
"only-same-output": {
"label": "Only apps from same output",
"description": "Show only apps from the output where the dock is located."
}
}
},
@@ -311,6 +318,10 @@
"use-app2unit": {
"label": "Use App2Unit to launch applications",
"description": "Uses an alternative launch method to better manage app processes and prevent issues."
},
"terminal-command": {
"label": "Terminal command",
"description": "Command to launch a terminal. E.g., 'kitty -e' or 'gnome-terminal --'."
}
}
},
@@ -491,7 +502,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": {
@@ -816,21 +827,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 +887,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 +912,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 +945,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 +968,27 @@
}
},
"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."
},
"no-active-player": "No active player"
},
"clock": {
"use-primary-color": {
@@ -924,6 +999,16 @@
"label": "Use monospaced font",
"description": "When enabled, the clock will use the monospaced font."
},
"use-custom-font": {
"label": "Use custom font",
"description": "Override the default font selection with a custom font for the clock."
},
"custom-font": {
"label": "Custom font",
"description": "Select a custom font for the clock display.",
"placeholder": "Select custom font...",
"search-placeholder": "Search fonts..."
},
"clock-display": {
"label": "Clock display",
"description": "Customize your clock's display by adding tokens from the list below. To use the 12-hour format, you must include the 'AP' token."
@@ -937,6 +1022,16 @@
"description": "Use a space to separate each part onto a new line."
},
"preview": "Preview"
},
"taskbar": {
"only-active-workspaces": {
"label": "Only from active workspaces",
"description": "Show only apps from active workspaces."
},
"only-same-output": {
"label": "Only from same output",
"description": "Show only apps from the output where the bar is located."
}
}
}
},
@@ -1075,13 +1170,17 @@
"close": "Close"
}
},
"launcher": {
"pin": "Pin to dock",
"unpin": "Unpin from dock"
},
"placeholders": {
"search-icons": "e.g., noctalia, niri, battery, cloud",
"profile-picture-path": "/home/user/.face",
"enter-width-pixels": "Enter width in pixels",
"enter-command": "Enter command to execute (app or custom script)",
"command-example": "echo \"Hello World\"",
"search-wallpapers": "Type to filter wallpapers...",
"search-launcher": "Search entries... or use > for commands",
"search": "Search...",
@@ -1179,13 +1278,18 @@
"title": "Session Menu",
"click-again": "Click again to execute immediately",
"action-in-seconds": "{action} in {seconds} seconds...",
"lock-subtitle": "Lock your session",
"end-subtitle": "End your session",
"lock": "Lock",
"suspend": "Suspend",
"lock-subtitle": "Lock your session",
"lock-and-suspend": "Lock and Suspend",
"lock-and-suspend-subtitle": "Lock session and put system to sleep",
"suspend": "Suspend",
"suspend-subtitle": "Put system to sleep",
"reboot": "Reboot",
"reboot-subtitle": "Restart your computer",
"logout": "Logout",
"shutdown": "Shutdown"
"logout-subtitle": "End your session",
"shutdown": "Shutdown",
"shutdown-subtitle": "Turn off your computer"
},
"plugins": {
"applications": "Applications",
@@ -1299,7 +1403,7 @@
},
"weather": {
"clear-sky": "Clear sky",
"mainly-clear": "Mainly clear",
"mainly-clear": "Mainly clear",
"partly-cloudy": "Partly cloudy",
"overcast": "Overcast",
"fog": "Fog",
@@ -1309,7 +1413,7 @@
"thunderstorm": "Thunderstorm",
"unknown": "Unknown"
},
"authentication": {
"failed": "Authentication failed",
"error": "Authentication error"

View File

@@ -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": {
@@ -58,20 +62,23 @@
"label": "Fuente predeterminada",
"description": "Fuente principal utilizada en toda la interfaz.",
"placeholder": "Seleccionar fuente predeterminada...",
"search-placeholder": "Buscar fuentes..."
"search-placeholder": "Buscar fuentes...",
"scale": {
"description": "Aumentar o disminuir el tamaño del texto estándar.",
"label": "Tamaño de fuente predeterminado"
}
},
"monospace": {
"label": "Fuente monoespaciada",
"description": "Fuente monoespaciada utilizada para la visualización de números y estadísticas.",
"placeholder": "Seleccionar fuente monoespaciada...",
"search-placeholder": "Buscar fuentes monoespaciadas..."
"search-placeholder": "Buscar fuentes monoespaciadas...",
"scale": {
"description": "Aumentar o disminuir el tamaño del texto monoespaciado.",
"label": "Tamaño de fuente monoespaciada"
}
},
"accent": {
"label": "Fuente de acento",
"description": "Fuente grande utilizada para visualizaciones destacadas.",
"placeholder": "Seleccionar fuente de visualización...",
"search-placeholder": "Buscar fuentes de visualización..."
}
"reset-scaling": "Restablecer la escala"
}
},
"audio": {
@@ -276,6 +283,10 @@
"section": {
"label": "Visualización en monitor",
"description": "Elige en qué monitor mostrar el dock."
},
"only-same-output": {
"description": "Mostrar solo las aplicaciones de la salida donde se encuentra el dock.",
"label": "Solo aplicaciones de la misma salida"
}
}
},
@@ -305,6 +316,10 @@
"use-app2unit": {
"label": "Usar App2Unit para lanzar aplicaciones",
"description": "Usa un método de lanzamiento alternativo para gestionar mejor los procesos de las aplicaciones y prevenir problemas."
},
"terminal-command": {
"label": "Comando de terminal",
"description": "Comando para iniciar un terminal. Por ejemplo, 'kitty -e' o 'gnome-terminal --'."
}
}
},
@@ -485,7 +500,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": {
@@ -810,21 +825,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 +885,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 +910,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 +943,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 +966,27 @@
}
},
"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."
},
"no-active-player": "Sin reproductor activo"
},
"clock": {
"use-primary-color": {
@@ -930,7 +1009,27 @@
"label": "Barra vertical",
"description": "Usa un espacio para separar cada parte en una nueva línea."
},
"preview": "Vista previa"
"preview": "Vista previa",
"custom-font": {
"description": "Seleccionar una fuente personalizada para la visualización del reloj.",
"label": "Fuente personalizada",
"placeholder": "Seleccionar fuente personalizada...",
"search-placeholder": "Buscar fuentes..."
},
"use-custom-font": {
"description": "Anular la selección de fuente predeterminada con una fuente personalizada para el reloj.",
"label": "Usar fuente personalizada"
}
},
"taskbar": {
"only-active-workspaces": {
"description": "Mostrar solo las aplicaciones de los espacios de trabajo activos.",
"label": "Solo desde espacios de trabajo activos"
},
"only-same-output": {
"description": "Muestra solo las aplicaciones del resultado donde se encuentra la barra.",
"label": "Solo de la misma salida"
}
}
}
},
@@ -1069,6 +1168,10 @@
"close": "Cerrar"
}
},
"launcher": {
"pin": "Anclar al dock",
"unpin": "Desanclar del dock"
},
"placeholders": {
"search-icons": "ej., noctalia, niri, battery, cloud",
"profile-picture-path": "/home/usuario/.face",
@@ -1172,13 +1275,18 @@
"title": "Menú de sesión",
"click-again": "Haz clic de nuevo para ejecutar inmediatamente",
"action-in-seconds": "{action} en {seconds} segundos...",
"lock-subtitle": "Bloquea tu sesión",
"end-subtitle": "Finaliza tu sesión",
"lock": "Bloquear",
"lock-subtitle": "Bloquea tu sesión",
"lock-and-suspend": "Bloquear y Suspender",
"lock-and-suspend-subtitle": "Bloquear sesión y poner el sistema en suspensión",
"suspend": "Suspender",
"reboot": "Reiniciar",
"logout": "Cerrar sesión",
"shutdown": "Apagar"
"logout-subtitle": "Finaliza tu sesión",
"shutdown": "Apagar",
"reboot-subtitle": "Reinicia tu computadora.",
"shutdown-subtitle": "Apaga tu computadora.",
"suspend-subtitle": "Poner el sistema en reposo"
},
"plugins": {
"applications": "Aplicaciones",
@@ -1318,4 +1426,4 @@
"charging": "Cargando.",
"discharging": "Descargando."
}
}
}

View File

@@ -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": {
@@ -58,20 +62,23 @@
"label": "Police par défaut",
"description": "Police principale utilisée dans toute l'interface.",
"placeholder": "Sélectionner la police par défaut...",
"search-placeholder": "Rechercher des polices..."
"search-placeholder": "Rechercher des polices...",
"scale": {
"description": "Augmenter ou diminuer la taille du texte standard.",
"label": "Taille de police par défaut"
}
},
"monospace": {
"label": "Police à chasse fixe",
"description": "Police à chasse fixe utilisée pour l'affichage des chiffres et des statistiques.",
"placeholder": "Sélectionner la police à chasse fixe...",
"search-placeholder": "Rechercher des polices à chasse fixe..."
"search-placeholder": "Rechercher des polices à chasse fixe...",
"scale": {
"description": "Augmenter ou diminuer la taille du texte à chasse fixe.",
"label": "Taille de police à chasse fixe"
}
},
"accent": {
"label": "Police d'accentuation",
"description": "Grande police utilisée pour les affichages proéminents.",
"placeholder": "Sélectionner la police d'affichage...",
"search-placeholder": "Rechercher des polices d'affichage..."
}
"reset-scaling": "Réinitialiser l'échelle"
}
},
"audio": {
@@ -276,6 +283,10 @@
"section": {
"label": "Affichage sur les moniteur",
"description": "Choisissez sur quels moniteurs afficher le dock."
},
"only-same-output": {
"description": "Afficher uniquement les applications de la sortie où le dock est situé.",
"label": "Seulement les applications de la même sortie"
}
}
},
@@ -305,6 +316,10 @@
"use-app2unit": {
"label": "Utiliser App2Unit pour lancer les applications",
"description": "Utilise une méthode de lancement alternative pour mieux gérer les processus des applications et prévenir les problèmes."
},
"terminal-command": {
"label": "Commande du terminal",
"description": "Commande pour lancer un terminal. Ex: 'kitty -e' ou 'gnome-terminal --'."
}
}
},
@@ -485,7 +500,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": {
@@ -810,21 +825,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 +885,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 +910,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 +943,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 +966,27 @@
}
},
"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."
},
"no-active-player": "Aucun lecteur actif"
},
"clock": {
"use-primary-color": {
@@ -930,7 +1009,27 @@
"label": "Barre verticale",
"description": "Utilisez un espace pour séparer chaque partie sur une nouvelle ligne."
},
"preview": "Aperçu"
"preview": "Aperçu",
"custom-font": {
"description": "Sélectionner une police personnalisée pour l'affichage de l'horloge.",
"label": "Police personnalisée",
"placeholder": "Sélectionner une police personnalisée...",
"search-placeholder": "Rechercher des polices..."
},
"use-custom-font": {
"description": "Remplacez la police par défaut par une police personnalisée pour l'horloge.",
"label": "Utiliser une police personnalisée."
}
},
"taskbar": {
"only-active-workspaces": {
"description": "Afficher uniquement les applications des espaces de travail actifs.",
"label": "Seulement depuis les espaces de travail actifs."
},
"only-same-output": {
"description": "Afficher uniquement les applications de la sortie où la barre est située.",
"label": "Seulement à partir de la même sortie"
}
}
}
},
@@ -1069,6 +1168,10 @@
"close": "Fermer"
}
},
"launcher": {
"pin": "Épingler au dock",
"unpin": "Retirer du dock"
},
"placeholders": {
"search-icons": "ex: noctalia, niri, batterie, nuage",
"profile-picture-path": "/home/user/.face",
@@ -1172,13 +1275,18 @@
"title": "Menu de session",
"click-again": "Cliquez à nouveau pour exécuter immédiatement",
"action-in-seconds": "{action} dans {seconds} secondes...",
"lock-subtitle": "Verrouiller votre session",
"end-subtitle": "Terminer votre session",
"lock": "Verrouiller",
"lock-subtitle": "Verrouiller votre session",
"lock-and-suspend": "Verrouiller et Mettre en veille",
"lock-and-suspend-subtitle": "Verrouiller la session et mettre le système en veille",
"suspend": "Mettre en veille",
"reboot": "Redémarrer",
"logout": "Déconnexion",
"shutdown": "Éteindre"
"logout-subtitle": "Terminer votre session",
"shutdown": "Éteindre",
"reboot-subtitle": "Redémarrez votre ordinateur.",
"shutdown-subtitle": "Éteignez votre ordinateur.",
"suspend-subtitle": "Mettre le système en veille"
},
"plugins": {
"applications": "Applications",
@@ -1318,4 +1426,4 @@
"charging": "En charge.",
"discharging": "En décharge."
}
}
}

View File

@@ -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": {
@@ -58,20 +62,23 @@
"label": "Fonte padrão",
"description": "Fonte principal usada em toda a interface.",
"placeholder": "Selecione a fonte padrão...",
"search-placeholder": "Pesquisar fontes..."
"search-placeholder": "Pesquisar fontes...",
"scale": {
"description": "Aumentar ou diminuir o tamanho do texto padrão.",
"label": "Tamanho de fonte padrão"
}
},
"monospace": {
"label": "Fonte monoespaçada",
"description": "Fonte monoespaçada usada para exibição de números e estatísticas.",
"placeholder": "Selecione a fonte monoespaçada...",
"search-placeholder": "Pesquisar fontes monoespaçadas..."
"search-placeholder": "Pesquisar fontes monoespaçadas...",
"scale": {
"description": "Aumentar ou diminuir o tamanho do texto monoespaçado.",
"label": "Tamanho da fonte monoespaçada"
}
},
"accent": {
"label": "Fonte de destaque",
"description": "Fonte grande usada para exibições proeminentes.",
"placeholder": "Selecione a fonte de exibição...",
"search-placeholder": "Pesquisar fontes de exibição..."
}
"reset-scaling": "Redefinir escala"
}
},
"audio": {
@@ -276,6 +283,10 @@
"section": {
"label": "Exibição no monitor",
"description": "Escolha em qual monitor exibir a dock."
},
"only-same-output": {
"description": "Mostrar apenas aplicativos da saída onde o dock está localizado.",
"label": "Apenas aplicativos da mesma saída"
}
}
},
@@ -305,6 +316,10 @@
"use-app2unit": {
"label": "Usar App2Unit para iniciar aplicativos",
"description": "Usa um método de inicialização alternativo para gerenciar melhor os processos de aplicativos e evitar problemas."
},
"terminal-command": {
"label": "Comando do terminal",
"description": "Comando para iniciar um terminal. Ex: 'kitty -e' ou 'gnome-terminal --'."
}
}
},
@@ -451,7 +466,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": {
@@ -713,7 +728,6 @@
}
}
},
"widgets": {
"tooltip": {
"placeholder": "Espaço reservado"
@@ -811,21 +825,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 +885,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 +910,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 +943,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 +966,27 @@
}
},
"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."
},
"no-active-player": "Nenhum player ativo"
},
"clock": {
"use-primary-color": {
@@ -931,7 +1009,27 @@
"label": "Barra vertical",
"description": "Use um espaço para separar cada parte em uma nova linha."
},
"preview": "Pré-visualização"
"preview": "Pré-visualização",
"custom-font": {
"description": "Selecione uma fonte personalizada para a exibição do relógio.",
"label": "Fonte personalizada",
"placeholder": "Selecionar fonte personalizada...",
"search-placeholder": "Procurar fontes..."
},
"use-custom-font": {
"description": "Substitua a fonte padrão por uma fonte personalizada para o relógio.",
"label": "Usar fonte personalizada"
}
},
"taskbar": {
"only-active-workspaces": {
"description": "Mostrar apenas aplicativos de áreas de trabalho ativas.",
"label": "Apenas de espaços de trabalho ativos"
},
"only-same-output": {
"description": "Mostrar apenas os aplicativos da saída onde a barra está localizada.",
"label": "Apenas da mesma saída"
}
}
}
},
@@ -1070,6 +1168,10 @@
"close": "Fechar"
}
},
"launcher": {
"pin": "Fixar no dock",
"unpin": "Desafixar do dock"
},
"placeholders": {
"search-icons": "ex., noctalia, niri, battery, cloud",
"profile-picture-path": "/home/usuario/.face",
@@ -1105,8 +1207,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 +1224,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",
@@ -1186,13 +1275,18 @@
"title": "Menu da Sessão",
"click-again": "Clique novamente para executar imediatamente",
"action-in-seconds": "{action} em {seconds} segundos...",
"lock-subtitle": "Bloqueie sua sessão",
"end-subtitle": "Encerre sua sessão",
"lock": "Bloquear",
"lock-subtitle": "Bloqueie sua sessão",
"lock-and-suspend": "Bloquear e Suspender",
"lock-and-suspend-subtitle": "Bloquear sessão e colocar o sistema em suspensão",
"suspend": "Suspender",
"reboot": "Reiniciar",
"logout": "Sair",
"shutdown": "Desligar"
"logout-subtitle": "Encerre sua sessão",
"shutdown": "Desligar",
"reboot-subtitle": "Reinicie o seu computador.",
"shutdown-subtitle": "Desligue o seu computador.",
"suspend-subtitle": "Colocar o sistema em suspensão."
},
"plugins": {
"applications": "Aplicativos",
@@ -1332,4 +1426,4 @@
"charging": "Carregando.",
"discharging": "Descarregando."
}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -107,13 +107,15 @@
"backgroundOpacity": 1,
"pinnedExecs": [],
"useApp2Unit": false,
"sortByMostUsed": true
"sortByMostUsed": true,
"terminalCommand": "xterm -e"
},
"dock": {
"autoHide": false,
"exclusive": false,
"backgroundOpacity": 1,
"floatingRatio": 1,
"onlySameOutput": true,
"monitors": [],
"pinnedApps": []
},
@@ -148,7 +150,8 @@
"ui": {
"fontDefault": "Roboto",
"fontFixed": "DejaVu Sans Mono",
"fontBillboard": "Inter",
"fontDefaultScale": 1,
"fontFixedScale": 1,
"monitorsScaling": [],
"idleInhibitorEnabled": false
},

View File

@@ -8,6 +8,7 @@ set -euo pipefail
# Configuration
FOLDER_PATH="Assets/Translations"
REFERENCE_FILE="en.json"
TRANSLATE_MODE=false
# Colors for terminal output
RED='\033[0;31m'
@@ -32,6 +33,152 @@ check_dependencies() {
print_color $YELLOW "On macOS: brew install jq" >&2
exit 1
fi
if $TRANSLATE_MODE && ! command -v curl &> /dev/null; then
print_color $RED "Error: 'curl' is required for translation mode but not installed." >&2
exit 1
fi
}
# Function to get Gemini API key
get_gemini_api_key() {
if [[ -z "${GEMINI_API_KEY:-}" ]]; then
print_color $RED "Error: GEMINI_API_KEY environment variable is not set" >&2
print_color $YELLOW "Please set it with: export GEMINI_API_KEY='your-api-key'" >&2
exit 1
fi
echo "$GEMINI_API_KEY"
}
# Function to get value from JSON using key path
get_json_value() {
local json_file=$1
local key_path=$2
# Convert dot-separated path to jq path
local jq_path=$(echo "$key_path" | sed 's/\./\.\["/g' | sed 's/$/"]/' | sed 's/^\.//')
local jq_query=".${jq_path}"
# Use a more robust approach: split by dots and build path
local -a path_parts
IFS='.' read -ra path_parts <<< "$key_path"
local jq_filter="."
for part in "${path_parts[@]}"; do
jq_filter="${jq_filter}[\"${part}\"]"
done
jq -r "$jq_filter // empty" "$json_file" 2>/dev/null || echo ""
}
# Function to list available Gemini models
list_gemini_models() {
local api_key=$(get_gemini_api_key)
print_color $BLUE "Fetching available Gemini models..." >&2
echo "" >&2
local response=$(curl -s -X GET \
"https://generativelanguage.googleapis.com/v1/models?key=${api_key}" \
-H "Content-Type: application/json" 2>/dev/null)
# Parse and display models
echo "$response" | jq -r '.models[] | "- \(.name) (\(.displayName))"' 2>/dev/null || {
print_color $RED "Failed to parse models list" >&2
echo "$response" >&2
exit 1
}
exit 0
}
# Function to translate text using Gemini API
translate_text() {
local text=$1
local target_language=$2
local api_key=$3
# Escape text for JSON
local escaped_text=$(echo "$text" | jq -Rs .)
# Prepare the API request
local prompt="Translate the following English text to ${target_language}. Return ONLY the translation, no explanations or additional text:\n\n${text}"
local escaped_prompt=$(echo "$prompt" | jq -Rs .)
local request_body=$(cat <<EOF
{
"contents": [{
"parts": [{
"text": ${escaped_prompt}
}]
}],
"generationConfig": {
"temperature": 0.3,
"maxOutputTokens": 1000
}
}
EOF
)
# Make API call to Gemini
local api_url="https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key=${api_key}"
print_color $BLUE " API URL: $api_url" >&2
local response=$(curl -s -X POST "$api_url" \
-H "Content-Type: application/json" \
-d "$request_body" 2>/dev/null)
print_color $BLUE " API Response: $response" >&2
# Extract the translation from response - try multiple parsing approaches
local translation=$(echo "$response" | jq -r '.candidates[0].content.parts[0].text // .text // empty' 2>/dev/null | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
if [[ -z "$translation" ]]; then
print_color $RED " Failed to parse translation. Full response:" >&2
echo "$response" | jq . >&2 2>/dev/null || echo "$response" >&2
echo ""
return 1
fi
print_color $GREEN " Parsed translation: $translation" >&2
echo "$translation"
}
# Function to inject translation into JSON file using jq
inject_translation() {
local json_file=$1
local key_path=$2
local value=$3
# Split key path into array
local -a path_parts
IFS='.' read -ra path_parts <<< "$key_path"
# Build jq path array
local jq_path="["
for i in "${!path_parts[@]}"; do
if [[ $i -gt 0 ]]; then
jq_path+=","
fi
jq_path+="\"${path_parts[$i]}\""
done
jq_path+="]"
# Create a temporary file
local temp_file=$(mktemp)
# Use jq to set the value at the path
jq --argjson path "$jq_path" --arg value "$value" 'setpath($path; $value)' "$json_file" > "$temp_file"
if [[ $? -eq 0 ]]; then
mv "$temp_file" "$json_file"
return 0
else
rm -f "$temp_file"
return 1
fi
}
# Function to extract all keys from a JSON file recursively
@@ -75,6 +222,9 @@ generate_header() {
echo "Generated: $timestamp"
echo "Reference file: $REFERENCE_FILE"
echo "Folder: $(realpath "$FOLDER_PATH")"
if $TRANSLATE_MODE; then
echo "Mode: TRANSLATION ENABLED"
fi
echo ""
echo "Notes:"
echo "- Keys are compared recursively through all nested JSON objects"
@@ -203,6 +353,49 @@ compare_language() {
done
rm -f "$temp_missing"
echo ""
# Translate missing keys if in translate mode
if $TRANSLATE_MODE; then
print_color $BLUE "Translating missing keys for $lang_name..." >&2
local api_key=$(get_gemini_api_key)
local translated_count=0
local failed_count=0
while IFS= read -r key; do
if [[ -n "$key" ]]; then
# Get English value
local en_value=$(get_json_value "$ref_file_path" "$key")
if [[ -n "$en_value" ]]; then
print_color $YELLOW " Translating: $key" >&2
# Translate the value
local translated_value=$(translate_text "$en_value" "$lang_name" "$api_key")
if [[ -n "$translated_value" ]]; then
# Inject translation into the file
if inject_translation "$lang_file" "$key" "$translated_value"; then
print_color $GREEN " ✓ Translated: $key" >&2
translated_count=$((translated_count + 1))
else
print_color $RED " ✗ Failed to inject: $key" >&2
failed_count=$((failed_count + 1))
fi
else
print_color $RED " ✗ Translation failed: $key" >&2
failed_count=$((failed_count + 1))
fi
# Small delay to avoid rate limiting
sleep 0.5
fi
fi
done <<< "$missing_keys"
echo ""
print_color $GREEN "Translation complete: $translated_count succeeded, $failed_count failed" >&2
echo ""
fi
else
echo "✅ No missing keys in $lang_name"
echo ""
@@ -351,6 +544,9 @@ main() {
if [[ -n "$target_language" ]]; then
echo "Target language: $target_language"
fi
if $TRANSLATE_MODE; then
echo "Translation mode: ENABLED"
fi
echo "Report generated: $(date '+%Y-%m-%d %H:%M:%S')"
echo ""
echo "================================================================================"
@@ -367,11 +563,13 @@ main() {
# Usage information
show_usage() {
echo "Usage: $0 [language_code]" >&2
echo "Usage: $0 [--translate] [language_code]" >&2
echo "" >&2
echo "This script compares JSON language files in '$FOLDER_PATH' against the English reference." >&2
echo "" >&2
echo "Arguments:" >&2
echo " --translate Enable automatic translation of missing keys using Gemini API" >&2
echo " --list-models List all available Gemini models and exit" >&2
echo " language_code Optional. Compare only the specified language (e.g., 'fr', 'es', 'de')" >&2
echo " If not provided, all language files will be compared" >&2
echo "" >&2
@@ -380,14 +578,18 @@ show_usage() {
echo " - Reference file: $REFERENCE_FILE" >&2
echo "" >&2
echo "Examples:" >&2
echo " $0 # Compare all languages" >&2
echo " $0 fr # Compare only French (fr.json)" >&2
echo " $0 es # Compare only Spanish (es.json)" >&2
echo " $0 # Compare all languages" >&2
echo " $0 fr # Compare only French (fr.json)" >&2
echo " $0 --list-models # List available Gemini models" >&2
echo " $0 --translate # Compare all and translate missing keys" >&2
echo " $0 --translate fr # Translate missing keys for French only" >&2
echo "" >&2
echo "Requirements:" >&2
echo " - jq must be installed" >&2
echo " - curl must be installed (for --translate mode)" >&2
echo " - $REFERENCE_FILE must exist in $FOLDER_PATH" >&2
echo " - Target language file must exist if specified" >&2
echo " - GEMINI_API_KEY environment variable must be set (for --translate mode)" >&2
echo "" >&2
echo "Output:" >&2
echo " - Comparison report is printed to stdout" >&2
@@ -396,29 +598,39 @@ show_usage() {
}
# Handle command line arguments
if [[ $# -gt 1 ]]; then
echo "Error: Too many arguments. Only one language code is allowed." >&2
echo "" >&2
show_usage
exit 1
fi
if [[ $# -eq 1 ]]; then
if [[ "$1" == "-h" || "$1" == "--help" ]]; then
show_usage
exit 0
else
# Validate language code format (basic check for reasonable filename)
if [[ ! "$1" =~ ^[a-zA-Z][a-zA-Z0-9_-]*$ ]]; then
echo "Error: Invalid language code format '$1'. Use alphanumeric characters, hyphens, and underscores only." >&2
echo "" >&2
target_lang=""
while [[ $# -gt 0 ]]; do
case $1 in
-h|--help)
show_usage
exit 1
fi
# Run main function with target language
main "$1"
fi
else
# Run main function for all languages
main ""
fi
exit 0
;;
--list-models)
list_gemini_models
;;
--translate)
TRANSLATE_MODE=true
shift
;;
*)
if [[ -n "$target_lang" ]]; then
echo "Error: Too many arguments. Only one language code is allowed." >&2
echo "" >&2
show_usage
exit 1
fi
# Validate language code format (basic check for reasonable filename)
if [[ ! "$1" =~ ^[a-zA-Z][a-zA-Z0-9_-]*$ ]]; then
echo "Error: Invalid language code format '$1'. Use alphanumeric characters, hyphens, and underscores only." >&2
echo "" >&2
show_usage
exit 1
fi
target_lang="$1"
shift
;;
esac
done
# Run main function
main "$target_lang"

View File

@@ -53,9 +53,10 @@ Singleton {
try {
var data = JSON.parse(text())
root.translations = data
Logger.log("I18n", `Loaded translations for "${root.langCode}"`)
root.isLoaded = true
root.translationsLoaded()
Logger.log("I18n", `Loaded translations for "${root.langCode}"`)
} catch (e) {
Logger.error("I18n", `Failed to parse translation file: ${e}`)
setLanguage("en")
@@ -170,11 +171,24 @@ Singleton {
// Detect user's favorite locale - languages
for (var i = 0; i < Qt.locale().uiLanguages.length; i++) {
const userLang = Qt.locale().uiLanguages[i].substring(0, 2)
if (availableLanguages.includes(userLang)) {
setLanguage(userLang)
const fullUserLang = Qt.locale().uiLanguages[i]
// Try full code match (such as zh CN, en US)
if (availableLanguages.includes(fullUserLang)) {
Logger.log("I18n", `Exact match found: "${fullUserLang}"`)
setLanguage(fullUserLang)
return
}
// If full code match fails, try short code matching (such as zh, en)
const shortUserLang = fullUserLang.substring(0, 2)
if (availableLanguages.includes(shortUserLang)) {
Logger.log("I18n", `Short code match found: "${shortUserLang}" from "${fullUserLang}"`)
setLanguage(shortUserLang)
return
}
Logger.log("I18n", `No match for system language: "${fullUserLang}"`)
}
// Fallback to first available language (preferably "en" if available)
@@ -202,7 +216,7 @@ Singleton {
const filePath = `file://${Quickshell.shellDir}/Assets/Translations/${langCode}.json`
fileView.path = filePath
isLoaded = false
Logger.log("I18n", `Loading translations from: ${filePath}`)
Logger.log("I18n", `Loading translations: ${langCode}`)
// Only load fallback translations if we are not using english and english is available
if (langCode !== "en" && availableLanguages.includes("en")) {

View File

@@ -50,7 +50,7 @@ Singleton {
}
function loadFontWithCacheBusting() {
Logger.log("Icons", "Loading font with cache busting:", cacheBustingPath)
Logger.log("Icons", "Loading font with cache busting")
// Destroy old loader first
if (currentFontLoader) {

View File

@@ -71,7 +71,13 @@ Singleton {
id: saveTimer
running: false
interval: 1000
onTriggered: settingsFileView.writeAdapter()
onTriggered: {
settingsFileView.writeAdapter()
// Write to fallback location if set
if (Quickshell.env("NOCTALIA_SETTINGS_FALLBACK")) {
settingsFallbackFileView.writeAdapter()
}
}
}
FileView {
@@ -90,15 +96,10 @@ Singleton {
}
onLoaded: function () {
if (!isLoaded) {
Logger.log("Settings", "----------------------------")
Logger.log("Settings", "Settings loaded successfully")
Logger.log("Settings", "Settings loaded")
upgradeSettingsData()
validateMonitorConfigurations()
kickOffServices()
isLoaded = true
// Emit the signal
@@ -106,11 +107,25 @@ Singleton {
}
}
onLoadFailed: function (error) {
if (error.toString().includes("No such file") || error === 2)
if (error.toString().includes("No such file") || error === 2) {
// File doesn't exist, create it with default values
writeAdapter()
// Also write to fallback if set
if (Quickshell.env("NOCTALIA_SETTINGS_FALLBACK")) {
settingsFallbackFileView.writeAdapter()
}
}
}
}
// Fallback FileView for writing settings to alternate location
FileView {
id: settingsFallbackFileView
path: Quickshell.env("NOCTALIA_SETTINGS_FALLBACK") || ""
adapter: Quickshell.env("NOCTALIA_SETTINGS_FALLBACK") ? adapter : null
printErrors: false
watchChanges: false
}
JsonAdapter {
id: adapter
@@ -224,6 +239,7 @@ Singleton {
property list<string> pinnedExecs: []
property bool useApp2Unit: false
property bool sortByMostUsed: true
property string terminalCommand: "xterm -e"
}
// dock
@@ -232,6 +248,7 @@ Singleton {
property bool exclusive: false
property real backgroundOpacity: 1.0
property real floatingRatio: 1.0
property bool onlySameOutput: true
property list<string> monitors: []
// Desktop entry IDs pinned to the dock (e.g., "org.kde.konsole", "firefox.desktop")
property list<string> pinnedApps: []
@@ -277,7 +294,8 @@ Singleton {
property JsonObject ui: JsonObject {
property string fontDefault: "Roboto"
property string fontFixed: "DejaVu Sans Mono"
property string fontBillboard: "Inter"
property real fontDefaultScale: 1.0
property real fontFixedScale: 1.0
property list<var> monitorsScaling: []
property bool idleInhibitorEnabled: false
}
@@ -515,17 +533,4 @@ Singleton {
const widgetAfter = JSON.stringify(widget)
return (widgetAfter !== widgetBefore)
}
// -----------------------------------------------------
// Kickoff essential services
function kickOffServices() {
LocationService.init()
NightLightService.apply()
ColorSchemeService.init()
MatugenService.init()
WallpaperService.init()
FontService.init()
HooksService.init()
BluetoothService.init()
}
}

View File

@@ -45,6 +45,7 @@ Singleton {
"keyboard": "keyboard",
"shutdown": "power",
"lock": "lock",
"lock-pause": "lock-pause",
"logout": "logout",
"reboot": "refresh",
"suspend": "player-pause",
@@ -70,6 +71,8 @@ Singleton {
"chevron-down": "chevron-down",
"caret-up": "caret-up-filled",
"caret-down": "caret-down-filled",
"star": "star",
"star-off": "star-off",
"battery-exclamation": "battery-exclamation",
"battery-charging": "battery-charging",
"battery-4": "battery-4",

View File

@@ -13,7 +13,7 @@ Variants {
required property ShellScreen modelData
active: Settings.isLoaded && modelData && Settings.data.wallpaper.enabled
active: modelData && Settings.data.wallpaper.enabled
sourceComponent: PanelWindow {
id: root
@@ -41,16 +41,10 @@ Variants {
property string futureWallpaper: ""
// Fillmode default is "crop"
property real fillMode: 1.0
property real fillMode: WallpaperService.getFillModeUniform()
property vector4d fillColor: Qt.vector4d(Settings.data.wallpaper.fillColor.r, Settings.data.wallpaper.fillColor.g, Settings.data.wallpaper.fillColor.b, 1.0)
// On startup assign wallpaper immediately
Component.onCompleted: {
fillMode = WallpaperService.getFillModeUniform()
var path = modelData ? WallpaperService.getWallpaper(modelData.name) : ""
setWallpaperImmediate(path)
}
Component.onCompleted: setWallpaperInitial()
Connections {
target: Settings.data.wallpaper
@@ -64,7 +58,6 @@ Variants {
target: WallpaperService
function onWallpaperChanged(screenName, path) {
if (screenName === modelData.name) {
// Update wallpaper display
// Set wallpaper immediately on startup
futureWallpaper = path
@@ -230,6 +223,7 @@ Variants {
easing.type: Easing.InOutCubic
onFinished: {
// Swap images after transition completes
currentWallpaper.source = ""
currentWallpaper.source = nextWallpaper.source
nextWallpaper.source = ""
transitionProgress = 0.0
@@ -239,9 +233,20 @@ Variants {
}
}
function setWallpaperInitial() {
// On startup, defer assigning wallpaper until the service cache is ready, retries every tick
if (!WallpaperService || !WallpaperService.isInitialized) {
Qt.callLater(setWallpaperInitial)
return
}
setWallpaperImmediate(WallpaperService.getWallpaper(modelData.name))
}
function setWallpaperImmediate(source) {
transitionAnimation.stop()
transitionProgress = 0.0
currentWallpaper.source = ""
currentWallpaper.source = source
nextWallpaper.source = ""
}
@@ -255,8 +260,12 @@ Variants {
// We are interrupting a transition
transitionAnimation.stop()
transitionProgress = 0
currentWallpaper.source = nextWallpaper.source
const newCurrentSource = nextWallpaper.source
currentWallpaper.source = ""
nextWallpaper.source = ""
currentWallpaper.source = newCurrentSource
}
nextWallpaper.source = source

View File

@@ -12,7 +12,7 @@ Variants {
delegate: Loader {
required property ShellScreen modelData
active: Settings.isLoaded && CompositorService.isNiri && modelData && Settings.data.wallpaper.enabled
active: CompositorService.isNiri && modelData && Settings.data.wallpaper.enabled
property string wallpaper: ""

View File

@@ -28,7 +28,7 @@ Variants {
}
}
active: Settings.isLoaded && BarService.isVisible && modelData && modelData.name ? (Settings.data.bar.monitors.includes(modelData.name) || (Settings.data.bar.monitors.length === 0)) : false
active: BarService.isVisible && modelData && modelData.name ? (Settings.data.bar.monitors.includes(modelData.name) || (Settings.data.bar.monitors.length === 0)) : false
sourceComponent: PanelWindow {
screen: modelData || null
@@ -55,6 +55,12 @@ Variants {
right: Settings.data.bar.floating && Settings.data.bar.position !== "left" ? Settings.data.bar.marginHorizontal * Style.marginXL * scaling : 0
}
Component.onCompleted: {
if (modelData && modelData.name) {
BarService.registerBar(modelData.name)
}
}
Item {
anchors.fill: parent
clip: true

View File

@@ -22,7 +22,7 @@ ColumnLayout {
NText {
text: root.label
font.pointSize: Style.fontSizeL * scaling
pointSize: Style.fontSizeL * scaling
color: Color.mSecondary
font.weight: Style.fontWeightMedium
Layout.fillWidth: true
@@ -67,7 +67,7 @@ ColumnLayout {
// One device BT icon
NIcon {
icon: BluetoothService.getDeviceIcon(modelData)
font.pointSize: Style.fontSizeXXL * scaling
pointSize: Style.fontSizeXXL * scaling
color: getContentColor(Color.mOnSurface)
Layout.alignment: Qt.AlignVCenter
}
@@ -79,7 +79,7 @@ ColumnLayout {
// Device name
NText {
text: modelData.name || modelData.deviceName
font.pointSize: Style.fontSizeM * scaling
pointSize: Style.fontSizeM * scaling
font.weight: Style.fontWeightMedium
elide: Text.ElideRight
color: getContentColor(Color.mOnSurface)
@@ -90,7 +90,7 @@ ColumnLayout {
NText {
text: BluetoothService.getStatusString(modelData)
visible: text !== ""
font.pointSize: Style.fontSizeXS * scaling
pointSize: Style.fontSizeXS * scaling
color: getContentColor(Color.mOnSurfaceVariant)
}
@@ -103,21 +103,21 @@ ColumnLayout {
// Device signal strength - "Unknown" when not connected
NText {
text: BluetoothService.getSignalStrength(modelData)
font.pointSize: Style.fontSizeXS * scaling
pointSize: Style.fontSizeXS * scaling
color: getContentColor(Color.mOnSurfaceVariant)
}
NIcon {
visible: modelData.signalStrength > 0 && !modelData.pairing && !modelData.blocked
text: BluetoothService.getSignalIcon(modelData)
font.pointSize: Style.fontSizeXS * scaling
pointSize: Style.fontSizeXS * scaling
color: getContentColor(Color.mOnSurface)
}
NText {
visible: modelData.signalStrength > 0 && !modelData.pairing && !modelData.blocked
text: (modelData.signalStrength !== undefined && modelData.signalStrength > 0) ? modelData.signalStrength + "%" : ""
font.pointSize: Style.fontSizeXS * scaling
pointSize: Style.fontSizeXS * scaling
color: getContentColor(Color.mOnSurface)
}
}
@@ -126,7 +126,7 @@ ColumnLayout {
NText {
visible: modelData.batteryAvailable
text: BluetoothService.getBattery(modelData)
font.pointSize: Style.fontSizeXS * scaling
pointSize: Style.fontSizeXS * scaling
color: getContentColor(Color.mOnSurfaceVariant)
}
}

View File

@@ -30,13 +30,13 @@ NPanel {
NIcon {
icon: "bluetooth"
font.pointSize: Style.fontSizeXXL * scaling
pointSize: Style.fontSizeXXL * scaling
color: Color.mPrimary
}
NText {
text: I18n.tr("bluetooth.panel.title")
font.pointSize: Style.fontSizeL * scaling
pointSize: Style.fontSizeL * scaling
font.weight: Style.fontWeightBold
color: Color.mOnSurface
Layout.fillWidth: true
@@ -88,21 +88,21 @@ NPanel {
NIcon {
icon: "bluetooth-off"
font.pointSize: 64 * scaling
pointSize: 64 * scaling
color: Color.mOnSurfaceVariant
Layout.alignment: Qt.AlignHCenter
}
NText {
text: I18n.tr("bluetooth.panel.disabled")
font.pointSize: Style.fontSizeL * scaling
pointSize: Style.fontSizeL * scaling
color: Color.mOnSurfaceVariant
Layout.alignment: Qt.AlignHCenter
}
NText {
text: I18n.tr("bluetooth.panel.enable-message")
font.pointSize: Style.fontSizeS * scaling
pointSize: Style.fontSizeS * scaling
color: Color.mOnSurfaceVariant
Layout.alignment: Qt.AlignHCenter
}
@@ -186,7 +186,7 @@ NPanel {
NIcon {
icon: "refresh"
font.pointSize: Style.fontSizeXXL * 1.5 * scaling
pointSize: Style.fontSizeXXL * 1.5 * scaling
color: Color.mPrimary
RotationAnimation on rotation {
@@ -200,14 +200,14 @@ NPanel {
NText {
text: I18n.tr("bluetooth.panel.scanning")
font.pointSize: Style.fontSizeL * scaling
pointSize: Style.fontSizeL * scaling
color: Color.mOnSurface
}
}
NText {
text: I18n.tr("bluetooth.panel.pairing-mode")
font.pointSize: Style.fontSizeM * scaling
pointSize: Style.fontSizeM * scaling
color: Color.mOnSurfaceVariant
Layout.alignment: Qt.AlignHCenter
}

View File

@@ -43,7 +43,7 @@ NPanel {
text: grid.title
Layout.fillWidth: true
horizontalAlignment: Text.AlignHCenter
font.pointSize: Style.fontSizeM * scaling
pointSize: Style.fontSizeM * scaling
font.weight: Style.fontWeightBold
color: Color.mPrimary
}
@@ -83,7 +83,7 @@ NPanel {
anchors.centerIn: parent
text: I18n.tr("calendar.panel.week")
color: Color.mOutline
font.pointSize: Style.fontSizeXS * scaling
pointSize: Style.fontSizeXS * scaling
font.weight: Style.fontWeightRegular
horizontalAlignment: Text.AlignHCenter
}
@@ -113,7 +113,7 @@ NPanel {
return Qt.locale().dayName(dayIndex, Locale.ShortFormat)
}
color: Color.mSecondary
font.pointSize: Style.fontSizeM * scaling
pointSize: Style.fontSizeM * scaling
font.weight: Style.fontWeightBold
horizontalAlignment: Text.AlignHCenter
}
@@ -148,7 +148,7 @@ NPanel {
NText {
anchors.centerIn: parent
color: Color.mOutline
font.pointSize: Style.fontSizeXS * scaling
pointSize: Style.fontSizeXS * scaling
font.weight: Style.fontWeightBold
text: {
// Calculate the date shown in the first column of this row
@@ -229,7 +229,7 @@ NPanel {
text: model.day
color: model.today ? Color.mOnPrimary : Color.mOnSurface
opacity: model.month === grid.month ? Style.opacityHeavy : Style.opacityLight
font.pointSize: Style.fontSizeM * scaling
pointSize: Style.fontSizeM * scaling
font.weight: model.today ? Style.fontWeightBold : Style.fontWeightRegular
}

View File

@@ -46,6 +46,13 @@ Item {
width: pillHeight + Math.max(0, pill.width - pillOverlap)
height: pillHeight
Connections {
target: root
function onTooltipTextChanged() {
TooltipService.updateText(root.tooltipText)
}
}
Rectangle {
id: pill
width: revealed ? pillMaxWidth : 1
@@ -77,8 +84,8 @@ Item {
return centerX + offset
}
text: root.text + root.suffix
font.family: Settings.data.ui.fontFixed
font.pointSize: textSize
family: Settings.data.ui.fontFixed
pointSize: textSize
font.weight: Style.fontWeightBold
color: forceOpen ? Color.mOnSurface : Color.mPrimary
visible: revealed
@@ -119,7 +126,7 @@ Item {
NIcon {
icon: root.icon
font.pointSize: iconSize
pointSize: iconSize
color: hovered ? Color.mOnTertiary : Color.mOnSurface
// Center horizontally
x: (iconCircle.width - width) / 2
@@ -195,14 +202,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 +219,7 @@ Item {
onEntered: {
hovered = true
root.entered()
tooltip.show()
TooltipService.show(pill, root.tooltipText, BarService.getTooltipDirection(), Style.tooltipDelayLong)
if (disableOpen || forceClose) {
return
}
@@ -234,7 +233,7 @@ Item {
if (!forceOpen && !forceClose) {
hide()
}
tooltip.hide()
TooltipService.hide()
}
onClicked: function (mouse) {
if (mouse.button === Qt.LeftButton) {

View File

@@ -58,6 +58,13 @@ Item {
width: buttonSize
height: revealed ? (buttonSize + maxPillHeight - pillOverlap) : buttonSize
Connections {
target: root
function onTooltipTextChanged() {
TooltipService.updateText(root.tooltipText)
}
}
Rectangle {
id: pill
width: revealed ? maxPillWidth : 1
@@ -91,8 +98,8 @@ Item {
return offset
}
text: root.text + root.suffix
font.family: Settings.data.ui.fontFixed
font.pointSize: textSize
family: Settings.data.ui.fontFixed
pointSize: textSize
font.weight: Style.fontWeightMedium
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
@@ -144,7 +151,7 @@ Item {
NIcon {
icon: root.icon
font.pointSize: iconSize
pointSize: iconSize
color: hovered ? Color.mOnTertiary : Color.mOnSurface
// Center horizontally
x: (iconCircle.width - width) / 2
@@ -236,15 +243,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 +260,7 @@ Item {
onEntered: {
hovered = true
root.entered()
tooltip.show()
TooltipService.show(pill, root.tooltipText, BarService.getTooltipDirection(), Style.tooltipDelayLong)
if (disableOpen || forceClose) {
return
}
@@ -276,7 +274,7 @@ Item {
if (!forceOpen && !forceClose) {
hide()
}
tooltip.hide()
TooltipService.hide()
}
onClicked: function (mouse) {
if (mouse.button === Qt.LeftButton) {

View File

@@ -29,7 +29,7 @@ Item {
id: loader
anchors.fill: parent
active: Settings.isLoaded && widgetId !== ""
active: widgetId !== ""
sourceComponent: {
if (!active) {
return null

View File

@@ -160,7 +160,7 @@ PopupWindow {
Layout.fillWidth: true
color: (modelData?.enabled ?? true) ? (mouseArea.containsMouse ? Color.mOnTertiary : Color.mOnSurface) : Color.mOnSurfaceVariant
text: modelData?.text !== "" ? modelData?.text.replace(/[\n\r]+/g, ' ') : "..."
font.pointSize: Style.fontSizeS * scaling
pointSize: Style.fontSizeS * scaling
verticalAlignment: Text.AlignVCenter
wrapMode: Text.WordWrap
}
@@ -175,7 +175,7 @@ PopupWindow {
NIcon {
icon: modelData?.hasChildren ? "menu" : ""
font.pointSize: Style.fontSizeS * scaling
pointSize: Style.fontSizeS * scaling
verticalAlignment: Text.AlignVCenter
visible: modelData?.hasChildren ?? false
color: (mouseArea.containsMouse ? Color.mOnTertiary : Color.mOnSurface)

View File

@@ -35,13 +35,13 @@ NPanel {
NIcon {
icon: Settings.data.network.wifiEnabled ? "wifi" : "wifi-off"
font.pointSize: Style.fontSizeXXL * scaling
pointSize: Style.fontSizeXXL * scaling
color: Settings.data.network.wifiEnabled ? Color.mPrimary : Color.mOnSurfaceVariant
}
NText {
text: I18n.tr("wifi.panel.title")
font.pointSize: Style.fontSizeL * scaling
pointSize: Style.fontSizeL * scaling
font.weight: Style.fontWeightBold
color: Color.mOnSurface
Layout.fillWidth: true
@@ -92,14 +92,14 @@ NPanel {
NIcon {
icon: "warning"
font.pointSize: Style.fontSizeL * scaling
pointSize: Style.fontSizeL * scaling
color: Color.mError
}
NText {
text: NetworkService.lastError
color: Color.mError
font.pointSize: Style.fontSizeS * scaling
pointSize: Style.fontSizeS * scaling
wrapMode: Text.Wrap
Layout.fillWidth: true
}
@@ -130,21 +130,21 @@ NPanel {
NIcon {
icon: "wifi-off"
font.pointSize: 64 * scaling
pointSize: 64 * scaling
color: Color.mOnSurfaceVariant
Layout.alignment: Qt.AlignHCenter
}
NText {
text: I18n.tr("wifi.panel.disabled")
font.pointSize: Style.fontSizeL * scaling
pointSize: Style.fontSizeL * scaling
color: Color.mOnSurfaceVariant
Layout.alignment: Qt.AlignHCenter
}
NText {
text: I18n.tr("wifi.panel.enable-message")
font.pointSize: Style.fontSizeS * scaling
pointSize: Style.fontSizeS * scaling
color: Color.mOnSurfaceVariant
Layout.alignment: Qt.AlignHCenter
}
@@ -173,7 +173,7 @@ NPanel {
NText {
text: I18n.tr("wifi.panel.searching")
font.pointSize: Style.fontSizeNormal * scaling
pointSize: Style.fontSizeNormal * scaling
color: Color.mOnSurfaceVariant
Layout.alignment: Qt.AlignHCenter
}
@@ -242,7 +242,7 @@ NPanel {
NIcon {
icon: NetworkService.signalIcon(modelData.signal)
font.pointSize: Style.fontSizeXXL * scaling
pointSize: Style.fontSizeXXL * scaling
color: modelData.connected ? Color.mPrimary : Color.mOnSurface
}
@@ -252,7 +252,7 @@ NPanel {
NText {
text: modelData.ssid
font.pointSize: Style.fontSizeNormal * scaling
pointSize: Style.fontSizeNormal * scaling
font.weight: modelData.connected ? Style.fontWeightBold : Style.fontWeightMedium
color: Color.mOnSurface
elide: Text.ElideRight
@@ -266,19 +266,19 @@ NPanel {
text: I18n.tr("system.signal-strength", {
"signal": modelData.signal
})
font.pointSize: Style.fontSizeXXS * scaling
pointSize: Style.fontSizeXXS * scaling
color: Color.mOnSurfaceVariant
}
NText {
text: "•"
font.pointSize: Style.fontSizeXXS * scaling
pointSize: Style.fontSizeXXS * scaling
color: Color.mOnSurfaceVariant
}
NText {
text: NetworkService.isSecured(modelData.security) ? modelData.security : "Open"
font.pointSize: Style.fontSizeXXS * scaling
pointSize: Style.fontSizeXXS * scaling
color: Color.mOnSurfaceVariant
}
@@ -298,7 +298,7 @@ NPanel {
id: connectedText
anchors.centerIn: parent
text: I18n.tr("wifi.panel.connected")
font.pointSize: Style.fontSizeXXS * scaling
pointSize: Style.fontSizeXXS * scaling
color: Color.mOnPrimary
}
}
@@ -314,7 +314,7 @@ NPanel {
id: disconnectingText
anchors.centerIn: parent
text: I18n.tr("wifi.panel.disconnecting")
font.pointSize: Style.fontSizeXXS * scaling
pointSize: Style.fontSizeXXS * scaling
color: Color.mOnPrimary
}
}
@@ -330,7 +330,7 @@ NPanel {
id: forgettingText
anchors.centerIn: parent
text: I18n.tr("wifi.panel.forgetting")
font.pointSize: Style.fontSizeXXS * scaling
pointSize: Style.fontSizeXXS * scaling
color: Color.mOnPrimary
}
}
@@ -348,7 +348,7 @@ NPanel {
id: savedText
anchors.centerIn: parent
text: I18n.tr("wifi.panel.saved")
font.pointSize: Style.fontSizeXXS * scaling
pointSize: Style.fontSizeXXS * scaling
color: Color.mOnSurfaceVariant
}
}
@@ -439,6 +439,7 @@ NPanel {
anchors.verticalCenter: parent.verticalCenter
anchors.margins: Style.marginS * scaling
text: passwordInput
font.family: Settings.data.ui.fontFixed
font.pointSize: Style.fontSizeS * scaling
color: Color.mOnSurface
echoMode: TextInput.Password
@@ -456,12 +457,12 @@ NPanel {
}
}
Text {
NText {
visible: parent.text.length === 0
anchors.verticalCenter: parent.verticalCenter
text: I18n.tr("wifi.panel.enter-password")
color: Color.mOnSurfaceVariant
font.pointSize: Style.fontSizeS * scaling
pointSize: Style.fontSizeS * scaling
}
}
}
@@ -508,13 +509,13 @@ NPanel {
RowLayout {
NIcon {
icon: "trash"
font.pointSize: Style.fontSizeL * scaling
pointSize: Style.fontSizeL * scaling
color: Color.mError
}
NText {
text: I18n.tr("wifi.panel.forget-network")
font.pointSize: Style.fontSizeS * scaling
pointSize: Style.fontSizeS * scaling
color: Color.mError
Layout.fillWidth: true
}
@@ -557,14 +558,14 @@ NPanel {
NIcon {
icon: "search"
font.pointSize: 64 * scaling
pointSize: 64 * scaling
color: Color.mOnSurfaceVariant
Layout.alignment: Qt.AlignHCenter
}
NText {
text: I18n.tr("wifi.panel.no-networks")
font.pointSize: Style.fontSizeL * scaling
pointSize: Style.fontSizeL * scaling
color: Color.mOnSurfaceVariant
Layout.alignment: Qt.AlignHCenter
}

View File

@@ -108,7 +108,7 @@ Item {
id: fullTitleMetrics
visible: false
text: windowTitle
font.pointSize: Style.fontSizeS * scaling
pointSize: Style.fontSizeS * scaling
font.weight: Style.fontWeightMedium
}
@@ -239,13 +239,13 @@ Item {
property real scrollX: 0
x: scrollX
Row {
RowLayout {
spacing: 50 * scaling // Gap between text copies
NText {
id: titleText
text: windowTitle
font.pointSize: Style.fontSizeS * scaling
pointSize: Style.fontSizeS * scaling
font.weight: Style.fontWeightMedium
verticalAlignment: Text.AlignVCenter
color: Color.mOnSurface
@@ -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")) {
TooltipService.show(root, windowTitle, BarService.getTooltipDirection())
}
}
onExited: {
tooltip.hide()
TooltipService.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() {

View File

@@ -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)
}

View File

@@ -36,7 +36,8 @@ Rectangle {
// Resolve settings: try user settings or defaults from BarWidgetRegistry
readonly property bool usePrimaryColor: widgetSettings.usePrimaryColor !== undefined ? widgetSettings.usePrimaryColor : widgetMetadata.usePrimaryColor
property bool useMonospacedFont: widgetSettings.useMonospacedFont !== undefined ? widgetSettings.useMonospacedFont : widgetMetadata.useMonospacedFont
readonly property bool useCustomFont: widgetSettings.useCustomFont !== undefined ? widgetSettings.useCustomFont : widgetMetadata.useCustomFont
readonly property string customFont: widgetSettings.customFont !== undefined ? widgetSettings.customFont : widgetMetadata.customFont
readonly property string formatHorizontal: widgetSettings.formatHorizontal !== undefined ? widgetSettings.formatHorizontal : widgetMetadata.formatHorizontal
readonly property string formatVertical: widgetSettings.formatVertical !== undefined ? widgetSettings.formatVertical : widgetMetadata.formatVertical
@@ -61,12 +62,12 @@ Rectangle {
spacing: Settings.data.bar.showCapsule ? -4 * scaling : -2 * scaling
Repeater {
id: repeater
model: Qt.formatDateTime(now, formatHorizontal.trim()).split("\\n")
model: Qt.locale().toString(now, formatHorizontal.trim()).split("\\n")
NText {
visible: text !== ""
text: modelData
font.family: useMonospacedFont ? Settings.data.ui.fontFixed : Settings.data.ui.fontDefault
font.pointSize: {
family: useCustomFont && customFont ? customFont : Settings.data.ui.fontDefault
pointSize: {
if (repeater.model.length == 1) {
return Style.fontSizeS * scaling
} else {
@@ -91,12 +92,12 @@ Rectangle {
anchors.centerIn: parent
spacing: -2 * scaling
Repeater {
model: Qt.formatDateTime(now, formatVertical.trim()).split(" ")
model: Qt.locale().toString(now, formatVertical.trim()).split(" ")
delegate: NText {
visible: text !== ""
text: modelData
font.family: useMonospacedFont ? Settings.data.ui.fontFixed : Settings.data.ui.fontDefault
font.pointSize: Style.fontSizeS * scaling
family: useCustomFont && customFont ? customFont : Settings.data.ui.fontDefault
pointSize: Style.fontSizeS * scaling
font.weight: Style.fontWeightBold
color: usePrimaryColor ? Color.mPrimary : Color.mOnSurface
wrapMode: Text.WordWrap
@@ -106,14 +107,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 +115,14 @@ Rectangle {
hoverEnabled: true
onEntered: {
if (!PanelService.getPanel("calendarPanel")?.active) {
tooltip.show()
TooltipService.show(root, I18n.tr("clock.tooltip"), BarService.getTooltipDirection())
}
}
onExited: {
tooltip.hide()
TooltipService.hide()
}
onClicked: {
tooltip.hide()
TooltipService.hide()
PanelService.getPanel("calendarPanel")?.toggle(this)
}
}

View File

@@ -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)
@@ -56,7 +54,7 @@ NIconButton {
height: width
source: {
if (customIconPath !== "")
return customIconPath
return customIconPath.startsWith("file://") ? customIconPath : "file://" + customIconPath
if (useDistroLogo)
return DistroLogoService.osLogo
return ""

View File

@@ -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

View File

@@ -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

View File

@@ -42,10 +42,28 @@ Item {
// Fixed width - no expansion
readonly property real widgetWidth: Math.max(1, screen.width * 0.06)
readonly property bool hasActivePlayer: MediaService.currentPlayer !== null && getTitle() !== ""
readonly property string placeholderText: I18n.tr("bar.widget-settings.media-mini.no-active-player")
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
opacity: !autoHide || getTitle() !== "" ? 1.0 : 0
opacity: !autoHide || hasActivePlayer || (!hasActivePlayer && !autoHide) ? 1.0 : 0
Behavior on opacity {
NumberAnimation {
duration: Style.animationNormal
@@ -136,21 +154,22 @@ Item {
anchors.verticalCenter: parent.verticalCenter
spacing: Style.marginS * scaling
visible: (barPosition === "top" || barPosition === "bottom") && getTitle() !== ""
visible: (barPosition === "top" || barPosition === "bottom")
z: 1 // Above the visualizer
NIcon {
id: windowIcon
icon: MediaService.isPlaying ? "media-pause" : "media-play"
font.pointSize: Style.fontSizeL * scaling
icon: hasActivePlayer ? (MediaService.isPlaying ? "media-pause" : "media-play") : "disc"
color: hasActivePlayer ? Color.mOnSurface : Color.mOnSurfaceVariant
pointSize: Style.fontSizeL * scaling
verticalAlignment: Text.AlignVCenter
Layout.alignment: Qt.AlignVCenter
visible: !showAlbumArt && getTitle() !== "" && !trackArt.visible
visible: !hasActivePlayer || (!showAlbumArt && !trackArt.visible)
}
ColumnLayout {
Layout.alignment: Qt.AlignVCenter
visible: showAlbumArt
visible: showAlbumArt && hasActivePlayer
spacing: 0
Item {
@@ -172,6 +191,10 @@ Item {
Item {
id: titleContainer
Layout.preferredWidth: {
if (!hasActivePlayer) {
// When no active player, take full width to center the placeholder
return mainContainer.width - Style.marginXXS * scaling * 2
}
// Calculate available width based on other elements in the row
var iconWidth = (windowIcon.visible ? (Style.fontSizeL * scaling + Style.marginS * scaling) : 0)
var albumArtWidth = (showAlbumArt ? (18 * scaling + Style.marginS * scaling) : 0)
@@ -180,7 +203,7 @@ Item {
return Math.max(20 * scaling, availableWidth)
}
Layout.maximumWidth: Layout.preferredWidth
Layout.alignment: Qt.AlignVCenter
Layout.alignment: hasActivePlayer ? Qt.AlignVCenter : Qt.AlignCenter
Layout.preferredHeight: titleText.height
clip: true
@@ -188,7 +211,7 @@ Item {
property bool isScrolling: false
property bool isResetting: false
property real textWidth: fullTitleMetrics.contentWidth
property real containerWidth: width
property real containerWidth: 0
property bool needsScrolling: textWidth > containerWidth
// Timer for "always" mode with delay
@@ -235,8 +258,15 @@ Item {
}
}
onWidthChanged: updateScrollingState()
Component.onCompleted: updateScrollingState()
onWidthChanged: {
containerWidth = width
updateScrollingState()
}
Component.onCompleted: {
containerWidth = width
updateScrollingState()
}
Connections {
target: mouseArea
@@ -249,29 +279,31 @@ Item {
Item {
id: scrollContainer
height: parent.height
width: childrenRect.width
width: parent.width
property real scrollX: 0
x: scrollX
x: hasActivePlayer ? scrollX : 0
Row {
RowLayout {
spacing: 50 * scaling // Gap between text copies
NText {
id: titleText
text: getTitle()
font.pointSize: Style.fontSizeS * scaling
text: hasActivePlayer ? getTitle() : placeholderText
pointSize: Style.fontSizeS * scaling
font.weight: Style.fontWeightMedium
verticalAlignment: Text.AlignVCenter
color: Color.mOnSurface
horizontalAlignment: hasActivePlayer ? Text.AlignLeft : Text.AlignHCenter
color: hasActivePlayer ? Color.mOnSurface : Color.mOnSurfaceVariant
}
NText {
text: getTitle()
text: hasActivePlayer ? getTitle() : placeholderText
font: titleText.font
verticalAlignment: Text.AlignVCenter
color: Color.mOnSurface
visible: titleContainer.needsScrolling && titleContainer.isScrolling
horizontalAlignment: hasActivePlayer ? Text.AlignLeft : Text.AlignHCenter
color: hasActivePlayer ? Color.mOnSurface : Color.mOnSurfaceVariant
visible: hasActivePlayer && titleContainer.needsScrolling && titleContainer.isScrolling
}
}
@@ -321,13 +353,13 @@ Item {
width: Style.baseWidgetSize * 0.5 * scaling
height: Style.baseWidgetSize * 0.5 * scaling
anchors.centerIn: parent
visible: getTitle() !== ""
NIcon {
id: mediaIconVertical
anchors.fill: parent
icon: MediaService.isPlaying ? "media-pause" : "media-play"
font.pointSize: Style.fontSizeL * scaling
icon: hasActivePlayer ? (MediaService.isPlaying ? "media-pause" : "media-play") : "disc"
color: hasActivePlayer ? Color.mOnSurface : Color.mOnSurfaceVariant
pointSize: Style.fontSizeL * scaling
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
}
@@ -339,10 +371,10 @@ Item {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
cursorShape: hasActivePlayer ? Qt.PointingHandCursor : Qt.ArrowCursor
acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton
onClicked: mouse => {
if (!MediaService.currentPlayer || !MediaService.canPlay) {
if (!hasActivePlayer || !MediaService.currentPlayer || !MediaService.canPlay) {
return
}
@@ -360,39 +392,15 @@ Item {
}
onEntered: {
if ((tooltip.text !== "") && (barPosition === "left" || barPosition === "right")) {
tooltip.show()
} else if ((tooltip.text !== "") && (scrollingMode === "never")) {
tooltip.show()
var textToShow = hasActivePlayer ? tooltipText : placeholderText
if ((textToShow !== "") && (barPosition === "left" || barPosition === "right") || (scrollingMode === "never")) {
TooltipService.show(root, textToShow, BarService.getTooltipDirection())
}
}
onExited: {
tooltip.hide()
TooltipService.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
}
}

View File

@@ -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) {

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -53,7 +53,7 @@ Rectangle {
id: percentMetrics
font.family: Settings.data.ui.fontFixed
font.weight: Style.fontWeightMedium
font.pointSize: textSize
font.pointSize: textSize * Settings.data.ui.fontFixedScale
text: "99%" // Use the longest possible string for measurement
}
@@ -61,7 +61,7 @@ Rectangle {
id: tempMetrics
font.family: Settings.data.ui.fontFixed
font.weight: Style.fontWeightMedium
font.pointSize: textSize
font.pointSize: textSize * Settings.data.ui.fontFixedScale
text: "99°" // Use the longest possible string for measurement
}
@@ -69,7 +69,7 @@ Rectangle {
id: memMetrics
font.family: Settings.data.ui.fontFixed
font.weight: Style.fontWeightMedium
font.pointSize: textSize
font.pointSize: textSize * Settings.data.ui.fontFixedScale
text: "99.9K" // Longest value part of network speed
}
@@ -106,7 +106,7 @@ Rectangle {
NIcon {
icon: "cpu-usage"
font.pointSize: iconSize
pointSize: iconSize
Layout.alignment: Qt.AlignCenter
Layout.row: isVertical ? 1 : 0
Layout.column: 0
@@ -114,8 +114,8 @@ Rectangle {
NText {
text: `${Math.round(SystemStatService.cpuUsage)}%`
font.family: Settings.data.ui.fontFixed
font.pointSize: textSize
family: Settings.data.ui.fontFixed
pointSize: textSize
font.weight: Style.fontWeightMedium
Layout.alignment: Qt.AlignCenter
Layout.preferredWidth: isVertical ? -1 : percentTextWidth
@@ -147,7 +147,7 @@ Rectangle {
NIcon {
icon: "cpu-temperature"
font.pointSize: iconSize
pointSize: iconSize
Layout.alignment: Qt.AlignCenter
Layout.row: isVertical ? 1 : 0
Layout.column: 0
@@ -155,8 +155,8 @@ Rectangle {
NText {
text: `${Math.round(SystemStatService.cpuTemp)}°`
font.family: Settings.data.ui.fontFixed
font.pointSize: textSize
family: Settings.data.ui.fontFixed
pointSize: textSize
font.weight: Style.fontWeightMedium
Layout.alignment: Qt.AlignCenter
Layout.preferredWidth: isVertical ? -1 : tempTextWidth
@@ -188,7 +188,7 @@ Rectangle {
NIcon {
icon: "memory"
font.pointSize: iconSize
pointSize: iconSize
Layout.alignment: Qt.AlignCenter
Layout.row: isVertical ? 1 : 0
Layout.column: 0
@@ -196,8 +196,8 @@ Rectangle {
NText {
text: showMemoryAsPercent ? `${Math.round(SystemStatService.memPercent)}%` : `${SystemStatService.memGb.toFixed(1)}G`
font.family: Settings.data.ui.fontFixed
font.pointSize: textSize
family: Settings.data.ui.fontFixed
pointSize: textSize
font.weight: Style.fontWeightMedium
Layout.alignment: Qt.AlignCenter
Layout.preferredWidth: isVertical ? -1 : (showMemoryAsPercent ? percentTextWidth : memTextWidth)
@@ -229,7 +229,7 @@ Rectangle {
NIcon {
icon: "download-speed"
font.pointSize: iconSize
pointSize: iconSize
Layout.alignment: Qt.AlignCenter
Layout.row: isVertical ? 1 : 0
Layout.column: 0
@@ -237,8 +237,8 @@ Rectangle {
NText {
text: isVertical ? SystemStatService.formatCompactSpeed(SystemStatService.rxSpeed) : SystemStatService.formatSpeed(SystemStatService.rxSpeed)
font.family: Settings.data.ui.fontFixed
font.pointSize: textSize
family: Settings.data.ui.fontFixed
pointSize: textSize
font.weight: Style.fontWeightMedium
Layout.alignment: Qt.AlignCenter
Layout.preferredWidth: isVertical ? -1 : memTextWidth
@@ -270,7 +270,7 @@ Rectangle {
NIcon {
icon: "upload-speed"
font.pointSize: iconSize
pointSize: iconSize
Layout.alignment: Qt.AlignCenter
Layout.row: isVertical ? 1 : 0
Layout.column: 0
@@ -278,8 +278,8 @@ Rectangle {
NText {
text: isVertical ? SystemStatService.formatCompactSpeed(SystemStatService.txSpeed) : SystemStatService.formatSpeed(SystemStatService.txSpeed)
font.family: Settings.data.ui.fontFixed
font.pointSize: textSize
family: Settings.data.ui.fontFixed
pointSize: textSize
font.weight: Style.fontWeightMedium
Layout.alignment: Qt.AlignCenter
Layout.preferredWidth: isVertical ? -1 : memTextWidth
@@ -311,7 +311,7 @@ Rectangle {
NIcon {
icon: "storage"
font.pointSize: iconSize
pointSize: iconSize
Layout.alignment: Qt.AlignCenter
Layout.row: isVertical ? 1 : 0
Layout.column: 0
@@ -319,8 +319,8 @@ Rectangle {
NText {
text: `${SystemStatService.diskPercent}%`
font.family: Settings.data.ui.fontFixed
font.pointSize: textSize
family: Settings.data.ui.fontFixed
pointSize: textSize
font.weight: Style.fontWeightMedium
Layout.alignment: Qt.AlignCenter
Layout.preferredWidth: isVertical ? -1 : percentTextWidth

View File

@@ -13,10 +13,27 @@ Rectangle {
property ShellScreen screen
property real scaling: 1.0
// Widget properties passed from Bar.qml for per-instance settings
property string widgetId: ""
property string section: ""
property int sectionWidgetIndex: -1
property int sectionWidgetsCount: 0
readonly property bool isVerticalBar: Settings.data.bar.position === "left" || Settings.data.bar.position === "right"
readonly property bool compact: (Settings.data.bar.density === "compact")
readonly property real itemSize: compact ? Style.capsuleHeight * 0.9 * scaling : Style.capsuleHeight * 0.8 * scaling
property var widgetMetadata: BarWidgetRegistry.widgetMetadata[widgetId]
property var widgetSettings: {
if (section && sectionWidgetIndex >= 0) {
var widgets = Settings.data.bar.widgets[section]
if (widgets && sectionWidgetIndex < widgets.length) {
return widgets[sectionWidgetIndex]
}
}
return {}
}
// Always visible when there are toplevels
implicitWidth: isVerticalBar ? Math.round(Style.capsuleHeight * scaling) : taskbarLayout.implicitWidth + Style.marginM * scaling * 2
implicitHeight: isVerticalBar ? taskbarLayout.implicitHeight + Style.marginM * scaling * 2 : Math.round(Style.capsuleHeight * scaling)
@@ -41,36 +58,36 @@ Rectangle {
columnSpacing: isVerticalBar ? 0 : Style.marginXXS * root.scaling
Repeater {
model: ToplevelManager && ToplevelManager.toplevels ? ToplevelManager.toplevels : []
model: CompositorService.windows
delegate: Item {
id: taskbarItem
required property Toplevel modelData
property Toplevel toplevel: modelData
property bool isActive: ToplevelManager.activeToplevel === modelData
required property var modelData
visible: (!widgetSettings.onlySameOutput || modelData.output == screen.name) && (!widgetSettings.onlyActiveWorkspaces || CompositorService.getActiveWorkspaces().map(ws => ws.id).includes(modelData.workspaceId))
Layout.preferredWidth: root.itemSize
Layout.preferredHeight: root.itemSize
Layout.alignment: Qt.AlignCenter
Rectangle {
id: iconBackground
anchors.centerIn: parent
IconImage {
id: appIcon
width: parent.width
height: parent.height
color: taskbarItem.isActive ? Color.mPrimary : root.color
border.width: 0
radius: Math.round(Style.radiusXS * root.scaling)
border.color: "transparent"
z: -1
source: ThemeIcons.iconForAppId(taskbarItem.modelData.appId)
smooth: true
asynchronous: true
opacity: modelData.isFocused ? Style.opacityFull : 0.6
IconImage {
id: appIcon
anchors.centerIn: parent
width: parent.width
height: parent.height
source: ThemeIcons.iconForAppId(taskbarItem.modelData.appId)
smooth: true
asynchronous: true
Rectangle {
anchors.bottomMargin: -2 * scaling
anchors.bottom: parent.bottom
anchors.horizontalCenter: parent.horizontalCenter
id: iconBackground
width: 4 * scaling
height: 4 * scaling
color: modelData.isFocused ? Color.mPrimary : Color.transparent
radius: width * 0.5
}
}
@@ -86,29 +103,20 @@ Rectangle {
if (mouse.button === Qt.LeftButton) {
try {
taskbarItem.modelData.activate()
CompositorService.focusWindow(taskbarItem.modelData.id)
} catch (error) {
Logger.error("Taskbar", "Failed to activate toplevel: " + error)
}
} else if (mouse.button === Qt.RightButton) {
try {
taskbarItem.modelData.close()
CompositorService.closeWindow(taskbarItem.modelData.id)
} catch (error) {
Logger.error("Taskbar", "Failed to close toplevel: " + error)
}
}
}
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: TooltipService.show(taskbarItem, taskbarItem.modelData.title || taskbarItem.modelData.appId || "Unknown app.", BarService.getTooltipDirection())
onExited: TooltipService.hide()
}
}
}

View File

@@ -102,7 +102,7 @@ Rectangle {
modelData.secondaryActivate && modelData.secondaryActivate()
} else if (mouse.button === Qt.RightButton) {
trayTooltip.hide()
TooltipService.hideImmediately()
// Close the menu if it was visible
if (trayPanel && trayPanel.visible) {
@@ -135,15 +135,11 @@ 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: {
trayPanel.close()
TooltipService.show(trayIcon, modelData.tooltipTitle || modelData.name || modelData.id || "Tray Item", BarService.getTooltipDirection())
}
onExited: TooltipService.hide()
}
}
}

View File

@@ -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

View File

@@ -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)
}

View File

@@ -7,6 +7,7 @@ import Quickshell
import Quickshell.Io
import qs.Commons
import qs.Services
import qs.Widgets
Item {
id: root
@@ -274,7 +275,7 @@ Item {
Loader {
active: (labelMode !== "none")
sourceComponent: Component {
Text {
NText {
x: (pill.width - width) / 2
y: (pill.height - height) / 2 + (height - contentHeight) / 2
text: {
@@ -284,9 +285,9 @@ Item {
return model.idx.toString()
}
}
font.pointSize: model.isFocused ? workspacePillContainer.height * 0.45 : workspacePillContainer.height * 0.42
family: Settings.data.ui.fontFixed
pointSize: model.isFocused ? workspacePillContainer.height * 0.45 : workspacePillContainer.height * 0.42
font.capitalization: Font.AllUppercase
font.family: Settings.data.ui.fontFixed
font.weight: Style.fontWeightBold
wrapMode: Text.Wrap
color: {
@@ -418,7 +419,7 @@ Item {
Loader {
active: (labelMode !== "none")
sourceComponent: Component {
Text {
NText {
x: (pillVertical.width - width) / 2
y: (pillVertical.height - height) / 2 + (height - contentHeight) / 2
text: {
@@ -428,9 +429,9 @@ Item {
return model.idx.toString()
}
}
font.pointSize: model.isFocused ? workspacePillContainerVertical.width * 0.45 : workspacePillContainerVertical.width * 0.42
family: Settings.data.ui.fontFixed
pointSize: model.isFocused ? workspacePillContainerVertical.width * 0.45 : workspacePillContainerVertical.width * 0.42
font.capitalization: Font.AllUppercase
font.family: Settings.data.ui.fontFixed
font.weight: Style.fontWeightBold
wrapMode: Text.Wrap
color: {

View File

@@ -28,7 +28,7 @@ NBox {
NIcon {
icon: "disc"
font.pointSize: Style.fontSizeXXXL * 3 * scaling
pointSize: Style.fontSizeXXXL * 3 * scaling
color: Color.mPrimary
Layout.alignment: Qt.AlignHCenter
}
@@ -68,13 +68,13 @@ NBox {
NIcon {
icon: "caret-down"
font.pointSize: Style.fontSizeXXL * scaling
pointSize: Style.fontSizeXXL * scaling
color: Color.mOnSurfaceVariant
}
NText {
text: playerSelectorButton.currentPlayer ? playerSelectorButton.currentPlayer.identity : ""
font.pointSize: Style.fontSizeXS * scaling
pointSize: Style.fontSizeXS * scaling
color: Color.mOnSurfaceVariant
Layout.fillWidth: true
}
@@ -147,7 +147,7 @@ NBox {
NIcon {
icon: "disc"
color: Color.mPrimary
font.pointSize: Style.fontSizeXXXL * 3 * scaling
pointSize: Style.fontSizeXXXL * 3 * scaling
visible: !trackArt.visible
anchors.centerIn: parent
}
@@ -162,7 +162,7 @@ NBox {
NText {
visible: MediaService.trackTitle !== ""
text: MediaService.trackTitle
font.pointSize: Style.fontSizeM * scaling
pointSize: Style.fontSizeM * scaling
font.weight: Style.fontWeightBold
elide: Text.ElideRight
wrapMode: Text.Wrap
@@ -174,7 +174,7 @@ NBox {
visible: MediaService.trackArtist !== ""
text: MediaService.trackArtist
color: Color.mPrimary
font.pointSize: Style.fontSizeXS * scaling
pointSize: Style.fontSizeXS * scaling
elide: Text.ElideRight
Layout.fillWidth: true
}
@@ -183,7 +183,7 @@ NBox {
visible: MediaService.trackAlbum !== ""
text: MediaService.trackAlbum
color: Color.mOnSurface
font.pointSize: Style.fontSizeXS * scaling
pointSize: Style.fontSizeXS * scaling
elide: Text.ElideRight
Layout.fillWidth: true
}

View File

@@ -45,7 +45,7 @@ NBox {
text: I18n.tr("system.uptime", {
"uptime": uptimeText
})
font.pointSize: Style.fontSizeS * scaling
pointSize: Style.fontSizeS * scaling
color: Color.mOnSurfaceVariant
}
}

View File

@@ -24,7 +24,7 @@ NBox {
NIcon {
Layout.alignment: Qt.AlignVCenter
icon: weatherReady ? LocationService.weatherSymbolFromCode(LocationService.data.weather.current_weather.weathercode) : ""
font.pointSize: Style.fontSizeXXXL * 1.75 * scaling
pointSize: Style.fontSizeXXXL * 1.75 * scaling
color: Color.mPrimary
}
@@ -36,7 +36,7 @@ NBox {
const chunks = Settings.data.location.name.split(",")
return chunks[0]
}
font.pointSize: Style.fontSizeL * scaling
pointSize: Style.fontSizeL * scaling
font.weight: Style.fontWeightBold
}
@@ -56,13 +56,13 @@ NBox {
temp = Math.round(temp)
return `${temp}°${suffix}`
}
font.pointSize: Style.fontSizeXL * scaling
pointSize: Style.fontSizeXL * scaling
font.weight: Style.fontWeightBold
}
NText {
text: weatherReady ? `(${LocationService.data.weather.timezone_abbreviation})` : ""
font.pointSize: Style.fontSizeXS * scaling
pointSize: Style.fontSizeXS * scaling
color: Color.mOnSurfaceVariant
visible: LocationService.data.weather
}
@@ -88,7 +88,7 @@ NBox {
NText {
text: {
var weatherDate = new Date(LocationService.data.weather.daily.time[index].replace(/-/g, "/"))
return Qt.formatDateTime(weatherDate, "ddd")
return Qt.locale().toString(weatherDate, "ddd")
}
color: Color.mOnSurface
Layout.alignment: Qt.AlignHCenter
@@ -96,7 +96,7 @@ NBox {
NIcon {
Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter
icon: LocationService.weatherSymbolFromCode(LocationService.data.weather.daily.weathercode[index])
font.pointSize: Style.fontSizeXXL * 1.6 * scaling
pointSize: Style.fontSizeXXL * 1.6 * scaling
color: Color.mPrimary
}
NText {
@@ -112,7 +112,7 @@ NBox {
min = Math.round(min)
return `${max}°/${min}°`
}
font.pointSize: Style.fontSizeXS * scaling
pointSize: Style.fontSizeXS * scaling
color: Color.mOnSurfaceVariant
}
}

View File

@@ -14,8 +14,19 @@ Variants {
delegate: Item {
id: root
required property ShellScreen modelData
property real scaling: ScalingService.getScreenScale(modelData)
property bool barIsReady: BarService.isBarReady(modelData.name)
Connections {
target: BarService
function onBarReadyChanged(screenName) {
if (screenName === modelData.name) {
barIsReady = true
}
}
}
Connections {
target: ScalingService
@@ -40,19 +51,14 @@ Variants {
function onPinnedAppsChanged() {
updateDockApps()
}
}
// Initial update when component is ready
Component.onCompleted: {
if (Settings.isLoaded && ToplevelManager) {
function onOnlySameOutputChanged() {
updateDockApps()
}
}
// Update when Settings are loaded
Connections {
target: Settings
function onSettingsLoaded() {
// Initial update when component is ready
Component.onCompleted: {
if (ToplevelManager) {
updateDockApps()
}
}
@@ -105,7 +111,7 @@ Variants {
// Strategy: Maintain app positions as much as possible
// 1. First pass: Add all running apps (both pinned and non-pinned) in their current order
runningApps.forEach(toplevel => {
if (toplevel && toplevel.appId) {
if (toplevel && toplevel.appId && !(Settings.data.dock.onlySameOutput && toplevel.screens && !toplevel.screens.includes(modelData))) {
const isPinned = pinnedApps.includes(toplevel.appId)
const appType = isPinned ? "pinned-running" : "running"
@@ -187,7 +193,7 @@ Variants {
// PEEK WINDOW - Always visible when auto-hide is enabled
Loader {
active: Settings.isLoaded && modelData && Settings.data.dock.monitors.includes(modelData.name) && autoHide
active: (barIsReady || !hasBar) && modelData && Settings.data.dock.monitors.includes(modelData.name) && autoHide
sourceComponent: PanelWindow {
id: peekWindow
@@ -233,7 +239,7 @@ Variants {
// DOCK WINDOW
Loader {
active: Settings.isLoaded && modelData && Settings.data.dock.monitors.includes(modelData.name) && dockLoaded && ToplevelManager && (dockApps.length > 0)
active: (barIsReady || !hasBar) && modelData && Settings.data.dock.monitors.includes(modelData.name) && dockLoaded && ToplevelManager && (dockApps.length > 0)
sourceComponent: PanelWindow {
id: dockWindow
@@ -373,14 +379,6 @@ Variants {
}
}
// Individual tooltip for this app
NTooltip {
id: appTooltip
target: appButton
positionAbove: true
visible: false
}
Image {
id: appIcon
width: iconSize
@@ -422,7 +420,7 @@ Variants {
anchors.centerIn: parent
visible: !appIcon.visible
icon: "question-mark"
font.pointSize: iconSize * 0.7
pointSize: iconSize * 0.7
color: appButton.isActive ? Color.mPrimary : Color.mOnSurfaceVariant
opacity: appButton.isRunning ? 1.0 : 0.6
scale: appButton.hovered ? 1.15 : 1.0
@@ -481,8 +479,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
TooltipService.show(appButton, tooltipText, "top")
if (autoHide) {
showTimer.stop()
hideTimer.stop()
@@ -492,7 +490,7 @@ Variants {
onExited: {
anyAppHovered = false
appTooltip.hide()
TooltipService.hide()
if (autoHide && !dockHovered && !peekHovered && !menuHovered) {
hideTimer.restart()
}
@@ -508,7 +506,7 @@ Variants {
// Close any other existing context menu first
root.closeAllContextMenus()
// Hide tooltip when showing context menu
appTooltip.hide()
TooltipService.hide()
contextMenu.show(appButton, modelData.toplevel || modelData)
return
}

View File

@@ -14,9 +14,12 @@ PopupWindow {
property var toplevel: null
property Item anchorItem: null
property real scaling: 1.0
property bool hovered: menuMouseArea.containsMouse || activateMouseArea.containsMouse || pinMouseArea.containsMouse || closeMouseArea.containsMouse
property bool hovered: menuMouseArea.containsMouse
property var onAppClosed: null // Callback function for when an app is closed
// Track which menu item is hovered
property int hoveredItem: -1 // -1: none, 0: focus, 1: pin, 2: close
signal requestClose
implicitWidth: 140 * scaling
@@ -70,25 +73,64 @@ PopupWindow {
visible = false
}
// Close menu when clicking on background, track hover for the whole menu area
MouseArea {
id: menuMouseArea
anchors.fill: parent
hoverEnabled: true
acceptedButtons: Qt.LeftButton | Qt.RightButton
onClicked: function (mouse) {
if (mouse.button === Qt.RightButton) {
root.hide() // Close on right-click
} else {
root.hide() // Close when clicking on the background (outside menu content)
// Helper function to determine which menu item is under the mouse
function getHoveredItem(mouseY) {
const itemHeight = 32 * scaling
const startY = Style.marginM * scaling
const relativeY = mouseY - startY
if (relativeY < 0)
return -1
const itemIndex = Math.floor(relativeY / itemHeight)
return itemIndex >= 0 && itemIndex < 3 ? itemIndex : -1
}
// Handle menu item clicks
function handleItemClick(itemIndex) {
switch (itemIndex) {
case 0:
// Focus
if (root.toplevel?.activate) {
root.toplevel.activate()
}
root.requestClose()
break
case 1:
// Pin/Unpin
if (root.toplevel?.appId) {
root.toggleAppPin(root.toplevel.appId)
}
root.requestClose()
break
case 2:
// Close
// Check if toplevel is still valid before trying to close it
const isValidToplevel = root.toplevel && ToplevelManager && ToplevelManager.toplevels.values.includes(root.toplevel)
if (isValidToplevel && root.toplevel.close) {
root.toplevel.close()
// Trigger immediate dock update callback if provided
if (root.onAppClosed && typeof root.onAppClosed === "function") {
Qt.callLater(root.onAppClosed)
}
} else {
Logger.warn("DockMenu", "Cannot close app - invalid toplevel reference")
}
root.hide()
root.requestClose()
break
}
}
Shortcut {
sequences: ["Escape"]
enabled: root.visible
onActivated: root.hide()
Timer {
id: closeTimer
interval: 500
repeat: false
running: false
onTriggered: {
root.hide()
}
}
Rectangle {
@@ -98,15 +140,35 @@ PopupWindow {
border.color: Color.mOutline
border.width: Math.max(1, Style.borderS * scaling)
// Prevent clicks inside the menu from closing it
// Single MouseArea to handle both auto-close and menu interactions
MouseArea {
id: menuMouseArea
anchors.fill: parent
onClicked: {
hoverEnabled: true
cursorShape: root.hoveredItem >= 0 ? Qt.PointingHandCursor : Qt.ArrowCursor
} // Do nothing, just consume the click
onEntered: {
closeTimer.stop()
}
onExited: {
root.hoveredItem = -1
closeTimer.start()
}
onPositionChanged: mouse => {
root.hoveredItem = root.getHoveredItem(mouse.y)
}
onClicked: mouse => {
const clickedItem = root.getHoveredItem(mouse.y)
if (clickedItem >= 0) {
root.handleItemClick(clickedItem)
}
}
}
Column {
ColumnLayout {
id: contextMenuColumn
anchors.fill: parent
anchors.margins: Style.marginM * scaling
@@ -114,12 +176,12 @@ PopupWindow {
// Focus item
Rectangle {
width: parent.width
Layout.fillWidth: true
height: 32 * scaling
color: activateMouseArea.containsMouse ? Color.mTertiary : Color.transparent
color: root.hoveredItem === 0 ? Color.mTertiary : Color.transparent
radius: Style.radiusXS * scaling
Row {
RowLayout {
anchors.left: parent.left
anchors.leftMargin: Style.marginS * scaling
anchors.verticalCenter: parent.verticalCenter
@@ -127,42 +189,28 @@ PopupWindow {
NIcon {
icon: "eye"
font.pointSize: Style.fontSizeL * scaling
color: activateMouseArea.containsMouse ? Color.mOnTertiary : Color.mOnSurfaceVariant
anchors.verticalCenter: parent.verticalCenter
pointSize: Style.fontSizeL * scaling
color: root.hoveredItem === 0 ? Color.mOnTertiary : Color.mOnSurfaceVariant
Layout.alignment: Qt.AlignVCenter
}
NText {
text: I18n.tr("dock.menu.focus")
font.pointSize: Style.fontSizeS * scaling
color: activateMouseArea.containsMouse ? Color.mOnTertiary : Color.mOnSurfaceVariant
anchors.verticalCenter: parent.verticalCenter
}
}
MouseArea {
id: activateMouseArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
if (root.toplevel?.activate) {
root.toplevel.activate()
}
root.requestClose()
pointSize: Style.fontSizeS * scaling
color: root.hoveredItem === 0 ? Color.mOnTertiary : Color.mOnSurfaceVariant
Layout.alignment: Qt.AlignVCenter
}
}
}
// Pin/Unpin item
Rectangle {
width: parent.width
Layout.fillWidth: true
height: 32 * scaling
color: pinMouseArea.containsMouse ? Color.mTertiary : Color.transparent
color: root.hoveredItem === 1 ? Color.mTertiary : Color.transparent
radius: Style.radiusXS * scaling
Row {
RowLayout {
anchors.left: parent.left
anchors.leftMargin: Style.marginS * scaling
anchors.verticalCenter: parent.verticalCenter
@@ -174,9 +222,9 @@ PopupWindow {
return "pin"
return root.isAppPinned(root.toplevel.appId) ? "unpin" : "pin"
}
font.pointSize: Style.fontSizeL * scaling
color: pinMouseArea.containsMouse ? Color.mOnTertiary : Color.mOnSurfaceVariant
anchors.verticalCenter: parent.verticalCenter
pointSize: Style.fontSizeL * scaling
color: root.hoveredItem === 1 ? Color.mOnTertiary : Color.mOnSurfaceVariant
Layout.alignment: Qt.AlignVCenter
}
NText {
@@ -185,36 +233,21 @@ PopupWindow {
return I18n.tr("dock.menu.pin")
return root.isAppPinned(root.toplevel.appId) ? I18n.tr("dock.menu.unpin") : I18n.tr("dock.menu.pin")
}
font.pointSize: Style.fontSizeS * scaling
color: pinMouseArea.containsMouse ? Color.mOnTertiary : Color.mOnSurfaceVariant
anchors.verticalCenter: parent.verticalCenter
}
}
MouseArea {
id: pinMouseArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
if (root.toplevel?.appId) {
root.toggleAppPin(root.toplevel.appId)
}
//root.hide()
root.requestClose()
pointSize: Style.fontSizeS * scaling
color: root.hoveredItem === 1 ? Color.mOnTertiary : Color.mOnSurfaceVariant
Layout.alignment: Qt.AlignVCenter
}
}
}
// Close item
Rectangle {
width: parent.width
Layout.fillWidth: true
height: 32 * scaling
color: closeMouseArea.containsMouse ? Color.mTertiary : Color.transparent
color: root.hoveredItem === 2 ? Color.mTertiary : Color.transparent
radius: Style.radiusXS * scaling
Row {
RowLayout {
anchors.left: parent.left
anchors.leftMargin: Style.marginS * scaling
anchors.verticalCenter: parent.verticalCenter
@@ -222,40 +255,16 @@ PopupWindow {
NIcon {
icon: "close"
font.pointSize: Style.fontSizeL * scaling
color: closeMouseArea.containsMouse ? Color.mOnTertiary : Color.mOnSurfaceVariant
anchors.verticalCenter: parent.verticalCenter
pointSize: Style.fontSizeL * scaling
color: root.hoveredItem === 2 ? Color.mOnTertiary : Color.mOnSurfaceVariant
Layout.alignment: Qt.AlignVCenter
}
NText {
text: I18n.tr("dock.menu.close")
font.pointSize: Style.fontSizeS * scaling
color: closeMouseArea.containsMouse ? Color.mOnTertiary : Color.mOnSurfaceVariant
anchors.verticalCenter: parent.verticalCenter
}
}
MouseArea {
id: closeMouseArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
// Check if toplevel is still valid before trying to close it
const isValidToplevel = root.toplevel && ToplevelManager && ToplevelManager.toplevels.values.includes(root.toplevel)
if (isValidToplevel && root.toplevel.close) {
root.toplevel.close()
// Trigger immediate dock update callback if provided
if (root.onAppClosed && typeof root.onAppClosed === "function") {
Qt.callLater(root.onAppClosed)
}
} else {
Logger.warn("DockMenu", "Cannot close app - invalid toplevel reference")
}
root.hide()
root.requestClose()
pointSize: Style.fontSizeS * scaling
color: root.hoveredItem === 2 ? Color.mOnTertiary : Color.mOnSurfaceVariant
Layout.alignment: Qt.AlignVCenter
}
}
}

View File

@@ -302,11 +302,34 @@ NPanel {
positionViewAtIndex(currentIndex, ListView.Contain)
}
}
onModelChanged: {
}
delegate: Rectangle {
id: entry
property bool isSelected: mouseArea.containsMouse || (index === selectedIndex)
// Accessor for app id
property string appId: (modelData && modelData.appId) ? String(modelData.appId) : ""
// Pin helpers
function togglePin(appId) {
if (!appId)
return
let arr = (Settings.data.dock.pinnedApps || []).slice()
const idx = arr.indexOf(appId)
if (idx >= 0)
arr.splice(idx, 1)
else
arr.push(appId)
Settings.data.dock.pinnedApps = arr
}
function isPinned(appId) {
const arr = Settings.data.dock.pinnedApps || []
return appId && arr.indexOf(appId) >= 0
}
// Property to reliably track the current item's ID.
// This changes whenever the delegate is recycled for a new item.
@@ -321,7 +344,7 @@ NPanel {
}
width: resultsList.width - Style.marginS * scaling
height: entryHeight
implicitHeight: entryHeight
radius: Style.radiusM * scaling
color: entry.isSelected ? Color.mTertiary : Color.mSurface
@@ -332,136 +355,152 @@ NPanel {
}
}
RowLayout {
ColumnLayout {
id: contentLayout
anchors.fill: parent
anchors.margins: Style.marginM * scaling
spacing: Style.marginM * scaling
// Icon badge or Image preview
Rectangle {
Layout.preferredWidth: badgeSize
Layout.preferredHeight: badgeSize
radius: Style.radiusM * scaling
color: Color.mSurfaceVariant
clip: true
// Top row - Main entry content with pin button
RowLayout {
Layout.fillWidth: true
spacing: Style.marginM * scaling
// Image preview for clipboard images
NImageRounded {
id: imagePreview
anchors.fill: parent
visible: modelData.isImage
imageRadius: Style.radiusM * scaling
// This property creates a dependency on the service's revision counter
readonly property int _rev: ClipboardService.revision
// Fetches from the service's cache.
// The dependency on `_rev` ensures this binding is re-evaluated when the cache is updated.
imagePath: {
_rev
return ClipboardService.getImageData(modelData.clipboardId) || ""
}
// Loading indicator
Rectangle {
anchors.fill: parent
visible: parent.status === Image.Loading
color: Color.mSurfaceVariant
BusyIndicator {
anchors.centerIn: parent
running: true
width: Style.baseWidgetSize * 0.5 * scaling
height: width
}
}
// Error fallback
onStatusChanged: status => {
if (status === Image.Error) {
iconLoader.visible = true
imagePreview.visible = false
}
}
}
// Icon fallback
Loader {
id: iconLoader
anchors.fill: parent
anchors.margins: Style.marginXS * scaling
visible: !modelData.isImage || imagePreview.status === Image.Error
active: visible
sourceComponent: Component {
IconImage {
anchors.fill: parent
source: modelData.icon ? ThemeIcons.iconFromName(modelData.icon, "application-x-executable") : ""
visible: modelData.icon && source !== ""
asynchronous: true
}
}
}
// Fallback text if no icon and no image
NText {
anchors.centerIn: parent
visible: !imagePreview.visible && !iconLoader.visible
text: modelData.name ? modelData.name.charAt(0).toUpperCase() : "?"
font.pointSize: Style.fontSizeXXL * scaling
font.weight: Style.fontWeightBold
color: Color.mOnPrimary
}
// Image type indicator overlay
// Icon badge or Image preview
Rectangle {
visible: modelData.isImage && imagePreview.visible
anchors.bottom: parent.bottom
anchors.right: parent.right
anchors.margins: 2 * scaling
width: formatLabel.width + 6 * scaling
height: formatLabel.height + 2 * scaling
Layout.preferredWidth: badgeSize
Layout.preferredHeight: badgeSize
radius: Style.radiusM * scaling
color: Color.mSurfaceVariant
clip: true
NText {
id: formatLabel
anchors.centerIn: parent
text: {
if (!modelData.isImage)
return ""
const desc = modelData.description || ""
const parts = desc.split(" • ")
return parts[0] || "IMG"
// Image preview for clipboard images
NImageRounded {
id: imagePreview
anchors.fill: parent
visible: modelData.isImage
imageRadius: Style.radiusM * scaling
// This property creates a dependency on the service's revision counter
readonly property int _rev: ClipboardService.revision
// Fetches from the service's cache.
// The dependency on `_rev` ensures this binding is re-evaluated when the cache is updated.
imagePath: {
_rev
return ClipboardService.getImageData(modelData.clipboardId) || ""
}
// Loading indicator
Rectangle {
anchors.fill: parent
visible: parent.status === Image.Loading
color: Color.mSurfaceVariant
BusyIndicator {
anchors.centerIn: parent
running: true
width: Style.baseWidgetSize * 0.5 * scaling
height: width
}
}
// Error fallback
onStatusChanged: status => {
if (status === Image.Error) {
iconLoader.visible = true
imagePreview.visible = false
}
}
}
// Icon fallback
Loader {
id: iconLoader
anchors.fill: parent
anchors.margins: Style.marginXS * scaling
visible: !modelData.isImage || imagePreview.status === Image.Error
active: visible
sourceComponent: Component {
IconImage {
anchors.fill: parent
source: modelData.icon ? ThemeIcons.iconFromName(modelData.icon, "application-x-executable") : ""
visible: modelData.icon && source !== ""
asynchronous: true
}
}
}
// Fallback text if no icon and no image
NText {
anchors.centerIn: parent
visible: !imagePreview.visible && !iconLoader.visible
text: modelData.name ? modelData.name.charAt(0).toUpperCase() : "?"
pointSize: Style.fontSizeXXL * scaling
font.weight: Style.fontWeightBold
color: Color.mOnPrimary
}
// Image type indicator overlay
Rectangle {
visible: modelData.isImage && imagePreview.visible
anchors.bottom: parent.bottom
anchors.right: parent.right
anchors.margins: 2 * scaling
width: formatLabel.width + 6 * scaling
height: formatLabel.height + 2 * scaling
radius: Style.radiusM * scaling
color: Color.mSurfaceVariant
NText {
id: formatLabel
anchors.centerIn: parent
text: {
if (!modelData.isImage)
return ""
const desc = modelData.description || ""
const parts = desc.split(" • ")
return parts[0] || "IMG"
}
pointSize: Style.fontSizeXXS * scaling
color: Color.mPrimary
}
font.pointSize: Style.fontSizeXXS * scaling
color: Color.mPrimary
}
}
}
// Text content
ColumnLayout {
Layout.fillWidth: true
spacing: 0 * scaling
NText {
text: modelData.name || "Unknown"
font.pointSize: Style.fontSizeL * scaling
font.weight: Style.fontWeightBold
color: entry.isSelected ? Color.mOnTertiary : Color.mOnSurface
elide: Text.ElideRight
// Text content
ColumnLayout {
Layout.fillWidth: true
spacing: 0 * scaling
NText {
text: modelData.name || "Unknown"
pointSize: Style.fontSizeL * scaling
font.weight: Style.fontWeightBold
color: entry.isSelected ? Color.mOnTertiary : Color.mOnSurface
elide: Text.ElideRight
Layout.fillWidth: true
}
NText {
text: modelData.description || ""
pointSize: Style.fontSizeS * scaling
color: entry.isSelected ? Color.mOnTertiary : Color.mOnSurfaceVariant
elide: Text.ElideRight
Layout.fillWidth: true
visible: text !== ""
}
}
NText {
text: modelData.description || ""
font.pointSize: Style.fontSizeS * scaling
color: entry.isSelected ? Color.mOnTertiary : Color.mOnSurfaceVariant
elide: Text.ElideRight
Layout.fillWidth: true
visible: text !== ""
// Pin/Unpin action icon button
NIconButton {
visible: !!entry.appId && !modelData.isImage && entry.isSelected && (Settings.data.dock.monitors && Settings.data.dock.monitors.length > 0)
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
icon: entry.isPinned(entry.appId) ? "unpin" : "pin"
tooltipText: entry.isPinned(entry.appId) ? I18n.tr("launcher.unpin") : I18n.tr("launcher.pin")
onClicked: entry.togglePin(entry.appId)
}
}
}
@@ -469,12 +508,17 @@ NPanel {
MouseArea {
id: mouseArea
anchors.fill: parent
z: -1
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
selectedIndex = index
ui.activate()
}
onClicked: mouse => {
if (mouse.button === Qt.LeftButton) {
selectedIndex = index
ui.activate()
mouse.accepted = true
}
}
acceptedButtons: Qt.LeftButton
}
}
}
@@ -492,7 +536,7 @@ NPanel {
const prefix = activePlugin?.name ? `${activePlugin.name}: ` : ""
return prefix + `${results.length} result${results.length !== 1 ? 's' : ''}`
}
font.pointSize: Style.fontSizeXS * scaling
pointSize: Style.fontSizeXS * scaling
color: Color.mOnSurfaceVariant
horizontalAlignment: Text.AlignCenter
}

View File

@@ -69,9 +69,15 @@ Item {
if (!query || query.trim() === "") {
// Return all apps, optionally sorted by usage
const favoriteApps = Settings.data.appLauncher.favoriteApps || []
let sorted
if (Settings.data.appLauncher.sortByMostUsed) {
sorted = entries.slice().sort((a, b) => {
// Favorites first
const aFav = favoriteApps.includes(getAppKey(a))
const bFav = favoriteApps.includes(getAppKey(b))
if (aFav !== bFav)
return aFav ? -1 : 1
const ua = getUsageCount(a)
const ub = getUsageCount(b)
if (ub !== ua)
@@ -79,7 +85,13 @@ Item {
return (a.name || "").toLowerCase().localeCompare((b.name || "").toLowerCase())
})
} else {
sorted = entries.slice().sort((a, b) => (a.name || "").toLowerCase().localeCompare((b.name || "").toLowerCase()))
sorted = entries.slice().sort((a, b) => {
const aFav = favoriteApps.includes(getAppKey(a))
const bFav = favoriteApps.includes(getAppKey(b))
if (aFav !== bFav)
return aFav ? -1 : 1
return (a.name || "").toLowerCase().localeCompare((b.name || "").toLowerCase())
})
}
return sorted.map(app => createResultEntry(app))
}
@@ -92,7 +104,18 @@ Item {
"limit": 20
})
return fuzzyResults.map(result => createResultEntry(result.obj))
// Sort favorites first within fuzzy results while preserving fuzzysort order otherwise
const favoriteApps = Settings.data.appLauncher.favoriteApps || []
const fav = []
const nonFav = []
for (const r of fuzzyResults) {
const app = r.obj
if (favoriteApps.includes(getAppKey(app)))
fav.push(r)
else
nonFav.push(r)
}
return fav.concat(nonFav).map(result => createResultEntry(result.obj))
} else {
// Fallback to simple search
const searchTerm = query.toLowerCase()
@@ -118,6 +141,7 @@ Item {
function createResultEntry(app) {
return {
"appId": getAppKey(app),
"name": app.name || "Unknown",
"description": app.genericName || app.comment || "",
"icon": app.icon || "application-x-executable",
@@ -137,10 +161,20 @@ Item {
Quickshell.execDetached(["app2unit", "--", app.id + ".desktop"])
else
Quickshell.execDetached(["app2unit", "--"].concat(app.command))
} else if (app.execute) {
app.execute()
} else {
Logger.log("ApplicationsPlugin", `Could not launch: ${app.name}`)
// Fallback logic when app2unit is not used
if (app.runInTerminal) {
// If app.execute() fails for terminal apps, we handle it manually.
Logger.log("ApplicationsPlugin", "Executing terminal app manually: " + app.name)
const terminal = Settings.data.appLauncher.terminalCommand.split(" ")
const command = terminal.concat(app.command)
Quickshell.execDetached(command)
} else if (app.execute) {
// Default execution for GUI apps
app.execute()
} else {
Logger.log("ApplicationsPlugin", `Could not launch: ${app.name}. No valid launch method.`)
}
}
}
}

View File

@@ -27,12 +27,12 @@ Loader {
}
function formatTime() {
return Settings.data.location.use12hourFormat ? Qt.formatDateTime(new Date(), "h:mm A") : Qt.formatDateTime(new Date(), "HH:mm")
return Settings.data.location.use12hourFormat ? Qt.locale().toString(new Date(), "h:mm A") : Qt.locale().toString(new Date(), "HH:mm")
}
function formatDate() {
// For full text date, day is always before month, so we use this format for everybody: Wednesday, September 17.
return Qt.formatDateTime(new Date(), "dddd, MMMM d")
return Qt.locale().toString(new Date(), "dddd, MMMM d")
}
function scheduleUnloadAfterUnlock() {
@@ -147,9 +147,8 @@ Loader {
NText {
id: timeText
text: formatTime()
font.family: Settings.data.ui.fontBillboard
// Smaller time display when using longer 12 hour format
font.pointSize: Settings.data.location.use12hourFormat ? Style.fontSizeXXXL * 4 * scaling : Style.fontSizeXXXL * 5 * scaling
pointSize: Settings.data.location.use12hourFormat ? Style.fontSizeXXXL * 4 * scaling : Style.fontSizeXXXL * 5 * scaling
font.weight: Style.fontWeightBold
font.letterSpacing: -2 * scaling
color: Color.mOnSurface
@@ -174,8 +173,7 @@ Loader {
NText {
id: dateText
text: formatDate()
font.family: Settings.data.ui.fontBillboard
font.pointSize: Style.fontSizeXXL * scaling
pointSize: Style.fontSizeXXL * scaling
font.weight: Font.Light
color: Color.mOnSurface
horizontalAlignment: Text.AlignHCenter
@@ -385,8 +383,8 @@ Loader {
NText {
text: I18n.tr("lock-screen.secure-terminal")
color: Color.mOnSurface
font.family: Settings.data.ui.fontFixed
font.pointSize: Style.fontSizeL * scaling
family: Settings.data.ui.fontFixed
pointSize: Style.fontSizeL * scaling
font.weight: Style.fontWeightBold
Layout.fillWidth: true
}
@@ -396,13 +394,13 @@ Loader {
NText {
text: keyboardLayout.currentLayout
color: Color.mOnSurface
font.family: Settings.data.ui.fontFixed
font.pointSize: Style.fontSizeM * scaling
family: Settings.data.ui.fontFixed
pointSize: Style.fontSizeM * scaling
font.weight: Style.fontWeightBold
}
NIcon {
icon: "keyboard"
font.pointSize: Style.fontSizeM * scaling
pointSize: Style.fontSizeM * scaling
color: Color.mOnSurface
}
}
@@ -412,15 +410,15 @@ Loader {
visible: batteryIndicator.batteryVisible
NIcon {
icon: BatteryService.getIcon(batteryIndicator.percent, batteryIndicator.charging, batteryIndicator.isReady)
font.pointSize: Style.fontSizeM * scaling
pointSize: Style.fontSizeM * scaling
color: batteryIndicator.charging ? Color.mPrimary : Color.mOnSurface
rotation: -90
}
NText {
text: Math.round(batteryIndicator.percent) + "%"
color: Color.mOnSurface
font.family: Settings.data.ui.fontFixed
font.pointSize: Style.fontSizeM * scaling
family: Settings.data.ui.fontFixed
pointSize: Style.fontSizeM * scaling
font.weight: Style.fontWeightBold
}
}
@@ -443,8 +441,8 @@ Loader {
NText {
text: Quickshell.env("USER") + "@noctalia:~$"
color: Color.mPrimary
font.family: Settings.data.ui.fontFixed
font.pointSize: Style.fontSizeL * scaling
family: Settings.data.ui.fontFixed
pointSize: Style.fontSizeL * scaling
font.weight: Style.fontWeightBold
}
@@ -452,8 +450,8 @@ Loader {
id: welcomeText
text: ""
color: Color.mOnSurface
font.family: Settings.data.ui.fontFixed
font.pointSize: Style.fontSizeL * scaling
family: Settings.data.ui.fontFixed
pointSize: Style.fontSizeL * scaling
property int currentIndex: 0
property string fullText: I18n.tr("system.welcome-back", {
"user": Quickshell.env("USER")
@@ -482,16 +480,16 @@ Loader {
NText {
text: Quickshell.env("USER") + "@noctalia:~$"
color: Color.mPrimary
font.family: Settings.data.ui.fontFixed
font.pointSize: Style.fontSizeL * scaling
family: Settings.data.ui.fontFixed
pointSize: Style.fontSizeL * scaling
font.weight: Style.fontWeightBold
}
NText {
text: I18n.tr("lock-screen.unlock-command")
color: Color.mOnSurface
font.family: Settings.data.ui.fontFixed
font.pointSize: Style.fontSizeL * scaling
family: Settings.data.ui.fontFixed
pointSize: Style.fontSizeL * scaling
}
}
@@ -502,8 +500,8 @@ Loader {
NText {
text: I18n.tr("lock-screen.password")
color: Color.mPrimary
font.family: Settings.data.ui.fontFixed
font.pointSize: Style.fontSizeL * scaling
family: Settings.data.ui.fontFixed
pointSize: Style.fontSizeL * scaling
font.weight: Style.fontWeightBold
}
@@ -545,8 +543,8 @@ Loader {
id: asterisksText
text: "*".repeat(passwordInput.text.length)
color: Color.mOnSurface
font.family: Settings.data.ui.fontFixed
font.pointSize: Style.fontSizeL * scaling
family: Settings.data.ui.fontFixed
pointSize: Style.fontSizeL * scaling
visible: passwordInput.activeFocus && !lockContext.unlockInProgress
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
@@ -612,8 +610,7 @@ Loader {
return Color.mError
return Color.transparent
}
font.family: "DejaVu Sans Mono"
font.pointSize: Style.fontSizeL * scaling
pointSize: Style.fontSizeL * scaling
Layout.fillWidth: true
SequentialAnimation on opacity {
@@ -647,8 +644,8 @@ Loader {
anchors.centerIn: parent
text: lockContext.unlockInProgress ? "EXECUTING" : "EXECUTE"
color: executeButtonArea.containsMouse ? Color.mOnPrimary : Color.mPrimary
font.family: Settings.data.ui.fontFixed
font.pointSize: Style.fontSizeM * scaling
family: Settings.data.ui.fontFixed
pointSize: Style.fontSizeM * scaling
font.weight: Style.fontWeightBold
}
@@ -767,8 +764,8 @@ Loader {
text: messages[index % messages.length]
color: Color.mError
font.family: Settings.data.ui.fontFixed
font.pointSize: Style.fontSizeXXL * scaling
family: Settings.data.ui.fontFixed
pointSize: Style.fontSizeXXL * scaling
font.weight: Style.fontWeightBold
x: baseX
@@ -885,7 +882,7 @@ Loader {
id: iconPower
anchors.centerIn: parent
icon: "shutdown"
font.pointSize: Style.fontSizeXXXL * scaling
pointSize: Style.fontSizeXXXL * scaling
color: powerButtonArea.containsMouse ? Color.mOnError : Color.mError
}
@@ -894,7 +891,7 @@ Loader {
anchors.horizontalCenter: parent.horizontalCenter
anchors.bottom: parent.top
anchors.bottomMargin: Style.marginM * scaling
radius: Style.radiusM * scaling
radius: Style.radiusS * scaling
color: Color.mSurface
border.color: Color.mOutline
border.width: Math.max(1, Style.borderS * scaling)
@@ -905,7 +902,8 @@ Loader {
anchors.margins: Style.marginM * scaling
anchors.fill: parent
text: I18n.tr("lock-screen.shut-down")
font.pointSize: Style.fontSizeM * scaling
pointSize: Style.fontSizeS * scaling
color: Color.mOnSurfaceVariant
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
@@ -936,7 +934,7 @@ Loader {
id: iconReboot
anchors.centerIn: parent
icon: "reboot"
font.pointSize: Style.fontSizeXXXL * scaling
pointSize: Style.fontSizeXXXL * scaling
color: restartButtonArea.containsMouse ? Color.mOnPrimary : Color.mPrimary
}
@@ -945,7 +943,7 @@ Loader {
anchors.horizontalCenter: parent.horizontalCenter
anchors.bottom: parent.top
anchors.bottomMargin: Style.marginM * scaling
radius: Style.radiusM * scaling
radius: Style.radiusS * scaling
color: Color.mSurface
border.color: Color.mOutline
border.width: Math.max(1, Style.borderS * scaling)
@@ -956,7 +954,8 @@ Loader {
anchors.margins: Style.marginM * scaling
anchors.fill: parent
text: I18n.tr("lock-screen.restart")
font.pointSize: Style.fontSizeM * scaling
pointSize: Style.fontSizeS * scaling
color: Color.mOnSurfaceVariant
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
@@ -988,7 +987,7 @@ Loader {
id: iconSuspend
anchors.centerIn: parent
icon: "suspend"
font.pointSize: Style.fontSizeXXXL * scaling
pointSize: Style.fontSizeXXXL * scaling
color: suspendButtonArea.containsMouse ? Color.mOnSecondary : Color.mSecondary
}
@@ -997,7 +996,7 @@ Loader {
anchors.horizontalCenter: parent.horizontalCenter
anchors.bottom: parent.top
anchors.bottomMargin: Style.marginM * scaling
radius: Style.radiusM * scaling
radius: Style.radiusS * scaling
color: Color.mSurface
border.color: Color.mOutline
border.width: Math.max(1, Style.borderS * scaling)
@@ -1008,7 +1007,8 @@ Loader {
anchors.margins: Style.marginM * scaling
anchors.fill: parent
text: I18n.tr("lock-screen.suspend")
font.pointSize: Style.fontSizeM * scaling
pointSize: Style.fontSizeS * scaling
color: Color.mOnSurfaceVariant
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}

View File

@@ -22,7 +22,7 @@ Variants {
property ListModel notificationModel: NotificationService.activeList
// If no notification display activated in settings, then show them all
active: Settings.isLoaded && modelData && (Settings.data.notifications.monitors.includes(modelData.name) || (Settings.data.notifications.monitors.length === 0))
active: modelData && (Settings.data.notifications.monitors.includes(modelData.name) || (Settings.data.notifications.monitors.length === 0))
Connections {
target: ScalingService
@@ -37,11 +37,11 @@ Variants {
screen: modelData
WlrLayershell.namespace: "noctalia-notifications"
WlrLayershell.layer: (Settings.isLoaded && Settings.data && Settings.data.notifications && Settings.data.notifications.alwaysOnTop) ? WlrLayer.Overlay : WlrLayer.Top
WlrLayershell.layer: (Settings.data.notifications && Settings.data.notifications.alwaysOnTop) ? WlrLayer.Overlay : WlrLayer.Top
color: Color.transparent
readonly property string location: (Settings.isLoaded && Settings.data && Settings.data.notifications && Settings.data.notifications.location) ? Settings.data.notifications.location : "top_right"
readonly property string location: (Settings.data.notifications && Settings.data.notifications.location) ? Settings.data.notifications.location : "top_right"
readonly property bool isTop: (location === "top") || (location.length >= 3 && location.substring(0, 3) === "top")
readonly property bool isBottom: (location === "bottom") || (location.length >= 6 && location.substring(0, 6) === "bottom")
readonly property bool isLeft: location.indexOf("_left") >= 0
@@ -162,7 +162,7 @@ Variants {
anchors.left: parent.left
anchors.right: parent.right
height: 2 * scaling
color: "transparent"
color: Color.transparent
property real availableWidth: parent.width - (2 * parent.radius)
@@ -307,7 +307,7 @@ Variants {
NText {
text: `${model.appName || I18n.tr("system.unknown-app")} · ${Time.formatRelativeTime(model.timestamp)}`
color: Color.mSecondary
font.pointSize: Style.fontSizeXS * scaling
pointSize: Style.fontSizeXS * scaling
}
Item {
@@ -317,7 +317,7 @@ Variants {
NText {
text: model.summary || I18n.tr("general.no-summary")
font.pointSize: Style.fontSizeL * scaling
pointSize: Style.fontSizeL * scaling
font.weight: Style.fontWeightMedium
color: Color.mOnSurface
textFormat: Text.PlainText
@@ -330,7 +330,7 @@ Variants {
NText {
text: model.body || ""
font.pointSize: Style.fontSizeM * scaling
pointSize: Style.fontSizeM * scaling
color: Color.mOnSurface
textFormat: Text.PlainText
wrapMode: Text.WrapAtWordBoundaryOrAnywhere

View File

@@ -32,13 +32,13 @@ NPanel {
NIcon {
icon: "bell"
font.pointSize: Style.fontSizeXXL * scaling
pointSize: Style.fontSizeXXL * scaling
color: Color.mPrimary
}
NText {
text: I18n.tr("notifications.panel.title")
font.pointSize: Style.fontSizeL * scaling
pointSize: Style.fontSizeL * scaling
font.weight: Style.fontWeightBold
color: Color.mOnSurface
Layout.fillWidth: true
@@ -88,21 +88,21 @@ NPanel {
NIcon {
icon: "bell-off"
font.pointSize: 64 * scaling
pointSize: 64 * scaling
color: Color.mOnSurfaceVariant
Layout.alignment: Qt.AlignHCenter
}
NText {
text: I18n.tr("notifications.panel.no-notifications")
font.pointSize: Style.fontSizeL * scaling
pointSize: Style.fontSizeL * scaling
color: Color.mOnSurfaceVariant
Layout.alignment: Qt.AlignHCenter
}
NText {
text: I18n.tr("notifications.panel.description")
font.pointSize: Style.fontSizeS * scaling
pointSize: Style.fontSizeS * scaling
color: Color.mOnSurfaceVariant
Layout.alignment: Qt.AlignHCenter
Layout.fillWidth: true
@@ -199,13 +199,13 @@ NPanel {
NText {
text: model.appName || "Unknown App"
font.pointSize: Style.fontSizeXS * scaling
pointSize: Style.fontSizeXS * scaling
color: Color.mSecondary
}
NText {
text: Time.formatRelativeTime(model.timestamp)
font.pointSize: Style.fontSizeXS * scaling
pointSize: Style.fontSizeXS * scaling
color: Color.mSecondary
}
@@ -217,7 +217,7 @@ NPanel {
// Summary
NText {
text: model.summary || I18n.tr("general.no-summary")
font.pointSize: Style.fontSizeM * scaling
pointSize: Style.fontSizeM * scaling
font.weight: Font.Medium
color: Color.mOnSurface
textFormat: Text.PlainText
@@ -230,7 +230,7 @@ NPanel {
// Body
NText {
text: model.body || ""
font.pointSize: Style.fontSizeS * scaling
pointSize: Style.fontSizeS * scaling
color: Color.mOnSurfaceVariant
textFormat: Text.PlainText
wrapMode: Text.Wrap

View File

@@ -22,7 +22,7 @@ Variants {
property ListModel notificationModel: NotificationService.activeList
// If no notification display activated in settings, then show them all
property bool canShowOnThisScreen: Settings.isLoaded && modelData && (Settings.data.osd.monitors.includes(modelData.name) || (Settings.data.osd.monitors.length === 0))
property bool canShowOnThisScreen: modelData && (Settings.data.osd.monitors.includes(modelData.name) || (Settings.data.osd.monitors.length === 0))
// Loader is only active when actually showing something
active: false
@@ -104,7 +104,7 @@ Variants {
id: panel
screen: modelData
readonly property string location: (Settings.isLoaded && Settings.data && Settings.data.osd && Settings.data.osd.location) ? Settings.data.osd.location : "top_right"
readonly property string location: (Settings.data.osd && Settings.data.osd.location) ? Settings.data.osd.location : "top_right"
readonly property bool isTop: (location === "top") || (location.length >= 3 && location.substring(0, 3) === "top")
readonly property bool isBottom: (location === "bottom") || (location.length >= 6 && location.substring(0, 6) === "bottom")
readonly property bool isLeft: (location.indexOf("_left") >= 0) || (location === "left")
@@ -243,7 +243,7 @@ Variants {
Item {
anchors.fill: parent
Row {
RowLayout {
anchors.left: parent.left
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
@@ -253,8 +253,8 @@ Variants {
NIcon {
icon: root.getIcon()
color: root.getIconColor()
font.pointSize: Style.fontSizeXL * root.scaling
anchors.verticalCenter: parent.verticalCenter
pointSize: Style.fontSizeXL * root.scaling
Layout.alignment: Qt.AlignVCenter
Behavior on color {
ColorAnimation {
@@ -266,11 +266,11 @@ Variants {
// Progress bar with calculated width
Rectangle {
width: Math.round(220 * root.scaling)
Layout.preferredWidth: Math.round(220 * root.scaling)
height: panel.barThickness
radius: Math.round(panel.barThickness / 2)
color: Color.mSurfaceVariant
anchors.verticalCenter: parent.verticalCenter
Layout.alignment: Qt.AlignVCenter
Rectangle {
anchors.left: parent.left
@@ -299,12 +299,12 @@ Variants {
NText {
text: root.getDisplayPercentage()
color: Color.mOnSurface
font.pointSize: Style.fontSizeS * root.scaling
font.family: Settings.data.ui.fontFixed
anchors.verticalCenter: parent.verticalCenter
pointSize: Style.fontSizeS * root.scaling
family: Settings.data.ui.fontFixed
Layout.alignment: Qt.AlignVCenter
horizontalAlignment: Text.AlignLeft
verticalAlignment: Text.AlignVCenter
width: Math.round(50 * root.scaling)
Layout.preferredWidth: Math.round(50 * root.scaling)
}
}
}
@@ -340,8 +340,8 @@ Variants {
id: percentText
text: root.getDisplayPercentage()
color: Color.mOnSurface
font.pointSize: Style.fontSizeS * root.scaling
font.family: Settings.data.ui.fontFixed
pointSize: Style.fontSizeS * root.scaling
family: Settings.data.ui.fontFixed
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
horizontalAlignment: Text.AlignHCenter
@@ -389,7 +389,7 @@ Variants {
NIcon {
icon: root.getIcon()
color: root.getIconColor()
font.pointSize: Style.fontSizeXL * root.scaling
pointSize: Style.fontSizeXL * root.scaling
Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom
Layout.bottomMargin: vMargin + Math.round(Style.marginM * root.scaling) + balanceDelta
Behavior on color {
@@ -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()
}
})
}
}

View File

@@ -14,7 +14,7 @@ NPanel {
id: root
preferredWidth: 440
preferredHeight: 410
preferredHeight: 480
panelAnchorHorizontalCenter: true
panelAnchorVerticalCenter: true
panelKeyboardFocus: true
@@ -32,26 +32,31 @@ NPanel {
"icon": "lock",
"title": I18n.tr("session-menu.lock"),
"subtitle": I18n.tr("session-menu.lock-subtitle")
}, {
"action": "lockAndSuspend",
"icon": "lock-pause",
"title": I18n.tr("session-menu.lock-and-suspend"),
"subtitle": I18n.tr("session-menu.lock-and-suspend-subtitle")
}, {
"action": "suspend",
"icon": "suspend",
"title": I18n.tr("session-menu.suspend"),
"subtitle": "Put the system to sleep"
"subtitle": I18n.tr("session-menu.suspend-subtitle")
}, {
"action": "reboot",
"icon": "reboot",
"title": I18n.tr("session-menu.reboot"),
"subtitle": "Restart the system"
"subtitle": I18n.tr("session-menu.reboot-subtitle")
}, {
"action": "logout",
"icon": "logout",
"title": I18n.tr("session-menu.logout"),
"subtitle": I18n.tr("session-menu.end-subtitle")
"subtitle": I18n.tr("session-menu.logout-subtitle")
}, {
"action": "shutdown",
"icon": "shutdown",
"title": I18n.tr("session-menu.shutdown"),
"subtitle": "Turn off the system",
"subtitle": I18n.tr("session-menu.shutdown-subtitle"),
"isShutdown": true
}]
@@ -97,6 +102,9 @@ NPanel {
lockScreen.active = true
}
break
case "lockAndSuspend":
CompositorService.lockAndSuspend()
break
case "suspend":
CompositorService.suspend()
break
@@ -268,7 +276,7 @@ NPanel {
"seconds": Math.ceil(timeRemaining / 1000)
}) : I18n.tr("session-menu.title")
font.weight: Style.fontWeightBold
font.pointSize: Style.fontSizeL * scaling
pointSize: Style.fontSizeL * scaling
color: timerActive ? Color.mPrimary : Color.mOnSurface
Layout.alignment: Qt.AlignVCenter
verticalAlignment: Text.AlignVCenter
@@ -377,7 +385,7 @@ NPanel {
return Color.mOnTertiary
return Color.mOnSurface
}
font.pointSize: Style.fontSizeXXXL * scaling
pointSize: Style.fontSizeXXXL * scaling
width: Style.baseWidgetSize * 0.6 * scaling
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
@@ -401,7 +409,7 @@ NPanel {
NText {
text: buttonRoot.title
font.weight: Style.fontWeightMedium
font.pointSize: Style.fontSizeM * scaling
pointSize: Style.fontSizeM * scaling
color: {
if (buttonRoot.pending)
return Color.mPrimary
@@ -426,7 +434,7 @@ NPanel {
}
return buttonRoot.subtitle
}
font.pointSize: Style.fontSizeXS * scaling
pointSize: Style.fontSizeXS * scaling
color: {
if (buttonRoot.pending)
return Color.mPrimary
@@ -456,7 +464,7 @@ NPanel {
NText {
anchors.centerIn: parent
text: Math.ceil(timeRemaining / 1000)
font.pointSize: Style.fontSizeS * scaling
pointSize: Style.fontSizeS * scaling
font.weight: Style.fontWeightBold
color: Color.mOnPrimary
}

View File

@@ -70,7 +70,7 @@ NBox {
NText {
text: sectionName + " Section"
font.pointSize: Style.fontSizeL * scaling
pointSize: Style.fontSizeL * scaling
font.weight: Style.fontWeightBold
color: Color.mOnSurface
Layout.alignment: Qt.AlignVCenter
@@ -225,7 +225,7 @@ NBox {
NText {
text: modelData.id
font.pointSize: Style.fontSizeS * scaling
pointSize: Style.fontSizeS * scaling
color: root.getWidgetColor(modelData)[1]
horizontalAlignment: Text.AlignHCenter
elide: Text.ElideRight
@@ -312,10 +312,10 @@ NBox {
z: 2000
clip: false // Ensure ghost isn't clipped
Text {
NText {
id: ghostText
anchors.centerIn: parent
font.pointSize: Style.fontSizeS * scaling
pointSize: Style.fontSizeS * scaling
color: Color.mOnPrimary
}
}

View File

@@ -61,7 +61,7 @@ Popup {
text: I18n.tr("system.widget-settings-title", {
"widget": widgetSettings.widgetId
})
font.pointSize: Style.fontSizeL * scaling
pointSize: Style.fontSizeL * scaling
font.weight: Style.fontWeightBold
color: Color.mPrimary
Layout.fillWidth: true
@@ -69,6 +69,7 @@ Popup {
NIconButton {
icon: "close"
tooltipText: "Close"
onClicked: widgetSettings.close()
}
}
@@ -132,7 +133,8 @@ Popup {
"Spacer": "WidgetSettings/SpacerSettings.qml",
"SystemMonitor": "WidgetSettings/SystemMonitorSettings.qml",
"Volume": "WidgetSettings/VolumeSettings.qml",
"Workspace": "WidgetSettings/WorkspaceSettings.qml"
"Workspace": "WidgetSettings/WorkspaceSettings.qml",
"Taskbar": "WidgetSettings/TaskbarSettings.qml"
}
const source = widgetSettingsMap[widgetId]

View File

@@ -23,26 +23,28 @@ ColumnLayout {
settings.autoHide = valueAutoHide
settings.showIcon = valueShowIcon
settings.scrollingMode = valueScrollingMode
console.log(JSON.stringify(settings))
return settings
}
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")

View File

@@ -16,7 +16,8 @@ ColumnLayout {
// Local state
property bool valueUsePrimaryColor: widgetData.usePrimaryColor !== undefined ? widgetData.usePrimaryColor : widgetMetadata.usePrimaryColor
property bool valueUseMonospacedFont: widgetData.useMonospacedFont !== undefined ? widgetData.useMonospacedFont : widgetMetadata.useMonospacedFont
property bool valueUseCustomFont: widgetData.useCustomFont !== undefined ? widgetData.useCustomFont : widgetMetadata.useCustomFont
property string valueCustomFont: widgetData.customFont !== undefined ? widgetData.customFont : widgetMetadata.customFont
property string valueFormatHorizontal: widgetData.formatHorizontal !== undefined ? widgetData.formatHorizontal : widgetMetadata.formatHorizontal
property string valueFormatVertical: widgetData.formatVertical !== undefined ? widgetData.formatVertical : widgetMetadata.formatVertical
@@ -29,7 +30,8 @@ ColumnLayout {
function saveSettings() {
var settings = Object.assign({}, widgetData || {})
settings.usePrimaryColor = valueUsePrimaryColor
settings.useMonospacedFont = valueUseMonospacedFont
settings.useCustomFont = valueUseCustomFont
settings.customFont = valueCustomFont
settings.formatHorizontal = valueFormatHorizontal.trim()
settings.formatVertical = valueFormatVertical.trim()
return settings
@@ -72,10 +74,26 @@ ColumnLayout {
NToggle {
Layout.fillWidth: true
label: I18n.tr("bar.widget-settings.clock.use-monospaced-font.label")
description: I18n.tr("bar.widget-settings.clock.use-monospaced-font.description")
checked: valueUseMonospacedFont
onToggled: checked => valueUseMonospacedFont = checked
label: I18n.tr("bar.widget-settings.clock.use-custom-font.label")
description: I18n.tr("bar.widget-settings.clock.use-custom-font.description")
checked: valueUseCustomFont
onToggled: checked => valueUseCustomFont = checked
}
NSearchableComboBox {
Layout.fillWidth: true
visible: valueUseCustomFont
label: I18n.tr("bar.widget-settings.clock.custom-font.label")
description: I18n.tr("bar.widget-settings.clock.custom-font.description")
model: FontService.availableFonts
currentKey: valueCustomFont
placeholder: I18n.tr("bar.widget-settings.clock.custom-font.placeholder")
searchPlaceholder: I18n.tr("bar.widget-settings.clock.custom-font.search-placeholder")
popupHeight: 420 * scaling
minimumWidth: 300 * scaling
onSelected: function (key) {
valueCustomFont = key
}
}
NDivider {
@@ -182,12 +200,12 @@ ColumnLayout {
// Horizontal
Repeater {
Layout.topMargin: Style.marginM * scaling
model: Qt.formatDateTime(now, valueFormatHorizontal.trim()).split("\\n")
model: Qt.locale().toString(now, valueFormatHorizontal.trim()).split("\\n")
delegate: NText {
visible: text !== ""
text: modelData
font.family: valueUseMonospacedFont ? Settings.data.ui.fontFixed : Settings.data.ui.fontDefault
font.pointSize: Style.fontSizeM * scaling
family: valueUseCustomFont && valueCustomFont ? valueCustomFont : (valueUseMonospacedFont ? Settings.data.ui.fontFixed : Settings.data.ui.fontDefault)
pointSize: Style.fontSizeM * scaling
font.weight: Style.fontWeightBold
color: valueUsePrimaryColor ? Color.mPrimary : Color.mOnSurface
wrapMode: Text.WordWrap
@@ -213,12 +231,12 @@ ColumnLayout {
Repeater {
Layout.topMargin: Style.marginM * scaling
model: Qt.formatDateTime(now, valueFormatVertical.trim()).split(" ")
model: Qt.locale().toString(now, valueFormatVertical.trim()).split(" ")
delegate: NText {
visible: text !== ""
text: modelData
font.family: valueUseMonospacedFont ? Settings.data.ui.fontFixed : Settings.data.ui.fontDefault
font.pointSize: Style.fontSizeM * scaling
family: valueUseCustomFont && valueCustomFont ? valueCustomFont : (valueUseMonospacedFont ? Settings.data.ui.fontFixed : Settings.data.ui.fontDefault)
pointSize: Style.fontSizeM * scaling
font.weight: Style.fontWeightBold
color: valueUsePrimaryColor ? Color.mPrimary : Color.mOnSurface
wrapMode: Text.WordWrap

View File

@@ -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
@@ -58,7 +59,7 @@ ColumnLayout {
NIcon {
Layout.alignment: Qt.AlignVCenter
icon: valueIcon
font.pointSize: Style.fontSizeXXL * 1.5 * scaling
pointSize: Style.fontSizeXXL * 1.5 * scaling
visible: valueIcon !== "" && valueCustomIconPath === ""
}
}

View File

@@ -37,7 +37,7 @@ ColumnLayout {
NIcon {
Layout.alignment: Qt.AlignVCenter
icon: valueIcon
font.pointSize: Style.fontSizeXL * scaling
pointSize: Style.fontSizeXL * scaling
visible: valueIcon !== ""
}
@@ -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
}

View File

@@ -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")

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -0,0 +1,43 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import qs.Commons
import qs.Widgets
import qs.Services
ColumnLayout {
id: root
spacing: Style.marginM * scaling
// Properties to receive data from parent
property var widgetData: null
property var widgetMetadata: null
// Local state
property bool valueOnlyActiveWorkspaces: widgetData.onlyActiveWorkspaces !== undefined ? widgetData.onlyActiveWorkspaces : widgetMetadata.onlyActiveWorkspaces
property bool valueOnlySameOutput: widgetData.onlySameOutput !== undefined ? widgetData.onlySameOutput : widgetMetadata.onlySameOutput
function saveSettings() {
var settings = Object.assign({}, widgetData || {})
settings.onlySameOutput = valueOnlySameOutput
settings.onlyActiveWorkspaces = valueOnlyActiveWorkspaces
console.log(JSON.stringify(settings))
return settings
}
NToggle {
Layout.fillWidth: true
label: I18n.tr("bar.widget-settings.taskbar.only-same-output.label")
description: I18n.tr("bar.widget-settings.taskbar.only-same-output.description")
checked: root.valueOnlySameOutput
onToggled: checked => root.valueOnlySameOutput = checked
}
NToggle {
Layout.fillWidth: true
label: I18n.tr("bar.widget-settings.taskbar.only-active-workspaces.label")
description: I18n.tr("bar.widget-settings.taskbar.only-active-workspaces.description")
checked: root.valueOnlyActiveWorkspaces
onToggled: checked => root.valueOnlyActiveWorkspaces = checked
}
}

View File

@@ -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")

View File

@@ -396,14 +396,14 @@ NPanel {
NIcon {
icon: modelData.icon
color: tabTextColor
font.pointSize: Style.fontSizeXL * scaling
pointSize: Style.fontSizeXL * scaling
}
// Tab label
NText {
text: I18n.tr(modelData.label)
color: tabTextColor
font.pointSize: Style.fontSizeM * scaling
pointSize: Style.fontSizeM * scaling
font.weight: Style.fontWeightBold
Layout.fillWidth: true
Layout.alignment: Qt.AlignVCenter
@@ -456,13 +456,13 @@ NPanel {
NIcon {
icon: root.tabsModel[currentTabIndex]?.icon
color: Color.mPrimary
font.pointSize: Style.fontSizeXXL * scaling
pointSize: Style.fontSizeXXL * scaling
}
// Main title
NText {
text: I18n.tr(root.tabsModel[currentTabIndex]?.label) || ""
font.pointSize: Style.fontSizeXL * scaling
pointSize: Style.fontSizeXL * scaling
font.weight: Style.fontWeightBold
color: Color.mPrimary
Layout.fillWidth: true

View File

@@ -91,14 +91,14 @@ ColumnLayout {
NIcon {
icon: "download"
font.pointSize: Style.fontSizeXXL * scaling
pointSize: Style.fontSizeXXL * scaling
color: updateArea.containsMouse ? Color.mSurface : Color.mPrimary
}
NText {
id: updateText
text: I18n.tr("settings.about.noctalia.download-latest")
font.pointSize: Style.fontSizeL * scaling
pointSize: Style.fontSizeL * scaling
color: updateArea.containsMouse ? Color.mSurface : Color.mPrimary
}
}
@@ -200,7 +200,7 @@ ColumnLayout {
NText {
text: (modelData.contributions || 0) + " " + ((modelData.contributions || 0) === 1 ? "commit" : "commits")
font.pointSize: Style.fontSizeXS * scaling
pointSize: Style.fontSizeXS * scaling
color: contributorArea.containsMouse ? Color.mOnTertiary : Color.mOnSurface
}
}

View File

@@ -150,6 +150,7 @@ ColumnLayout {
// AudioService Devices
ColumnLayout {
spacing: Style.marginS * scaling
Layout.fillWidth: true
NHeader {
label: I18n.tr("settings.audio.devices.section.label")
@@ -175,14 +176,15 @@ ColumnLayout {
Repeater {
model: AudioService.sinks
NRadioButton {
required property PwNode modelData
ButtonGroup.group: sinks
required property PwNode modelData
text: modelData.description
checked: AudioService.sink?.id === modelData.id
onClicked: {
AudioService.setAudioSink(modelData)
localVolume = AudioService.volume
}
text: modelData.description
Layout.fillWidth: true
}
}
}
@@ -204,12 +206,14 @@ ColumnLayout {
Repeater {
model: AudioService.sources
//Layout.fillWidth: true
NRadioButton {
required property PwNode modelData
ButtonGroup.group: sources
required property PwNode modelData
text: modelData.description
checked: AudioService.source?.id === modelData.id
onClicked: AudioService.setAudioSource(modelData)
text: modelData.description
Layout.fillWidth: true
}
}
}
@@ -305,7 +309,7 @@ ColumnLayout {
NText {
text: modelData
color: Color.mOnSurface
font.pointSize: Style.fontSizeS * scaling
pointSize: Style.fontSizeS * scaling
Layout.alignment: Qt.AlignVCenter
Layout.leftMargin: Style.marginS * scaling
}

View File

@@ -140,7 +140,7 @@ ColumnLayout {
NText {
text: I18n.tr("settings.bar.appearance.margins.vertical")
font.pointSize: Style.fontSizeXS * scaling
pointSize: Style.fontSizeXS * scaling
color: Color.mOnSurfaceVariant
}
@@ -160,7 +160,7 @@ ColumnLayout {
NText {
text: I18n.tr("settings.bar.appearance.margins.horizontal")
font.pointSize: Style.fontSizeXS * scaling
pointSize: Style.fontSizeXS * scaling
color: Color.mOnSurfaceVariant
}

View File

@@ -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")
@@ -259,7 +261,7 @@ ColumnLayout {
var chunks = schemePath.replace(".json", "").split("/")
return chunks[chunks.length - 1]
}
font.pointSize: Style.fontSizeM * scaling
pointSize: Style.fontSizeM * scaling
font.weight: Style.fontWeightBold
color: getSchemeColor(modelData, "mOnSurface")
Layout.fillWidth: true
@@ -324,7 +326,7 @@ ColumnLayout {
NIcon {
icon: "check"
font.pointSize: Style.fontSizeM * scaling
pointSize: Style.fontSizeM * scaling
font.weight: Style.fontWeightBold
color: Color.mOnSecondary
anchors.centerIn: parent

View File

@@ -126,7 +126,7 @@ ColumnLayout {
// Reset button container
Item {
Layout.preferredWidth: 40 * scaling
Layout.preferredWidth: 30 * scaling
Layout.preferredHeight: 30 * scaling
NIconButton {
@@ -168,13 +168,13 @@ ColumnLayout {
// Empty container to match scale row layout
Item {
Layout.preferredWidth: 40 * scaling
Layout.preferredWidth: 30 * scaling
Layout.preferredHeight: 30 * scaling
// Method text positioned in the button area
NText {
text: brightnessMonitor ? brightnessMonitor.method : ""
font.pointSize: Style.fontSizeXS * scaling
pointSize: Style.fontSizeXS * scaling
color: Color.mOnSurfaceVariant
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
@@ -186,22 +186,18 @@ ColumnLayout {
}
}
}
// Brightness Step Section
ColumnLayout {
spacing: Style.marginS * scaling
Layout.fillWidth: true
NSpinBox {
Layout.fillWidth: true
label: I18n.tr("settings.display.monitors.brightness-step.label")
description: I18n.tr("settings.display.monitors.brightness-step.description")
minimum: 1
maximum: 50
value: Settings.data.brightness.brightnessStep
stepSize: 1
suffix: "%"
onValueChanged: Settings.data.brightness.brightnessStep = value
}
// Brightness Step
NSpinBox {
Layout.fillWidth: true
label: I18n.tr("settings.display.monitors.brightness-step.label")
description: I18n.tr("settings.display.monitors.brightness-step.description")
minimum: 1
maximum: 50
value: Settings.data.brightness.brightnessStep
stepSize: 1
suffix: "%"
onValueChanged: Settings.data.brightness.brightnessStep = value
}
}
@@ -258,7 +254,7 @@ ColumnLayout {
NText {
text: I18n.tr("settings.display.night-light.temperature.night")
font.pointSize: Style.fontSizeM * scaling
pointSize: Style.fontSizeM * scaling
color: Color.mOnSurfaceVariant
Layout.alignment: Qt.AlignVCenter
}
@@ -280,7 +276,7 @@ ColumnLayout {
NText {
text: I18n.tr("settings.display.night-light.temperature.day")
font.pointSize: Style.fontSizeM * scaling
pointSize: Style.fontSizeM * scaling
color: Color.mOnSurfaceVariant
Layout.alignment: Qt.AlignVCenter
}
@@ -327,7 +323,7 @@ ColumnLayout {
NText {
text: I18n.tr("settings.display.night-light.manual-schedule.sunrise")
font.pointSize: Style.fontSizeM * scaling
pointSize: Style.fontSizeM * scaling
color: Color.mOnSurfaceVariant
}
@@ -345,7 +341,7 @@ ColumnLayout {
NText {
text: I18n.tr("settings.display.night-light.manual-schedule.sunset")
font.pointSize: Style.fontSizeM * scaling
pointSize: Style.fontSizeM * scaling
color: Color.mOnSurfaceVariant
}

View File

@@ -118,6 +118,13 @@ ColumnLayout {
}
}
NToggle {
label: I18n.tr("settings.dock.monitors.only-same-output.label")
description: I18n.tr("settings.dock.monitors.only-same-output.description")
checked: Settings.data.dock.onlySameOutput
onToggled: checked => Settings.data.dock.onlySameOutput = checked
}
NDivider {
Layout.fillWidth: true
Layout.topMargin: Style.marginXL * scaling

View File

@@ -187,6 +187,7 @@ ColumnLayout {
}
}
}
NDivider {
Layout.fillWidth: true
Layout.topMargin: Style.marginXL * scaling
@@ -236,25 +237,79 @@ ColumnLayout {
}
}
NSearchableComboBox {
label: I18n.tr("settings.general.fonts.accent.label")
description: I18n.tr("settings.general.fonts.accent.description")
model: FontService.displayFonts
currentKey: Settings.data.ui.fontBillboard
placeholder: I18n.tr("settings.general.fonts.accent.placeholder")
searchPlaceholder: I18n.tr("settings.general.fonts.accent.search-placeholder")
popupHeight: 320 * scaling
minimumWidth: 300 * scaling
onSelected: function (key) {
Settings.data.ui.fontBillboard = key
ColumnLayout {
NLabel {
label: I18n.tr("settings.general.fonts.default.scale.label")
description: I18n.tr("settings.general.fonts.default.scale.description")
}
RowLayout {
spacing: Style.marginL * scaling
Layout.fillWidth: true
NValueSlider {
Layout.fillWidth: true
from: 0.75
to: 1.25
stepSize: 0.01
value: Settings.data.ui.fontDefaultScale
onMoved: value => Settings.data.ui.fontDefaultScale = value
text: Math.floor(Settings.data.ui.fontDefaultScale * 100) + "%"
}
// Reset button container
Item {
Layout.preferredWidth: 30 * scaling
Layout.preferredHeight: 30 * scaling
NIconButton {
icon: "refresh"
baseSize: Style.baseWidgetSize * 0.9
tooltipText: I18n.tr("settings.general.fonts.reset-scaling")
onClicked: Settings.data.ui.fontDefaultScale = 1.0
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
}
}
}
}
ColumnLayout {
NLabel {
label: I18n.tr("settings.general.fonts.monospace.scale.label")
description: I18n.tr("settings.general.fonts.monospace.scale.description")
}
RowLayout {
spacing: Style.marginL * scaling
Layout.fillWidth: true
NValueSlider {
Layout.fillWidth: true
from: 0.75
to: 1.25
stepSize: 0.01
value: Settings.data.ui.fontFixedScale
onMoved: value => Settings.data.ui.fontFixedScale = value
text: Math.floor(Settings.data.ui.fontFixedScale * 100) + "%"
}
// Reset button container
Item {
Layout.preferredWidth: 30 * scaling
Layout.preferredHeight: 30 * scaling
NIconButton {
icon: "refresh"
baseSize: Style.baseWidgetSize * 0.9
tooltipText: I18n.tr("settings.general.fonts.reset-scaling")
onClicked: Settings.data.ui.fontFixedScale = 1.0
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
}
}
}
}
}
}
NDivider {
Layout.fillWidth: true
Layout.topMargin: Style.marginXL * scaling
Layout.bottomMargin: Style.marginXL * scaling
}
}

View File

@@ -53,14 +53,14 @@ ColumnLayout {
NText {
text: I18n.tr("settings.launcher.settings.background-opacity.label")
font.pointSize: Style.fontSizeL * scaling
pointSize: Style.fontSizeL * scaling
font.weight: Style.fontWeightBold
color: Color.mOnSurface
}
NText {
text: I18n.tr("settings.launcher.settings.background-opacity.description")
font.pointSize: Style.fontSizeXS * scaling
pointSize: Style.fontSizeXS * scaling
color: Color.mOnSurfaceVariant
wrapMode: Text.WordWrap
Layout.fillWidth: true
@@ -99,6 +99,16 @@ ColumnLayout {
onToggled: checked => Settings.data.appLauncher.useApp2Unit = checked
}
NTextInput {
label: I18n.tr("settings.launcher.settings.terminal-command.label")
description: I18n.tr("settings.launcher.settings.terminal-command.description")
Layout.fillWidth: true
text: Settings.data.appLauncher.terminalCommand
onEditingFinished: {
Settings.data.appLauncher.terminalCommand = text
}
}
NDivider {
Layout.fillWidth: true
Layout.topMargin: Style.marginXL * scaling

View File

@@ -46,7 +46,7 @@ ColumnLayout {
"name": LocationService.stableName,
"coordinates": LocationService.displayCoordinates
})
font.pointSize: Style.fontSizeS * scaling
pointSize: Style.fontSizeS * scaling
color: Color.mOnSurfaceVariant
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignRight

View File

@@ -77,7 +77,7 @@ ColumnLayout {
text: (modelData.name || "Unknown")
color: Color.mPrimary
font.weight: Style.fontWeightBold
font.pointSize: Style.fontSizeM * scaling
pointSize: Style.fontSizeM * scaling
}
NTextInputButton {
@@ -327,7 +327,7 @@ ColumnLayout {
id: chipLabel
anchors.centerIn: parent
text: parent.label
font.pointSize: Style.fontSizeS * scaling
pointSize: Style.fontSizeS * scaling
color: parent.selected ? Color.mOnPrimary : Color.mOnSurface
}
}

View File

@@ -100,7 +100,7 @@ Rectangle {
return Color.mOnSurface
}
}
font.pointSize: Style.fontSizeXXL * 1.5 * scaling
pointSize: Style.fontSizeXXL * 1.5 * scaling
Layout.alignment: Qt.AlignVCenter
}
@@ -114,7 +114,7 @@ Rectangle {
Layout.fillWidth: true
text: root.message
color: Color.mOnSurface
font.pointSize: Style.fontSizeL * scaling
pointSize: Style.fontSizeL * scaling
font.weight: Style.fontWeightBold
wrapMode: Text.WordWrap
visible: text.length > 0
@@ -124,7 +124,7 @@ Rectangle {
Layout.fillWidth: true
text: root.description
color: Color.mOnSurface
font.pointSize: Style.fontSizeM * scaling
pointSize: Style.fontSizeM * scaling
wrapMode: Text.WordWrap
visible: text.length > 0
}

View File

@@ -16,6 +16,6 @@ Variants {
scaling: ScalingService.getScreenScale(modelData)
// Only activate on enabled screens
active: Settings.isLoaded && modelData && (Settings.data.notifications.monitors.includes(modelData.name) || Settings.data.notifications.monitors.length === 0)
active: modelData && (Settings.data.notifications.monitors.includes(modelData.name) || Settings.data.notifications.monitors.length === 0)
}
}

View File

@@ -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() {
@@ -128,7 +128,7 @@ Item {
screen: root.screen
readonly property string location: (Settings.isLoaded && Settings.data && Settings.data.notifications && Settings.data.notifications.location) ? Settings.data.notifications.location : "top_right"
readonly property string location: (Settings.data.notifications && Settings.data.notifications.location) ? Settings.data.notifications.location : "top_right"
readonly property bool isTop: (location === "top") || (location.length >= 3 && location.substring(0, 3) === "top")
readonly property bool isBottom: (location === "bottom") || (location.length >= 6 && location.substring(0, 6) === "bottom")
readonly property bool isLeft: location.indexOf("_left") >= 0

406
Modules/Tooltip/Tooltip.qml Normal file
View File

@@ -0,0 +1,406 @@
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: 320
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: true
visible: false
color: Color.transparent
anchor.item: targetItem
anchor.rect.x: anchorX
anchor.rect.y: anchorY
// 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
delay = showDelay
// 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 || !targetItem.parent || !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)
root.implicitWidth = tipWidth
const tipHeight = tooltipText.implicitHeight + (padding * 2 * scaling)
root.implicitHeight = tipHeight
// 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() {
visible = false
animatingOut = false
pendingShow = false
text = ""
isPositioned = false
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 * root.scaling)
radius: Style.radiusS * root.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
pointSize: Style.fontSizeS * root.scaling
color: Color.mOnSurfaceVariant
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
wrapMode: Text.WordWrap
width: root.maxWidth * root.scaling - (root.padding * 2 * root.scaling)
}
}
}
Component.onCompleted: {
reset()
}
Component.onDestruction: {
reset()
}
}

View File

@@ -4,6 +4,7 @@ import QtQuick.Controls
import Quickshell
import Quickshell.Wayland
import qs.Commons
import qs.Modules.Settings
import qs.Services
import qs.Widgets
import "../../Helpers/FuzzySort.js" as FuzzySort
@@ -51,18 +52,29 @@ NPanel {
NIcon {
icon: "settings-wallpaper-selector"
font.pointSize: Style.fontSizeXXL * scaling
pointSize: Style.fontSizeXXL * scaling
color: Color.mPrimary
}
NText {
text: I18n.tr("wallpaper.panel.title")
font.pointSize: Style.fontSizeL * scaling
pointSize: Style.fontSizeL * scaling
font.weight: Style.fontWeightBold
color: Color.mOnSurface
Layout.fillWidth: true
}
NIconButton {
icon: "settings"
tooltipText: I18n.tr("settings.wallpaper.settings.section.label")
baseSize: Style.baseWidgetSize * 0.8
onClicked: {
var settingsPanel = PanelService.getPanel("settingsPanel")
settingsPanel.requestedTab = SettingsPanel.Tab.Wallpaper
settingsPanel.open()
}
}
NIconButton {
icon: "refresh"
tooltipText: I18n.tr("tooltips.refresh-wallpaper-list")
@@ -122,11 +134,11 @@ NPanel {
}
}
contentItem: Text {
contentItem: NText {
text: parent.text
font.pointSize: Style.fontSizeL * scaling
pointSize: Style.fontSizeL * scaling
font.weight: screenTabBar.currentIndex === index ? Style.fontWeightBold : Style.fontWeightRegular
font.family: Settings.data.ui.fontDefault
family: Settings.data.ui.fontDefault
color: screenTabBar.currentIndex === index ? Color.mOnSecondary : Color.mOnSurfaceVariant
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
@@ -177,7 +189,7 @@ NPanel {
NText {
text: I18n.tr("wallpaper.panel.search")
color: Color.mOnSurface
font.pointSize: Style.fontSizeM * scaling
pointSize: Style.fontSizeM * scaling
Layout.preferredWidth: implicitWidth
}
@@ -355,7 +367,7 @@ NPanel {
NIcon {
icon: "check"
font.pointSize: Style.fontSizeM * scaling
pointSize: Style.fontSizeM * scaling
font.weight: Style.fontWeightBold
color: Color.mOnSecondary
anchors.centerIn: parent
@@ -393,7 +405,7 @@ NPanel {
text: filename
color: Color.mOnSurfaceVariant
opacity: 0.5
font.pointSize: Style.fontSizeXS * scaling
pointSize: Style.fontSizeXS * scaling
Layout.fillWidth: true
Layout.leftMargin: Style.marginS * scaling
Layout.rightMargin: Style.marginS * scaling
@@ -431,7 +443,7 @@ NPanel {
}
NIcon {
icon: "folder-open"
font.pointSize: Style.fontSizeXXL * scaling
pointSize: Style.fontSizeXXL * scaling
color: Color.mOnSurface
Layout.alignment: Qt.AlignHCenter
}

View File

@@ -9,6 +9,13 @@ Singleton {
property bool hasAudioVisualizer: false
property bool isVisible: true
property var readyBars: ({})
// Registry to store actual widget instances
// Key format: "screenName|section|widgetId|index"
property var widgetInstances: ({})
signal barReadyChanged(string screenName)
// Simple timer that run once when the widget structure has changed
// and determine if any MediaMini widget has the visualizer on
@@ -28,14 +35,24 @@ Singleton {
}
}
// Registry to store actual widget instances
// Key format: "screenName|section|widgetId|index"
property var widgetInstances: ({})
Component.onCompleted: {
Logger.log("BarService", "Service started")
}
// Function for the Bar to call when it's ready
function registerBar(screenName) {
if (!readyBars[screenName]) {
readyBars[screenName] = true
Logger.log("BarService", "Bar is ready on screen:", screenName)
barReadyChanged(screenName)
}
}
// Function for the Dock to check if the bar is ready
function isBarReady(screenName) {
return readyBars[screenName] || false
}
// Register a widget instance
function registerWidget(screenName, section, widgetId, index, instance) {
const key = [screenName, section, widgetId, index].join("|")
@@ -202,4 +219,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"
}
}
}

View File

@@ -41,7 +41,7 @@ Singleton {
"ActiveWindow": {
"allowUserSettings": true,
"showIcon": true,
"autoHide": true,
"autoHide": false,
"scrollingMode": "hover"
},
"Battery": {
@@ -56,7 +56,8 @@ Singleton {
"Clock": {
"allowUserSettings": true,
"usePrimaryColor": true,
"useMonospacedFont": true,
"useCustomFont": false,
"customFont": "",
"formatHorizontal": "HH:mm ddd, MMM dd",
"formatVertical": "HH mm - dd MM"
},
@@ -81,7 +82,7 @@ Singleton {
},
"MediaMini": {
"allowUserSettings": true,
"autoHide": true,
"autoHide": false,
"scrollingMode": "hover",
"showAlbumArt": false,
"showVisualizer": false,
@@ -109,6 +110,11 @@ Singleton {
"showNetworkStats": false,
"showDiskUsage": false
},
"Taskbar": {
"allowUserSettings": true,
"onlySameOutput": true,
"onlyActiveWorkspaces": true
},
"Workspace": {
"allowUserSettings": true,
"labelMode": "index",

View File

@@ -16,6 +16,9 @@ Singleton {
return monitors.find(m => m.modelData === screen)
}
// Signal emitted when a specific monitor's brightness changes, includes monitor context
signal monitorBrightnessChanged(var monitor, real newBrightness)
function getAvailableMethods(): list<string> {
var methods = []
if (monitors.some(m => m.isDdc))
@@ -119,6 +122,52 @@ Singleton {
// Signal for brightness changes
signal brightnessUpdated(real newBrightness)
// Execute a system command to get the current brightness value directly
readonly property Process refreshProc: Process {
stdout: StdioCollector {
onStreamFinished: {
var dataText = text.trim()
if (dataText === "") {
return
}
var lines = dataText.split("\n")
if (lines.length >= 2) {
var current = parseInt(lines[0].trim())
var max = parseInt(lines[1].trim())
if (!isNaN(current) && !isNaN(max) && max > 0) {
var newBrightness = current / max
// Only update if it's actually different (avoid feedback loops)
if (Math.abs(newBrightness - monitor.brightness) > 0.01) {
// Update internal value to match system state
monitor.brightness = newBrightness
monitor.brightnessUpdated(monitor.brightness)
root.monitorBrightnessChanged(monitor, monitor.brightness)
//Logger.log("Brightness", "Refreshed brightness from system:", monitor.modelData.name, monitor.brightness)
}
}
}
}
}
}
// Function to actively refresh the brightness from system
function refreshBrightnessFromSystem() {
if (!monitor.isDdc && !monitor.isAppleDisplay) {
// For internal displays, query the system directly
refreshProc.command = ["sh", "-c", "cat " + monitor.brightnessPath + " && " + "cat " + monitor.maxBrightnessPath]
refreshProc.running = true
} else if (monitor.isDdc) {
// For DDC displays, get the current value
refreshProc.command = ["ddcutil", "-b", monitor.busNum, "getvcp", "10", "--brief"]
refreshProc.running = true
} else if (monitor.isAppleDisplay) {
// For Apple displays, get the current value
refreshProc.command = ["asdbctl", "get"]
refreshProc.running = true
}
}
// FileView to watch for external brightness changes (internal displays only)
readonly property FileView brightnessWatcher: FileView {
id: brightnessWatcher
@@ -126,23 +175,11 @@ Singleton {
path: (!monitor.isDdc && !monitor.isAppleDisplay && monitor.brightnessPath !== "") ? monitor.brightnessPath : ""
watchChanges: path !== ""
onFileChanged: {
reload()
if (monitor.ignoreNextChange) {
monitor.ignoreNextChange = false
return
}
if (text() === "")
return
var current = parseInt(text().trim())
if (!isNaN(current) && monitor.maxBrightness > 0) {
var newBrightness = current / monitor.maxBrightness
// Only update if it's actually different (avoid feedback loops)
if (Math.abs(newBrightness - monitor.brightness) > 0.01) {
monitor.brightness = newBrightness
monitor.brightnessUpdated(monitor.brightness)
//Logger.log("Brightness", "External change detected:", monitor.modelData.name, monitor.brightness)
}
}
// When a file change is detected, actively refresh from system
// to ensure we get the most up-to-date value
Qt.callLater(() => {
monitor.refreshBrightnessFromSystem()
})
}
}
@@ -193,6 +230,7 @@ Singleton {
// Always update
monitor.brightnessUpdated(monitor.brightness)
root.monitorBrightnessChanged(monitor, monitor.brightness)
}
}
}
@@ -229,20 +267,21 @@ Singleton {
value = Math.max(0, Math.min(1, value))
var rounded = Math.round(value * 100)
if (Math.round(monitor.brightness * 100) === rounded)
return
if (timer.running) {
monitor.queuedBrightness = value
return
}
// Update internal value and trigger UI feedback
monitor.brightness = value
brightnessUpdated(monitor.brightness)
monitor.brightnessUpdated(value)
root.monitorBrightnessChanged(monitor, monitor.brightness)
if (isAppleDisplay) {
monitor.ignoreNextChange = true
Quickshell.execDetached(["asdbctl", "set", rounded])
} else if (isDdc) {
monitor.ignoreNextChange = true
Quickshell.execDetached(["ddcutil", "-b", busNum, "setvcp", "10", rounded])
} else {
monitor.ignoreNextChange = true

View File

@@ -10,7 +10,7 @@ Singleton {
id: root
// Public API
property bool active: Settings.isLoaded && Settings.data.appLauncher.enableClipboardHistory && cliphistAvailable
property bool active: Settings.data.appLauncher.enableClipboardHistory && cliphistAvailable
property bool loading: false
property var items: [] // [{id, preview, mime, isImage}]

View File

@@ -14,7 +14,7 @@ Singleton {
// Generic workspace and window data
property ListModel workspaces: ListModel {}
property var windows: []
property ListModel windows: ListModel {}
property int focusedWindowIndex: -1
// Generic events
@@ -82,10 +82,17 @@ Singleton {
workspaceChanged()
})
backend.activeWindowChanged.connect(activeWindowChanged)
backend.activeWindowChanged.connect(() => {
// Sync active window when it changes
// TODO: Avoid re-syncing all windows
syncWindows()
// Forward the signal
activeWindowChanged()
})
backend.windowListChanged.connect(() => {
// Sync windows when they change
windows = backend.windows
syncWindows()
// Forward the signal
windowListChanged()
})
@@ -97,7 +104,7 @@ Singleton {
// Initial sync
syncWorkspaces()
windows = backend.windows
syncWindows()
focusedWindowIndex = backend.focusedWindowIndex
}
@@ -111,10 +118,28 @@ Singleton {
workspacesChanged()
}
// Get window title for focused window
function syncWindows() {
windows.clear()
const ws = backend.windows
for (var i = 0; i < ws.length; i++) {
windows.append(ws[i])
}
// Emit signal to notify listeners that workspace list has been updated
windowListChanged()
}
// Get focused window
function getFocusedWindow() {
if (focusedWindowIndex >= 0 && focusedWindowIndex < windows.count) {
return windows.get(focusedWindowIndex)
}
return null
}
// Get focused window title
function getFocusedWindowTitle() {
if (focusedWindowIndex >= 0 && focusedWindowIndex < windows.length) {
return windows[focusedWindowIndex].title || ""
if (focusedWindowIndex >= 0 && focusedWindowIndex < windows.count) {
return windows.get(focusedWindowIndex).title || ""
}
return ""
}
@@ -139,12 +164,34 @@ Singleton {
return null
}
// Get focused window
function getFocusedWindow() {
if (focusedWindowIndex >= 0 && focusedWindowIndex < windows.length) {
return windows[focusedWindowIndex]
// Get active workspaces
function getActiveWorkspaces() {
const activeWorkspaces = []
for (var i = 0; i < workspaces.count; i++) {
const ws = workspaces.get(i)
if (ws.isActive) {
activeWorkspaces.push(ws)
}
}
return activeWorkspaces
}
// Set focused window
function focusWindow(windowId) {
if (backend && backend.focusWindow) {
backend.focusWindow(windowId)
} else {
Logger.warn("Compositor", "No backend available for window focus")
}
}
// Close window
function closeWindow(windowId) {
if (backend && backend.closeWindow) {
backend.closeWindow(windowId)
} else {
Logger.warn("Compositor", "No backend available for window closing")
}
return null
}
// Session management
@@ -167,4 +214,16 @@ Singleton {
function suspend() {
Quickshell.execDetached(["systemctl", "suspend"])
}
function lockAndSuspend() {
try {
if (PanelService && PanelService.lockScreen && !PanelService.lockScreen.active) {
PanelService.lockScreen.active = true
}
} catch (e) {
Logger.warn("Compositor", "Failed to activate lock screen before suspend: " + e)
}
// Queue suspend to the next event loop cycle to allow lock UI to render
Qt.callLater(suspend)
}
}

View File

@@ -278,6 +278,22 @@ Item {
}
}
function focusWindow(windowId) {
try {
Hyprland.dispatch(`focuswindow ${windowId}`)
} catch (e) {
Logger.error("HyprlandService", "Failed to switch window:", e)
}
}
function closeWindow(windowId) {
try {
Hyprland.dispatch(`killwindow ${windowId}`)
} catch (e) {
Logger.error("HyprlandService", "Failed to close window:", e)
}
}
function logout() {
try {
Quickshell.execDetached(["hyprctl", "dispatch", "exit"])

View File

@@ -10,7 +10,7 @@ import qs.Services
Singleton {
id: root
property string dynamicConfigPath: Settings.isLoaded ? Settings.cacheDir + "matugen.dynamic.toml" : ""
property string dynamicConfigPath: Settings.cacheDir + "matugen.dynamic.toml"
// External state management
Connections {
@@ -47,11 +47,6 @@ Singleton {
// Generate colors using current wallpaper and settings
function generateFromWallpaper() {
if (!Settings.isLoaded) {
Logger.log("Matugen", "Settings not loaded yet, skipping wallpaper color generation")
return
}
Logger.log("Matugen", "Generating from wallpaper on screen:", Screen.name)
var wp = WallpaperService.getWallpaper(Screen.name).replace(/'/g, "'\\''")
if (wp === "") {

View File

@@ -6,6 +6,9 @@ import qs.Commons
Item {
id: root
// Sorts floating windows after scrolling ones
property int floatingWindowPosition: Number.MAX_SAFE_INTEGER
// Properties that match the facade interface
property ListModel workspaces: ListModel {}
property var windows: []
@@ -91,32 +94,7 @@ Item {
onRead: function (line) {
try {
const windowsData = JSON.parse(line)
const windowsList = []
for (const win of windowsData) {
windowsList.push({
"id": win.id,
"title": win.title || "",
"appId": win.app_id || "",
"workspaceId": win.workspace_id || null,
"isFocused": win.is_focused === true
})
}
windowsList.sort((a, b) => a.id - b.id)
windows = windowsList
windowListChanged()
// Update focused window index
focusedWindowIndex = -1
for (var i = 0; i < windowsList.length; i++) {
if (windowsList[i].isFocused) {
focusedWindowIndex = i
break
}
}
activeWindowChanged()
recollectWindows(windowsData)
} catch (e) {
Logger.error("NiriService", "Failed to parse windows:", e, line)
}
@@ -147,8 +125,9 @@ Item {
updateWorkspaces()
} else if (event.WindowFocusChanged) {
handleWindowFocusChanged(event.WindowFocusChanged)
} else if (event.WindowLayoutsChanged) {
handleWindowLayoutsChanged(event.WindowLayoutsChanged)
}
// Removed OverviewOpenedOrClosed handling
} catch (e) {
Logger.error("NiriService", "Error parsing event stream:", e, data)
}
@@ -156,19 +135,81 @@ Item {
}
}
// Utility functions
function getWindowPosition(layout) {
if (layout.pos_in_scrolling_layout) {
return {
"x": layout.pos_in_scrolling_layout[0],
"y": layout.pos_in_scrolling_layout[1]
}
} else {
return {
"x": floatingWindowPosition,
"y": floatingWindowPosition
}
}
}
function getWindowOutput(win) {
for (var i = 0; i < workspaces.count; i++) {
if (workspaces.get(i).id === win.workspace_id) {
return workspaces.get(i).output
}
}
return null
}
function getWindowData(win) {
return {
"id": win.id,
"title": win.title || "",
"appId": win.app_id || "",
"workspaceId": win.workspace_id || -1,
"isFocused": win.is_focused === true,
"output": getWindowOutput(win) || "",
"position": getWindowPosition(win.layout)
}
}
// Sort windows
// 1. by workspace ID
// 2. by position X
// 3. by position Y
function compareWindows(a, b) {
if (a.workspaceId !== b.workspaceId) {
return a.workspaceId - b.workspaceId
}
if (a.position.x !== b.position.x) {
return a.position.x - b.position.x
}
return a.position.y - b.position.y
}
function recollectWindows(windowsData) {
const windowsList = []
for (const win of windowsData) {
windowsList.push(getWindowData(win))
}
windowsList.sort(compareWindows)
windows = windowsList
windowListChanged()
focusedWindowIndex = -1
for (var i = 0; i < windowsList.length; i++) {
if (windowsList[i].isFocused) {
focusedWindowIndex = i
break
}
}
activeWindowChanged()
}
// Event handlers
function handleWindowOpenedOrChanged(eventData) {
try {
const windowData = eventData.window
const existingIndex = windows.findIndex(w => w.id === windowData.id)
const newWindow = {
"id": windowData.id,
"title": windowData.title || "",
"appId": windowData.app_id || "",
"workspaceId": windowData.workspace_id || null,
"isFocused": windowData.is_focused === true
}
const newWindow = getWindowData(windowData)
if (existingIndex >= 0) {
// Update existing window
@@ -176,8 +217,8 @@ Item {
} else {
// Add new window
windows.push(newWindow)
windows.sort((a, b) => a.id - b.id)
}
windows.sort(compareWindows)
// Update focused window index if this window is focused
if (newWindow.isFocused) {
@@ -186,6 +227,9 @@ Item {
// Only emit activeWindowChanged if the focused window actually changed
if (oldFocusedIndex !== focusedWindowIndex) {
if (oldFocusedIndex >= 0 && oldFocusedIndex < windows.length) {
windows[oldFocusedIndex].isFocused = false
}
activeWindowChanged()
}
}
@@ -223,32 +267,7 @@ Item {
function handleWindowsChanged(eventData) {
try {
const windowsData = eventData.windows
const windowsList = []
for (const win of windowsData) {
windowsList.push({
"id": win.id,
"title": win.title || "",
"appId": win.app_id || "",
"workspaceId": win.workspace_id || null,
"isFocused": win.is_focused === true
})
}
windowsList.sort((a, b) => a.id - b.id)
windows = windowsList
windowListChanged()
// Update focused window index
focusedWindowIndex = -1
for (var i = 0; i < windowsList.length; i++) {
if (windowsList[i].isFocused) {
focusedWindowIndex = i
break
}
}
activeWindowChanged()
recollectWindows(windowsData)
} catch (e) {
Logger.error("NiriService", "Error handling WindowsChanged:", e)
}
@@ -258,8 +277,17 @@ Item {
try {
const focusedId = eventData.id
if (windows[focusedWindowIndex]) {
windows[focusedWindowIndex].isFocused = false
}
if (focusedId) {
const newIndex = windows.findIndex(w => w.id === focusedId)
if (newIndex >= 0 && newIndex < windows.length) {
windows[newIndex].isFocused = true
}
focusedWindowIndex = newIndex >= 0 ? newIndex : -1
} else {
focusedWindowIndex = -1
@@ -271,6 +299,25 @@ Item {
}
}
function handleWindowLayoutsChanged(eventData) {
try {
for (const change of eventData.changes) {
const windowId = change[0]
const layout = change[1]
const window = windows.find(w => w.id === windowId)
if (window) {
window.position = getWindowPosition(layout)
}
}
windows.sort(compareWindows)
windowListChanged()
} catch (e) {
Logger.error("NiriService", "Error handling WindowLayoutChanged:", e)
}
}
// Public functions
function switchToWorkspace(workspaceId) {
try {
@@ -280,6 +327,22 @@ Item {
}
}
function focusWindow(windowId) {
try {
Quickshell.execDetached(["niri", "msg", "action", "focus-window", "--id", windowId.toString()])
} catch (e) {
Logger.error("NiriService", "Failed to switch window:", e)
}
}
function closeWindow(windowId) {
try {
Quickshell.execDetached(["niri", "msg", "action", "close-window", "--id", windowId.toString()])
} catch (e) {
Logger.error("NiriService", "Failed to close window:", e)
}
}
function logout() {
try {
Quickshell.execDetached(["niri", "msg", "action", "quit", "--skip-confirmation"])

View File

@@ -25,12 +25,10 @@ Singleton {
property var activeMap: ({})
property var imageQueue: []
property var progressTimers: ({})
// Simple image cacher
PanelWindow {
implicitHeight: 1
implicitWidth: 1
color: "transparent"
color: Color.transparent
mask: Region {}
Image {
@@ -137,7 +135,6 @@ Singleton {
const dest = Settings.cacheDirImagesNotifications + imageId + ".png"
// Skip if already queued
for (const req of imageQueue) {
if (req.imageId === imageId)
return
@@ -149,7 +146,6 @@ Singleton {
"imageId": imageId
})
// If we have a single item in the queue, process it immediately
if (imageQueue.length === 1)
cacher.source = path
}
@@ -262,7 +258,6 @@ Singleton {
items.push(copy)
}
adapter.notifications = items
// Actually write the file
historyFileView.writeAdapter()
} catch (e) {
Logger.error("Notifications", "Save history failed:", e)
@@ -275,10 +270,8 @@ Singleton {
for (const item of adapter.notifications || []) {
const time = new Date(item.timestamp)
// Check if we have a cached image and try to use it
let cachedImage = item.cachedImage || ""
if (item.originalImage && item.originalImage.startsWith("image://") && !cachedImage) {
// Try to generate the expected cached path
const imageId = generateImageId(item, item.originalImage)
if (imageId) {
cachedImage = Settings.cacheDirImagesNotifications + imageId + ".png"
@@ -301,15 +294,54 @@ Singleton {
}
}
// Helpers
function getAppName(name) {
if (!name?.includes("."))
return name || ""
const entries = DesktopEntries.byId(name)
if (entries?.length)
return entries[0].name || name
const parts = name.split(".")
return parts[parts.length - 1].charAt(0).toUpperCase() + parts[parts.length - 1].slice(1)
if (!name || name.trim() === "")
return "Unknown"
name = name.trim()
if (name.includes(".") && (name.startsWith("com.") || name.startsWith("org.") || name.startsWith("io.") || name.startsWith("net."))) {
const parts = name.split(".")
let appPart = parts[parts.length - 1]
if (!appPart || appPart === "app" || appPart === "desktop") {
appPart = parts[parts.length - 2] || parts[0]
}
if (appPart) {
name = appPart
}
}
if (name.includes(".")) {
const parts = name.split(".")
let displayName = parts[parts.length - 1]
if (!displayName || /^\d+$/.test(displayName)) {
displayName = parts[parts.length - 2] || parts[0]
}
if (displayName) {
displayName = displayName.charAt(0).toUpperCase() + displayName.slice(1)
displayName = displayName.replace(/([a-z])([A-Z])/g, '$1 $2')
displayName = displayName.replace(/app$/i, '').trim()
displayName = displayName.replace(/desktop$/i, '').trim()
displayName = displayName.replace(/flatpak$/i, '').trim()
if (!displayName) {
displayName = parts[parts.length - 1].charAt(0).toUpperCase() + parts[parts.length - 1].slice(1)
}
}
return displayName || name
}
let displayName = name.charAt(0).toUpperCase() + name.slice(1)
displayName = displayName.replace(/([a-z])([A-Z])/g, '$1 $2')
displayName = displayName.replace(/app$/i, '').trim()
displayName = displayName.replace(/desktop$/i, '').trim()
return displayName || name
}
function getIcon(icon) {
@@ -326,13 +358,10 @@ Singleton {
function generateImageId(notification, image) {
if (image && image.startsWith("image://")) {
// For qsimage URLs, try to use a combination that's unique per user
if (image.startsWith("image://qsimage/")) {
// Try to use app name + summary for uniqueness (summary often contains username)
const key = (notification.appName || "") + "|" + (notification.summary || "")
return Checksum.sha256(key)
}
return Checksum.sha256(image)
}
return ""
@@ -368,7 +397,6 @@ Singleton {
for (var i = 0; i < historyList.count; i++) {
const notif = historyList.get(i)
if (notif.id === notificationId) {
// Delete cached image if it exists
if (notif.cachedImage && !notif.cachedImage.startsWith("image://")) {
Quickshell.execDetached(["rm", "-f", notif.cachedImage])
}
@@ -381,7 +409,6 @@ Singleton {
}
function clearHistory() {
// Remove all cached images
try {
Quickshell.execDetached(["sh", "-c", `rm -rf "${Settings.cacheDirImagesNotifications}"*`])
} catch (e) {

View File

@@ -15,19 +15,13 @@ Singleton {
Component.onCompleted: {
Logger.log("Scaling", "Service started")
}
Connections {
target: Settings
function onSettingsLoaded() {
// Initialize cache from Settings once they are loaded on startup
var monitors = Settings.data.ui.monitorsScaling || []
for (var i = 0; i < monitors.length; i++) {
if (monitors[i].name && monitors[i].scale !== undefined) {
currentScales[monitors[i].name] = monitors[i].scale
root.scaleChanged(monitors[i].name, monitors[i].scale)
Logger.log("Scaling", "Caching scaling for", monitors[i].name, ":", monitors[i].scale)
}
// Initialize cache from Settings once they are loaded on startup
var monitors = Settings.data.ui.monitorsScaling || []
for (var i = 0; i < monitors.length; i++) {
if (monitors[i].name && monitors[i].scale !== undefined) {
currentScales[monitors[i].name] = monitors[i].scale
root.scaleChanged(monitors[i].name, monitors[i].scale)
Logger.log("Scaling", "Caching scaling for", monitors[i].name, ":", monitors[i].scale)
}
}
}

114
Services/TooltipService.qml Normal file
View File

@@ -0,0 +1,114 @@
pragma Singleton
import QtQuick
import Quickshell
import qs.Commons
import qs.Modules.Tooltip
Singleton {
id: root
property var activeTooltip: null
property var pendingTooltip: null // Track tooltip being created
property Component tooltipComponent: Component {
Tooltip {}
}
function show(target, text, direction, delay) {
// Don't create if no text
if (!target || !text) {
Logger.log("Tooltip", "No target or text")
return
}
// If we have a pending tooltip for a different target, cancel it
if (pendingTooltip && pendingTooltip.targetItem !== target) {
pendingTooltip.hideImmediately()
pendingTooltip.destroy()
pendingTooltip = null
}
// If we have an active tooltip for a different target, hide it
if (activeTooltip && activeTooltip.targetItem !== target) {
activeTooltip.hideImmediately()
// Don't destroy immediately - let it clean itself up
activeTooltip = null
}
// If we already have a tooltip for this target, just update it
if (activeTooltip && activeTooltip.targetItem === target) {
activeTooltip.updateText(text)
return activeTooltip
}
// Create new tooltip instance
const newTooltip = tooltipComponent.createObject(null)
if (newTooltip) {
// Track as pending until it's visible
pendingTooltip = newTooltip
// Connect cleanup when tooltip hides
newTooltip.visibleChanged.connect(() => {
if (!newTooltip.visible) {
// Clean up after a delay to avoid interfering with new tooltips
Qt.callLater(() => {
if (newTooltip && !newTooltip.visible) {
if (activeTooltip === newTooltip) {
activeTooltip = null
}
if (pendingTooltip === newTooltip) {
pendingTooltip = null
}
newTooltip.destroy()
}
})
} else {
// Tooltip is now visible, move from pending to active
if (pendingTooltip === newTooltip) {
activeTooltip = newTooltip
pendingTooltip = null
}
}
})
// Show the tooltip
newTooltip.show(target, text, direction || "auto", delay || Style.tooltipDelay)
return newTooltip
} else {
Logger.error("Tooltip", "Failed to create tooltip instance")
}
return null
}
function hide() {
if (pendingTooltip) {
pendingTooltip.hide()
}
if (activeTooltip) {
activeTooltip.hide()
}
}
function hideImmediately() {
if (pendingTooltip) {
pendingTooltip.hideImmediately()
pendingTooltip.destroy()
pendingTooltip = null
}
if (activeTooltip) {
activeTooltip.hideImmediately()
activeTooltip.destroy()
activeTooltip = null
}
}
function updateText(newText) {
if (activeTooltip) {
activeTooltip.updateText(newText)
}
}
}

View File

@@ -8,7 +8,7 @@ Singleton {
id: root
// Public properties
property string baseVersion: "2.14.4"
property string baseVersion: "2.15.0"
property bool isDevelopment: false
property string currentVersion: `v${!isDevelopment ? baseVersion : baseVersion + "-dev"}`

View File

@@ -26,6 +26,8 @@ Singleton {
// Cache for current wallpapers - can be updated directly since we use signals for notifications
property var currentWallpapers: ({})
property bool isInitialized: false
// Signals for reactive UI updates
signal wallpaperChanged(string screenName, string path)
// Emitted when a wallpaper changes
@@ -75,18 +77,18 @@ Singleton {
function init() {
Logger.log("Wallpaper", "Service started")
// Rebuild cache from persisted settings
var monitors = Settings.data.wallpaper.monitors || []
translateModels()
// Rebuild cache from settings
currentWallpapers = ({})
var monitors = Settings.data.wallpaper.monitors || []
for (var i = 0; i < monitors.length; i++) {
if (monitors[i].name && monitors[i].wallpaper) {
currentWallpapers[monitors[i].name] = monitors[i].wallpaper
// Notify listeners so Background updates immediately after settings load
root.wallpaperChanged(monitors[i].name, monitors[i].wallpaper)
}
}
translateModels()
isInitialized = true
}
// -------------------------------------------------

View File

@@ -110,8 +110,11 @@ void main() {
// Add extra range for smoothness to ensure complete coverage
// Adjust smoothness for aspect ratio to maintain consistent visual appearance
float adjustedSmoothness = mappedSmoothness * max(1.0, ubuf.aspectRatio);
float radius = ubuf.progress * (maxDist + adjustedSmoothness);
// Start the radius from -adjustedSmoothness, this ensures the disc is completely hidden when progress=0
float totalDistance = maxDist + 2.0 * adjustedSmoothness;
float radius = -adjustedSmoothness + ubuf.progress * totalDistance;
// Use smoothstep for a smooth edge transition
float factor = smoothstep(radius - adjustedSmoothness, radius + adjustedSmoothness, dist);

Binary file not shown.

View File

@@ -2,6 +2,7 @@ import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import qs.Commons
import qs.Services
Rectangle {
id: root
@@ -77,9 +78,8 @@ Rectangle {
NIcon {
Layout.alignment: Qt.AlignVCenter
visible: root.icon !== ""
icon: root.icon
font.pointSize: root.iconSize
pointSize: root.iconSize
color: {
if (!root.enabled)
return Color.mOnSurfaceVariant
@@ -104,7 +104,7 @@ Rectangle {
Layout.alignment: Qt.AlignVCenter
visible: root.text !== ""
text: root.text
font.pointSize: root.fontSize
pointSize: root.fontSize
font.weight: root.fontWeight
color: {
if (!root.enabled)
@@ -126,12 +126,6 @@ Rectangle {
}
}
NTooltip {
id: tooltip
target: root
text: root.tooltipText
}
// Mouse interaction
MouseArea {
id: mouseArea
@@ -144,18 +138,18 @@ Rectangle {
onEntered: {
root.hovered = true
if (tooltipText) {
tooltip.show()
TooltipService.show(root, root.tooltipText)
}
}
onExited: {
root.hovered = false
if (tooltipText) {
tooltip.hide()
TooltipService.hide()
}
}
onPressed: mouse => {
if (tooltipText) {
tooltip.hide()
TooltipService.hide()
}
if (mouse.button === Qt.LeftButton) {
root.clicked()
@@ -169,7 +163,7 @@ Rectangle {
onCanceled: {
root.hovered = false
if (tooltipText) {
tooltip.hide()
TooltipService.hide()
}
}
}

Some files were not shown because too many files have changed in this diff Show More