mirror of
https://github.com/zoriya/noctalia-shell.git
synced 2025-12-06 06:36:15 +00:00
Compare commits
88 Commits
8975e4f979
...
b2f601df80
| Author | SHA1 | Date | |
|---|---|---|---|
| b2f601df80 | |||
| 25c3fbbef8 | |||
| eb9c35a9e5 | |||
| bf000f409c | |||
|
|
e3c722fec8 | ||
|
|
2f92445e8a | ||
|
|
59678022ac | ||
|
|
f56109ad35 | ||
|
|
a5fdf67b05 | ||
|
|
d04ae8a81d | ||
|
|
81e9419e83 | ||
|
|
cbe9dc448b | ||
|
|
c15adb5322 | ||
|
|
9d3aacc5f1 | ||
|
|
3f0392d137 | ||
|
|
1d75bf3aec | ||
|
|
0d9c4974b3 | ||
|
|
002df77b78 | ||
|
|
0f83f25a2e | ||
|
|
8b6a6b6bce | ||
|
|
676942a942 | ||
|
|
d6a6341e1a | ||
|
|
60280aa8d4 | ||
|
|
bbde9f9b10 | ||
|
|
faa074c330 | ||
|
|
72ce9953b6 | ||
|
|
7400801dbc | ||
|
|
b9c12ec14c | ||
|
|
5187c8075b | ||
|
|
cfffcdcd24 | ||
|
|
634a9b1a86 | ||
|
|
459f89e751 | ||
|
|
1a5888f277 | ||
|
|
33af5b9ab9 | ||
|
|
4d13cfeedb | ||
|
|
0fe84273df | ||
|
|
e34248d5cb | ||
|
|
1ff64efc64 | ||
|
|
e108a3b45b | ||
|
|
aa34a7ed36 | ||
|
|
190392ead7 | ||
|
|
cee39cce58 | ||
|
|
2035895c04 | ||
|
|
a61a40fd7c | ||
|
|
3efee10a6b | ||
|
|
2d1034ece8 | ||
|
|
69c60a6a5a | ||
|
|
1aac585d62 | ||
|
|
a8ec22984a | ||
|
|
7a3b488546 | ||
|
|
6b80da8e63 | ||
|
|
9911c722a0 | ||
|
|
04fd3b96b2 | ||
|
|
ba8733c5c0 | ||
|
|
5cc71b4da2 | ||
|
|
c3066e1dd5 | ||
|
|
5ee808e186 | ||
|
|
152c8efaa1 | ||
|
|
b93b74ae59 | ||
|
|
8eaa5cc034 | ||
|
|
75bc25747f | ||
|
|
32927aa8a2 | ||
|
|
afd156bbb8 | ||
|
|
24210697bf | ||
|
|
f7bdb3f059 | ||
|
|
c8c6fd7568 | ||
|
|
83d1cbacc7 | ||
|
|
74ba883dd8 | ||
|
|
85ec32336b | ||
|
|
9ef788a354 | ||
|
|
1b706f8469 | ||
|
|
5604d79961 | ||
|
|
7a64758ac8 | ||
|
|
c390e97c7a | ||
|
|
8730eb0e71 | ||
|
|
1dced9a7bc | ||
|
|
aa7563838b | ||
|
|
6dc2bf5a16 | ||
|
|
2c1c1da64c | ||
|
|
0ba80b10a9 | ||
|
|
89d7a63248 | ||
|
|
42f782abbf | ||
|
|
bd1c9d30d9 | ||
|
|
724fed6001 | ||
|
|
5f2a6ffc9d | ||
|
|
211a9e4033 | ||
|
|
bcebaa0185 | ||
|
|
8331d1954d |
1
.github/pull_request_template.md
vendored
1
.github/pull_request_template.md
vendored
@@ -22,6 +22,7 @@ Describe how you tested your changes and mark the relevant items.
|
|||||||
- [ ] Tested on Hyprland
|
- [ ] Tested on Hyprland
|
||||||
- [ ] Tested on sway
|
- [ ] Tested on sway
|
||||||
- [ ] Tested with different bar positions and density settings
|
- [ ] Tested with different bar positions and density settings
|
||||||
|
- [ ] Tested at different interface scaling values
|
||||||
- [ ] Tested with multiple monitors (if applicable)
|
- [ ] Tested with multiple monitors (if applicable)
|
||||||
|
|
||||||
## Screenshots / Videos
|
## Screenshots / Videos
|
||||||
|
|||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,3 +1,4 @@
|
|||||||
.qmlls.ini
|
.qmlls.ini
|
||||||
.zed
|
.zed
|
||||||
Bin/battery-manager/uninstall-battery-manager.sh
|
Bin/battery-manager/uninstall-battery-manager.sh
|
||||||
|
.idea
|
||||||
74
Assets/Launcher/emoji.json
Normal file
74
Assets/Launcher/emoji.json
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
[
|
||||||
|
{"emoji": "😀", "name": "grinning face", "keywords": ["smile", "happy", "grin"], "category": "people"},
|
||||||
|
{"emoji": "😂", "name": "face with tears of joy", "keywords": ["laugh", "cry", "happy", "joy"], "category": "people"},
|
||||||
|
{"emoji": "😍", "name": "smiling face with heart-eyes", "keywords": ["love", "heart", "eyes", "smile"], "category": "people"},
|
||||||
|
{"emoji": "🤔", "name": "thinking face", "keywords": ["think", "ponder", "consider"], "category": "people"},
|
||||||
|
{"emoji": "😎", "name": "smiling face with sunglasses", "keywords": ["cool", "sunglasses", "smile"], "category": "people"},
|
||||||
|
{"emoji": "🥳", "name": "partying face", "keywords": ["party", "hat", "horn", "celebration"], "category": "people"},
|
||||||
|
{"emoji": "🤩", "name": "star-struck", "keywords": ["star", "eyes", "amazed", "wow"], "category": "people"},
|
||||||
|
{"emoji": "🤯", "name": "exploding head", "keywords": ["mind", "blown", "explode", "shocked"], "category": "people"},
|
||||||
|
{"emoji": "👍", "name": "thumbs up", "keywords": ["like", "good", "agree", "ok"], "category": "people"},
|
||||||
|
{"emoji": "👎", "name": "thumbs down", "keywords": ["dislike", "bad", "disagree", "no"], "category": "people"},
|
||||||
|
{"emoji": "🐱", "name": "cat face", "keywords": ["cat", "kitten", "pet", "meow"], "category": "animals"},
|
||||||
|
{"emoji": "🐶", "name": "dog face", "keywords": ["dog", "puppy", "pet", "woof"], "category": "animals"},
|
||||||
|
{"emoji": "🦊", "name": "fox face", "keywords": ["fox", "animal", "cute", "wild"], "category": "animals"},
|
||||||
|
{"emoji": "🐼", "name": "panda", "keywords": ["panda", "bear", "animal", "cute"], "category": "animals"},
|
||||||
|
{"emoji": "🦄", "name": "unicorn", "keywords": ["unicorn", "horse", "magic", "fantasy"], "category": "animals"},
|
||||||
|
{"emoji": "🦁", "name": "lion", "keywords": ["lion", "animal", "face", "majestic"], "category": "animals"},
|
||||||
|
{"emoji": "🐢", "name": "turtle", "keywords": ["turtle", "slow", "animal", "shell"], "category": "animals"},
|
||||||
|
{"emoji": "🐙", "name": "octopus", "keywords": ["octopus", "animal", "ocean", "sea"], "category": "animals"},
|
||||||
|
{"emoji": "🌻", "name": "sunflower", "keywords": ["sunflower", "flower", "nature", "yellow"], "category": "nature"},
|
||||||
|
{"emoji": "🌺", "name": "hibiscus", "keywords": ["hibiscus", "flower", "nature", "plant"], "category": "nature"},
|
||||||
|
{"emoji": "🌍", "name": "earth globe europe-africa", "keywords": ["earth", "world", "globe", "nature"], "category": "nature"},
|
||||||
|
{"emoji": "🌞", "name": "sun with face", "keywords": ["sun", "nature", "bright", "weather"], "category": "nature"},
|
||||||
|
{"emoji": "🌙", "name": "crescent moon", "keywords": ["moon", "night", "sky", "sleep"], "category": "nature"},
|
||||||
|
{"emoji": "🌈", "name": "rainbow", "keywords": ["rainbow", "color", "weather", "sky"], "category": "nature"},
|
||||||
|
{"emoji": "🔥", "name": "fire", "keywords": ["fire", "hot", "flame", "burn"], "category": "nature"},
|
||||||
|
{"emoji": "💧", "name": "droplet", "keywords": ["water", "drop", "drip", "liquid"], "category": "nature"},
|
||||||
|
{"emoji": "🍎", "name": "red apple", "keywords": ["apple", "fruit", "food", "red"], "category": "food"},
|
||||||
|
{"emoji": "🍕", "name": "pizza", "keywords": ["pizza", "food", "italian", "cheese"], "category": "food"},
|
||||||
|
{"emoji": " sushi", "name": "sushi", "keywords": ["sushi", "food", "japanese", "rice"], "category": "food"},
|
||||||
|
{"emoji": "🍔", "name": "hamburger", "keywords": ["hamburger", "food", "burger", "fast food"], "category": "food"},
|
||||||
|
{"emoji": "🍦", "name": "soft ice cream", "keywords": ["ice cream", "dessert", "food", "sweet"], "category": "food"},
|
||||||
|
{"emoji": "🍩", "name": "doughnut", "keywords": ["donut", "doughnut", "food", "sweet"], "category": "food"},
|
||||||
|
{"emoji": "🍪", "name": "cookie", "keywords": ["cookie", "food", "sweet", "biscuit"], "category": "food"},
|
||||||
|
{"emoji": "🍺", "name": "beer mug", "keywords": ["beer", "drink", "alcohol", "pub"], "category": "food"},
|
||||||
|
{"emoji": "🍷", "name": "wine glass", "keywords": ["wine", "drink", "alcohol", "glass"], "category": "food"},
|
||||||
|
{"emoji": "☕", "name": "hot beverage", "keywords": ["coffee", "hot", "drink", "cafe"], "category": "food"},
|
||||||
|
{"emoji": "⚽", "name": "soccer ball", "keywords": ["soccer", "football", "ball", "sport"], "category": "activity"},
|
||||||
|
{"emoji": "🏀", "name": "basketball", "keywords": ["basketball", "ball", "sport", "game"], "category": "activity"},
|
||||||
|
{"emoji": "🎯", "name": "direct hit", "keywords": ["target", "bullseye", "aim", "goal"], "category": "activity"},
|
||||||
|
{"emoji": "🎮", "name": "video game", "keywords": ["game", "video game", "play", "console"], "category": "activity"},
|
||||||
|
{"emoji": "🎲", "name": "game die", "keywords": ["dice", "game", "board", "random"], "category": "activity"},
|
||||||
|
{"emoji": "🎨", "name": "artist palette", "keywords": ["art", "paint", "colors", "creative"], "category": "activity"},
|
||||||
|
{"emoji": "🎤", "name": "microphone", "keywords": ["mic", "microphone", "sing", "karaoke"], "category": "activity"},
|
||||||
|
{"emoji": "🎬", "name": "clapper board", "keywords": ["movie", "film", "action", "director"], "category": "activity"},
|
||||||
|
{"emoji": "🚗", "name": "automobile", "keywords": ["car", "vehicle", "transport", "drive"], "category": "travel"},
|
||||||
|
{"emoji": "✈️", "name": "airplane", "keywords": ["plane", "flight", "travel", "fly"], "category": "travel"},
|
||||||
|
{"emoji": "🚀", "name": "rocket", "keywords": ["space", "launch", "fast", "ship"], "category": "travel"},
|
||||||
|
{"emoji": "🚲", "name": "bicycle", "keywords": ["bike", "cycle", "transport", "exercise"], "category": "travel"},
|
||||||
|
{"emoji": "🚂", "name": "locomotive", "keywords": ["train", "steam", "vehicle", "transport"], "category": "travel"},
|
||||||
|
{"emoji": "🚢", "name": "ship", "keywords": ["ship", "boat", "water", "transport"], "category": "travel"},
|
||||||
|
{"emoji": "🏠", "name": "house", "keywords": ["home", "house", "building", "residence"], "category": "objects"},
|
||||||
|
{"emoji": "🏢", "name": "office building", "keywords": ["office", "building", "work", "business"], "category": "objects"},
|
||||||
|
{"emoji": "🏥", "name": "hospital", "keywords": ["hospital", "medical", "health", "doctor"], "category": "objects"},
|
||||||
|
{"emoji": "🏦", "name": "bank", "keywords": ["bank", "money", "finance", "building"], "category": "objects"},
|
||||||
|
{"emoji": "🏪", "name": "convenience store", "keywords": ["store", "shop", "convenience", "grocery"], "category": "objects"},
|
||||||
|
{"emoji": "🎁", "name": "gift", "keywords": ["present", "gift", "box", "birthday"], "category": "objects"},
|
||||||
|
{"emoji": "💡", "name": "light bulb", "keywords": ["idea", "light", "bright", "thinking"], "category": "objects"},
|
||||||
|
{"emoji": "💻", "name": "laptop computer", "keywords": ["computer", "laptop", "pc", "work"], "category": "objects"},
|
||||||
|
{"emoji": "📱", "name": "mobile phone", "keywords": ["phone", "smartphone", "cellphone", "mobile"], "category": "objects"},
|
||||||
|
{"emoji": "🔑", "name": "key", "keywords": ["key", "password", "secret", "access"], "category": "objects"},
|
||||||
|
{"emoji": "🔒", "name": "locked", "keywords": ["lock", "secure", "private", "closed"], "category": "objects"},
|
||||||
|
{"emoji": "⭐", "name": "star", "keywords": ["star", "rating", "favorite", "bright"], "category": "symbols"},
|
||||||
|
{"emoji": "❤️", "name": "red heart", "keywords": ["heart", "love", "like", "affection"], "category": "symbols"},
|
||||||
|
{"emoji": "💯", "name": "hundred points", "keywords": ["percent", "perfect", "score", "100"], "category": "symbols"},
|
||||||
|
{"emoji": "©️", "name": "copyright", "keywords": ["copyright", "symbol", "c", "legal"], "category": "symbols"},
|
||||||
|
{"emoji": "®️", "name": "registered", "keywords": ["registered", "symbol", "r", "trademark"], "category": "symbols"},
|
||||||
|
{"emoji": "™️", "name": "trade mark", "keywords": ["trademark", "tm", "symbol", "mark"], "category": "symbols"},
|
||||||
|
{"emoji": "✔️", "name": "check mark", "keywords": ["check", "mark", "ok", "correct"], "category": "symbols"},
|
||||||
|
{"emoji": "❌", "name": "cross mark", "keywords": ["x", "cross", "mark", "no", "wrong"], "category": "symbols"},
|
||||||
|
{"emoji": "⚠️", "name": "warning", "keywords": ["warning", "exclamation", "caution", "alert"], "category": "symbols"},
|
||||||
|
{"emoji": "🎉", "name": "party popper", "keywords": ["party", "celebration", "tada", "congrats"], "category": "symbols"},
|
||||||
|
{"emoji": "🔔", "name": "bell", "keywords": ["bell", "sound", "notification", "ring"], "category": "symbols"}
|
||||||
|
]
|
||||||
16
Assets/MatugenTemplates/cava.ini
Normal file
16
Assets/MatugenTemplates/cava.ini
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
[color]
|
||||||
|
background = '{{colors.surface.default.hex}}'
|
||||||
|
foreground = '{{colors.primary.default.hex}}'
|
||||||
|
|
||||||
|
gradient = 1
|
||||||
|
gradient_color_1 = '{{colors.primary_container.default.hex}}'
|
||||||
|
gradient_color_2 = '{{colors.primary.default.hex}}'
|
||||||
|
gradient_color_3 = '{{colors.on_primary_container.default.hex}}'
|
||||||
|
|
||||||
|
horizontal_gradient = 0
|
||||||
|
horizontal_gradient_color_1 = '{{colors.primary_container.default.hex}}'
|
||||||
|
horizontal_gradient_color_2 = '{{colors.primary.default.hex}}'
|
||||||
|
horizontal_gradient_color_3 = '{{colors.on_primary_container.default.hex}}'
|
||||||
|
horizontal_gradient_color_4 = '{{colors.primary.default.hex}}'
|
||||||
|
horizontal_gradient_color_5 = '{{colors.primary_container.default.hex}}'
|
||||||
|
|
||||||
@@ -231,6 +231,10 @@
|
|||||||
"description": "Audio-Visualizer anzeigen, wenn Musik abgespielt wird.",
|
"description": "Audio-Visualizer anzeigen, wenn Musik abgespielt wird.",
|
||||||
"label": "Visualizer anzeigen"
|
"label": "Visualizer anzeigen"
|
||||||
},
|
},
|
||||||
|
"show-progress-ring": {
|
||||||
|
"description": "Runden Fortschrittsindikator anzeigen, der den Titelfortschritt anzeigt.",
|
||||||
|
"label": "Fortschrittsring anzeigen"
|
||||||
|
},
|
||||||
"use-fixed-width": {
|
"use-fixed-width": {
|
||||||
"description": "Wenn aktiviert, verwendet das Widget immer die maximale Breite, anstatt sich dynamisch an den Inhalt anzupassen.",
|
"description": "Wenn aktiviert, verwendet das Widget immer die maximale Breite, anstatt sich dynamisch an den Inhalt anzupassen.",
|
||||||
"label": "Feste Breite verwenden"
|
"label": "Feste Breite verwenden"
|
||||||
@@ -373,6 +377,10 @@
|
|||||||
"description": "Arbeitsbereiche ohne Fenster nicht anzeigen.",
|
"description": "Arbeitsbereiche ohne Fenster nicht anzeigen.",
|
||||||
"label": "Unbesetzte ausblenden"
|
"label": "Unbesetzte ausblenden"
|
||||||
},
|
},
|
||||||
|
"follow-focused-screen": {
|
||||||
|
"description": "Zeigt Arbeitsbereiche vom aktuell fokussierten Bildschirm an, statt vom Bildschirm, auf dem sich die Leiste befindet.",
|
||||||
|
"label": "Fokussiertem Bildschirm folgen"
|
||||||
|
},
|
||||||
"label-mode": {
|
"label-mode": {
|
||||||
"description": "Wählen Sie, wie Arbeitsbereichs-Beschriftungen angezeigt werden.",
|
"description": "Wählen Sie, wie Arbeitsbereichs-Beschriftungen angezeigt werden.",
|
||||||
"label": "Beschriftungsmodus"
|
"label": "Beschriftungsmodus"
|
||||||
@@ -381,14 +389,20 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"battery": {
|
"battery": {
|
||||||
|
"brightness": "Helligkeit",
|
||||||
|
"charge-level": "Ladestand",
|
||||||
"charging": "Wird geladen.",
|
"charging": "Wird geladen.",
|
||||||
"charging-rate": "Laderate: {rate} W.",
|
"charging-rate": "Laderate: {rate} W.",
|
||||||
"discharging": "Wird entladen.",
|
"discharging": "Wird entladen.",
|
||||||
"discharging-rate": "Entladerate: {rate} W.",
|
"discharging-rate": "Entladerate: {rate} W.",
|
||||||
"health": "Zustand: {percent}%",
|
"health": "Zustand: {percent}%",
|
||||||
"idle": "Leerlauf.",
|
"idle": "Leerlauf.",
|
||||||
|
"inhibit-idle-description": "Hält das System wach.",
|
||||||
|
"inhibit-idle-label": "Wach halten",
|
||||||
"no-battery-detected": "Keine Batterie erkannt.",
|
"no-battery-detected": "Keine Batterie erkannt.",
|
||||||
|
"panel-title": "Akku",
|
||||||
"plugged-in": "Angeschlossen.",
|
"plugged-in": "Angeschlossen.",
|
||||||
|
"power-profile": "Energieprofil",
|
||||||
"time-left": "Verbleibende Zeit: {time}.",
|
"time-left": "Verbleibende Zeit: {time}.",
|
||||||
"time-until-full": "Zeit bis vollständig geladen: {time}."
|
"time-until-full": "Zeit bis vollständig geladen: {time}."
|
||||||
},
|
},
|
||||||
@@ -494,7 +508,7 @@
|
|||||||
"password": "Passwort eingeben...",
|
"password": "Passwort eingeben...",
|
||||||
"restart": "Neu starten",
|
"restart": "Neu starten",
|
||||||
"shut-down": "Herunterfahren",
|
"shut-down": "Herunterfahren",
|
||||||
"suspend": "Ruhezustand",
|
"suspend": "Energie sparen",
|
||||||
"welcome-back": "Willkommen zurück,"
|
"welcome-back": "Willkommen zurück,"
|
||||||
},
|
},
|
||||||
"notifications": {
|
"notifications": {
|
||||||
@@ -503,6 +517,15 @@
|
|||||||
"description": "Ihre Benachrichtigungen werden hier angezeigt, sobald sie eintreffen.",
|
"description": "Ihre Benachrichtigungen werden hier angezeigt, sobald sie eintreffen.",
|
||||||
"no-notifications": "Keine Benachrichtigungen",
|
"no-notifications": "Keine Benachrichtigungen",
|
||||||
"title": "Benachrichtigungen"
|
"title": "Benachrichtigungen"
|
||||||
|
},
|
||||||
|
"time": {
|
||||||
|
"now": "jetzt",
|
||||||
|
"diffM": "vor 1 Minute",
|
||||||
|
"diffMM": "vor {diff} Minuten",
|
||||||
|
"diffH": "vor 1 Stunde",
|
||||||
|
"diffHH": "vor {diff} Stunden",
|
||||||
|
"diffD": "vor 1 Tag",
|
||||||
|
"diffDD": "vor {diff} Tagen"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"options": {
|
"options": {
|
||||||
@@ -670,7 +693,11 @@
|
|||||||
"clipboard-history-disabled-description": "Zwischenablageverlauf in den Einstellungen aktivieren oder cliphist installieren",
|
"clipboard-history-disabled-description": "Zwischenablageverlauf in den Einstellungen aktivieren oder cliphist installieren",
|
||||||
"clipboard-loading": "Lade Zwischenablageverlauf...",
|
"clipboard-loading": "Lade Zwischenablageverlauf...",
|
||||||
"clipboard-loading-description": "Bitte warten",
|
"clipboard-loading-description": "Bitte warten",
|
||||||
"clipboard-search-description": "Zwischenablageverlauf durchsuchen"
|
"clipboard-search-description": "Zwischenablageverlauf durchsuchen",
|
||||||
|
"emoji": "Emoji-Auswahl",
|
||||||
|
"emoji-loading": "Lade Emojis...",
|
||||||
|
"emoji-loading-description": "Bitte warten",
|
||||||
|
"emoji-search-description": "Emojis suchen und kopieren"
|
||||||
},
|
},
|
||||||
"quickSettings": {
|
"quickSettings": {
|
||||||
"bluetooth": {
|
"bluetooth": {
|
||||||
@@ -749,11 +776,11 @@
|
|||||||
"action-in-seconds": "{action} in {seconds} Sekunden...",
|
"action-in-seconds": "{action} in {seconds} Sekunden...",
|
||||||
"hibernate": "Ruhezustand",
|
"hibernate": "Ruhezustand",
|
||||||
"lock": "Sperren",
|
"lock": "Sperren",
|
||||||
"lock-and-suspend": "Sperren und Ruhezustand",
|
"lock-and-suspend": "Sperren und Energie sparen",
|
||||||
"logout": "Abmelden",
|
"logout": "Abmelden",
|
||||||
"reboot": "Neu starten",
|
"reboot": "Neu starten",
|
||||||
"shutdown": "Herunterfahren",
|
"shutdown": "Herunterfahren",
|
||||||
"suspend": "Energiesparmodus",
|
"suspend": "Energie sparen",
|
||||||
"title": "Sitzungsmenü"
|
"title": "Sitzungsmenü"
|
||||||
},
|
},
|
||||||
"settings": {
|
"settings": {
|
||||||
@@ -1048,6 +1075,10 @@
|
|||||||
"description": "Schreibe {filepath}.",
|
"description": "Schreibe {filepath}.",
|
||||||
"description-missing": "Benötigt die Installation von {app}"
|
"description-missing": "Benötigt die Installation von {app}"
|
||||||
},
|
},
|
||||||
|
"cava": {
|
||||||
|
"description": "Schreibe {filepath}.",
|
||||||
|
"description-missing": "Benötigt die Installation von {app}"
|
||||||
|
},
|
||||||
"vicinae": {
|
"vicinae": {
|
||||||
"description": "Schreibt {filepath} und lädt neu",
|
"description": "Schreibt {filepath} und lädt neu",
|
||||||
"description-missing": "Erfordert die Installation von {app}"
|
"description-missing": "Erfordert die Installation von {app}"
|
||||||
@@ -1507,8 +1538,8 @@
|
|||||||
"label": "Kompakter Sperrbildschirm"
|
"label": "Kompakter Sperrbildschirm"
|
||||||
},
|
},
|
||||||
"lock-on-suspend": {
|
"lock-on-suspend": {
|
||||||
"description": "Den Bildschirm beim Suspendieren des Systems automatisch sperren.",
|
"description": "Den Bildschirm beim Energiesparen automatisch sperren.",
|
||||||
"label": "Sperren beim Ruhezustand"
|
"label": "Sperren beim Energiesparen"
|
||||||
},
|
},
|
||||||
"show-hibernate": {
|
"show-hibernate": {
|
||||||
"description": "Die Option 'Ruhezustand' in den Energieaktionen anzeigen.",
|
"description": "Die Option 'Ruhezustand' in den Energieaktionen anzeigen.",
|
||||||
@@ -2249,23 +2280,11 @@
|
|||||||
"widgets": {
|
"widgets": {
|
||||||
"color-picker": {
|
"color-picker": {
|
||||||
"apply": "Anwenden",
|
"apply": "Anwenden",
|
||||||
"brightness": "Helligkeit",
|
|
||||||
"cancel": "Abbrechen",
|
"cancel": "Abbrechen",
|
||||||
"hex": {
|
|
||||||
"description": "Geben Sie einen hexadezimalen Farbcode ein.",
|
|
||||||
"label": "Hex-Farbe"
|
|
||||||
},
|
|
||||||
"palette": {
|
"palette": {
|
||||||
"description": "Wählen Sie aus einer großen Auswahl vordefinierter Farben.",
|
"description": "Wählen Sie aus einer großen Auswahl vordefinierter Farben.",
|
||||||
"label": "Palette"
|
"label": "Palette",
|
||||||
},
|
"theme-colors": "Schneller Zugriff auf die Farbpalette Ihres Themes."
|
||||||
"rgb": {
|
|
||||||
"description": "Passen Sie Rot-, Grün-, Blau- und Helligkeitswerte an.",
|
|
||||||
"label": "RGB-Werte"
|
|
||||||
},
|
|
||||||
"theme-colors": {
|
|
||||||
"description": "Schnellzugriff auf die Farbpalette Ihres Themes.",
|
|
||||||
"label": "Theme-Farben"
|
|
||||||
},
|
},
|
||||||
"title": "Farbauswahl"
|
"title": "Farbauswahl"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -231,6 +231,10 @@
|
|||||||
"description": "Display an audio visualizer when music is playing.",
|
"description": "Display an audio visualizer when music is playing.",
|
||||||
"label": "Show visualizer"
|
"label": "Show visualizer"
|
||||||
},
|
},
|
||||||
|
"show-progress-ring": {
|
||||||
|
"description": "Display a circular progress indicator showing track progress.",
|
||||||
|
"label": "Show progress ring"
|
||||||
|
},
|
||||||
"use-fixed-width": {
|
"use-fixed-width": {
|
||||||
"description": "When enabled, the widget will always use the maximum width instead of dynamically adjusting to content.",
|
"description": "When enabled, the widget will always use the maximum width instead of dynamically adjusting to content.",
|
||||||
"label": "Use Fixed Width"
|
"label": "Use Fixed Width"
|
||||||
@@ -373,6 +377,10 @@
|
|||||||
"description": "Don't display workspaces without windows.",
|
"description": "Don't display workspaces without windows.",
|
||||||
"label": "Hide unoccupied"
|
"label": "Hide unoccupied"
|
||||||
},
|
},
|
||||||
|
"follow-focused-screen": {
|
||||||
|
"description": "Display workspaces from the currently focused screen, rather than the screen where the bar is located.",
|
||||||
|
"label": "Follow Focused Screen"
|
||||||
|
},
|
||||||
"label-mode": {
|
"label-mode": {
|
||||||
"description": "Choose how workspace labels are displayed.",
|
"description": "Choose how workspace labels are displayed.",
|
||||||
"label": "Label Mode"
|
"label": "Label Mode"
|
||||||
@@ -381,14 +389,20 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"battery": {
|
"battery": {
|
||||||
|
"brightness": "Brightness",
|
||||||
|
"charge-level": "Charge level",
|
||||||
"charging": "Charging",
|
"charging": "Charging",
|
||||||
"charging-rate": "Charging rate: {rate} W",
|
"charging-rate": "Charging rate: {rate} W",
|
||||||
"discharging": "Discharging",
|
"discharging": "Discharging",
|
||||||
"discharging-rate": "Discharging rate: {rate} W",
|
"discharging-rate": "Discharging rate: {rate} W",
|
||||||
"health": "Health: {percent}%",
|
"health": "Health: {percent}%",
|
||||||
"idle": "Idle",
|
"idle": "Idle",
|
||||||
|
"inhibit-idle-description": "Keeps the system awake.",
|
||||||
|
"inhibit-idle-label": "Keep awake",
|
||||||
"no-battery-detected": "No battery detected",
|
"no-battery-detected": "No battery detected",
|
||||||
|
"panel-title": "Battery",
|
||||||
"plugged-in": "Plugged in",
|
"plugged-in": "Plugged in",
|
||||||
|
"power-profile": "Power profile",
|
||||||
"time-left": "Time left: {time}",
|
"time-left": "Time left: {time}",
|
||||||
"time-until-full": "Time until full: {time}"
|
"time-until-full": "Time until full: {time}"
|
||||||
},
|
},
|
||||||
@@ -503,6 +517,15 @@
|
|||||||
"description": "Your notifications will show up here as they arrive.",
|
"description": "Your notifications will show up here as they arrive.",
|
||||||
"no-notifications": "No notifications",
|
"no-notifications": "No notifications",
|
||||||
"title": "Notifications"
|
"title": "Notifications"
|
||||||
|
},
|
||||||
|
"time": {
|
||||||
|
"now": "now",
|
||||||
|
"diffM": "1 minute ago",
|
||||||
|
"diffMM": "{diff} minutes ago",
|
||||||
|
"diffH": "1 hour ago",
|
||||||
|
"diffHH": "{diff} hours ago",
|
||||||
|
"diffD": "1 day ago",
|
||||||
|
"diffDD": "{diff} days ago"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"options": {
|
"options": {
|
||||||
@@ -570,7 +593,8 @@
|
|||||||
"follow_bar": "Follow bar (default)",
|
"follow_bar": "Follow bar (default)",
|
||||||
"top_center": "Top center",
|
"top_center": "Top center",
|
||||||
"top_left": "Top left",
|
"top_left": "Top left",
|
||||||
"top_right": "Top right"
|
"top_right": "Top right",
|
||||||
|
"bar": "Bar (inline)"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"osd": {
|
"osd": {
|
||||||
@@ -670,7 +694,11 @@
|
|||||||
"clipboard-history-disabled-description": "Enable clipboard history in settings or install cliphist",
|
"clipboard-history-disabled-description": "Enable clipboard history in settings or install cliphist",
|
||||||
"clipboard-loading": "Loading clipboard history...",
|
"clipboard-loading": "Loading clipboard history...",
|
||||||
"clipboard-loading-description": "Please wait",
|
"clipboard-loading-description": "Please wait",
|
||||||
"clipboard-search-description": "Search clipboard history"
|
"clipboard-search-description": "Search clipboard history",
|
||||||
|
"emoji": "Emoji picker",
|
||||||
|
"emoji-loading": "Loading emojis...",
|
||||||
|
"emoji-loading-description": "Please wait",
|
||||||
|
"emoji-search-description": "Search and copy emojis"
|
||||||
},
|
},
|
||||||
"quickSettings": {
|
"quickSettings": {
|
||||||
"bluetooth": {
|
"bluetooth": {
|
||||||
@@ -1048,6 +1076,10 @@
|
|||||||
"description": "Write {filepath}.",
|
"description": "Write {filepath}.",
|
||||||
"description-missing": "Requires {app} to be installed"
|
"description-missing": "Requires {app} to be installed"
|
||||||
},
|
},
|
||||||
|
"cava": {
|
||||||
|
"description": "Write {filepath}.",
|
||||||
|
"description-missing": "Requires {app} to be installed"
|
||||||
|
},
|
||||||
"vicinae": {
|
"vicinae": {
|
||||||
"description": "Write {filepath} and reload",
|
"description": "Write {filepath} and reload",
|
||||||
"description-missing": "Requires {app} to be installed"
|
"description-missing": "Requires {app} to be installed"
|
||||||
@@ -2249,23 +2281,11 @@
|
|||||||
"widgets": {
|
"widgets": {
|
||||||
"color-picker": {
|
"color-picker": {
|
||||||
"apply": "Apply",
|
"apply": "Apply",
|
||||||
"brightness": "Brightness",
|
|
||||||
"cancel": "Cancel",
|
"cancel": "Cancel",
|
||||||
"hex": {
|
|
||||||
"description": "Enter a hexadecimal color code.",
|
|
||||||
"label": "Hex color"
|
|
||||||
},
|
|
||||||
"palette": {
|
"palette": {
|
||||||
"description": "Choose from a wide range of predefined colors.",
|
"description": "Choose from a wide range of predefined colors.",
|
||||||
"label": "Palette"
|
"label": "Palette",
|
||||||
},
|
"theme-colors": "Quick access to your theme's color palette."
|
||||||
"rgb": {
|
|
||||||
"description": "Adjust red, green, blue, and brightness values.",
|
|
||||||
"label": "RGB values"
|
|
||||||
},
|
|
||||||
"theme-colors": {
|
|
||||||
"description": "Quick access to your theme's color palette.",
|
|
||||||
"label": "Theme colors"
|
|
||||||
},
|
},
|
||||||
"title": "Color picker"
|
"title": "Color picker"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -231,6 +231,10 @@
|
|||||||
"description": "Mostrar un visualizador de audio cuando se reproduce música.",
|
"description": "Mostrar un visualizador de audio cuando se reproduce música.",
|
||||||
"label": "Mostrar visualizador"
|
"label": "Mostrar visualizador"
|
||||||
},
|
},
|
||||||
|
"show-progress-ring": {
|
||||||
|
"description": "Mostrar un indicador de progreso circular que muestre el progreso de la pista.",
|
||||||
|
"label": "Mostrar anillo de progreso"
|
||||||
|
},
|
||||||
"use-fixed-width": {
|
"use-fixed-width": {
|
||||||
"description": "Cuando está activado, el widget siempre usará el ancho máximo en lugar de ajustarse dinámicamente al contenido.",
|
"description": "Cuando está activado, el widget siempre usará el ancho máximo en lugar de ajustarse dinámicamente al contenido.",
|
||||||
"label": "Usar Ancho Fijo"
|
"label": "Usar Ancho Fijo"
|
||||||
@@ -373,6 +377,10 @@
|
|||||||
"description": "No mostrar espacios de trabajo sin ventanas.",
|
"description": "No mostrar espacios de trabajo sin ventanas.",
|
||||||
"label": "Ocultar desocupados"
|
"label": "Ocultar desocupados"
|
||||||
},
|
},
|
||||||
|
"follow-focused-screen": {
|
||||||
|
"description": "Mostrar espacios de trabajo de la pantalla actualmente enfocada, en lugar de la pantalla donde se encuentra la barra.",
|
||||||
|
"label": "Seguir Pantalla Enfocada"
|
||||||
|
},
|
||||||
"label-mode": {
|
"label-mode": {
|
||||||
"description": "Elegir cómo se muestran las etiquetas de los espacios de trabajo.",
|
"description": "Elegir cómo se muestran las etiquetas de los espacios de trabajo.",
|
||||||
"label": "Modo de etiqueta"
|
"label": "Modo de etiqueta"
|
||||||
@@ -381,14 +389,20 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"battery": {
|
"battery": {
|
||||||
|
"brightness": "Brillo",
|
||||||
|
"charge-level": "Nivel de carga",
|
||||||
"charging": "Cargando.",
|
"charging": "Cargando.",
|
||||||
"charging-rate": "Tasa de carga: {rate} W.",
|
"charging-rate": "Tasa de carga: {rate} W.",
|
||||||
"discharging": "Descargando.",
|
"discharging": "Descargando.",
|
||||||
"discharging-rate": "Tasa de descarga: {rate} W.",
|
"discharging-rate": "Tasa de descarga: {rate} W.",
|
||||||
"health": "Salud: {percent}%",
|
"health": "Salud: {percent}%",
|
||||||
"idle": "Inactivo.",
|
"idle": "Inactivo.",
|
||||||
|
"inhibit-idle-description": "Mantiene el sistema despierto.",
|
||||||
|
"inhibit-idle-label": "Mantener despierto",
|
||||||
"no-battery-detected": "No se detectó ninguna batería.",
|
"no-battery-detected": "No se detectó ninguna batería.",
|
||||||
|
"panel-title": "Batería",
|
||||||
"plugged-in": "Conectado.",
|
"plugged-in": "Conectado.",
|
||||||
|
"power-profile": "Perfil de energía",
|
||||||
"time-left": "Tiempo restante: {time}.",
|
"time-left": "Tiempo restante: {time}.",
|
||||||
"time-until-full": "Tiempo hasta carga completa: {time}."
|
"time-until-full": "Tiempo hasta carga completa: {time}."
|
||||||
},
|
},
|
||||||
@@ -503,6 +517,15 @@
|
|||||||
"description": "Tus notificaciones aparecerán aquí a medida que lleguen.",
|
"description": "Tus notificaciones aparecerán aquí a medida que lleguen.",
|
||||||
"no-notifications": "No hay notificaciones",
|
"no-notifications": "No hay notificaciones",
|
||||||
"title": "Notificaciones"
|
"title": "Notificaciones"
|
||||||
|
},
|
||||||
|
"time": {
|
||||||
|
"now": "ahora",
|
||||||
|
"diffM": "hace 1 minuto",
|
||||||
|
"diffMM": "hace {diff} minutos",
|
||||||
|
"diffH": "hace 1 hora",
|
||||||
|
"diffHH": "hace {diff} horas",
|
||||||
|
"diffD": "hace 1 día",
|
||||||
|
"diffDD": "hace {diff} días"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"options": {
|
"options": {
|
||||||
@@ -670,7 +693,11 @@
|
|||||||
"clipboard-history-disabled-description": "Activa el historial del portapapeles en la configuración o instala cliphist",
|
"clipboard-history-disabled-description": "Activa el historial del portapapeles en la configuración o instala cliphist",
|
||||||
"clipboard-loading": "Cargando historial del portapapeles...",
|
"clipboard-loading": "Cargando historial del portapapeles...",
|
||||||
"clipboard-loading-description": "Por favor espera",
|
"clipboard-loading-description": "Por favor espera",
|
||||||
"clipboard-search-description": "Buscar en el historial del portapapeles"
|
"clipboard-search-description": "Buscar en el historial del portapapeles",
|
||||||
|
"emoji": "Selector de emojis",
|
||||||
|
"emoji-loading": "Cargando emojis...",
|
||||||
|
"emoji-loading-description": "Por favor espera",
|
||||||
|
"emoji-search-description": "Buscar y copiar emojis"
|
||||||
},
|
},
|
||||||
"quickSettings": {
|
"quickSettings": {
|
||||||
"bluetooth": {
|
"bluetooth": {
|
||||||
@@ -1048,6 +1075,10 @@
|
|||||||
"description": "Escribe {filepath}.",
|
"description": "Escribe {filepath}.",
|
||||||
"description-missing": "Requiere que {app} esté instalado/a."
|
"description-missing": "Requiere que {app} esté instalado/a."
|
||||||
},
|
},
|
||||||
|
"cava": {
|
||||||
|
"description": "Escribe {filepath}.",
|
||||||
|
"description-missing": "Requiere que {app} esté instalado/a."
|
||||||
|
},
|
||||||
"vicinae": {
|
"vicinae": {
|
||||||
"description": "Escribir {filepath} y recargar",
|
"description": "Escribir {filepath} y recargar",
|
||||||
"description-missing": "Requiere que {app} esté instalado"
|
"description-missing": "Requiere que {app} esté instalado"
|
||||||
@@ -2249,23 +2280,11 @@
|
|||||||
"widgets": {
|
"widgets": {
|
||||||
"color-picker": {
|
"color-picker": {
|
||||||
"apply": "Aplicar",
|
"apply": "Aplicar",
|
||||||
"brightness": "Brillo",
|
|
||||||
"cancel": "Cancelar",
|
"cancel": "Cancelar",
|
||||||
"hex": {
|
|
||||||
"description": "Ingresa un código de color hexadecimal.",
|
|
||||||
"label": "Color hexadecimal"
|
|
||||||
},
|
|
||||||
"palette": {
|
"palette": {
|
||||||
"description": "Elige entre una amplia gama de colores predefinidos.",
|
"description": "Elige entre una amplia gama de colores predefinidos.",
|
||||||
"label": "Paleta"
|
"label": "Paleta",
|
||||||
},
|
"theme-colors": "Acceso rápido a la paleta de colores de tu tema."
|
||||||
"rgb": {
|
|
||||||
"description": "Ajusta los valores de rojo, verde, azul y brillo.",
|
|
||||||
"label": "Valores RGB"
|
|
||||||
},
|
|
||||||
"theme-colors": {
|
|
||||||
"description": "Acceso rápido a la paleta de colores de tu tema.",
|
|
||||||
"label": "Colores del tema"
|
|
||||||
},
|
},
|
||||||
"title": "Selector de colores"
|
"title": "Selector de colores"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -231,6 +231,10 @@
|
|||||||
"description": "Afficher un visualiseur audio quand la musique est en cours de lecture.",
|
"description": "Afficher un visualiseur audio quand la musique est en cours de lecture.",
|
||||||
"label": "Afficher le visualiseur"
|
"label": "Afficher le visualiseur"
|
||||||
},
|
},
|
||||||
|
"show-progress-ring": {
|
||||||
|
"description": "Afficher un indicateur de progression circulaire montrant la progression de la piste.",
|
||||||
|
"label": "Afficher l'anneau de progression"
|
||||||
|
},
|
||||||
"use-fixed-width": {
|
"use-fixed-width": {
|
||||||
"description": "Lorsque activé, le widget utilisera toujours la largeur maximale au lieu de s'ajuster dynamiquement au contenu.",
|
"description": "Lorsque activé, le widget utilisera toujours la largeur maximale au lieu de s'ajuster dynamiquement au contenu.",
|
||||||
"label": "Utiliser une Largeur Fixe"
|
"label": "Utiliser une Largeur Fixe"
|
||||||
@@ -373,6 +377,10 @@
|
|||||||
"description": "Ne pas afficher les espaces de travail sans fenêtres.",
|
"description": "Ne pas afficher les espaces de travail sans fenêtres.",
|
||||||
"label": "Masquer les inoccupés"
|
"label": "Masquer les inoccupés"
|
||||||
},
|
},
|
||||||
|
"follow-focused-screen": {
|
||||||
|
"description": "Afficher les espaces de travail de l'écran actuellement ciblé, plutôt que de l'écran où se trouve la barre.",
|
||||||
|
"label": "Suivre l'Écran Ciblé"
|
||||||
|
},
|
||||||
"label-mode": {
|
"label-mode": {
|
||||||
"description": "Choisir comment les étiquettes d'espace de travail sont affichées.",
|
"description": "Choisir comment les étiquettes d'espace de travail sont affichées.",
|
||||||
"label": "Mode d'étiquette"
|
"label": "Mode d'étiquette"
|
||||||
@@ -381,14 +389,20 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"battery": {
|
"battery": {
|
||||||
|
"brightness": "Luminosité",
|
||||||
|
"charge-level": "Niveau de charge",
|
||||||
"charging": "En charge.",
|
"charging": "En charge.",
|
||||||
"charging-rate": "Taux de charge : {rate} W.",
|
"charging-rate": "Taux de charge : {rate} W.",
|
||||||
"discharging": "En décharge.",
|
"discharging": "En décharge.",
|
||||||
"discharging-rate": "Taux de décharge : {rate} W.",
|
"discharging-rate": "Taux de décharge : {rate} W.",
|
||||||
"health": "État : {percent}%",
|
"health": "État : {percent}%",
|
||||||
"idle": "Inactif.",
|
"idle": "Inactif.",
|
||||||
|
"inhibit-idle-description": "Maintient le système éveillé.",
|
||||||
|
"inhibit-idle-label": "Garder éveillé",
|
||||||
"no-battery-detected": "Aucune batterie détectée.",
|
"no-battery-detected": "Aucune batterie détectée.",
|
||||||
|
"panel-title": "Batterie",
|
||||||
"plugged-in": "Branché.",
|
"plugged-in": "Branché.",
|
||||||
|
"power-profile": "Profil d’alimentation",
|
||||||
"time-left": "Temps restant : {time}.",
|
"time-left": "Temps restant : {time}.",
|
||||||
"time-until-full": "Temps jusqu'à charge complète : {time}."
|
"time-until-full": "Temps jusqu'à charge complète : {time}."
|
||||||
},
|
},
|
||||||
@@ -503,6 +517,15 @@
|
|||||||
"description": "Vos notifications apparaîtront ici à mesure qu'elles arriveront.",
|
"description": "Vos notifications apparaîtront ici à mesure qu'elles arriveront.",
|
||||||
"no-notifications": "Aucune notification",
|
"no-notifications": "Aucune notification",
|
||||||
"title": "Notifications"
|
"title": "Notifications"
|
||||||
|
},
|
||||||
|
"time": {
|
||||||
|
"now": "maintenant",
|
||||||
|
"diffM": "il y a 1 minute",
|
||||||
|
"diffMM": "il y a {diff} minutes",
|
||||||
|
"diffH": "il y a 1 heure",
|
||||||
|
"diffHH": "il y a {diff} heures",
|
||||||
|
"diffD": "il y a 1 jour",
|
||||||
|
"diffDD": "il y a {diff} jours"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"options": {
|
"options": {
|
||||||
@@ -670,7 +693,11 @@
|
|||||||
"clipboard-history-disabled-description": "Activez l'historique du presse-papiers dans les paramètres ou installez cliphist",
|
"clipboard-history-disabled-description": "Activez l'historique du presse-papiers dans les paramètres ou installez cliphist",
|
||||||
"clipboard-loading": "Chargement de l'historique du presse-papiers...",
|
"clipboard-loading": "Chargement de l'historique du presse-papiers...",
|
||||||
"clipboard-loading-description": "Veuillez patienter",
|
"clipboard-loading-description": "Veuillez patienter",
|
||||||
"clipboard-search-description": "Rechercher dans l'historique du presse-papiers"
|
"clipboard-search-description": "Rechercher dans l'historique du presse-papiers",
|
||||||
|
"emoji": "Sélecteur d'émojis",
|
||||||
|
"emoji-loading": "Chargement des émojis...",
|
||||||
|
"emoji-loading-description": "Veuillez patienter",
|
||||||
|
"emoji-search-description": "Rechercher et copier des émojis"
|
||||||
},
|
},
|
||||||
"quickSettings": {
|
"quickSettings": {
|
||||||
"bluetooth": {
|
"bluetooth": {
|
||||||
@@ -1048,6 +1075,10 @@
|
|||||||
"description": "Écrire {filepath}.",
|
"description": "Écrire {filepath}.",
|
||||||
"description-missing": "Nécessite l'installation de {app}"
|
"description-missing": "Nécessite l'installation de {app}"
|
||||||
},
|
},
|
||||||
|
"cava": {
|
||||||
|
"description": "Écrire {filepath}.",
|
||||||
|
"description-missing": "Nécessite l'installation de {app}"
|
||||||
|
},
|
||||||
"vicinae": {
|
"vicinae": {
|
||||||
"description": "Écrire {filepath} et recharger",
|
"description": "Écrire {filepath} et recharger",
|
||||||
"description-missing": "Nécessite que le lanceur {app} soit installé"
|
"description-missing": "Nécessite que le lanceur {app} soit installé"
|
||||||
@@ -2249,23 +2280,11 @@
|
|||||||
"widgets": {
|
"widgets": {
|
||||||
"color-picker": {
|
"color-picker": {
|
||||||
"apply": "Appliquer",
|
"apply": "Appliquer",
|
||||||
"brightness": "Luminosité",
|
|
||||||
"cancel": "Annuler",
|
"cancel": "Annuler",
|
||||||
"hex": {
|
|
||||||
"description": "Entrez un code couleur hexadécimal.",
|
|
||||||
"label": "Couleur hexadécimale"
|
|
||||||
},
|
|
||||||
"palette": {
|
"palette": {
|
||||||
"description": "Choisissez parmi une large gamme de couleurs prédéfinies.",
|
"description": "Choisissez parmi une large gamme de couleurs prédéfinies.",
|
||||||
"label": "Palette"
|
"label": "Palette",
|
||||||
},
|
"theme-colors": "Accès rapide à la palette de couleurs de votre thème."
|
||||||
"rgb": {
|
|
||||||
"description": "Ajustez les valeurs de rouge, vert, bleu et de luminosité.",
|
|
||||||
"label": "Valeurs RVB"
|
|
||||||
},
|
|
||||||
"theme-colors": {
|
|
||||||
"description": "Accès rapide à la palette de couleurs de votre thème.",
|
|
||||||
"label": "Couleurs du thème"
|
|
||||||
},
|
},
|
||||||
"title": "Sélecteur de couleurs"
|
"title": "Sélecteur de couleurs"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -231,6 +231,10 @@
|
|||||||
"description": "Toon een audiovisualizer wanneer muziek wordt afgespeeld.",
|
"description": "Toon een audiovisualizer wanneer muziek wordt afgespeeld.",
|
||||||
"label": "Visualizer tonen"
|
"label": "Visualizer tonen"
|
||||||
},
|
},
|
||||||
|
"show-progress-ring": {
|
||||||
|
"description": "Toon een circulaire voortgangsindicator die het bestandsspoor voortgang toont.",
|
||||||
|
"label": "Voortgangscirkel tonen"
|
||||||
|
},
|
||||||
"use-fixed-width": {
|
"use-fixed-width": {
|
||||||
"description": "Indien ingeschakeld gebruikt de widget altijd de maximale breedte in plaats van zich aan te passen aan de inhoud.",
|
"description": "Indien ingeschakeld gebruikt de widget altijd de maximale breedte in plaats van zich aan te passen aan de inhoud.",
|
||||||
"label": "Vaste breedte gebruiken"
|
"label": "Vaste breedte gebruiken"
|
||||||
@@ -373,6 +377,10 @@
|
|||||||
"description": "Werkruimten zonder vensters niet weergeven.",
|
"description": "Werkruimten zonder vensters niet weergeven.",
|
||||||
"label": "Ongebruikte verbergen"
|
"label": "Ongebruikte verbergen"
|
||||||
},
|
},
|
||||||
|
"follow-focused-screen": {
|
||||||
|
"description": "Werkruimten weergeven van het momenteel gefocuste scherm, in plaats van het scherm waar de balk zich bevindt.",
|
||||||
|
"label": "Gefocust Scherm Volgen"
|
||||||
|
},
|
||||||
"label-mode": {
|
"label-mode": {
|
||||||
"description": "Kies hoe labels van werkruimten worden weergegeven.",
|
"description": "Kies hoe labels van werkruimten worden weergegeven.",
|
||||||
"label": "Labelmodus"
|
"label": "Labelmodus"
|
||||||
@@ -381,14 +389,20 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"battery": {
|
"battery": {
|
||||||
|
"brightness": "Helderheid",
|
||||||
|
"charge-level": "Laadniveau",
|
||||||
"charging": "Opladen.",
|
"charging": "Opladen.",
|
||||||
"charging-rate": "Laadsnelheid: {rate} W.",
|
"charging-rate": "Laadsnelheid: {rate} W.",
|
||||||
"discharging": "Ontladen.",
|
"discharging": "Ontladen.",
|
||||||
"discharging-rate": "Ontlaadsnelheid: {rate} W.",
|
"discharging-rate": "Ontlaadsnelheid: {rate} W.",
|
||||||
"health": "Gezondheid: {percent}%",
|
"health": "Gezondheid: {percent}%",
|
||||||
"idle": "In rust.",
|
"idle": "In rust.",
|
||||||
|
"inhibit-idle-description": "Houdt het systeem wakker.",
|
||||||
|
"inhibit-idle-label": "Wakker houden",
|
||||||
"no-battery-detected": "Geen batterij gedetecteerd.",
|
"no-battery-detected": "Geen batterij gedetecteerd.",
|
||||||
|
"panel-title": "Batterij",
|
||||||
"plugged-in": "Op netstroom.",
|
"plugged-in": "Op netstroom.",
|
||||||
|
"power-profile": "Energieprofiel",
|
||||||
"time-left": "Resterende tijd: {time}.",
|
"time-left": "Resterende tijd: {time}.",
|
||||||
"time-until-full": "Tijd tot vol: {time}."
|
"time-until-full": "Tijd tot vol: {time}."
|
||||||
},
|
},
|
||||||
@@ -503,6 +517,15 @@
|
|||||||
"description": "Je meldingen verschijnen hier zodra ze binnenkomen.",
|
"description": "Je meldingen verschijnen hier zodra ze binnenkomen.",
|
||||||
"no-notifications": "Geen meldingen",
|
"no-notifications": "Geen meldingen",
|
||||||
"title": "Meldingen"
|
"title": "Meldingen"
|
||||||
|
},
|
||||||
|
"time": {
|
||||||
|
"now": "nu",
|
||||||
|
"diffM": "1 minuut geleden",
|
||||||
|
"diffMM": "{diff} minuten geleden",
|
||||||
|
"diffH": "1 uur geleden",
|
||||||
|
"diffHH": "{diff} uur geleden",
|
||||||
|
"diffD": "1 dag geleden",
|
||||||
|
"diffDD": "{diff} dagen geleden"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"options": {
|
"options": {
|
||||||
@@ -670,7 +693,11 @@
|
|||||||
"clipboard-history-disabled-description": "Schakel klembordgeschiedenis in de instellingen in of installeer cliphist",
|
"clipboard-history-disabled-description": "Schakel klembordgeschiedenis in de instellingen in of installeer cliphist",
|
||||||
"clipboard-loading": "Klembordgeschiedenis laden...",
|
"clipboard-loading": "Klembordgeschiedenis laden...",
|
||||||
"clipboard-loading-description": "Even geduld",
|
"clipboard-loading-description": "Even geduld",
|
||||||
"clipboard-search-description": "Zoek in klembordgeschiedenis"
|
"clipboard-search-description": "Zoek in klembordgeschiedenis",
|
||||||
|
"emoji": "Emoji-kiezer",
|
||||||
|
"emoji-loading": "Emoji's laden...",
|
||||||
|
"emoji-loading-description": "Even geduld",
|
||||||
|
"emoji-search-description": "Zoek en kopieer emoji's"
|
||||||
},
|
},
|
||||||
"quickSettings": {
|
"quickSettings": {
|
||||||
"bluetooth": {
|
"bluetooth": {
|
||||||
@@ -1048,6 +1075,10 @@
|
|||||||
"description": "Schrijf {filepath}.",
|
"description": "Schrijf {filepath}.",
|
||||||
"description-missing": "Vereist dat {app} is geïnstalleerd."
|
"description-missing": "Vereist dat {app} is geïnstalleerd."
|
||||||
},
|
},
|
||||||
|
"cava": {
|
||||||
|
"description": "Schrijf {filepath}.",
|
||||||
|
"description-missing": "Vereist dat {app} is geïnstalleerd."
|
||||||
|
},
|
||||||
"vicinae": {
|
"vicinae": {
|
||||||
"description": "Schrijf {filepath} en herlaad.",
|
"description": "Schrijf {filepath} en herlaad.",
|
||||||
"description-missing": "Vereist dat {app} is geïnstalleerd."
|
"description-missing": "Vereist dat {app} is geïnstalleerd."
|
||||||
@@ -2249,23 +2280,11 @@
|
|||||||
"widgets": {
|
"widgets": {
|
||||||
"color-picker": {
|
"color-picker": {
|
||||||
"apply": "Toepassen",
|
"apply": "Toepassen",
|
||||||
"brightness": "Helderheid",
|
|
||||||
"cancel": "Annuleren",
|
"cancel": "Annuleren",
|
||||||
"hex": {
|
|
||||||
"description": "Voer een hexadecimale kleurcode in.",
|
|
||||||
"label": "Hex-kleur"
|
|
||||||
},
|
|
||||||
"palette": {
|
"palette": {
|
||||||
"description": "Kies uit een breed scala aan vooraf gedefinieerde kleuren.",
|
"description": "Kies uit een breed scala aan vooraf gedefinieerde kleuren.",
|
||||||
"label": "Palet"
|
"label": "Palet",
|
||||||
},
|
"theme-colors": "Snelle toegang tot het kleurenpalet van je thema."
|
||||||
"rgb": {
|
|
||||||
"description": "Pas de waarden voor rood, groen, blauw en helderheid aan.",
|
|
||||||
"label": "RGB-waarden"
|
|
||||||
},
|
|
||||||
"theme-colors": {
|
|
||||||
"description": "Snelle toegang tot het kleurenpalet van je thema.",
|
|
||||||
"label": "Themakleuren"
|
|
||||||
},
|
},
|
||||||
"title": "Kleurkiezer"
|
"title": "Kleurkiezer"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -231,6 +231,10 @@
|
|||||||
"description": "Exibir um visualizador de áudio quando música está sendo reproduzida.",
|
"description": "Exibir um visualizador de áudio quando música está sendo reproduzida.",
|
||||||
"label": "Mostrar visualizador"
|
"label": "Mostrar visualizador"
|
||||||
},
|
},
|
||||||
|
"show-progress-ring": {
|
||||||
|
"description": "Exibir um indicador de progresso circular mostrando o progresso da faixa.",
|
||||||
|
"label": "Mostrar anel de progresso"
|
||||||
|
},
|
||||||
"use-fixed-width": {
|
"use-fixed-width": {
|
||||||
"description": "Quando ativado, o widget sempre usará a largura máxima em vez de ajustar dinamicamente ao conteúdo.",
|
"description": "Quando ativado, o widget sempre usará a largura máxima em vez de ajustar dinamicamente ao conteúdo.",
|
||||||
"label": "Usar Largura Fixa"
|
"label": "Usar Largura Fixa"
|
||||||
@@ -373,6 +377,10 @@
|
|||||||
"description": "Não exibir áreas de trabalho sem janelas.",
|
"description": "Não exibir áreas de trabalho sem janelas.",
|
||||||
"label": "Ocultar desocupados"
|
"label": "Ocultar desocupados"
|
||||||
},
|
},
|
||||||
|
"follow-focused-screen": {
|
||||||
|
"description": "Exibir áreas de trabalho da tela atualmente em foco, em vez da tela onde a barra está localizada.",
|
||||||
|
"label": "Seguir Tela em Foco"
|
||||||
|
},
|
||||||
"label-mode": {
|
"label-mode": {
|
||||||
"description": "Escolher como os rótulos de espaço de trabalho são exibidos.",
|
"description": "Escolher como os rótulos de espaço de trabalho são exibidos.",
|
||||||
"label": "Modo de rótulo"
|
"label": "Modo de rótulo"
|
||||||
@@ -381,14 +389,20 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"battery": {
|
"battery": {
|
||||||
|
"brightness": "Brilho",
|
||||||
|
"charge-level": "Nível de carga",
|
||||||
"charging": "Carregando.",
|
"charging": "Carregando.",
|
||||||
"charging-rate": "Taxa de carregamento: {rate} W.",
|
"charging-rate": "Taxa de carregamento: {rate} W.",
|
||||||
"discharging": "Descarregando.",
|
"discharging": "Descarregando.",
|
||||||
"discharging-rate": "Taxa de descarregamento: {rate} W.",
|
"discharging-rate": "Taxa de descarregamento: {rate} W.",
|
||||||
"health": "Saúde: {percent}%",
|
"health": "Saúde: {percent}%",
|
||||||
"idle": "Ocioso.",
|
"idle": "Ocioso.",
|
||||||
|
"inhibit-idle-description": "Mantém o sistema acordado.",
|
||||||
|
"inhibit-idle-label": "Manter acordado",
|
||||||
"no-battery-detected": "Nenhuma bateria detectada.",
|
"no-battery-detected": "Nenhuma bateria detectada.",
|
||||||
|
"panel-title": "Bateria",
|
||||||
"plugged-in": "Conectado.",
|
"plugged-in": "Conectado.",
|
||||||
|
"power-profile": "Perfil de energia",
|
||||||
"time-left": "Tempo restante: {time}.",
|
"time-left": "Tempo restante: {time}.",
|
||||||
"time-until-full": "Tempo até carga completa: {time}."
|
"time-until-full": "Tempo até carga completa: {time}."
|
||||||
},
|
},
|
||||||
@@ -503,6 +517,15 @@
|
|||||||
"description": "Suas notificações aparecerão aqui assim que chegarem.",
|
"description": "Suas notificações aparecerão aqui assim que chegarem.",
|
||||||
"no-notifications": "Nenhuma notificação",
|
"no-notifications": "Nenhuma notificação",
|
||||||
"title": "Notificações"
|
"title": "Notificações"
|
||||||
|
},
|
||||||
|
"time": {
|
||||||
|
"now": "agora",
|
||||||
|
"diffM": "há 1 minuto",
|
||||||
|
"diffMM": "há {diff} minutos",
|
||||||
|
"diffH": "há 1 hora",
|
||||||
|
"diffHH": "há {diff} horas",
|
||||||
|
"diffD": "há 1 dia",
|
||||||
|
"diffDD": "há {diff} dias"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"options": {
|
"options": {
|
||||||
@@ -670,7 +693,11 @@
|
|||||||
"clipboard-history-disabled-description": "Ative o histórico da área de transferência nas configurações ou instale o cliphist",
|
"clipboard-history-disabled-description": "Ative o histórico da área de transferência nas configurações ou instale o cliphist",
|
||||||
"clipboard-loading": "Carregando histórico da área de transferência...",
|
"clipboard-loading": "Carregando histórico da área de transferência...",
|
||||||
"clipboard-loading-description": "Por favor, aguarde",
|
"clipboard-loading-description": "Por favor, aguarde",
|
||||||
"clipboard-search-description": "Pesquisar no histórico da área de transferência"
|
"clipboard-search-description": "Pesquisar no histórico da área de transferência",
|
||||||
|
"emoji": "Seletor de emojis",
|
||||||
|
"emoji-loading": "Carregando emojis...",
|
||||||
|
"emoji-loading-description": "Por favor, aguarde",
|
||||||
|
"emoji-search-description": "Buscar e copiar emojis"
|
||||||
},
|
},
|
||||||
"quickSettings": {
|
"quickSettings": {
|
||||||
"bluetooth": {
|
"bluetooth": {
|
||||||
@@ -1048,6 +1075,10 @@
|
|||||||
"description": "Escreva em {filepath}.",
|
"description": "Escreva em {filepath}.",
|
||||||
"description-missing": "Requer que o {app} esteja instalado."
|
"description-missing": "Requer que o {app} esteja instalado."
|
||||||
},
|
},
|
||||||
|
"cava": {
|
||||||
|
"description": "Escreva em {filepath}.",
|
||||||
|
"description-missing": "Requer que o {app} esteja instalado."
|
||||||
|
},
|
||||||
"vicinae": {
|
"vicinae": {
|
||||||
"description": "Escrever {filepath} e recarregar",
|
"description": "Escrever {filepath} e recarregar",
|
||||||
"description-missing": "Requer que o {app} esteja instalado"
|
"description-missing": "Requer que o {app} esteja instalado"
|
||||||
@@ -2249,23 +2280,11 @@
|
|||||||
"widgets": {
|
"widgets": {
|
||||||
"color-picker": {
|
"color-picker": {
|
||||||
"apply": "Aplicar",
|
"apply": "Aplicar",
|
||||||
"brightness": "Brilho",
|
|
||||||
"cancel": "Cancelar",
|
"cancel": "Cancelar",
|
||||||
"hex": {
|
|
||||||
"description": "Digite um código de cor hexadecimal.",
|
|
||||||
"label": "Cor hexadecimal"
|
|
||||||
},
|
|
||||||
"palette": {
|
"palette": {
|
||||||
"description": "Escolha entre uma vasta gama de cores predefinidas.",
|
"description": "Escolha entre uma vasta gama de cores predefinidas.",
|
||||||
"label": "Paleta"
|
"label": "Paleta",
|
||||||
},
|
"theme-colors": "Acesso rápido à paleta de cores do seu tema."
|
||||||
"rgb": {
|
|
||||||
"description": "Ajuste os valores de vermelho, verde, azul e brilho.",
|
|
||||||
"label": "Valores RGB"
|
|
||||||
},
|
|
||||||
"theme-colors": {
|
|
||||||
"description": "Acesso rápido à paleta de cores do seu tema.",
|
|
||||||
"label": "Cores do tema"
|
|
||||||
},
|
},
|
||||||
"title": "Seletor de cores"
|
"title": "Seletor de cores"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -231,6 +231,10 @@
|
|||||||
"description": "Отображать аудиовизуализатор при воспроизведении музыки.",
|
"description": "Отображать аудиовизуализатор при воспроизведении музыки.",
|
||||||
"label": "Показывать визуализатор"
|
"label": "Показывать визуализатор"
|
||||||
},
|
},
|
||||||
|
"show-progress-ring": {
|
||||||
|
"description": "Отображать круговой индикатор прогресса воспроизведения трека.",
|
||||||
|
"label": "Показывать кольцо прогресса"
|
||||||
|
},
|
||||||
"use-fixed-width": {
|
"use-fixed-width": {
|
||||||
"description": "Если включено, виджет всегда будет использовать максимальную ширину вместо динамической подстройки под содержимое.",
|
"description": "Если включено, виджет всегда будет использовать максимальную ширину вместо динамической подстройки под содержимое.",
|
||||||
"label": "Использовать фиксированную ширину"
|
"label": "Использовать фиксированную ширину"
|
||||||
@@ -373,6 +377,10 @@
|
|||||||
"description": "Не отображать рабочие пространства без окон.",
|
"description": "Не отображать рабочие пространства без окон.",
|
||||||
"label": "Скрыть незанятые"
|
"label": "Скрыть незанятые"
|
||||||
},
|
},
|
||||||
|
"follow-focused-screen": {
|
||||||
|
"description": "Отображать рабочие пространства с текущего активного экрана, а не с экрана, на котором расположена панель.",
|
||||||
|
"label": "Следовать за Активным Экраном"
|
||||||
|
},
|
||||||
"label-mode": {
|
"label-mode": {
|
||||||
"description": "Выберите, как отображаются метки рабочих пространств.",
|
"description": "Выберите, как отображаются метки рабочих пространств.",
|
||||||
"label": "Режим метки"
|
"label": "Режим метки"
|
||||||
@@ -381,14 +389,20 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"battery": {
|
"battery": {
|
||||||
|
"brightness": "Яркость",
|
||||||
|
"charge-level": "Уровень заряда",
|
||||||
"charging": "Зарядка.",
|
"charging": "Зарядка.",
|
||||||
"charging-rate": "Скорость зарядки: {rate} Вт.",
|
"charging-rate": "Скорость зарядки: {rate} Вт.",
|
||||||
"discharging": "Разрядка.",
|
"discharging": "Разрядка.",
|
||||||
"discharging-rate": "Скорость разрядки: {rate} Вт.",
|
"discharging-rate": "Скорость разрядки: {rate} Вт.",
|
||||||
"health": "Здоровье: {percent}%",
|
"health": "Здоровье: {percent}%",
|
||||||
"idle": "Простой.",
|
"idle": "Простой.",
|
||||||
|
"inhibit-idle-description": "Не дает системе уснуть.",
|
||||||
|
"inhibit-idle-label": "Не давать засыпать",
|
||||||
"no-battery-detected": "Батарея не обнаружена.",
|
"no-battery-detected": "Батарея не обнаружена.",
|
||||||
|
"panel-title": "Батарея",
|
||||||
"plugged-in": "Подключено.",
|
"plugged-in": "Подключено.",
|
||||||
|
"power-profile": "Профиль питания",
|
||||||
"time-left": "Осталось времени: {time}.",
|
"time-left": "Осталось времени: {time}.",
|
||||||
"time-until-full": "Время до полной зарядки: {time}."
|
"time-until-full": "Время до полной зарядки: {time}."
|
||||||
},
|
},
|
||||||
@@ -503,6 +517,15 @@
|
|||||||
"description": "Ваши уведомления будут появляться здесь по мере их поступления.",
|
"description": "Ваши уведомления будут появляться здесь по мере их поступления.",
|
||||||
"no-notifications": "Нет уведомлений",
|
"no-notifications": "Нет уведомлений",
|
||||||
"title": "Уведомления"
|
"title": "Уведомления"
|
||||||
|
},
|
||||||
|
"time": {
|
||||||
|
"now": "сейчас",
|
||||||
|
"diffM": "1 минуту назад",
|
||||||
|
"diffMM": "{diff} минут назад",
|
||||||
|
"diffH": "1 час назад",
|
||||||
|
"diffHH": "{diff} часов назад",
|
||||||
|
"diffD": "1 день назад",
|
||||||
|
"diffDD": "{diff} дней назад"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"options": {
|
"options": {
|
||||||
@@ -670,7 +693,11 @@
|
|||||||
"clipboard-history-disabled-description": "Включите историю буфера обмена в настройках или установите cliphist",
|
"clipboard-history-disabled-description": "Включите историю буфера обмена в настройках или установите cliphist",
|
||||||
"clipboard-loading": "Загрузка истории буфера обмена...",
|
"clipboard-loading": "Загрузка истории буфера обмена...",
|
||||||
"clipboard-loading-description": "Пожалуйста, подождите",
|
"clipboard-loading-description": "Пожалуйста, подождите",
|
||||||
"clipboard-search-description": "Поиск в истории буфера обмена"
|
"clipboard-search-description": "Поиск в истории буфера обмена",
|
||||||
|
"emoji": "Выбор эмодзи",
|
||||||
|
"emoji-loading": "Загрузка эмодзи...",
|
||||||
|
"emoji-loading-description": "Пожалуйста, подождите",
|
||||||
|
"emoji-search-description": "Поиск и копирование эмодзи"
|
||||||
},
|
},
|
||||||
"quickSettings": {
|
"quickSettings": {
|
||||||
"bluetooth": {
|
"bluetooth": {
|
||||||
@@ -1048,6 +1075,10 @@
|
|||||||
"description": "Записать {filepath}.",
|
"description": "Записать {filepath}.",
|
||||||
"description-missing": "Требуется установка {app}"
|
"description-missing": "Требуется установка {app}"
|
||||||
},
|
},
|
||||||
|
"cava": {
|
||||||
|
"description": "Записать {filepath}.",
|
||||||
|
"description-missing": "Требуется установка {app}"
|
||||||
|
},
|
||||||
"vicinae": {
|
"vicinae": {
|
||||||
"description": "Записать {filepath} и перезагрузить",
|
"description": "Записать {filepath} и перезагрузить",
|
||||||
"description-missing": "Требуется установка {app}"
|
"description-missing": "Требуется установка {app}"
|
||||||
@@ -2249,23 +2280,11 @@
|
|||||||
"widgets": {
|
"widgets": {
|
||||||
"color-picker": {
|
"color-picker": {
|
||||||
"apply": "Применить",
|
"apply": "Применить",
|
||||||
"brightness": "Яркость",
|
|
||||||
"cancel": "Отмена",
|
"cancel": "Отмена",
|
||||||
"hex": {
|
|
||||||
"description": "Введите шестнадцатеричный код цвета.",
|
|
||||||
"label": "Hex-цвет"
|
|
||||||
},
|
|
||||||
"palette": {
|
"palette": {
|
||||||
"description": "Выберите из широкого диапазона предопределенных цветов.",
|
"description": "Выберите из широкого диапазона предопределенных цветов.",
|
||||||
"label": "Палитра"
|
"label": "Палитра",
|
||||||
},
|
"theme-colors": "Быстрый доступ к цветовой палитре вашей темы."
|
||||||
"rgb": {
|
|
||||||
"description": "Настройте значения красного, зеленого, синего и яркости.",
|
|
||||||
"label": "Значения RGB"
|
|
||||||
},
|
|
||||||
"theme-colors": {
|
|
||||||
"description": "Быстрый доступ к цветовой палитре вашей темы.",
|
|
||||||
"label": "Цвета темы"
|
|
||||||
},
|
},
|
||||||
"title": "Выбор цвета"
|
"title": "Выбор цвета"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -231,6 +231,10 @@
|
|||||||
"description": "Müzik çalarken bir ses görselleştirici göster.",
|
"description": "Müzik çalarken bir ses görselleştirici göster.",
|
||||||
"label": "Görselleştiriciyi göster"
|
"label": "Görselleştiriciyi göster"
|
||||||
},
|
},
|
||||||
|
"show-progress-ring": {
|
||||||
|
"description": "Parça ilerlemesini gösteren dairesel bir ilerleme göstergesi gösterin.",
|
||||||
|
"label": "İlerleme halkası göster"
|
||||||
|
},
|
||||||
"use-fixed-width": {
|
"use-fixed-width": {
|
||||||
"description": "Etkinleştirildiğinde, widget dinamik olarak içerik göre ayarlamak yerine her zaman maksimum genişliği kullanır.",
|
"description": "Etkinleştirildiğinde, widget dinamik olarak içerik göre ayarlamak yerine her zaman maksimum genişliği kullanır.",
|
||||||
"label": "Sabit Genişlik Kullan"
|
"label": "Sabit Genişlik Kullan"
|
||||||
@@ -373,6 +377,10 @@
|
|||||||
"description": "Penceresi olmayan çalışma alanlarını gösterme.",
|
"description": "Penceresi olmayan çalışma alanlarını gösterme.",
|
||||||
"label": "Dolu olmayanları gizle"
|
"label": "Dolu olmayanları gizle"
|
||||||
},
|
},
|
||||||
|
"follow-focused-screen": {
|
||||||
|
"description": "Çubuğun bulunduğu ekran yerine, şu anda odaklanmış ekrandaki çalışma alanlarını göster.",
|
||||||
|
"label": "Odaklanmış Ekranı Takip Et"
|
||||||
|
},
|
||||||
"label-mode": {
|
"label-mode": {
|
||||||
"description": "Çalışma alanı etiketlerinin nasıl gösterileceğini seçin.",
|
"description": "Çalışma alanı etiketlerinin nasıl gösterileceğini seçin.",
|
||||||
"label": "Etiket Modu"
|
"label": "Etiket Modu"
|
||||||
@@ -381,14 +389,20 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"battery": {
|
"battery": {
|
||||||
|
"brightness": "Parlaklık",
|
||||||
|
"charge-level": "Şarj seviyesi",
|
||||||
"charging": "Şarj oluyor.",
|
"charging": "Şarj oluyor.",
|
||||||
"charging-rate": "Şarj oranı: {rate} W.",
|
"charging-rate": "Şarj oranı: {rate} W.",
|
||||||
"discharging": "Deşarj oluyor.",
|
"discharging": "Deşarj oluyor.",
|
||||||
"discharging-rate": "Deşarj oranı: {rate} W.",
|
"discharging-rate": "Deşarj oranı: {rate} W.",
|
||||||
"health": "Sağlık: {percent}%",
|
"health": "Sağlık: {percent}%",
|
||||||
"idle": "Boşta.",
|
"idle": "Boşta.",
|
||||||
|
"inhibit-idle-description": "Sistemi uyanık tutar.",
|
||||||
|
"inhibit-idle-label": "Uyanık tut",
|
||||||
"no-battery-detected": "Pil tespit edilmedi.",
|
"no-battery-detected": "Pil tespit edilmedi.",
|
||||||
|
"panel-title": "Pil",
|
||||||
"plugged-in": "Prize takılı.",
|
"plugged-in": "Prize takılı.",
|
||||||
|
"power-profile": "Güç profili",
|
||||||
"time-left": "Kalan süre: {time}.",
|
"time-left": "Kalan süre: {time}.",
|
||||||
"time-until-full": "Dolma süresi: {time}."
|
"time-until-full": "Dolma süresi: {time}."
|
||||||
},
|
},
|
||||||
@@ -503,6 +517,15 @@
|
|||||||
"description": "Bildirimler geldikçe burada görünecek.",
|
"description": "Bildirimler geldikçe burada görünecek.",
|
||||||
"no-notifications": "Bildirim yok",
|
"no-notifications": "Bildirim yok",
|
||||||
"title": "Bildirimler"
|
"title": "Bildirimler"
|
||||||
|
},
|
||||||
|
"time": {
|
||||||
|
"now": "şimdi",
|
||||||
|
"diffM": "1 dakika önce",
|
||||||
|
"diffMM": "{diff} dakika önce",
|
||||||
|
"diffH": "1 saat önce",
|
||||||
|
"diffHH": "{diff} saat önce",
|
||||||
|
"diffD": "1 gün önce",
|
||||||
|
"diffDD": "{diff} gün önce"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"options": {
|
"options": {
|
||||||
@@ -670,7 +693,11 @@
|
|||||||
"clipboard-history-disabled-description": "Panoya geçmişini ayarlarda etkinleştir veya cliphist kur",
|
"clipboard-history-disabled-description": "Panoya geçmişini ayarlarda etkinleştir veya cliphist kur",
|
||||||
"clipboard-loading": "Panoya geçmişi yükleniyor...",
|
"clipboard-loading": "Panoya geçmişi yükleniyor...",
|
||||||
"clipboard-loading-description": "Lütfen bekleyin",
|
"clipboard-loading-description": "Lütfen bekleyin",
|
||||||
"clipboard-search-description": "Panoya geçmişini ara"
|
"clipboard-search-description": "Panoya geçmişini ara",
|
||||||
|
"emoji": "Emoji seçici",
|
||||||
|
"emoji-loading": "Emojiler yükleniyor...",
|
||||||
|
"emoji-loading-description": "Lütfen bekleyin",
|
||||||
|
"emoji-search-description": "Emoji arama ve kopyalama"
|
||||||
},
|
},
|
||||||
"quickSettings": {
|
"quickSettings": {
|
||||||
"bluetooth": {
|
"bluetooth": {
|
||||||
@@ -1048,6 +1075,10 @@
|
|||||||
"description": "{filepath} dosyasına yaz.",
|
"description": "{filepath} dosyasına yaz.",
|
||||||
"description-missing": "Kurulum için {app} gereklidir"
|
"description-missing": "Kurulum için {app} gereklidir"
|
||||||
},
|
},
|
||||||
|
"cava": {
|
||||||
|
"description": "{filepath} dosyasına yaz.",
|
||||||
|
"description-missing": "Kurulum için {app} gereklidir"
|
||||||
|
},
|
||||||
"vicinae": {
|
"vicinae": {
|
||||||
"description": "{filepath} dosyasına yaz ve yeniden yükle",
|
"description": "{filepath} dosyasına yaz ve yeniden yükle",
|
||||||
"description-missing": "Kurulum için {app} gereklidir"
|
"description-missing": "Kurulum için {app} gereklidir"
|
||||||
@@ -2249,23 +2280,11 @@
|
|||||||
"widgets": {
|
"widgets": {
|
||||||
"color-picker": {
|
"color-picker": {
|
||||||
"apply": "Uygula",
|
"apply": "Uygula",
|
||||||
"brightness": "Parlaklık",
|
|
||||||
"cancel": "İptal",
|
"cancel": "İptal",
|
||||||
"hex": {
|
|
||||||
"description": "Onaltılık renk kodu girin.",
|
|
||||||
"label": "Hex renk"
|
|
||||||
},
|
|
||||||
"palette": {
|
"palette": {
|
||||||
"description": "Önceden tanımlı renkler geniş yelpazesinden seçin.",
|
"description": "Önceden tanımlı renkler geniş yelpazesinden seçin.",
|
||||||
"label": "Palet"
|
"label": "Palet",
|
||||||
},
|
"theme-colors": "Temanızın renk paletine hızlı erişim."
|
||||||
"rgb": {
|
|
||||||
"description": "Kırmızı, yeşil, mavi ve parlaklık değerlerini ayarlayın.",
|
|
||||||
"label": "RGB değerleri"
|
|
||||||
},
|
|
||||||
"theme-colors": {
|
|
||||||
"description": "Temanızın renk paletine hızlı erişim.",
|
|
||||||
"label": "Tema renkleri"
|
|
||||||
},
|
},
|
||||||
"title": "Renk seçici"
|
"title": "Renk seçici"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -231,6 +231,10 @@
|
|||||||
"description": "Відображати аудіовізуалізатор під час відтворення музики.",
|
"description": "Відображати аудіовізуалізатор під час відтворення музики.",
|
||||||
"label": "Показувати візуалізатор"
|
"label": "Показувати візуалізатор"
|
||||||
},
|
},
|
||||||
|
"show-progress-ring": {
|
||||||
|
"description": "Відображати круговий індикатор прогресу, що показує просування треку.",
|
||||||
|
"label": "Показувати кільце прогресу"
|
||||||
|
},
|
||||||
"use-fixed-width": {
|
"use-fixed-width": {
|
||||||
"description": "Коли увімкнено, віджет завжди використовуватиме максимальну ширину замість динамічного налаштування до вмісту.",
|
"description": "Коли увімкнено, віджет завжди використовуватиме максимальну ширину замість динамічного налаштування до вмісту.",
|
||||||
"label": "Використовувати фіксовану ширину"
|
"label": "Використовувати фіксовану ширину"
|
||||||
@@ -373,6 +377,10 @@
|
|||||||
"description": "Не відображати робочі простори без вікон.",
|
"description": "Не відображати робочі простори без вікон.",
|
||||||
"label": "Приховати незайняті"
|
"label": "Приховати незайняті"
|
||||||
},
|
},
|
||||||
|
"follow-focused-screen": {
|
||||||
|
"description": "Відображати робочі простори з поточного активного екрана, а не з екрана, на якому розташована панель.",
|
||||||
|
"label": "Слідувати за Активним Екраном"
|
||||||
|
},
|
||||||
"label-mode": {
|
"label-mode": {
|
||||||
"description": "Виберіть, як відображаються мітки робочих просторів.",
|
"description": "Виберіть, як відображаються мітки робочих просторів.",
|
||||||
"label": "Режим міток"
|
"label": "Режим міток"
|
||||||
@@ -381,14 +389,20 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"battery": {
|
"battery": {
|
||||||
|
"brightness": "Яскравість",
|
||||||
|
"charge-level": "Рівень заряду",
|
||||||
"charging": "Зарядка.",
|
"charging": "Зарядка.",
|
||||||
"charging-rate": "Швидкість зарядки: {rate} Вт.",
|
"charging-rate": "Швидкість зарядки: {rate} Вт.",
|
||||||
"discharging": "Розрядка.",
|
"discharging": "Розрядка.",
|
||||||
"discharging-rate": "Швидкість розрядки: {rate} Вт.",
|
"discharging-rate": "Швидкість розрядки: {rate} Вт.",
|
||||||
"health": "Здоров'я: {percent}%",
|
"health": "Здоров'я: {percent}%",
|
||||||
"idle": "Бездіяльність.",
|
"idle": "Бездіяльність.",
|
||||||
|
"inhibit-idle-description": "Підтримує систему активною.",
|
||||||
|
"inhibit-idle-label": "Не давати заснути",
|
||||||
"no-battery-detected": "Батарею не виявлено.",
|
"no-battery-detected": "Батарею не виявлено.",
|
||||||
|
"panel-title": "Батарея",
|
||||||
"plugged-in": "Підключено.",
|
"plugged-in": "Підключено.",
|
||||||
|
"power-profile": "Профіль живлення",
|
||||||
"time-left": "Залишилось часу: {time}.",
|
"time-left": "Залишилось часу: {time}.",
|
||||||
"time-until-full": "Час до повного заряду: {time}."
|
"time-until-full": "Час до повного заряду: {time}."
|
||||||
},
|
},
|
||||||
@@ -503,6 +517,15 @@
|
|||||||
"description": "Ваші сповіщення з'являтимуться тут по мірі надходження.",
|
"description": "Ваші сповіщення з'являтимуться тут по мірі надходження.",
|
||||||
"no-notifications": "Немає сповіщень",
|
"no-notifications": "Немає сповіщень",
|
||||||
"title": "Сповіщення"
|
"title": "Сповіщення"
|
||||||
|
},
|
||||||
|
"time": {
|
||||||
|
"now": "зараз",
|
||||||
|
"diffM": "1 хвилину тому",
|
||||||
|
"diffMM": "{diff} хвилин тому",
|
||||||
|
"diffH": "1 годину тому",
|
||||||
|
"diffHH": "{diff} годин тому",
|
||||||
|
"diffD": "1 день тому",
|
||||||
|
"diffDD": "{diff} днів тому"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"options": {
|
"options": {
|
||||||
@@ -670,7 +693,11 @@
|
|||||||
"clipboard-history-disabled-description": "Увімкніть історію буфера обміну в налаштуваннях або встановіть cliphist",
|
"clipboard-history-disabled-description": "Увімкніть історію буфера обміну в налаштуваннях або встановіть cliphist",
|
||||||
"clipboard-loading": "Завантаження історії буфера обміну...",
|
"clipboard-loading": "Завантаження історії буфера обміну...",
|
||||||
"clipboard-loading-description": "Зачекайте, будь ласка",
|
"clipboard-loading-description": "Зачекайте, будь ласка",
|
||||||
"clipboard-search-description": "Пошук в історії буфера обміну"
|
"clipboard-search-description": "Пошук в історії буфера обміну",
|
||||||
|
"emoji": "Обрати емодзі",
|
||||||
|
"emoji-loading": "Завантаження емодзі...",
|
||||||
|
"emoji-loading-description": "Зачекайте, будь ласка",
|
||||||
|
"emoji-search-description": "Пошук і копіювання емодзі"
|
||||||
},
|
},
|
||||||
"quickSettings": {
|
"quickSettings": {
|
||||||
"bluetooth": {
|
"bluetooth": {
|
||||||
@@ -1048,6 +1075,10 @@
|
|||||||
"description": "Записати {filepath}.",
|
"description": "Записати {filepath}.",
|
||||||
"description-missing": "Потрібна установка {app}"
|
"description-missing": "Потрібна установка {app}"
|
||||||
},
|
},
|
||||||
|
"cava": {
|
||||||
|
"description": "Записати {filepath}.",
|
||||||
|
"description-missing": "Потрібна установка {app}"
|
||||||
|
},
|
||||||
"vicinae": {
|
"vicinae": {
|
||||||
"description": "Записати {filepath} та перезавантажити",
|
"description": "Записати {filepath} та перезавантажити",
|
||||||
"description-missing": "Потрібна установка {app}"
|
"description-missing": "Потрібна установка {app}"
|
||||||
@@ -2249,23 +2280,11 @@
|
|||||||
"widgets": {
|
"widgets": {
|
||||||
"color-picker": {
|
"color-picker": {
|
||||||
"apply": "Застосувати",
|
"apply": "Застосувати",
|
||||||
"brightness": "Яскравість",
|
|
||||||
"cancel": "Скасувати",
|
"cancel": "Скасувати",
|
||||||
"hex": {
|
|
||||||
"description": "Введіть шістнадцятковий код кольору.",
|
|
||||||
"label": "Hex-колір"
|
|
||||||
},
|
|
||||||
"palette": {
|
"palette": {
|
||||||
"description": "Виберіть із широкого діапазону попередньо визначених кольорів.",
|
"description": "Виберіть із широкого діапазону попередньо визначених кольорів.",
|
||||||
"label": "Палітра"
|
"label": "Палітра",
|
||||||
},
|
"theme-colors": "Швидкий доступ до колірної палітри вашої теми."
|
||||||
"rgb": {
|
|
||||||
"description": "Налаштуйте значення червоного, зеленого, синього та яскравості.",
|
|
||||||
"label": "Значення RGB"
|
|
||||||
},
|
|
||||||
"theme-colors": {
|
|
||||||
"description": "Швидкий доступ до колірної палітри вашої теми.",
|
|
||||||
"label": "Кольори теми"
|
|
||||||
},
|
},
|
||||||
"title": "Вибір кольору"
|
"title": "Вибір кольору"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -231,6 +231,10 @@
|
|||||||
"description": "播放音乐时显示音频可视化器。",
|
"description": "播放音乐时显示音频可视化器。",
|
||||||
"label": "显示可视化器"
|
"label": "显示可视化器"
|
||||||
},
|
},
|
||||||
|
"show-progress-ring": {
|
||||||
|
"description": "显示显示曲目进度的圆形进度指示器。",
|
||||||
|
"label": "显示进度环"
|
||||||
|
},
|
||||||
"use-fixed-width": {
|
"use-fixed-width": {
|
||||||
"description": "启用后,小部件将始终使用最大宽度,而不根据内容动态调整。",
|
"description": "启用后,小部件将始终使用最大宽度,而不根据内容动态调整。",
|
||||||
"label": "使用固定宽度"
|
"label": "使用固定宽度"
|
||||||
@@ -373,6 +377,10 @@
|
|||||||
"description": "不显示没有窗口的工作区。",
|
"description": "不显示没有窗口的工作区。",
|
||||||
"label": "隐藏未占用"
|
"label": "隐藏未占用"
|
||||||
},
|
},
|
||||||
|
"follow-focused-screen": {
|
||||||
|
"description": "显示当前焦点屏幕的工作区,而不是任务栏所在屏幕的工作区。",
|
||||||
|
"label": "跟随焦点屏幕"
|
||||||
|
},
|
||||||
"label-mode": {
|
"label-mode": {
|
||||||
"description": "选择工作区标签的显示方式。",
|
"description": "选择工作区标签的显示方式。",
|
||||||
"label": "标签模式"
|
"label": "标签模式"
|
||||||
@@ -381,14 +389,20 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"battery": {
|
"battery": {
|
||||||
|
"brightness": "亮度",
|
||||||
|
"charge-level": "电量",
|
||||||
"charging": "正在充电。",
|
"charging": "正在充电。",
|
||||||
"charging-rate": "充电速率:{rate} W。",
|
"charging-rate": "充电速率:{rate} W。",
|
||||||
"discharging": "正在放电。",
|
"discharging": "正在放电。",
|
||||||
"discharging-rate": "放电速率:{rate} W。",
|
"discharging-rate": "放电速率:{rate} W。",
|
||||||
"health": "健康状况:{percent}%",
|
"health": "健康状况:{percent}%",
|
||||||
"idle": "空闲。",
|
"idle": "空闲。",
|
||||||
|
"inhibit-idle-description": "保持系统唤醒。",
|
||||||
|
"inhibit-idle-label": "保持唤醒",
|
||||||
"no-battery-detected": "未检测到电池。",
|
"no-battery-detected": "未检测到电池。",
|
||||||
|
"panel-title": "电池",
|
||||||
"plugged-in": "已接通电源。",
|
"plugged-in": "已接通电源。",
|
||||||
|
"power-profile": "电源模式",
|
||||||
"time-left": "剩余时间:{time}。",
|
"time-left": "剩余时间:{time}。",
|
||||||
"time-until-full": "充满所需时间:{time}。"
|
"time-until-full": "充满所需时间:{time}。"
|
||||||
},
|
},
|
||||||
@@ -503,6 +517,15 @@
|
|||||||
"description": "您的通知将在到达时显示在此处。",
|
"description": "您的通知将在到达时显示在此处。",
|
||||||
"no-notifications": "无通知",
|
"no-notifications": "无通知",
|
||||||
"title": "通知"
|
"title": "通知"
|
||||||
|
},
|
||||||
|
"time": {
|
||||||
|
"now": "现在",
|
||||||
|
"diffM": "1 分钟前",
|
||||||
|
"diffMM": "{diff} 分钟前",
|
||||||
|
"diffH": "1 小时前",
|
||||||
|
"diffHH": "{diff} 小时前",
|
||||||
|
"diffD": "1 天前",
|
||||||
|
"diffDD": "{diff} 天前"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"options": {
|
"options": {
|
||||||
@@ -670,7 +693,11 @@
|
|||||||
"clipboard-history-disabled-description": "在设置中启用剪贴板历史记录或安装 cliphist",
|
"clipboard-history-disabled-description": "在设置中启用剪贴板历史记录或安装 cliphist",
|
||||||
"clipboard-loading": "正在加载剪贴板历史记录...",
|
"clipboard-loading": "正在加载剪贴板历史记录...",
|
||||||
"clipboard-loading-description": "请稍候",
|
"clipboard-loading-description": "请稍候",
|
||||||
"clipboard-search-description": "搜索剪贴板历史记录"
|
"clipboard-search-description": "搜索剪贴板历史记录",
|
||||||
|
"emoji": "表情符号选择器",
|
||||||
|
"emoji-loading": "正在加载表情符号...",
|
||||||
|
"emoji-loading-description": "请稍候",
|
||||||
|
"emoji-search-description": "搜索和复制表情符号"
|
||||||
},
|
},
|
||||||
"quickSettings": {
|
"quickSettings": {
|
||||||
"bluetooth": {
|
"bluetooth": {
|
||||||
@@ -1048,6 +1075,10 @@
|
|||||||
"description": "写入 {filepath}。",
|
"description": "写入 {filepath}。",
|
||||||
"description-missing": "需要安装 {app}"
|
"description-missing": "需要安装 {app}"
|
||||||
},
|
},
|
||||||
|
"cava": {
|
||||||
|
"description": "写入 {filepath}。",
|
||||||
|
"description-missing": "需要安装 {app}"
|
||||||
|
},
|
||||||
"vicinae": {
|
"vicinae": {
|
||||||
"description": "写入 {filepath} 并重新加载",
|
"description": "写入 {filepath} 并重新加载",
|
||||||
"description-missing": "需要安装 {app}"
|
"description-missing": "需要安装 {app}"
|
||||||
@@ -2249,23 +2280,11 @@
|
|||||||
"widgets": {
|
"widgets": {
|
||||||
"color-picker": {
|
"color-picker": {
|
||||||
"apply": "应用",
|
"apply": "应用",
|
||||||
"brightness": "亮度",
|
|
||||||
"cancel": "取消",
|
"cancel": "取消",
|
||||||
"hex": {
|
|
||||||
"description": "输入十六进制颜色代码。",
|
|
||||||
"label": "十六进制颜色"
|
|
||||||
},
|
|
||||||
"palette": {
|
"palette": {
|
||||||
"description": "从各种预定义颜色中选择。",
|
"description": "从各种预定义颜色中选择。",
|
||||||
"label": "调色板"
|
"label": "调色板",
|
||||||
},
|
"theme-colors": "快速访问您主题的调色板。"
|
||||||
"rgb": {
|
|
||||||
"description": "调整红、绿、蓝和亮度值。",
|
|
||||||
"label": "RGB 值"
|
|
||||||
},
|
|
||||||
"theme-colors": {
|
|
||||||
"description": "快速访问您的主题调色板。",
|
|
||||||
"label": "主题颜色"
|
|
||||||
},
|
},
|
||||||
"title": "颜色选择器"
|
"title": "颜色选择器"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -70,7 +70,7 @@
|
|||||||
"animationDisabled": false,
|
"animationDisabled": false,
|
||||||
"compactLockScreen": false,
|
"compactLockScreen": false,
|
||||||
"lockOnSuspend": true,
|
"lockOnSuspend": true,
|
||||||
"showHibernateOnLockScreen": true,
|
"showHibernateOnLockScreen": false,
|
||||||
"enableShadows": true,
|
"enableShadows": true,
|
||||||
"shadowDirection": "bottom_right",
|
"shadowDirection": "bottom_right",
|
||||||
"shadowOffsetX": 2,
|
"shadowOffsetX": 2,
|
||||||
@@ -326,6 +326,7 @@
|
|||||||
"walker": false,
|
"walker": false,
|
||||||
"code": false,
|
"code": false,
|
||||||
"spicetify": false,
|
"spicetify": false,
|
||||||
|
"telegram": false,
|
||||||
"enableUserTemplates": false
|
"enableUserTemplates": false
|
||||||
},
|
},
|
||||||
"nightLight": {
|
"nightLight": {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
if [ "$#" -ne 1 ]; then
|
if [ "$#" -ne 1 ]; then
|
||||||
# Print usage information to standard error.
|
# Print usage information to standard error.
|
||||||
echo "Error: No application specified." >&2
|
echo "Error: No application specified." >&2
|
||||||
echo "Usage: $0 {kitty|ghostty|foot|alacritty|wezterm|fuzzel|walker|pywalfox}" >&2
|
echo "Usage: $0 {kitty|ghostty|foot|alacritty|wezterm|fuzzel|walker|pywalfox|cava}" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -200,6 +200,58 @@ pywalfox)
|
|||||||
pywalfox update
|
pywalfox update
|
||||||
;;
|
;;
|
||||||
|
|
||||||
|
cava)
|
||||||
|
echo "🎨 Applying 'noctalia' theme to cava..."
|
||||||
|
CONFIG_FILE="$HOME/.config/cava/config"
|
||||||
|
THEME_MODIFIED=false
|
||||||
|
|
||||||
|
# Check if the config file exists.
|
||||||
|
if [ -f "$CONFIG_FILE" ]; then
|
||||||
|
# Check if [color] section exists
|
||||||
|
if grep -q '^\[color\]' "$CONFIG_FILE"; then
|
||||||
|
echo "[color] section found, checking theme setting..."
|
||||||
|
# Check if theme is already set to noctalia under [color]
|
||||||
|
if sed -n '/^\[color\]/,/^\[/p' "$CONFIG_FILE" | grep -q '^theme = "noctalia"'; then
|
||||||
|
echo "Theme already set to noctalia under [color], skipping modification."
|
||||||
|
else
|
||||||
|
# Check if theme line exists under [color] section
|
||||||
|
if sed -n '/^\[color\]/,/^\[/p' "$CONFIG_FILE" | grep -q '^theme = '; then
|
||||||
|
# Replace existing theme line under [color]
|
||||||
|
sed -i '/^\[color\]/,/^\[/{s/^theme = .*/theme = "noctalia"/}' "$CONFIG_FILE"
|
||||||
|
THEME_MODIFIED=true
|
||||||
|
else
|
||||||
|
# Add theme line after [color]
|
||||||
|
sed -i '/^\[color\]/a theme = "noctalia"' "$CONFIG_FILE"
|
||||||
|
THEME_MODIFIED=true
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "[color] section not found, adding it with theme..."
|
||||||
|
# Add [color] section with theme at the end of file
|
||||||
|
echo "" >>"$CONFIG_FILE"
|
||||||
|
echo "[color]" >>"$CONFIG_FILE"
|
||||||
|
echo 'theme = "noctalia"' >>"$CONFIG_FILE"
|
||||||
|
THEME_MODIFIED=true
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Reload cava if it's running
|
||||||
|
if pgrep -f cava >/dev/null; then
|
||||||
|
echo "Reloading cava configuration..."
|
||||||
|
pkill -USR1 cava
|
||||||
|
echo "✅ Cava reloaded successfully"
|
||||||
|
else
|
||||||
|
if [ "$THEME_MODIFIED" = true ]; then
|
||||||
|
echo "✅ Configuration updated. Start cava to see the changes."
|
||||||
|
else
|
||||||
|
echo "✅ Configuration already correct."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "Error: cava config file not found at $CONFIG_FILE" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
|
||||||
*)
|
*)
|
||||||
# Handle unknown application names.
|
# Handle unknown application names.
|
||||||
echo "Error: Unknown application '$APP_NAME'." >&2
|
echo "Error: Unknown application '$APP_NAME'." >&2
|
||||||
|
|||||||
@@ -215,7 +215,7 @@ Singleton {
|
|||||||
property bool animationDisabled: false
|
property bool animationDisabled: false
|
||||||
property bool compactLockScreen: false
|
property bool compactLockScreen: false
|
||||||
property bool lockOnSuspend: true
|
property bool lockOnSuspend: true
|
||||||
property bool showHibernateOnLockScreen: true
|
property bool showHibernateOnLockScreen: false
|
||||||
property bool enableShadows: true
|
property bool enableShadows: true
|
||||||
property string shadowDirection: "bottom_right"
|
property string shadowDirection: "bottom_right"
|
||||||
property int shadowOffsetX: 2
|
property int shadowOffsetX: 2
|
||||||
@@ -508,6 +508,7 @@ Singleton {
|
|||||||
property bool code: false
|
property bool code: false
|
||||||
property bool spicetify: false
|
property bool spicetify: false
|
||||||
property bool telegram: false
|
property bool telegram: false
|
||||||
|
property bool cava: false
|
||||||
property bool enableUserTemplates: false
|
property bool enableUserTemplates: false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -796,6 +797,220 @@ Singleton {
|
|||||||
Logger.w("Settings", "Failed to read raw JSON for dimDesktop migration:", error);
|
Logger.w("Settings", "Failed to read raw JSON for dimDesktop migration:", error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// -----------------
|
||||||
|
// 8th. Migrate ShellState-related files from old cache files to ShellState
|
||||||
|
// This consolidates migrations that were previously in individual service files
|
||||||
|
if (typeof ShellState !== 'undefined' && ShellState.isLoaded) {
|
||||||
|
migrateShellStateFiles();
|
||||||
|
} else {
|
||||||
|
// Wait for ShellState to be ready
|
||||||
|
Qt.callLater(() => {
|
||||||
|
if (typeof ShellState !== 'undefined' && ShellState.isLoaded) {
|
||||||
|
migrateShellStateFiles();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------
|
||||||
|
// Migrate old cache files to ShellState
|
||||||
|
function migrateShellStateFiles() {
|
||||||
|
// Migrate display.json → ShellState (CompositorService)
|
||||||
|
migrateDisplayFile();
|
||||||
|
|
||||||
|
// Migrate notifications-state.json → ShellState (NotificationService)
|
||||||
|
migrateNotificationsStateFile();
|
||||||
|
|
||||||
|
// Migrate changelog-state.json → ShellState (UpdateService)
|
||||||
|
migrateChangelogStateFile();
|
||||||
|
|
||||||
|
// Migrate color-schemes-list.json → ShellState (SchemeDownloader)
|
||||||
|
migrateColorSchemesListFile();
|
||||||
|
|
||||||
|
// Migrate wallpaper paths from Settings → ShellState (WallpaperService)
|
||||||
|
migrateWallpaperPaths();
|
||||||
|
}
|
||||||
|
|
||||||
|
function migrateDisplayFile() {
|
||||||
|
// Check if ShellState already has display data
|
||||||
|
const cached = ShellState.getDisplay();
|
||||||
|
if (cached && Object.keys(cached).length > 0) {
|
||||||
|
return; // Already migrated
|
||||||
|
}
|
||||||
|
|
||||||
|
const oldDisplayPath = cacheDir + "display.json";
|
||||||
|
const migrationFileView = Qt.createQmlObject(`
|
||||||
|
import QtQuick
|
||||||
|
import Quickshell.Io
|
||||||
|
import qs.Commons
|
||||||
|
FileView {
|
||||||
|
id: migrationView
|
||||||
|
path: "${oldDisplayPath}"
|
||||||
|
printErrors: false
|
||||||
|
adapter: JsonAdapter {
|
||||||
|
property var displays: ({})
|
||||||
|
}
|
||||||
|
onLoaded: {
|
||||||
|
if (adapter.displays && Object.keys(adapter.displays).length > 0) {
|
||||||
|
ShellState.setDisplay(adapter.displays);
|
||||||
|
Logger.i("Settings", "Migrated display.json to ShellState");
|
||||||
|
}
|
||||||
|
migrationView.destroy();
|
||||||
|
}
|
||||||
|
onLoadFailed: {
|
||||||
|
migrationView.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`, root, "displayMigrationView");
|
||||||
|
}
|
||||||
|
|
||||||
|
function migrateNotificationsStateFile() {
|
||||||
|
// Check if ShellState already has notifications state
|
||||||
|
const cached = ShellState.getNotificationsState();
|
||||||
|
if (cached && cached.lastSeenTs && cached.lastSeenTs > 0) {
|
||||||
|
return; // Already migrated
|
||||||
|
}
|
||||||
|
|
||||||
|
// Also check Settings for lastSeenTs
|
||||||
|
if (adapter.notifications && adapter.notifications.lastSeenTs) {
|
||||||
|
ShellState.setNotificationsState({
|
||||||
|
lastSeenTs: adapter.notifications.lastSeenTs
|
||||||
|
});
|
||||||
|
Logger.i("Settings", "Migrated notifications lastSeenTs from Settings to ShellState");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const oldStatePath = cacheDir + "notifications-state.json";
|
||||||
|
const migrationFileView = Qt.createQmlObject(`
|
||||||
|
import QtQuick
|
||||||
|
import Quickshell.Io
|
||||||
|
import qs.Commons
|
||||||
|
FileView {
|
||||||
|
id: migrationView
|
||||||
|
path: "${oldStatePath}"
|
||||||
|
printErrors: false
|
||||||
|
adapter: JsonAdapter {
|
||||||
|
property real lastSeenTs: 0
|
||||||
|
}
|
||||||
|
onLoaded: {
|
||||||
|
if (adapter.lastSeenTs && adapter.lastSeenTs > 0) {
|
||||||
|
ShellState.setNotificationsState({
|
||||||
|
lastSeenTs: adapter.lastSeenTs
|
||||||
|
});
|
||||||
|
Logger.i("Settings", "Migrated notifications-state.json to ShellState");
|
||||||
|
}
|
||||||
|
migrationView.destroy();
|
||||||
|
}
|
||||||
|
onLoadFailed: {
|
||||||
|
migrationView.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`, root, "notificationsMigrationView");
|
||||||
|
}
|
||||||
|
|
||||||
|
function migrateChangelogStateFile() {
|
||||||
|
// Check if ShellState already has changelog state
|
||||||
|
const cached = ShellState.getChangelogState();
|
||||||
|
if (cached && cached.lastSeenVersion && cached.lastSeenVersion !== "") {
|
||||||
|
return; // Already migrated
|
||||||
|
}
|
||||||
|
|
||||||
|
// Also check Settings for lastSeenVersion
|
||||||
|
if (adapter.changelog && adapter.changelog.lastSeenVersion) {
|
||||||
|
ShellState.setChangelogState({
|
||||||
|
lastSeenVersion: adapter.changelog.lastSeenVersion
|
||||||
|
});
|
||||||
|
Logger.i("Settings", "Migrated changelog lastSeenVersion from Settings to ShellState");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const oldChangelogPath = cacheDir + "changelog-state.json";
|
||||||
|
const migrationFileView = Qt.createQmlObject(`
|
||||||
|
import QtQuick
|
||||||
|
import Quickshell.Io
|
||||||
|
import qs.Commons
|
||||||
|
FileView {
|
||||||
|
id: migrationView
|
||||||
|
path: "${oldChangelogPath}"
|
||||||
|
printErrors: false
|
||||||
|
adapter: JsonAdapter {
|
||||||
|
property string lastSeenVersion: ""
|
||||||
|
}
|
||||||
|
onLoaded: {
|
||||||
|
if (adapter.lastSeenVersion && adapter.lastSeenVersion !== "") {
|
||||||
|
ShellState.setChangelogState({
|
||||||
|
lastSeenVersion: adapter.lastSeenVersion
|
||||||
|
});
|
||||||
|
Logger.i("Settings", "Migrated changelog-state.json to ShellState");
|
||||||
|
}
|
||||||
|
migrationView.destroy();
|
||||||
|
}
|
||||||
|
onLoadFailed: {
|
||||||
|
migrationView.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`, root, "changelogMigrationView");
|
||||||
|
}
|
||||||
|
|
||||||
|
function migrateColorSchemesListFile() {
|
||||||
|
// Check if ShellState already has color schemes list
|
||||||
|
const cached = ShellState.getColorSchemesList();
|
||||||
|
if (cached && cached.schemes && cached.schemes.length > 0) {
|
||||||
|
return; // Already migrated
|
||||||
|
}
|
||||||
|
|
||||||
|
const oldSchemesPath = cacheDir + "color-schemes-list.json";
|
||||||
|
const migrationFileView = Qt.createQmlObject(`
|
||||||
|
import QtQuick
|
||||||
|
import Quickshell.Io
|
||||||
|
import qs.Commons
|
||||||
|
FileView {
|
||||||
|
id: migrationView
|
||||||
|
path: "${oldSchemesPath}"
|
||||||
|
printErrors: false
|
||||||
|
adapter: JsonAdapter {
|
||||||
|
property var schemes: []
|
||||||
|
property real timestamp: 0
|
||||||
|
}
|
||||||
|
onLoaded: {
|
||||||
|
if (adapter.schemes && adapter.schemes.length > 0) {
|
||||||
|
ShellState.setColorSchemesList({
|
||||||
|
schemes: adapter.schemes,
|
||||||
|
timestamp: adapter.timestamp || 0
|
||||||
|
});
|
||||||
|
Logger.i("Settings", "Migrated color-schemes-list.json to ShellState");
|
||||||
|
}
|
||||||
|
migrationView.destroy();
|
||||||
|
}
|
||||||
|
onLoadFailed: {
|
||||||
|
migrationView.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`, root, "schemesMigrationView");
|
||||||
|
}
|
||||||
|
|
||||||
|
function migrateWallpaperPaths() {
|
||||||
|
// Check if ShellState already has wallpaper paths
|
||||||
|
const cached = ShellState.getWallpapers();
|
||||||
|
if (cached && Object.keys(cached).length > 0) {
|
||||||
|
return; // Already migrated
|
||||||
|
}
|
||||||
|
|
||||||
|
// Migrate from Settings wallpaper.monitors
|
||||||
|
var monitors = adapter.wallpaper.monitors || [];
|
||||||
|
if (monitors.length > 0) {
|
||||||
|
var wallpapers = {};
|
||||||
|
for (var i = 0; i < monitors.length; i++) {
|
||||||
|
if (monitors[i].name && monitors[i].wallpaper) {
|
||||||
|
wallpapers[monitors[i].name] = monitors[i].wallpaper;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (Object.keys(wallpapers).length > 0) {
|
||||||
|
ShellState.setWallpapers(wallpapers);
|
||||||
|
Logger.i("Settings", "Migrated wallpaper paths from Settings to ShellState");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------
|
// -----------------------------------------------------
|
||||||
|
|||||||
182
Commons/ShellState.qml
Normal file
182
Commons/ShellState.qml
Normal file
@@ -0,0 +1,182 @@
|
|||||||
|
pragma Singleton
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Io
|
||||||
|
|
||||||
|
// Centralized shell state management for small cache files
|
||||||
|
Singleton {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property string stateFile: ""
|
||||||
|
property bool isLoaded: false
|
||||||
|
|
||||||
|
// State properties for different services
|
||||||
|
readonly property alias data: adapter
|
||||||
|
|
||||||
|
// Signals for state changes
|
||||||
|
signal displayStateChanged
|
||||||
|
signal notificationsStateChanged
|
||||||
|
signal changelogStateChanged
|
||||||
|
signal colorSchemesListChanged
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
// Setup state file path (needs Settings to be available)
|
||||||
|
Qt.callLater(() => {
|
||||||
|
if (typeof Settings !== 'undefined' && Settings.cacheDir) {
|
||||||
|
stateFile = Settings.cacheDir + "shell-state.json";
|
||||||
|
stateFileView.path = stateFile;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// FileView for shell state
|
||||||
|
FileView {
|
||||||
|
id: stateFileView
|
||||||
|
printErrors: false
|
||||||
|
watchChanges: false
|
||||||
|
|
||||||
|
adapter: JsonAdapter {
|
||||||
|
id: adapter
|
||||||
|
|
||||||
|
// CompositorService: display scales
|
||||||
|
property var display: ({})
|
||||||
|
|
||||||
|
// NotificationService: notification state
|
||||||
|
property var notificationsState: ({
|
||||||
|
lastSeenTs: 0
|
||||||
|
})
|
||||||
|
|
||||||
|
// UpdateService: changelog state
|
||||||
|
property var changelogState: ({
|
||||||
|
lastSeenVersion: ""
|
||||||
|
})
|
||||||
|
|
||||||
|
// SchemeDownloader: color schemes list
|
||||||
|
property var colorSchemesList: ({
|
||||||
|
schemes: [],
|
||||||
|
timestamp: 0
|
||||||
|
})
|
||||||
|
|
||||||
|
// WallpaperService: current wallpapers per screen
|
||||||
|
property var wallpapers: ({})
|
||||||
|
}
|
||||||
|
|
||||||
|
onLoaded: {
|
||||||
|
root.isLoaded = true;
|
||||||
|
Logger.d("ShellState", "Loaded state file");
|
||||||
|
}
|
||||||
|
|
||||||
|
onLoadFailed: error => {
|
||||||
|
if (error === 2) {
|
||||||
|
// File doesn't exist, will be created on first write
|
||||||
|
root.isLoaded = true;
|
||||||
|
Logger.d("ShellState", "State file doesn't exist, will create on first write");
|
||||||
|
} else {
|
||||||
|
Logger.e("ShellState", "Failed to load state file:", error);
|
||||||
|
root.isLoaded = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debounced save timer
|
||||||
|
Timer {
|
||||||
|
id: saveTimer
|
||||||
|
interval: 300
|
||||||
|
onTriggered: performSave()
|
||||||
|
}
|
||||||
|
|
||||||
|
property bool saveQueued: false
|
||||||
|
|
||||||
|
function save() {
|
||||||
|
saveQueued = true;
|
||||||
|
saveTimer.restart();
|
||||||
|
}
|
||||||
|
|
||||||
|
function performSave() {
|
||||||
|
if (!saveQueued || !stateFile) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
saveQueued = false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Ensure cache directory exists
|
||||||
|
Quickshell.execDetached(["mkdir", "-p", Settings.cacheDir]);
|
||||||
|
|
||||||
|
Qt.callLater(() => {
|
||||||
|
try {
|
||||||
|
stateFileView.writeAdapter();
|
||||||
|
Logger.d("ShellState", "Saved state file");
|
||||||
|
} catch (writeError) {
|
||||||
|
Logger.e("ShellState", "Failed to write state file:", writeError);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
Logger.e("ShellState", "Failed to save state:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convenience functions for each service
|
||||||
|
|
||||||
|
// Display state (CompositorService)
|
||||||
|
function setDisplay(displayData) {
|
||||||
|
adapter.display = displayData;
|
||||||
|
save();
|
||||||
|
displayStateChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDisplay() {
|
||||||
|
return adapter.display || {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notifications state (NotificationService)
|
||||||
|
function setNotificationsState(stateData) {
|
||||||
|
adapter.notificationsState = stateData;
|
||||||
|
save();
|
||||||
|
notificationsStateChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
function getNotificationsState() {
|
||||||
|
return adapter.notificationsState || {
|
||||||
|
lastSeenTs: 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Changelog state (UpdateService)
|
||||||
|
function setChangelogState(stateData) {
|
||||||
|
adapter.changelogState = stateData;
|
||||||
|
save();
|
||||||
|
changelogStateChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
function getChangelogState() {
|
||||||
|
return adapter.changelogState || {
|
||||||
|
lastSeenVersion: ""
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Color schemes list (SchemeDownloader)
|
||||||
|
function setColorSchemesList(listData) {
|
||||||
|
adapter.colorSchemesList = listData;
|
||||||
|
save();
|
||||||
|
colorSchemesListChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
function getColorSchemesList() {
|
||||||
|
return adapter.colorSchemesList || {
|
||||||
|
schemes: [],
|
||||||
|
timestamp: 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wallpapers (WallpaperService)
|
||||||
|
function setWallpapers(wallpapersData) {
|
||||||
|
adapter.wallpapers = wallpapersData;
|
||||||
|
save();
|
||||||
|
}
|
||||||
|
|
||||||
|
function getWallpapers() {
|
||||||
|
return adapter.wallpapers || {};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -100,11 +100,23 @@ Singleton {
|
|||||||
return "";
|
return "";
|
||||||
const diff = Date.now() - date.getTime();
|
const diff = Date.now() - date.getTime();
|
||||||
if (diff < 60000)
|
if (diff < 60000)
|
||||||
return "now";
|
return I18n.tr("notifications.time.now");
|
||||||
|
if (diff < 120000)
|
||||||
|
return I18n.tr("notifications.time.diffM");
|
||||||
if (diff < 3600000)
|
if (diff < 3600000)
|
||||||
return `${Math.floor(diff / 60000)}m ago`;
|
return I18n.tr("notifications.time.diffMM", {
|
||||||
|
"diff": Math.floor(diff / 60000)
|
||||||
|
});
|
||||||
|
if (diff < 7200000)
|
||||||
|
return I18n.tr("notifications.time.diffH");
|
||||||
if (diff < 86400000)
|
if (diff < 86400000)
|
||||||
return `${Math.floor(diff / 3600000)}h ago`;
|
return I18n.tr("notifications.time.diffHH", {
|
||||||
return `${Math.floor(diff / 86400000)}d ago`;
|
"diff": Math.floor(diff / 3600000)
|
||||||
|
});
|
||||||
|
if (diff < 172800000)
|
||||||
|
return I18n.tr("notifications.time.diffD");
|
||||||
|
return I18n.tr("notifications.time.diffDD", {
|
||||||
|
"diff": Math.floor(diff / 86400000)
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
187
Helpers/ColorList.js
Normal file
187
Helpers/ColorList.js
Normal file
@@ -0,0 +1,187 @@
|
|||||||
|
var colors = [
|
||||||
|
// --- REDS ---
|
||||||
|
{ name: "MistyRose", color: "mistyrose" },
|
||||||
|
{ name: "LightPink", color: "lightpink" },
|
||||||
|
{ name: "Pink", color: "pink" },
|
||||||
|
{ name: "PaleVioletRed", color: "palevioletred" },
|
||||||
|
{ name: "Pink 500", color: "#E91E63" }, // Material
|
||||||
|
{ name: "HotPink", color: "hotpink" },
|
||||||
|
{ name: "DeepPink", color: "deeppink" },
|
||||||
|
{ name: "MediumVioletRed", color: "mediumvioletred" },
|
||||||
|
{ name: "LightSalmon", color: "lightsalmon" },
|
||||||
|
{ name: "Salmon", color: "salmon" },
|
||||||
|
{ name: "DarkSalmon", color: "darksalmon" },
|
||||||
|
{ name: "LightCoral", color: "lightcoral" },
|
||||||
|
{ name: "IndianRed", color: "indianred" },
|
||||||
|
{ name: "Alizarin", color: "#E74C3C" }, // Flat UI
|
||||||
|
{ name: "Red 500", color: "#F44336" }, // Material
|
||||||
|
{ name: "Crimson", color: "crimson" },
|
||||||
|
{ name: "Red", color: "red" },
|
||||||
|
{ name: "FireBrick", color: "firebrick" },
|
||||||
|
{ name: "DarkRed", color: "darkred" },
|
||||||
|
{ name: "Maroon", color: "maroon" },
|
||||||
|
{ name: "Brown", color: "brown" },
|
||||||
|
|
||||||
|
// --- ORANGES & BROWNS ---
|
||||||
|
{ name: "Coral", color: "coral" },
|
||||||
|
{ name: "Tomato", color: "tomato" },
|
||||||
|
{ name: "OrangeRed", color: "orangered" },
|
||||||
|
{ name: "Deep Orange 500", color: "#FF5722" }, // Material
|
||||||
|
{ name: "DarkOrange", color: "darkorange" },
|
||||||
|
{ name: "Carrot", color: "#E67E22" }, // Flat UI
|
||||||
|
{ name: "Orange 500", color: "#FF9800" }, // Material
|
||||||
|
{ name: "Orange", color: "orange" },
|
||||||
|
{ name: "SandyBrown", color: "sandybrown" },
|
||||||
|
{ name: "Peru", color: "peru" },
|
||||||
|
{ name: "Chocolate", color: "chocolate" },
|
||||||
|
{ name: "SaddleBrown", color: "saddlebrown" },
|
||||||
|
{ name: "Sienna", color: "sienna" },
|
||||||
|
{ name: "Brown 500", color: "#795548" }, // Material
|
||||||
|
|
||||||
|
// --- YELLOWS, BEIGES & GOLDS ---
|
||||||
|
{ name: "LightYellow", color: "lightyellow" },
|
||||||
|
{ name: "LemonChiffon", color: "lemonchiffon" },
|
||||||
|
{ name: "LightGoldenrodYellow", color: "lightgoldenrodyellow" },
|
||||||
|
{ name: "PapayaWhip", color: "papayawhip" },
|
||||||
|
{ name: "Moccasin", color: "moccasin" },
|
||||||
|
{ name: "PeachPuff", color: "peachpuff" },
|
||||||
|
{ name: "NavajoWhite", color: "navajowhite" },
|
||||||
|
{ name: "Wheat", color: "wheat" },
|
||||||
|
{ name: "BurlyWood", color: "burlywood" },
|
||||||
|
{ name: "Tan", color: "tan" },
|
||||||
|
{ name: "Bisque", color: "bisque" },
|
||||||
|
{ name: "BlanchedAlmond", color: "blanchedalmond" },
|
||||||
|
{ name: "Cornsilk", color: "cornsilk" },
|
||||||
|
{ name: "PaleGoldenrod", color: "palegoldenrod" },
|
||||||
|
{ name: "Khaki", color: "khaki" },
|
||||||
|
{ name: "DarkKhaki", color: "darkkhaki" },
|
||||||
|
{ name: "Goldenrod", color: "goldenrod" },
|
||||||
|
{ name: "DarkGoldenrod", color: "darkgoldenrod" },
|
||||||
|
{ name: "Sun Flower", color: "#F1C40F" }, // Flat UI
|
||||||
|
{ name: "Yellow 500", color: "#FFEB3B" }, // Material
|
||||||
|
{ name: "Yellow", color: "yellow" },
|
||||||
|
{ name: "Gold", color: "gold" },
|
||||||
|
{ name: "Amber 500", color: "#FFC107" }, // Material
|
||||||
|
|
||||||
|
// --- GREENS ---
|
||||||
|
{ name: "GreenYellow", color: "greenyellow" },
|
||||||
|
{ name: "Chartreuse", color: "chartreuse" },
|
||||||
|
{ name: "LawnGreen", color: "lawngreen" },
|
||||||
|
{ name: "Lime 500", color: "#CDDC39" }, // Material
|
||||||
|
{ name: "Lime", color: "lime" },
|
||||||
|
{ name: "LimeGreen", color: "limegreen" },
|
||||||
|
{ name: "PaleGreen", color: "palegreen" },
|
||||||
|
{ name: "LightGreen", color: "lightgreen" },
|
||||||
|
{ name: "Light Green 500", color: "#8BC34A" }, // Material
|
||||||
|
{ name: "MediumSpringGreen", color: "mediumspringgreen" },
|
||||||
|
{ name: "SpringGreen", color: "springgreen" },
|
||||||
|
{ name: "Emerald", color: "#2ECC71" }, // Flat UI
|
||||||
|
{ name: "Green 500", color: "#4CAF50" }, // Material
|
||||||
|
{ name: "MediumSeaGreen", color: "mediumseagreen" },
|
||||||
|
{ name: "SeaGreen", color: "seagreen" },
|
||||||
|
{ name: "ForestGreen", color: "forestgreen" },
|
||||||
|
{ name: "Green", color: "green" },
|
||||||
|
{ name: "DarkGreen", color: "darkgreen" },
|
||||||
|
{ name: "YellowGreen", color: "yellowgreen" },
|
||||||
|
{ name: "OliveDrab", color: "olivedrab" },
|
||||||
|
{ name: "Olive", color: "olive" },
|
||||||
|
{ name: "DarkOliveGreen", color: "darkolivegreen" },
|
||||||
|
|
||||||
|
// --- TEALS & CYANS ---
|
||||||
|
{ name: "MediumAquamarine", color: "mediumaquamarine" },
|
||||||
|
{ name: "DarkSeaGreen", color: "darkseagreen" },
|
||||||
|
{ name: "LightSeaGreen", color: "lightseagreen" },
|
||||||
|
{ name: "DarkCyan", color: "darkcyan" },
|
||||||
|
{ name: "Teal", color: "teal" },
|
||||||
|
{ name: "Turquoise", color: "#1ABC9C" }, // Flat UI
|
||||||
|
{ name: "LightCyan", color: "lightcyan" },
|
||||||
|
{ name: "PaleTurquoise", color: "paleturquoise" },
|
||||||
|
{ name: "Aquamarine", color: "aquamarine" },
|
||||||
|
{ name: "Turquoise", color: "turquoise" },
|
||||||
|
{ name: "MediumTurquoise", color: "mediumturquoise" },
|
||||||
|
{ name: "DarkTurquoise", color: "darkturquoise" },
|
||||||
|
{ name: "Aqua", color: "aqua" },
|
||||||
|
{ name: "Cyan", color: "cyan" },
|
||||||
|
{ name: "Cyan 500", color: "#00BCD4" }, // Material
|
||||||
|
{ name: "CadetBlue", color: "cadetblue" },
|
||||||
|
{ name: "Teal 500", color: "#009688" }, // Material
|
||||||
|
{ name: "DarkSlateGray", color: "darkslategray" },
|
||||||
|
|
||||||
|
// --- BLUES ---
|
||||||
|
{ name: "PowderBlue", color: "powderblue" },
|
||||||
|
{ name: "LightBlue", color: "lightblue" },
|
||||||
|
{ name: "SkyBlue", color: "skyblue" },
|
||||||
|
{ name: "LightSkyBlue", color: "lightskyblue" },
|
||||||
|
{ name: "Light Blue 500", color: "#03A9F4" }, // Material
|
||||||
|
{ name: "DeepSkyBlue", color: "deepskyblue" },
|
||||||
|
{ name: "DodgerBlue", color: "dodgerblue" },
|
||||||
|
{ name: "CornflowerBlue", color: "cornflowerblue" },
|
||||||
|
{ name: "Peter River", color: "#3498DB" }, // Flat UI
|
||||||
|
{ name: "Blue 500", color: "#2196F3" }, // Material
|
||||||
|
{ name: "SteelBlue", color: "steelblue" },
|
||||||
|
{ name: "LightSteelBlue", color: "lightsteelblue" },
|
||||||
|
{ name: "RoyalBlue", color: "royalblue" },
|
||||||
|
{ name: "Blue", color: "blue" },
|
||||||
|
{ name: "MediumBlue", color: "mediumblue" },
|
||||||
|
{ name: "Belize Hole", color: "#2980B9" }, // Flat UI
|
||||||
|
{ name: "DarkBlue", color: "darkblue" },
|
||||||
|
{ name: "Navy", color: "navy" },
|
||||||
|
{ name: "MidnightBlue", color: "midnightblue" },
|
||||||
|
{ name: "Midnight Blue", color: "#2C3E50" }, // Flat UI (Same name, different color)
|
||||||
|
{ name: "Indigo 500", color: "#3F51B5" }, // Material
|
||||||
|
{ name: "DarkSlateBlue", color: "darkslateblue" },
|
||||||
|
{ name: "MediumSlateBlue", color: "mediumslateblue" },
|
||||||
|
{ name: "SlateBlue", color: "slateblue" },
|
||||||
|
|
||||||
|
// --- PURPLES & MAGENTAS ---
|
||||||
|
{ name: "Lavender", color: "lavender" },
|
||||||
|
{ name: "Thistle", color: "thistle" },
|
||||||
|
{ name: "Plum", color: "plum" },
|
||||||
|
{ name: "Violet", color: "violet" },
|
||||||
|
{ name: "Orchid", color: "orchid" },
|
||||||
|
{ name: "Fuchsia", color: "fuchsia" },
|
||||||
|
{ name: "Magenta", color: "magenta" },
|
||||||
|
{ name: "MediumOrchid", color: "mediumorchid" },
|
||||||
|
{ name: "MediumPurple", color: "mediumpurple" },
|
||||||
|
{ name: "Amethyst", color: "#9B59B6" }, // Flat UI
|
||||||
|
{ name: "Purple 500", color: "#9C27B0" }, // Material
|
||||||
|
{ name: "BlueViolet", color: "blueviolet" },
|
||||||
|
{ name: "DarkViolet", color: "darkviolet" },
|
||||||
|
{ name: "DarkOrchid", color: "darkorchid" },
|
||||||
|
{ name: "DarkMagenta", color: "darkmagenta" },
|
||||||
|
{ name: "Purple", color: "purple" },
|
||||||
|
{ name: "Deep Purple 500", color: "#673AB7" }, // Material
|
||||||
|
{ name: "Indigo", color: "indigo" },
|
||||||
|
|
||||||
|
// --- NEUTRALS ---
|
||||||
|
{ name: "White", color: "white" },
|
||||||
|
{ name: "Snow", color: "snow" },
|
||||||
|
{ name: "HoneyDew", color: "honeydew" },
|
||||||
|
{ name: "MintCream", color: "mintcream" },
|
||||||
|
{ name: "Azure", color: "azure" },
|
||||||
|
{ name: "AliceBlue", color: "aliceblue" },
|
||||||
|
{ name: "GhostWhite", color: "ghostwhite" },
|
||||||
|
{ name: "WhiteSmoke", color: "whitesmoke" },
|
||||||
|
{ name: "Seashell", color: "seashell" },
|
||||||
|
{ name: "Beige", color: "beige" },
|
||||||
|
{ name: "OldLace", color: "oldlace" },
|
||||||
|
{ name: "FloralWhite", color: "floralwhite" },
|
||||||
|
{ name: "Ivory", color: "ivory" },
|
||||||
|
{ name: "AntiqueWhite", color: "antiquewhite" },
|
||||||
|
{ name: "Linen", color: "linen" },
|
||||||
|
{ name: "LavenderBlush", color: "lavenderblush" },
|
||||||
|
{ name: "Gainsboro", color: "gainsboro" },
|
||||||
|
{ name: "LightGray", color: "lightgray" },
|
||||||
|
{ name: "Silver", color: "silver" },
|
||||||
|
{ name: "DarkGray", color: "darkgray" },
|
||||||
|
{ name: "Gray", color: "gray" },
|
||||||
|
{ name: "Grey 500", color: "#9E9E9E" }, // Material
|
||||||
|
{ name: "Concrete", color: "#95A5A6" }, // Flat UI
|
||||||
|
{ name: "DimGray", color: "dimgray" },
|
||||||
|
{ name: "LightSlateGray", color: "lightslategray" },
|
||||||
|
{ name: "SlateGray", color: "slategray" },
|
||||||
|
{ name: "Asbestos", color: "#7F8C8D" }, // Flat UI
|
||||||
|
{ name: "Blue Grey 500", color: "#607D8B" }, // Material
|
||||||
|
{ name: "Wet Asphalt", color: "#34495E" }, // Flat UI
|
||||||
|
{ name: "Black", color: "black" }
|
||||||
|
]
|
||||||
@@ -122,7 +122,8 @@ Item {
|
|||||||
forceOpen: isReady && (testMode || battery.isLaptopBattery) && displayMode === "alwaysShow"
|
forceOpen: isReady && (testMode || battery.isLaptopBattery) && displayMode === "alwaysShow"
|
||||||
forceClose: displayMode === "alwaysHide" || !isReady || (!testMode && !battery.isLaptopBattery)
|
forceClose: displayMode === "alwaysHide" || !isReady || (!testMode && !battery.isLaptopBattery)
|
||||||
customBackgroundColor: isLowBattery ? Color.mError : Qt.rgba(0, 0, 0, 0)
|
customBackgroundColor: isLowBattery ? Color.mError : Qt.rgba(0, 0, 0, 0)
|
||||||
customTextIconColor: isLowBattery ? Color.mOnError : Qt.rgba(0, 0, 0, 0)
|
customTextIconColor: isLowBattery ? Color.mOnError : charging ? Color.mPrimary : Qt.rgba(0, 0, 0, 0)
|
||||||
|
|
||||||
tooltipText: {
|
tooltipText: {
|
||||||
let lines = [];
|
let lines = [];
|
||||||
if (testMode) {
|
if (testMode) {
|
||||||
@@ -168,6 +169,7 @@ Item {
|
|||||||
}
|
}
|
||||||
return lines.join("\n");
|
return lines.join("\n");
|
||||||
}
|
}
|
||||||
|
onClicked: PanelService.getPanel("batteryPanel", screen)?.toggle(this)
|
||||||
onRightClicked: {
|
onRightClicked: {
|
||||||
var popupMenuWindow = PanelService.getPopupMenuWindow(screen);
|
var popupMenuWindow = PanelService.getPopupMenuWindow(screen);
|
||||||
if (popupMenuWindow) {
|
if (popupMenuWindow) {
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ Item {
|
|||||||
readonly property bool showVisualizer: (widgetSettings.showVisualizer !== undefined) ? widgetSettings.showVisualizer : widgetMetadata.showVisualizer
|
readonly property bool showVisualizer: (widgetSettings.showVisualizer !== undefined) ? widgetSettings.showVisualizer : widgetMetadata.showVisualizer
|
||||||
readonly property string visualizerType: (widgetSettings.visualizerType !== undefined && widgetSettings.visualizerType !== "") ? widgetSettings.visualizerType : widgetMetadata.visualizerType
|
readonly property string visualizerType: (widgetSettings.visualizerType !== undefined && widgetSettings.visualizerType !== "") ? widgetSettings.visualizerType : widgetMetadata.visualizerType
|
||||||
readonly property string scrollingMode: (widgetSettings.scrollingMode !== undefined) ? widgetSettings.scrollingMode : widgetMetadata.scrollingMode
|
readonly property string scrollingMode: (widgetSettings.scrollingMode !== undefined) ? widgetSettings.scrollingMode : widgetMetadata.scrollingMode
|
||||||
|
readonly property bool showProgressRing: (widgetSettings.showProgressRing !== undefined) ? widgetSettings.showProgressRing : widgetMetadata.showProgressRing
|
||||||
|
|
||||||
// Maximum widget width with user settings support
|
// Maximum widget width with user settings support
|
||||||
readonly property real maxWidth: (widgetSettings.maxWidth !== undefined) ? widgetSettings.maxWidth : Math.max(widgetMetadata.maxWidth, screen ? screen.width * 0.06 : 0)
|
readonly property real maxWidth: (widgetSettings.maxWidth !== undefined) ? widgetSettings.maxWidth : Math.max(widgetMetadata.maxWidth, screen ? screen.width * 0.06 : 0)
|
||||||
@@ -321,14 +322,79 @@ Item {
|
|||||||
Layout.preferredWidth: Math.round(21 * scaling)
|
Layout.preferredWidth: Math.round(21 * scaling)
|
||||||
Layout.preferredHeight: Math.round(21 * scaling)
|
Layout.preferredHeight: Math.round(21 * scaling)
|
||||||
|
|
||||||
|
// Background for progress circle
|
||||||
|
Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
radius: width / 2
|
||||||
|
color: Color.transparent
|
||||||
|
}
|
||||||
|
|
||||||
|
// Progress circle
|
||||||
|
Canvas {
|
||||||
|
id: progressCanvas
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: 0 // Align exactly with parent to avoid clipping
|
||||||
|
visible: showProgressRing // Control visibility with setting
|
||||||
|
z: 0 // Behind the album art
|
||||||
|
|
||||||
|
// Calculate progress ratio: 0 to 1
|
||||||
|
property real progressRatio: {
|
||||||
|
if (!MediaService.currentPlayer || MediaService.trackLength <= 0)
|
||||||
|
return 0;
|
||||||
|
const r = MediaService.currentPosition / MediaService.trackLength;
|
||||||
|
if (isNaN(r) || !isFinite(r))
|
||||||
|
return 0;
|
||||||
|
return Math.max(0, Math.min(1, r));
|
||||||
|
}
|
||||||
|
|
||||||
|
onProgressRatioChanged: requestPaint()
|
||||||
|
|
||||||
|
onPaint: {
|
||||||
|
var ctx = getContext("2d");
|
||||||
|
var centerX = width / 2;
|
||||||
|
var centerY = height / 2;
|
||||||
|
var radius = Math.min(width, height) / 2 - (1.25 * scaling); // Larger radius, accounting for line width to approach edge
|
||||||
|
|
||||||
|
ctx.reset();
|
||||||
|
|
||||||
|
// Background circle (full track, not played yet)
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(centerX, centerY, radius, 0, 2 * Math.PI);
|
||||||
|
ctx.lineWidth = 3 * scaling; // Thicker line width based on scaling property
|
||||||
|
ctx.strokeStyle = Qt.alpha(Color.mOnSurface, 0.4); // More opaque for better visibility
|
||||||
|
ctx.stroke();
|
||||||
|
|
||||||
|
// Progress arc (played portion)
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(centerX, centerY, radius, -Math.PI / 2, -Math.PI / 2 + progressRatio * 2 * Math.PI);
|
||||||
|
ctx.lineWidth = 3 * scaling; // Thicker line width based on scaling property
|
||||||
|
ctx.strokeStyle = Color.mPrimary; // Use primary color for progress
|
||||||
|
ctx.lineCap = "round";
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Connection to update progress when media position changes
|
||||||
|
Connections {
|
||||||
|
target: MediaService
|
||||||
|
function onCurrentPositionChanged() {
|
||||||
|
progressCanvas.requestPaint();
|
||||||
|
}
|
||||||
|
function onTrackLengthChanged() {
|
||||||
|
progressCanvas.requestPaint();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
NImageCircled {
|
NImageCircled {
|
||||||
id: trackArt
|
id: trackArt
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
anchors.margins: showProgressRing ? (2.5 * scaling) : 0.5 // Make album art smaller only when progress ring is visible, scaled with widget
|
||||||
imagePath: MediaService.trackArtUrl
|
imagePath: MediaService.trackArtUrl
|
||||||
fallbackIcon: MediaService.isPlaying ? "media-pause" : "media-play"
|
fallbackIcon: MediaService.isPlaying ? "media-pause" : "media-play"
|
||||||
fallbackIconSize: 10
|
fallbackIconSize: 10
|
||||||
borderWidth: 0
|
borderWidth: 0
|
||||||
border.color: Color.transparent
|
border.color: Color.transparent
|
||||||
|
z: 1 // In front of the progress circle
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -484,6 +550,52 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Progress circle for vertical layout - follows background radius
|
||||||
|
Canvas {
|
||||||
|
id: progressCanvasVertical
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: 0 // Align with parent container (mainContainer which matches mediaMini)
|
||||||
|
visible: isVerticalBar && showProgressRing // Control visibility with setting
|
||||||
|
z: 0 // Behind other content
|
||||||
|
|
||||||
|
// Calculate progress ratio: 0 to 1
|
||||||
|
property real progressRatio: {
|
||||||
|
if (!MediaService.currentPlayer || MediaService.trackLength <= 0)
|
||||||
|
return 0;
|
||||||
|
const r = MediaService.currentPosition / MediaService.trackLength;
|
||||||
|
if (isNaN(r) || !isFinite(r))
|
||||||
|
return 0;
|
||||||
|
return Math.max(0, Math.min(1, r));
|
||||||
|
}
|
||||||
|
|
||||||
|
onProgressRatioChanged: requestPaint()
|
||||||
|
|
||||||
|
onPaint: {
|
||||||
|
var ctx = getContext("2d");
|
||||||
|
var centerX = width / 2;
|
||||||
|
var centerY = height / 2;
|
||||||
|
// Align with mediaMini radius which is circular in vertical mode
|
||||||
|
var radius = Math.min(width, height) / 2 - 4; // Position ring near the outer edge of background
|
||||||
|
|
||||||
|
ctx.reset();
|
||||||
|
|
||||||
|
// Background circle (full track, not played yet)
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(centerX, centerY, radius, 0, 2 * Math.PI);
|
||||||
|
ctx.lineWidth = 1.5 * scaling; // Line width based on scaling property, thinner for vertical layout
|
||||||
|
ctx.strokeStyle = Qt.alpha(Color.mOnSurface, 0.4); // More opaque for better visibility
|
||||||
|
ctx.stroke();
|
||||||
|
|
||||||
|
// Progress arc (played portion)
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(centerX, centerY, radius, -Math.PI / 2, -Math.PI / 2 + progressRatio * 2 * Math.PI);
|
||||||
|
ctx.lineWidth = 1.5 * scaling; // Line width based on scaling property, thinner for vertical layout
|
||||||
|
ctx.strokeStyle = Color.mPrimary; // Use primary color for progress
|
||||||
|
ctx.lineCap = "round";
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Vertical layout for left/right bars - icon only
|
// Vertical layout for left/right bars - icon only
|
||||||
Item {
|
Item {
|
||||||
id: verticalLayout
|
id: verticalLayout
|
||||||
@@ -507,10 +619,22 @@ Item {
|
|||||||
pointSize: Style.fontSizeL * scaling
|
pointSize: Style.fontSizeL * scaling
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
z: 1 // In front of the progress circle
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Connection to update vertical progress when media position changes
|
||||||
|
Connections {
|
||||||
|
target: MediaService
|
||||||
|
function onCurrentPositionChanged() {
|
||||||
|
progressCanvasVertical.requestPaint();
|
||||||
|
}
|
||||||
|
function onTrackLengthChanged() {
|
||||||
|
progressCanvasVertical.requestPaint();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Mouse area for hover detection
|
// Mouse area for hover detection
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: mouseArea
|
id: mouseArea
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import qs.Services.System
|
|||||||
import qs.Services.UI
|
import qs.Services.UI
|
||||||
import qs.Widgets
|
import qs.Widgets
|
||||||
|
|
||||||
NIconButton {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property ShellScreen screen
|
property ShellScreen screen
|
||||||
@@ -33,6 +33,9 @@ NIconButton {
|
|||||||
readonly property bool showUnreadBadge: (widgetSettings.showUnreadBadge !== undefined) ? widgetSettings.showUnreadBadge : widgetMetadata.showUnreadBadge
|
readonly property bool showUnreadBadge: (widgetSettings.showUnreadBadge !== undefined) ? widgetSettings.showUnreadBadge : widgetMetadata.showUnreadBadge
|
||||||
readonly property bool hideWhenZero: (widgetSettings.hideWhenZero !== undefined) ? widgetSettings.hideWhenZero : widgetMetadata.hideWhenZero
|
readonly property bool hideWhenZero: (widgetSettings.hideWhenZero !== undefined) ? widgetSettings.hideWhenZero : widgetMetadata.hideWhenZero
|
||||||
|
|
||||||
|
implicitWidth: pill.width
|
||||||
|
implicitHeight: pill.height
|
||||||
|
|
||||||
function computeUnreadCount() {
|
function computeUnreadCount() {
|
||||||
var since = NotificationService.lastSeenTs;
|
var since = NotificationService.lastSeenTs;
|
||||||
var count = 0;
|
var count = 0;
|
||||||
@@ -46,17 +49,6 @@ NIconButton {
|
|||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
baseSize: Style.capsuleHeight
|
|
||||||
applyUiScale: false
|
|
||||||
density: Settings.data.bar.density
|
|
||||||
icon: NotificationService.doNotDisturb ? "bell-off" : "bell"
|
|
||||||
tooltipText: NotificationService.doNotDisturb ? I18n.tr("tooltips.open-notification-history-disable-dnd") : I18n.tr("tooltips.open-notification-history-enable-dnd")
|
|
||||||
tooltipDirection: BarService.getTooltipDirection()
|
|
||||||
colorBg: Style.capsuleColor
|
|
||||||
colorFg: Color.mOnSurface
|
|
||||||
colorBorder: Color.transparent
|
|
||||||
colorBorderHover: Color.transparent
|
|
||||||
|
|
||||||
NPopupContextMenu {
|
NPopupContextMenu {
|
||||||
id: contextMenu
|
id: contextMenu
|
||||||
|
|
||||||
@@ -94,6 +86,46 @@ NIconButton {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BarPill {
|
||||||
|
id: pill
|
||||||
|
|
||||||
|
property string currentNotif
|
||||||
|
Connections {
|
||||||
|
target: NotificationService.activeList
|
||||||
|
function onCountChanged() {
|
||||||
|
// keep current text a bit longer for the animation
|
||||||
|
if (NotificationService.activeList.count > 0) {
|
||||||
|
var notif = NotificationService.activeList.get(0)
|
||||||
|
var summary = notif.summary.trim()
|
||||||
|
var body = notif.body.trim()
|
||||||
|
pill.currentNotif = `${summary}: ${body}`.replace(/\n/g, " ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
function dismiss(notificationId) {
|
||||||
|
if (Settings.data.notifications?.location == "bar") {
|
||||||
|
NotificationService.dismissActiveNotification(notificationId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
NotificationService.animateAndRemove.connect(dismiss);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
screen: root.screen
|
||||||
|
density: Settings.data.bar.density
|
||||||
|
oppositeDirection: BarService.getPillDirection(root)
|
||||||
|
icon: NotificationService.doNotDisturb ? "bell-off" : "bell"
|
||||||
|
tooltipText: NotificationService.doNotDisturb ? I18n.tr("tooltips.open-notification-history-disable-dnd") : I18n.tr("tooltips.open-notification-history-enable-dnd")
|
||||||
|
|
||||||
|
text: currentNotif
|
||||||
|
forceOpen: Settings.data.notifications?.location == "bar" && NotificationService.activeList.count > 0
|
||||||
|
// prevent open via mouse over
|
||||||
|
forceClose: NotificationService.activeList.count == 0
|
||||||
|
|
||||||
|
opacity: NotificationService.doNotDisturb || computeUnreadCount() > 0 ? 100 : 0
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
var panel = PanelService.getPanel("notificationHistoryPanel", screen);
|
var panel = PanelService.getPanel("notificationHistoryPanel", screen);
|
||||||
panel?.toggle(this);
|
panel?.toggle(this);
|
||||||
@@ -108,6 +140,7 @@ NIconButton {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Loader {
|
Loader {
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
@@ -127,4 +160,5 @@ NIconButton {
|
|||||||
visible: count > 0 || !hideWhenZero
|
visible: count > 0 || !hideWhenZero
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -240,6 +240,7 @@ Rectangle {
|
|||||||
Layout.alignment: Qt.AlignCenter
|
Layout.alignment: Qt.AlignCenter
|
||||||
Layout.row: isVertical ? 1 : 0
|
Layout.row: isVertical ? 1 : 0
|
||||||
Layout.column: 0
|
Layout.column: 0
|
||||||
|
Layout.fillWidth: isVertical
|
||||||
implicitWidth: iconSize
|
implicitWidth: iconSize
|
||||||
implicitHeight: iconSize
|
implicitHeight: iconSize
|
||||||
|
|
||||||
@@ -314,6 +315,7 @@ Rectangle {
|
|||||||
Layout.alignment: Qt.AlignCenter
|
Layout.alignment: Qt.AlignCenter
|
||||||
Layout.row: isVertical ? 1 : 0
|
Layout.row: isVertical ? 1 : 0
|
||||||
Layout.column: 0
|
Layout.column: 0
|
||||||
|
Layout.fillWidth: isVertical
|
||||||
implicitWidth: iconSize
|
implicitWidth: iconSize
|
||||||
implicitHeight: iconSize
|
implicitHeight: iconSize
|
||||||
|
|
||||||
@@ -381,6 +383,7 @@ Rectangle {
|
|||||||
Layout.alignment: Qt.AlignCenter
|
Layout.alignment: Qt.AlignCenter
|
||||||
Layout.row: isVertical ? 1 : 0
|
Layout.row: isVertical ? 1 : 0
|
||||||
Layout.column: 0
|
Layout.column: 0
|
||||||
|
Layout.fillWidth: isVertical
|
||||||
implicitWidth: iconSize
|
implicitWidth: iconSize
|
||||||
implicitHeight: iconSize
|
implicitHeight: iconSize
|
||||||
|
|
||||||
@@ -429,13 +432,20 @@ Rectangle {
|
|||||||
rowSpacing: Style.marginXXS
|
rowSpacing: Style.marginXXS
|
||||||
columnSpacing: Style.marginXXS
|
columnSpacing: Style.marginXXS
|
||||||
|
|
||||||
|
Item {
|
||||||
|
Layout.alignment: Qt.AlignCenter
|
||||||
|
Layout.row: isVertical ? 1 : 0
|
||||||
|
Layout.column: 0
|
||||||
|
Layout.fillWidth: isVertical
|
||||||
|
implicitWidth: iconSize
|
||||||
|
implicitHeight: iconSize
|
||||||
|
|
||||||
NIcon {
|
NIcon {
|
||||||
icon: "download-speed"
|
icon: "download-speed"
|
||||||
pointSize: iconSize
|
pointSize: iconSize
|
||||||
applyUiScale: false
|
applyUiScale: false
|
||||||
Layout.alignment: Qt.AlignCenter
|
anchors.centerIn: parent
|
||||||
Layout.row: isVertical ? 1 : 0
|
}
|
||||||
Layout.column: 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NText {
|
NText {
|
||||||
@@ -472,13 +482,20 @@ Rectangle {
|
|||||||
rowSpacing: Style.marginXXS
|
rowSpacing: Style.marginXXS
|
||||||
columnSpacing: Style.marginXXS
|
columnSpacing: Style.marginXXS
|
||||||
|
|
||||||
|
Item {
|
||||||
|
Layout.alignment: Qt.AlignCenter
|
||||||
|
Layout.row: isVertical ? 1 : 0
|
||||||
|
Layout.column: 0
|
||||||
|
Layout.fillWidth: isVertical
|
||||||
|
implicitWidth: iconSize
|
||||||
|
implicitHeight: iconSize
|
||||||
|
|
||||||
NIcon {
|
NIcon {
|
||||||
icon: "upload-speed"
|
icon: "upload-speed"
|
||||||
pointSize: iconSize
|
pointSize: iconSize
|
||||||
applyUiScale: false
|
applyUiScale: false
|
||||||
Layout.alignment: Qt.AlignCenter
|
anchors.centerIn: parent
|
||||||
Layout.row: isVertical ? 1 : 0
|
}
|
||||||
Layout.column: 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NText {
|
NText {
|
||||||
@@ -530,16 +547,23 @@ Rectangle {
|
|||||||
rowSpacing: Style.marginXXS
|
rowSpacing: Style.marginXXS
|
||||||
columnSpacing: Style.marginXXS
|
columnSpacing: Style.marginXXS
|
||||||
|
|
||||||
|
Item {
|
||||||
|
Layout.alignment: Qt.AlignCenter
|
||||||
|
Layout.row: isVertical ? 1 : 0
|
||||||
|
Layout.column: 0
|
||||||
|
Layout.fillWidth: isVertical
|
||||||
|
implicitWidth: iconSize
|
||||||
|
implicitHeight: iconSize
|
||||||
|
|
||||||
NIcon {
|
NIcon {
|
||||||
icon: "storage"
|
icon: "storage"
|
||||||
pointSize: iconSize
|
pointSize: iconSize
|
||||||
applyUiScale: false
|
applyUiScale: false
|
||||||
Layout.alignment: Qt.AlignCenter
|
anchors.centerIn: parent
|
||||||
Layout.row: isVertical ? 1 : 0
|
|
||||||
Layout.column: 0
|
|
||||||
// Invert color when disk indicator active (vertical uses highlight colors)
|
// Invert color when disk indicator active (vertical uses highlight colors)
|
||||||
color: isVertical ? (diskCritical ? criticalColor : (diskWarning ? warningColor : Color.mOnSurface)) : ((diskWarning || diskCritical) ? Color.mSurfaceVariant : Color.mOnSurface)
|
color: isVertical ? (diskCritical ? criticalColor : (diskWarning ? warningColor : Color.mOnSurface)) : ((diskWarning || diskCritical) ? Color.mSurfaceVariant : Color.mOnSurface)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
NText {
|
NText {
|
||||||
id: toto
|
id: toto
|
||||||
|
|||||||
@@ -121,7 +121,6 @@ Item {
|
|||||||
autoHide: false
|
autoHide: false
|
||||||
forceOpen: !isBarVertical && root.displayMode === "alwaysShow"
|
forceOpen: !isBarVertical && root.displayMode === "alwaysShow"
|
||||||
forceClose: isBarVertical || root.displayMode === "alwaysHide" || !pill.text
|
forceClose: isBarVertical || root.displayMode === "alwaysHide" || !pill.text
|
||||||
onClicked: PanelService.getPanel("vpnPanel", screen)?.toggle(this)
|
|
||||||
onRightClicked: {
|
onRightClicked: {
|
||||||
var popupMenuWindow = PanelService.getPopupMenuWindow(screen);
|
var popupMenuWindow = PanelService.getPopupMenuWindow(screen);
|
||||||
if (popupMenuWindow) {
|
if (popupMenuWindow) {
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ Item {
|
|||||||
|
|
||||||
readonly property string labelMode: (widgetSettings.labelMode !== undefined) ? widgetSettings.labelMode : widgetMetadata.labelMode
|
readonly property string labelMode: (widgetSettings.labelMode !== undefined) ? widgetSettings.labelMode : widgetMetadata.labelMode
|
||||||
readonly property bool hideUnoccupied: (widgetSettings.hideUnoccupied !== undefined) ? widgetSettings.hideUnoccupied : widgetMetadata.hideUnoccupied
|
readonly property bool hideUnoccupied: (widgetSettings.hideUnoccupied !== undefined) ? widgetSettings.hideUnoccupied : widgetMetadata.hideUnoccupied
|
||||||
|
readonly property bool followFocusedScreen: (widgetSettings.followFocusedScreen !== undefined) ? widgetSettings.followFocusedScreen : widgetMetadata.followFocusedScreen
|
||||||
readonly property int characterCount: isVertical ? 2 : ((widgetSettings.characterCount !== undefined) ? widgetSettings.characterCount : widgetMetadata.characterCount)
|
readonly property int characterCount: isVertical ? 2 : ((widgetSettings.characterCount !== undefined) ? widgetSettings.characterCount : widgetMetadata.characterCount)
|
||||||
|
|
||||||
property bool isDestroying: false
|
property bool isDestroying: false
|
||||||
@@ -162,10 +163,20 @@ Item {
|
|||||||
|
|
||||||
function refreshWorkspaces() {
|
function refreshWorkspaces() {
|
||||||
localWorkspaces.clear();
|
localWorkspaces.clear();
|
||||||
|
|
||||||
|
var focusedOutput = null;
|
||||||
|
if (followFocusedScreen) {
|
||||||
|
for (var i = 0; i < CompositorService.workspaces.count; i++) {
|
||||||
|
const ws = CompositorService.workspaces.get(i);
|
||||||
|
if (ws.isFocused)
|
||||||
|
focusedOutput = ws.output.toLowerCase();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (screen !== null) {
|
if (screen !== null) {
|
||||||
for (var i = 0; i < CompositorService.workspaces.count; i++) {
|
for (var i = 0; i < CompositorService.workspaces.count; i++) {
|
||||||
const ws = CompositorService.workspaces.get(i);
|
const ws = CompositorService.workspaces.get(i);
|
||||||
if (ws.output.toLowerCase() === screen.name.toLowerCase()) {
|
if ((followFocusedScreen && ws.output.toLowerCase() == focusedOutput) || (!followFocusedScreen && ws.output.toLowerCase() == screen.name.toLowerCase())) {
|
||||||
if (hideUnoccupied && !ws.isOccupied && !ws.isFocused) {
|
if (hideUnoccupied && !ws.isOccupied && !ws.isFocused) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ Scope {
|
|||||||
root.unlocked();
|
root.unlocked();
|
||||||
} else {
|
} else {
|
||||||
Logger.i("LockContext", "Authentication failed");
|
Logger.i("LockContext", "Authentication failed");
|
||||||
errorMessage = "Authentication failed";
|
errorMessage = I18n.tr("lock-screen.authentication-failed");
|
||||||
showFailure = true;
|
showFailure = true;
|
||||||
root.failed();
|
root.failed();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -343,15 +343,15 @@ Loader {
|
|||||||
text: {
|
text: {
|
||||||
var lang = I18n.locale.name.split("_")[0];
|
var lang = I18n.locale.name.split("_")[0];
|
||||||
var formats = {
|
var formats = {
|
||||||
"en": "dddd, MMMM d",
|
|
||||||
"de": "dddd, d. MMMM",
|
"de": "dddd, d. MMMM",
|
||||||
"fr": "dddd d MMMM",
|
|
||||||
"es": "dddd, d 'de' MMMM",
|
"es": "dddd, d 'de' MMMM",
|
||||||
|
"fr": "dddd d MMMM",
|
||||||
"pt": "dddd, d 'de' MMMM",
|
"pt": "dddd, d 'de' MMMM",
|
||||||
"zh": "yyyy年M月d日 dddd",
|
"zh": "yyyy年M月d日 dddd",
|
||||||
"nl": "dddd d MMMM"
|
"uk": "dddd, d MMMM",
|
||||||
|
"tr": "dddd, d MMMM"
|
||||||
};
|
};
|
||||||
return I18n.locale.toString(Time.now, formats[lang] || "dddd, d MMMM");
|
return I18n.locale.toString(Time.now, formats[lang] || "dddd, MMMM d");
|
||||||
}
|
}
|
||||||
pointSize: Style.fontSizeXL
|
pointSize: Style.fontSizeXL
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
@@ -383,12 +383,12 @@ Loader {
|
|||||||
|
|
||||||
// Error notification
|
// Error notification
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: 450
|
width: errorRowLayout.implicitWidth + Style.marginXL * 1.5
|
||||||
height: 60
|
height: 50
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
anchors.bottom: parent.bottom
|
anchors.bottom: parent.bottom
|
||||||
anchors.bottomMargin: (Settings.data.general.compactLockScreen ? 240 : 320) * Style.uiScaleRatio
|
anchors.bottomMargin: (Settings.data.general.compactLockScreen ? 280 : 360) * Style.uiScaleRatio
|
||||||
radius: 30
|
radius: Style.radiusL
|
||||||
color: Color.mError
|
color: Color.mError
|
||||||
border.color: Color.mError
|
border.color: Color.mError
|
||||||
border.width: 1
|
border.width: 1
|
||||||
@@ -396,6 +396,7 @@ Loader {
|
|||||||
opacity: visible ? 1.0 : 0.0
|
opacity: visible ? 1.0 : 0.0
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
|
id: errorRowLayout
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
spacing: 10
|
spacing: 10
|
||||||
|
|
||||||
@@ -524,7 +525,7 @@ Loader {
|
|||||||
}
|
}
|
||||||
Text {
|
Text {
|
||||||
id: hibernateText
|
id: hibernateText
|
||||||
text: Settings.data.general.showHibernateOnLockScreen ? I18n.tr("session-menu.hibernate") : ""
|
text: I18n.tr("session-menu.hibernate")
|
||||||
font.pointSize: buttonRowTextMeasurer.fontSize
|
font.pointSize: buttonRowTextMeasurer.fontSize
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
}
|
}
|
||||||
@@ -547,10 +548,12 @@ Loader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Calculate minimum width based on button requirements
|
// Calculate minimum width based on button requirements
|
||||||
// Button row needs: margins + 5 buttons + 4 spacings + margins
|
// Button row needs: margins + buttons (4 or 5 depending on hibernate visibility) + spacings + margins
|
||||||
// Plus ColumnLayout margins (14 on each side = 28 total)
|
// Plus ColumnLayout margins (14 on each side = 28 total)
|
||||||
// Add extra buffer to ensure password input has proper padding
|
// Add extra buffer to ensure password input has proper padding
|
||||||
property real minButtonRowWidth: buttonRowTextMeasurer.minButtonWidth > 0 ? ((Settings.data.general.showHibernateOnLockScreen ? 5 : 4) * buttonRowTextMeasurer.minButtonWidth) + 40 + (2 * Style.marginM) + 28 + (2 * Style.marginM) : 750
|
property int buttonCount: Settings.data.general.showHibernateOnLockScreen ? 5 : 4
|
||||||
|
property int spacingCount: buttonCount - 1
|
||||||
|
property real minButtonRowWidth: buttonRowTextMeasurer.minButtonWidth > 0 ? (buttonCount * buttonRowTextMeasurer.minButtonWidth) + (spacingCount * 10) + 40 + (2 * Style.marginM) + 28 + (2 * Style.marginM) : 750
|
||||||
width: Math.max(750, minButtonRowWidth)
|
width: Math.max(750, minButtonRowWidth)
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
@@ -749,7 +752,7 @@ Loader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Forecast
|
// 3-day forecast
|
||||||
RowLayout {
|
RowLayout {
|
||||||
visible: Settings.data.location.weatherEnabled && LocationService.data.weather !== null
|
visible: Settings.data.location.weatherEnabled && LocationService.data.weather !== null
|
||||||
Layout.preferredWidth: 260
|
Layout.preferredWidth: 260
|
||||||
@@ -757,7 +760,7 @@ Loader {
|
|||||||
spacing: 4
|
spacing: 4
|
||||||
|
|
||||||
Repeater {
|
Repeater {
|
||||||
model: MediaService.currentPlayer && MediaService.canPlay ? 3 : 4
|
model: 3
|
||||||
delegate: ColumnLayout {
|
delegate: ColumnLayout {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
spacing: 3
|
spacing: 3
|
||||||
@@ -804,6 +807,8 @@ Loader {
|
|||||||
|
|
||||||
Item {
|
Item {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
visible: !(Settings.data.location.weatherEnabled && LocationService.data.weather !== null)
|
||||||
|
Layout.preferredWidth: visible ? 1 : 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// Battery and Keyboard Layout (full mode only)
|
// Battery and Keyboard Layout (full mode only)
|
||||||
@@ -1019,6 +1024,7 @@ Loader {
|
|||||||
id: eyeButtonArea
|
id: eyeButtonArea
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: parent.parent.passwordVisible = !parent.parent.passwordVisible
|
onClicked: parent.parent.passwordVisible = !parent.parent.passwordVisible
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1055,6 +1061,7 @@ Loader {
|
|||||||
id: submitButtonArea
|
id: submitButtonArea
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: lockContext.tryUnlock()
|
onClicked: lockContext.tryUnlock()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1113,6 +1120,7 @@ Loader {
|
|||||||
id: logoutButtonArea
|
id: logoutButtonArea
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: CompositorService.logout()
|
onClicked: CompositorService.logout()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1162,6 +1170,7 @@ Loader {
|
|||||||
id: suspendButtonArea
|
id: suspendButtonArea
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: CompositorService.suspend()
|
onClicked: CompositorService.suspend()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1181,7 +1190,6 @@ Loader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
visible: Settings.data.general.showHibernateOnLockScreen
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.minimumWidth: buttonRowTextMeasurer.minButtonWidth
|
Layout.minimumWidth: buttonRowTextMeasurer.minButtonWidth
|
||||||
Layout.preferredHeight: Settings.data.general.compactLockScreen ? 36 : 48
|
Layout.preferredHeight: Settings.data.general.compactLockScreen ? 36 : 48
|
||||||
@@ -1189,6 +1197,7 @@ Loader {
|
|||||||
color: hibernateButtonArea.containsMouse ? Color.mHover : "transparent"
|
color: hibernateButtonArea.containsMouse ? Color.mHover : "transparent"
|
||||||
border.color: Color.mOutline
|
border.color: Color.mOutline
|
||||||
border.width: 1
|
border.width: 1
|
||||||
|
visible: Settings.data.general.showHibernateOnLockScreen
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
@@ -1212,6 +1221,7 @@ Loader {
|
|||||||
id: hibernateButtonArea
|
id: hibernateButtonArea
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: CompositorService.hibernate()
|
onClicked: CompositorService.hibernate()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1261,6 +1271,7 @@ Loader {
|
|||||||
id: rebootButtonArea
|
id: rebootButtonArea
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: CompositorService.reboot()
|
onClicked: CompositorService.reboot()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1310,6 +1321,7 @@ Loader {
|
|||||||
id: shutdownButtonArea
|
id: shutdownButtonArea
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: CompositorService.shutdown()
|
onClicked: CompositorService.shutdown()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -69,6 +69,13 @@ Item {
|
|||||||
backgroundColor: panelBackgroundColor
|
backgroundColor: panelBackgroundColor
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Battery
|
||||||
|
PanelBackground {
|
||||||
|
panel: root.windowRoot.batteryPanelPlaceholder
|
||||||
|
shapeContainer: backgroundsShape
|
||||||
|
backgroundColor: panelBackgroundColor
|
||||||
|
}
|
||||||
|
|
||||||
// Bluetooth
|
// Bluetooth
|
||||||
PanelBackground {
|
PanelBackground {
|
||||||
panel: root.windowRoot.bluetoothPanelPlaceholder
|
panel: root.windowRoot.bluetoothPanelPlaceholder
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import qs.Commons
|
|||||||
import qs.Modules.Bar
|
import qs.Modules.Bar
|
||||||
import qs.Modules.Bar.Extras
|
import qs.Modules.Bar.Extras
|
||||||
import qs.Modules.Panels.Audio
|
import qs.Modules.Panels.Audio
|
||||||
|
import qs.Modules.Panels.Battery
|
||||||
import qs.Modules.Panels.Bluetooth
|
import qs.Modules.Panels.Bluetooth
|
||||||
import qs.Modules.Panels.Brightness
|
import qs.Modules.Panels.Brightness
|
||||||
import qs.Modules.Panels.Calendar
|
import qs.Modules.Panels.Calendar
|
||||||
@@ -33,6 +34,7 @@ PanelWindow {
|
|||||||
|
|
||||||
// Expose panels as readonly property aliases
|
// Expose panels as readonly property aliases
|
||||||
readonly property alias audioPanel: audioPanel
|
readonly property alias audioPanel: audioPanel
|
||||||
|
readonly property alias batteryPanel: batteryPanel
|
||||||
readonly property alias bluetoothPanel: bluetoothPanel
|
readonly property alias bluetoothPanel: bluetoothPanel
|
||||||
readonly property alias brightnessPanel: brightnessPanel
|
readonly property alias brightnessPanel: brightnessPanel
|
||||||
readonly property alias calendarPanel: calendarPanel
|
readonly property alias calendarPanel: calendarPanel
|
||||||
@@ -49,6 +51,7 @@ PanelWindow {
|
|||||||
|
|
||||||
// Expose panel placeholders for AllBackgrounds
|
// Expose panel placeholders for AllBackgrounds
|
||||||
readonly property var audioPanelPlaceholder: audioPanel.panelPlaceholder
|
readonly property var audioPanelPlaceholder: audioPanel.panelPlaceholder
|
||||||
|
readonly property var batteryPanelPlaceholder: batteryPanel.panelPlaceholder
|
||||||
readonly property var bluetoothPanelPlaceholder: bluetoothPanel.panelPlaceholder
|
readonly property var bluetoothPanelPlaceholder: bluetoothPanel.panelPlaceholder
|
||||||
readonly property var brightnessPanelPlaceholder: brightnessPanel.panelPlaceholder
|
readonly property var brightnessPanelPlaceholder: brightnessPanel.panelPlaceholder
|
||||||
readonly property var calendarPanelPlaceholder: calendarPanel.panelPlaceholder
|
readonly property var calendarPanelPlaceholder: calendarPanel.panelPlaceholder
|
||||||
@@ -168,6 +171,12 @@ PanelWindow {
|
|||||||
screen: root.screen
|
screen: root.screen
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BatteryPanel {
|
||||||
|
id: batteryPanel
|
||||||
|
objectName: "batteryPanel-" + (root.screen?.name || "unknown")
|
||||||
|
screen: root.screen
|
||||||
|
}
|
||||||
|
|
||||||
BluetoothPanel {
|
BluetoothPanel {
|
||||||
id: bluetoothPanel
|
id: bluetoothPanel
|
||||||
objectName: "bluetoothPanel-" + (root.screen?.name || "unknown")
|
objectName: "bluetoothPanel-" + (root.screen?.name || "unknown")
|
||||||
|
|||||||
@@ -47,6 +47,10 @@ Item {
|
|||||||
property bool isPanelVisible: false
|
property bool isPanelVisible: false
|
||||||
property bool isClosing: false
|
property bool isClosing: false
|
||||||
property bool opacityFadeComplete: false
|
property bool opacityFadeComplete: false
|
||||||
|
property bool sizeAnimationComplete: false
|
||||||
|
|
||||||
|
// Derived state: track opening transition
|
||||||
|
readonly property bool isOpening: isPanelVisible && !isClosing && !sizeAnimationComplete
|
||||||
|
|
||||||
// Content size (set by SmartPanelWindow when content size changes)
|
// Content size (set by SmartPanelWindow when content size changes)
|
||||||
property real contentPreferredWidth: 0
|
property real contentPreferredWidth: 0
|
||||||
@@ -563,6 +567,9 @@ Item {
|
|||||||
readonly property bool shouldAnimateWidth: !shouldAnimateHeight && (animateFromLeft || animateFromRight)
|
readonly property bool shouldAnimateWidth: !shouldAnimateHeight && (animateFromLeft || animateFromRight)
|
||||||
readonly property bool shouldAnimateHeight: animateFromTop || animateFromBottom
|
readonly property bool shouldAnimateHeight: animateFromTop || animateFromBottom
|
||||||
|
|
||||||
|
// Track whether we're in an initial open/close state transition vs normal content resizing
|
||||||
|
readonly property bool isStateTransition: root.isOpening || root.isClosing
|
||||||
|
|
||||||
// Current animated width/height
|
// Current animated width/height
|
||||||
readonly property real currentWidth: {
|
readonly property real currentWidth: {
|
||||||
if (isClosing && opacityFadeComplete && shouldAnimateWidth)
|
if (isClosing && opacityFadeComplete && shouldAnimateWidth)
|
||||||
@@ -603,11 +610,10 @@ Item {
|
|||||||
|
|
||||||
Behavior on width {
|
Behavior on width {
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
duration: {
|
// During opening: use 0ms if not animating width, otherwise use normal duration
|
||||||
if (!panelBackground.shouldAnimateWidth)
|
// During closing: use 0ms if not animating width, otherwise use fast duration
|
||||||
return 0;
|
// During normal content resizing: always use normal duration
|
||||||
return root.isClosing ? Style.animationFast : Style.animationNormal;
|
duration: (root.isOpening && !panelBackground.shouldAnimateWidth) ? 0 : root.isOpening ? Style.animationNormal : (root.isClosing && !panelBackground.shouldAnimateWidth) ? 0 : root.isClosing ? Style.animationFast : Style.animationNormal
|
||||||
}
|
|
||||||
easing.type: Easing.BezierSpline
|
easing.type: Easing.BezierSpline
|
||||||
easing.bezierCurve: panelBackground.bezierCurve
|
easing.bezierCurve: panelBackground.bezierCurve
|
||||||
}
|
}
|
||||||
@@ -615,7 +621,10 @@ Item {
|
|||||||
|
|
||||||
Behavior on height {
|
Behavior on height {
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
duration: root.isClosing ? Style.animationFast : Style.animationNormal
|
// During opening: use 0ms if not animating height, otherwise use normal duration
|
||||||
|
// During closing: use 0ms if not animating height, otherwise use fast duration
|
||||||
|
// During normal content resizing: always use normal duration
|
||||||
|
duration: (root.isOpening && !panelBackground.shouldAnimateHeight) ? 0 : root.isOpening ? Style.animationNormal : (root.isClosing && !panelBackground.shouldAnimateHeight) ? 0 : root.isClosing ? Style.animationFast : Style.animationNormal
|
||||||
easing.type: Easing.BezierSpline
|
easing.type: Easing.BezierSpline
|
||||||
easing.bezierCurve: panelBackground.bezierCurve
|
easing.bezierCurve: panelBackground.bezierCurve
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -99,6 +99,9 @@ PanelWindow {
|
|||||||
onOpacityFadeCompleteChanged: {
|
onOpacityFadeCompleteChanged: {
|
||||||
placeholder.opacityFadeComplete = opacityFadeComplete;
|
placeholder.opacityFadeComplete = opacityFadeComplete;
|
||||||
}
|
}
|
||||||
|
onSizeAnimationCompleteChanged: {
|
||||||
|
placeholder.sizeAnimationComplete = sizeAnimationComplete;
|
||||||
|
}
|
||||||
|
|
||||||
// Panel control functions
|
// Panel control functions
|
||||||
function toggle(buttonItem, buttonName) {
|
function toggle(buttonItem, buttonName) {
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ Variants {
|
|||||||
property ListModel notificationModel: NotificationService.activeList
|
property ListModel notificationModel: NotificationService.activeList
|
||||||
|
|
||||||
// Always create window (but with 0x0 dimensions when no notifications)
|
// Always create window (but with 0x0 dimensions when no notifications)
|
||||||
active: true
|
active: Settings.data.notifications?.location != "bar"
|
||||||
|
|
||||||
// Keep loader active briefly after last notification to allow animations to complete
|
// Keep loader active briefly after last notification to allow animations to complete
|
||||||
Timer {
|
Timer {
|
||||||
@@ -393,23 +393,22 @@ Variants {
|
|||||||
RowLayout {
|
RowLayout {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
spacing: Style.marginL
|
spacing: Style.marginL
|
||||||
Layout.margins: Style.marginM
|
Layout.leftMargin: Style.marginM * 2
|
||||||
|
Layout.rightMargin: Style.marginM * 2
|
||||||
|
Layout.topMargin: Style.marginM
|
||||||
|
Layout.bottomMargin: Style.marginM
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
NImageCircled {
|
NImageCircled {
|
||||||
Layout.preferredWidth: Math.round(40 * Style.uiScaleRatio)
|
Layout.preferredWidth: Math.round(40 * Style.uiScaleRatio)
|
||||||
Layout.preferredHeight: Math.round(40 * Style.uiScaleRatio)
|
Layout.preferredHeight: Math.round(40 * Style.uiScaleRatio)
|
||||||
Layout.alignment: Qt.AlignTop
|
Layout.alignment: Qt.AlignVCenter
|
||||||
Layout.topMargin: 30
|
|
||||||
imagePath: model.originalImage || ""
|
imagePath: model.originalImage || ""
|
||||||
borderColor: Color.transparent
|
borderColor: Color.transparent
|
||||||
borderWidth: 0
|
borderWidth: 0
|
||||||
fallbackIcon: "bell"
|
fallbackIcon: "bell"
|
||||||
fallbackIconSize: 24
|
fallbackIconSize: 24
|
||||||
}
|
}
|
||||||
Item {
|
|
||||||
Layout.fillHeight: true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
@@ -430,9 +429,18 @@ Variants {
|
|||||||
}
|
}
|
||||||
|
|
||||||
NText {
|
NText {
|
||||||
text: `${model.appName || I18n.tr("system.unknown-app")} · ${Time.formatRelativeTime(model.timestamp)}`
|
text: model.appName || "Unknown App"
|
||||||
color: Color.mSecondary
|
|
||||||
pointSize: Style.fontSizeXS
|
pointSize: Style.fontSizeXS
|
||||||
|
font.weight: Style.fontWeightBold
|
||||||
|
color: Color.mSecondary
|
||||||
|
}
|
||||||
|
|
||||||
|
NText {
|
||||||
|
textFormat: Text.PlainText
|
||||||
|
text: " " + Time.formatRelativeTime(model.timestamp)
|
||||||
|
pointSize: Style.fontSizeXXS
|
||||||
|
color: Color.mOnSurfaceVariant
|
||||||
|
Layout.alignment: Qt.AlignBottom
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
|
|||||||
296
Modules/Panels/Battery/BatteryPanel.qml
Normal file
296
Modules/Panels/Battery/BatteryPanel.qml
Normal file
@@ -0,0 +1,296 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Services.UPower
|
||||||
|
import qs.Commons
|
||||||
|
import qs.Modules.MainScreen
|
||||||
|
import qs.Services.Hardware
|
||||||
|
import qs.Services.Power
|
||||||
|
import qs.Widgets
|
||||||
|
|
||||||
|
SmartPanel {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
preferredWidth: Math.round(360 * Style.uiScaleRatio)
|
||||||
|
preferredHeight: Math.round(460 * Style.uiScaleRatio)
|
||||||
|
|
||||||
|
readonly property var battery: UPower.displayDevice
|
||||||
|
readonly property bool isReady: battery && battery.ready && battery.isLaptopBattery && battery.isPresent
|
||||||
|
readonly property int percent: isReady ? Math.round(battery.percentage * 100) : -1
|
||||||
|
readonly property bool charging: isReady ? battery.state === UPowerDeviceState.Charging : false
|
||||||
|
readonly property bool healthSupported: isReady && battery.healthSupported
|
||||||
|
readonly property bool healthAvailable: healthSupported
|
||||||
|
readonly property int healthPercent: healthAvailable ? Math.round(battery.healthPercentage) : -1
|
||||||
|
readonly property bool powerProfileAvailable: PowerProfileService.available
|
||||||
|
readonly property var powerProfiles: [PowerProfile.PowerSaver, PowerProfile.Balanced, PowerProfile.Performance]
|
||||||
|
readonly property string timeText: {
|
||||||
|
if (!isReady)
|
||||||
|
return I18n.tr("battery.no-battery-detected");
|
||||||
|
if (charging && battery.timeToFull > 0) {
|
||||||
|
return I18n.tr("battery.time-until-full", {
|
||||||
|
"time": Time.formatVagueHumanReadableDuration(battery.timeToFull)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (!charging && battery.timeToEmpty > 0) {
|
||||||
|
return I18n.tr("battery.time-left", {
|
||||||
|
"time": Time.formatVagueHumanReadableDuration(battery.timeToEmpty)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return I18n.tr("battery.idle");
|
||||||
|
}
|
||||||
|
readonly property string iconName: BatteryService.getIcon(percent, charging, isReady)
|
||||||
|
readonly property bool profilesAvailable: PowerProfileService.available
|
||||||
|
property int profileIndex: profileToIndex(PowerProfileService.profile)
|
||||||
|
property bool manualInhibitActive: manualInhibitorEnabled()
|
||||||
|
|
||||||
|
panelContent: Item {
|
||||||
|
property real contentPreferredHeight: mainLayout.implicitHeight + Style.marginL * 2
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: mainLayout
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: Style.marginL
|
||||||
|
spacing: Style.marginM
|
||||||
|
|
||||||
|
// HEADER
|
||||||
|
NBox {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
implicitHeight: headerRow.implicitHeight + (Style.marginM * 2)
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: headerRow
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: Style.marginM
|
||||||
|
spacing: Style.marginM
|
||||||
|
|
||||||
|
NIcon {
|
||||||
|
pointSize: Style.fontSizeXXL
|
||||||
|
color: root.charging ? Color.mPrimary : Color.mOnSurface
|
||||||
|
icon: iconName
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
spacing: Style.marginXXS
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
NText {
|
||||||
|
text: I18n.tr("battery.panel-title")
|
||||||
|
pointSize: Style.fontSizeL
|
||||||
|
font.weight: Style.fontWeightBold
|
||||||
|
color: Color.mOnSurface
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
|
||||||
|
NText {
|
||||||
|
text: timeText
|
||||||
|
pointSize: Style.fontSizeS
|
||||||
|
color: Color.mOnSurfaceVariant
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NIconButton {
|
||||||
|
icon: "close"
|
||||||
|
tooltipText: I18n.tr("tooltips.close")
|
||||||
|
baseSize: Style.baseWidgetSize * 0.8
|
||||||
|
onClicked: root.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Charge level + health/time
|
||||||
|
NBox {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
height: chargeLayout.implicitHeight + Style.marginL * 2
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: chargeLayout
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: Style.marginL
|
||||||
|
spacing: Style.marginS
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
spacing: Style.marginS
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
NText {
|
||||||
|
text: I18n.tr("battery.charge-level")
|
||||||
|
color: Color.mOnSurface
|
||||||
|
pointSize: Style.fontSizeS
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
height: Math.round(8 * Style.uiScaleRatio)
|
||||||
|
radius: height / 2
|
||||||
|
color: Color.mSurfaceVariant
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
height: parent.height
|
||||||
|
radius: parent.radius
|
||||||
|
width: {
|
||||||
|
var ratio = Math.max(0, Math.min(1, percent / 100));
|
||||||
|
return parent.width * ratio;
|
||||||
|
}
|
||||||
|
color: Color.mPrimary
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NText {
|
||||||
|
text: percent >= 0 ? `${percent}%` : "--"
|
||||||
|
color: Color.mOnSurface
|
||||||
|
pointSize: Style.fontSizeS
|
||||||
|
font.weight: Style.fontWeightBold
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
spacing: Style.marginL
|
||||||
|
visible: healthAvailable
|
||||||
|
|
||||||
|
NText {
|
||||||
|
text: I18n.tr("battery.health", {
|
||||||
|
"percent": healthPercent
|
||||||
|
})
|
||||||
|
color: Color.mOnSurface
|
||||||
|
pointSize: Style.fontSizeS
|
||||||
|
font.weight: Style.fontWeightMedium
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Power profile and idle inhibit controls
|
||||||
|
NBox {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
height: controlsLayout.implicitHeight + Style.marginM * 2
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: controlsLayout
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: Style.marginM
|
||||||
|
spacing: Style.marginM
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: ppd
|
||||||
|
visible: root.powerProfileAvailable
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
spacing: Style.marginS
|
||||||
|
NIcon {
|
||||||
|
icon: PowerProfileService.getIcon()
|
||||||
|
pointSize: Style.fontSizeM
|
||||||
|
color: Color.mPrimary
|
||||||
|
}
|
||||||
|
NText {
|
||||||
|
text: I18n.tr("battery.power-profile")
|
||||||
|
font.weight: Style.fontWeightBold
|
||||||
|
color: Color.mOnSurface
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
NText {
|
||||||
|
text: PowerProfileService.getName(profileIndex)
|
||||||
|
color: Color.mOnSurfaceVariant
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NValueSlider {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
from: 0
|
||||||
|
to: 2
|
||||||
|
stepSize: 1
|
||||||
|
snapAlways: true
|
||||||
|
value: profileIndex
|
||||||
|
enabled: profilesAvailable
|
||||||
|
onPressedChanged: (pressed, v) => {
|
||||||
|
if (!pressed) {
|
||||||
|
setProfileByIndex(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onMoved: v => {
|
||||||
|
profileIndex = v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
spacing: Style.marginS
|
||||||
|
|
||||||
|
NIcon {
|
||||||
|
icon: manualInhibitActive ? "keep-awake-on" : "keep-awake-off"
|
||||||
|
pointSize: Style.fontSizeL
|
||||||
|
color: manualInhibitActive ? Color.mPrimary : Color.mOnSurfaceVariant
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
NToggle {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
checked: manualInhibitActive
|
||||||
|
label: I18n.tr("battery.inhibit-idle-label")
|
||||||
|
description: I18n.tr("battery.inhibit-idle-description")
|
||||||
|
onToggled: function (checked) {
|
||||||
|
if (checked) {
|
||||||
|
IdleInhibitorService.addManualInhibitor(null);
|
||||||
|
} else {
|
||||||
|
IdleInhibitorService.removeManualInhibitor();
|
||||||
|
}
|
||||||
|
manualInhibitActive = checked;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function profileToIndex(p) {
|
||||||
|
return powerProfiles.indexOf(p) ?? 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function indexToProfile(idx) {
|
||||||
|
return powerProfiles[idx] ?? PowerProfile.Balanced;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setProfileByIndex(idx) {
|
||||||
|
var prof = indexToProfile(idx);
|
||||||
|
profileIndex = idx;
|
||||||
|
PowerProfileService.setProfile(prof);
|
||||||
|
}
|
||||||
|
|
||||||
|
function manualInhibitorEnabled() {
|
||||||
|
return IdleInhibitorService.activeInhibitors && IdleInhibitorService.activeInhibitors.indexOf("manual") >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: IdleInhibitorService
|
||||||
|
|
||||||
|
function onIsInhibitedChanged() {
|
||||||
|
manualInhibitActive = manualInhibitorEnabled();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: inhibitorPoll
|
||||||
|
interval: 1000
|
||||||
|
repeat: true
|
||||||
|
running: true
|
||||||
|
onTriggered: manualInhibitActive = manualInhibitorEnabled()
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: PowerProfileService
|
||||||
|
|
||||||
|
function onProfileChanged() {
|
||||||
|
profileIndex = profileToIndex(PowerProfileService.profile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,7 +13,7 @@ import qs.Widgets
|
|||||||
SmartPanel {
|
SmartPanel {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
readonly property bool previewActive: searchText.startsWith(">clip") && Settings.data.appLauncher.enableClipPreview
|
readonly property bool previewActive: searchText.startsWith(">clip") && Settings.data.appLauncher.enableClipPreview && ClipboardService.items.length > 0 && selectedIndex >= 0 && results[selectedIndex] && results[selectedIndex].clipboardId
|
||||||
|
|
||||||
// Panel configuration
|
// Panel configuration
|
||||||
readonly property int listPanelWidth: Math.round(600 * Style.uiScaleRatio)
|
readonly property int listPanelWidth: Math.round(600 * Style.uiScaleRatio)
|
||||||
@@ -220,6 +220,14 @@ SmartPanel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
EmojiPlugin {
|
||||||
|
id: emojiPlugin
|
||||||
|
Component.onCompleted: {
|
||||||
|
registerPlugin(this);
|
||||||
|
Logger.d("Launcher", "Registered: EmojiPlugin");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Navigation functions
|
// Navigation functions
|
||||||
function selectNextWrapped() {
|
function selectNextWrapped() {
|
||||||
if (results.length > 0) {
|
if (results.length > 0) {
|
||||||
@@ -492,7 +500,7 @@ SmartPanel {
|
|||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
spacing: Style.marginM
|
spacing: Style.marginM
|
||||||
|
|
||||||
// Icon badge or Image preview
|
// Icon badge or Image preview or Emoji
|
||||||
Rectangle {
|
Rectangle {
|
||||||
Layout.preferredWidth: badgeSize
|
Layout.preferredWidth: badgeSize
|
||||||
Layout.preferredHeight: badgeSize
|
Layout.preferredHeight: badgeSize
|
||||||
@@ -503,7 +511,7 @@ SmartPanel {
|
|||||||
NImageRounded {
|
NImageRounded {
|
||||||
id: imagePreview
|
id: imagePreview
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
visible: modelData.isImage
|
visible: modelData.isImage && !modelData.emojiChar
|
||||||
imageRadius: Style.radiusM
|
imageRadius: Style.radiusM
|
||||||
|
|
||||||
// This property creates a dependency on the service's revision counter
|
// This property creates a dependency on the service's revision counter
|
||||||
@@ -542,26 +550,28 @@ SmartPanel {
|
|||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: Style.marginXS
|
anchors.margins: Style.marginXS
|
||||||
|
|
||||||
visible: !modelData.isImage || imagePreview.status === Image.Error
|
visible: !modelData.isImage && !modelData.emojiChar || (modelData.isImage && imagePreview.status === Image.Error)
|
||||||
active: visible
|
active: visible
|
||||||
|
|
||||||
sourceComponent: Component {
|
sourceComponent: Component {
|
||||||
IconImage {
|
IconImage {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
source: modelData.icon ? ThemeIcons.iconFromName(modelData.icon, "application-x-executable") : ""
|
source: modelData.icon ? ThemeIcons.iconFromName(modelData.icon, "application-x-executable") : ""
|
||||||
visible: modelData.icon && source !== ""
|
visible: modelData.icon && source !== "" && !modelData.emojiChar
|
||||||
asynchronous: true
|
asynchronous: true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Emoji display - takes precedence when emojiChar is present
|
||||||
NText {
|
NText {
|
||||||
|
id: emojiDisplay
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
visible: !imagePreview.visible && !iconLoader.visible
|
visible: modelData.emojiChar ? true : (!imagePreview.visible && !iconLoader.visible)
|
||||||
text: modelData.name ? modelData.name.charAt(0).toUpperCase() : "?"
|
text: modelData.emojiChar ? modelData.emojiChar : (modelData.name ? modelData.name.charAt(0).toUpperCase() : "?")
|
||||||
pointSize: Style.fontSizeXXL
|
pointSize: modelData.emojiChar ? Style.fontSizeXXXL : Style.fontSizeXXL // Larger font for emojis
|
||||||
font.weight: Style.fontWeightBold
|
font.weight: Style.fontWeightBold
|
||||||
color: Color.mOnPrimary
|
color: modelData.emojiChar ? Color.mOnSurface : Color.mOnPrimary // Different color for emojis
|
||||||
}
|
}
|
||||||
|
|
||||||
// Image type indicator overlay
|
// Image type indicator overlay
|
||||||
|
|||||||
96
Modules/Panels/Launcher/Plugins/EmojiPlugin.qml
Normal file
96
Modules/Panels/Launcher/Plugins/EmojiPlugin.qml
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
import QtQuick
|
||||||
|
import Quickshell
|
||||||
|
import qs.Commons
|
||||||
|
import qs.Services.Keyboard
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
// Plugin metadata
|
||||||
|
property string name: I18n.tr("plugins.emoji")
|
||||||
|
property var launcher: null
|
||||||
|
property bool handleSearch: false
|
||||||
|
|
||||||
|
// Force update results when emoji service loads
|
||||||
|
Connections {
|
||||||
|
target: EmojiService
|
||||||
|
function onLoadedChanged() {
|
||||||
|
if (EmojiService.loaded && root.launcher) {
|
||||||
|
// Update launcher results to refresh the UI
|
||||||
|
root.launcher?.updateResults();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize plugin
|
||||||
|
function init() {
|
||||||
|
Logger.i("EmojiPlugin", "Initialized");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if this plugin handles the command
|
||||||
|
function handleCommand(searchText) {
|
||||||
|
return searchText.startsWith(">emoji");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return available commands when user types ">"
|
||||||
|
function commands() {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
"name": ">emoji",
|
||||||
|
"description": I18n.tr("plugins.emoji-search-description"),
|
||||||
|
"icon": "face-smile",
|
||||||
|
"isImage": false,
|
||||||
|
"onActivate": function () {
|
||||||
|
launcher.setSearchText(">emoji ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get search results
|
||||||
|
function getResults(searchText) {
|
||||||
|
if (!searchText.startsWith(">emoji")) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!EmojiService.loaded) {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
"name": I18n.tr("plugins.emoji-loading"),
|
||||||
|
"description": I18n.tr("plugins.emoji-loading-description"),
|
||||||
|
"icon": "view-refresh",
|
||||||
|
"isImage": false,
|
||||||
|
"onActivate": function () {}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
const query = searchText.slice(6).trim();
|
||||||
|
const emojis = EmojiService.search(query);
|
||||||
|
return emojis.map(formatEmojiEntry);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format an emoji entry for the results list
|
||||||
|
function formatEmojiEntry(emoji) {
|
||||||
|
let title = emoji.name;
|
||||||
|
let description = (emoji.keywords || []).join(", ");
|
||||||
|
|
||||||
|
if (emoji.category) {
|
||||||
|
description += " • Category: " + emoji.category;
|
||||||
|
}
|
||||||
|
|
||||||
|
const emojiChar = emoji.emoji;
|
||||||
|
|
||||||
|
return {
|
||||||
|
"name": title,
|
||||||
|
"description": description,
|
||||||
|
"icon": null,
|
||||||
|
"isImage": false,
|
||||||
|
"emojiChar": emojiChar,
|
||||||
|
"onActivate": function () {
|
||||||
|
EmojiService.copy(emojiChar);
|
||||||
|
launcher.close();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -238,13 +238,16 @@ SmartPanel {
|
|||||||
NText {
|
NText {
|
||||||
text: model.appName || "Unknown App"
|
text: model.appName || "Unknown App"
|
||||||
pointSize: Style.fontSizeXS
|
pointSize: Style.fontSizeXS
|
||||||
|
font.weight: Style.fontWeightBold
|
||||||
color: Color.mSecondary
|
color: Color.mSecondary
|
||||||
}
|
}
|
||||||
|
|
||||||
NText {
|
NText {
|
||||||
text: Time.formatRelativeTime(model.timestamp)
|
textFormat: Text.PlainText
|
||||||
pointSize: Style.fontSizeXS
|
text: " " + Time.formatRelativeTime(model.timestamp)
|
||||||
color: Color.mSecondary
|
pointSize: Style.fontSizeXXS
|
||||||
|
color: Color.mOnSurfaceVariant
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -47,6 +47,18 @@ ColumnLayout {
|
|||||||
return settings;
|
return settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NScrollView {
|
||||||
|
Layout.preferredWidth: Math.round(600 * Style.uiScaleRatio)
|
||||||
|
Layout.preferredHeight: Math.round(700 * Style.uiScaleRatio)
|
||||||
|
horizontalPolicy: ScrollBar.AlwaysOff
|
||||||
|
verticalPolicy: ScrollBar.AsNeeded
|
||||||
|
padding: Style.marginL
|
||||||
|
focus: true
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
width: parent.width
|
||||||
|
spacing: Style.marginM
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
spacing: Style.marginM
|
spacing: Style.marginM
|
||||||
|
|
||||||
@@ -316,4 +328,6 @@ ColumnLayout {
|
|||||||
placeholderText: String(widgetMetadata.textIntervalMs || 3000)
|
placeholderText: String(widgetMetadata.textIntervalMs || 3000)
|
||||||
text: widgetData && widgetData.textIntervalMs !== undefined ? String(widgetData.textIntervalMs) : ""
|
text: widgetData && widgetData.textIntervalMs !== undefined ? String(widgetData.textIntervalMs) : ""
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ ColumnLayout {
|
|||||||
property string valueScrollingMode: widgetData.scrollingMode || widgetMetadata.scrollingMode
|
property string valueScrollingMode: widgetData.scrollingMode || widgetMetadata.scrollingMode
|
||||||
property int valueMaxWidth: widgetData.maxWidth !== undefined ? widgetData.maxWidth : widgetMetadata.maxWidth
|
property int valueMaxWidth: widgetData.maxWidth !== undefined ? widgetData.maxWidth : widgetMetadata.maxWidth
|
||||||
property bool valueUseFixedWidth: widgetData.useFixedWidth !== undefined ? widgetData.useFixedWidth : widgetMetadata.useFixedWidth
|
property bool valueUseFixedWidth: widgetData.useFixedWidth !== undefined ? widgetData.useFixedWidth : widgetMetadata.useFixedWidth
|
||||||
|
property bool valueShowProgressRing: widgetData.showProgressRing !== undefined ? widgetData.showProgressRing : widgetMetadata.showProgressRing
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
if (widgetData && widgetData.hideMode !== undefined) {
|
if (widgetData && widgetData.hideMode !== undefined) {
|
||||||
@@ -41,6 +42,7 @@ ColumnLayout {
|
|||||||
settings.scrollingMode = valueScrollingMode;
|
settings.scrollingMode = valueScrollingMode;
|
||||||
settings.maxWidth = parseInt(widthInput.text) || widgetMetadata.maxWidth;
|
settings.maxWidth = parseInt(widthInput.text) || widgetMetadata.maxWidth;
|
||||||
settings.useFixedWidth = valueUseFixedWidth;
|
settings.useFixedWidth = valueUseFixedWidth;
|
||||||
|
settings.showProgressRing = valueShowProgressRing;
|
||||||
return settings;
|
return settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,6 +132,13 @@ ColumnLayout {
|
|||||||
onToggled: checked => valueUseFixedWidth = checked
|
onToggled: checked => valueUseFixedWidth = checked
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NToggle {
|
||||||
|
label: I18n.tr("bar.widget-settings.media-mini.show-progress-ring.label")
|
||||||
|
description: I18n.tr("bar.widget-settings.media-mini.show-progress-ring.description")
|
||||||
|
checked: valueShowProgressRing
|
||||||
|
onToggled: checked => valueShowProgressRing = checked
|
||||||
|
}
|
||||||
|
|
||||||
NComboBox {
|
NComboBox {
|
||||||
label: I18n.tr("bar.widget-settings.media-mini.scrolling-mode.label")
|
label: I18n.tr("bar.widget-settings.media-mini.scrolling-mode.label")
|
||||||
description: I18n.tr("bar.widget-settings.media-mini.scrolling-mode.description")
|
description: I18n.tr("bar.widget-settings.media-mini.scrolling-mode.description")
|
||||||
|
|||||||
@@ -67,7 +67,6 @@ ColumnLayout {
|
|||||||
description: I18n.tr("bar.widget-settings.taskbar-grouped.show-labels-only-when-occupied.description")
|
description: I18n.tr("bar.widget-settings.taskbar-grouped.show-labels-only-when-occupied.description")
|
||||||
checked: root.valueShowLabelsOnlyWhenOccupied
|
checked: root.valueShowLabelsOnlyWhenOccupied
|
||||||
onToggled: checked => root.valueShowLabelsOnlyWhenOccupied = checked
|
onToggled: checked => root.valueShowLabelsOnlyWhenOccupied = checked
|
||||||
visible: !root.valueHideUnoccupied
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NToggle {
|
NToggle {
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ ColumnLayout {
|
|||||||
|
|
||||||
property string valueLabelMode: widgetData.labelMode !== undefined ? widgetData.labelMode : widgetMetadata.labelMode
|
property string valueLabelMode: widgetData.labelMode !== undefined ? widgetData.labelMode : widgetMetadata.labelMode
|
||||||
property bool valueHideUnoccupied: widgetData.hideUnoccupied !== undefined ? widgetData.hideUnoccupied : widgetMetadata.hideUnoccupied
|
property bool valueHideUnoccupied: widgetData.hideUnoccupied !== undefined ? widgetData.hideUnoccupied : widgetMetadata.hideUnoccupied
|
||||||
|
property bool valueFollowFocusedScreen: widgetData.followFocusedScreen !== undefined ? widgetData.followFocusedScreen : widgetMetadata.followFocusedScreen
|
||||||
property int valueCharacterCount: widgetData.characterCount !== undefined ? widgetData.characterCount : widgetMetadata.characterCount
|
property int valueCharacterCount: widgetData.characterCount !== undefined ? widgetData.characterCount : widgetMetadata.characterCount
|
||||||
|
|
||||||
function saveSettings() {
|
function saveSettings() {
|
||||||
@@ -21,6 +22,7 @@ ColumnLayout {
|
|||||||
settings.labelMode = valueLabelMode;
|
settings.labelMode = valueLabelMode;
|
||||||
settings.hideUnoccupied = valueHideUnoccupied;
|
settings.hideUnoccupied = valueHideUnoccupied;
|
||||||
settings.characterCount = valueCharacterCount;
|
settings.characterCount = valueCharacterCount;
|
||||||
|
settings.followFocusedScreen = valueFollowFocusedScreen;
|
||||||
return settings;
|
return settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,6 +53,13 @@ ColumnLayout {
|
|||||||
minimumWidth: 200
|
minimumWidth: 200
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NToggle {
|
||||||
|
label: I18n.tr("bar.widget-settings.workspace.follow-focused-screen.label")
|
||||||
|
description: I18n.tr("bar.widget-settings.workspace.follow-focused-screen.description")
|
||||||
|
checked: valueFollowFocusedScreen
|
||||||
|
onToggled: checked => valueFollowFocusedScreen = checked
|
||||||
|
}
|
||||||
|
|
||||||
NToggle {
|
NToggle {
|
||||||
label: I18n.tr("bar.widget-settings.workspace.hide-unoccupied.label")
|
label: I18n.tr("bar.widget-settings.workspace.hide-unoccupied.label")
|
||||||
description: I18n.tr("bar.widget-settings.workspace.hide-unoccupied.description")
|
description: I18n.tr("bar.widget-settings.workspace.hide-unoccupied.description")
|
||||||
|
|||||||
@@ -853,6 +853,24 @@ ColumnLayout {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NCheckbox {
|
||||||
|
label: "Cava"
|
||||||
|
description: ProgramCheckerService.cavaAvailable ? I18n.tr("settings.color-scheme.templates.programs.cava.description", {
|
||||||
|
"filepath": "~/.config/cava/themes/noctalia"
|
||||||
|
}) : I18n.tr("settings.color-scheme.templates.programs.cava.description-missing", {
|
||||||
|
"app": "cava"
|
||||||
|
})
|
||||||
|
checked: Settings.data.templates.cava
|
||||||
|
enabled: ProgramCheckerService.cavaAvailable
|
||||||
|
opacity: ProgramCheckerService.cavaAvailable ? 1.0 : 0.6
|
||||||
|
onToggled: checked => {
|
||||||
|
if (ProgramCheckerService.cavaAvailable) {
|
||||||
|
Settings.data.templates.cava = checked;
|
||||||
|
AppThemeService.generate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Miscellaneous
|
// Miscellaneous
|
||||||
NCollapsible {
|
NCollapsible {
|
||||||
|
|||||||
@@ -26,8 +26,7 @@ Popup {
|
|||||||
property var schemeColorsCache: ({})
|
property var schemeColorsCache: ({})
|
||||||
property int cacheVersion: 0
|
property int cacheVersion: 0
|
||||||
|
|
||||||
// Cache for available schemes list
|
// Cache for available schemes list (uses ShellState singleton)
|
||||||
property string schemesCacheFile: Settings.cacheDir + "color-schemes-list.json"
|
|
||||||
property int schemesCacheUpdateFrequency: 2 * 60 * 60 // 2 hours in seconds
|
property int schemesCacheUpdateFrequency: 2 * 60 * 60 // 2 hours in seconds
|
||||||
|
|
||||||
// Cache for repo branch info (to reduce API calls during downloads)
|
// Cache for repo branch info (to reduce API calls during downloads)
|
||||||
@@ -99,34 +98,6 @@ Popup {
|
|||||||
xhr.send();
|
xhr.send();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cache file for schemes list
|
|
||||||
FileView {
|
|
||||||
id: schemesCacheFileView
|
|
||||||
path: schemesCacheFile
|
|
||||||
printErrors: false
|
|
||||||
|
|
||||||
JsonAdapter {
|
|
||||||
id: schemesCacheAdapter
|
|
||||||
property var schemes: []
|
|
||||||
property real timestamp: 0
|
|
||||||
}
|
|
||||||
|
|
||||||
onLoaded: {
|
|
||||||
loadSchemesFromCache();
|
|
||||||
}
|
|
||||||
|
|
||||||
onLoadFailed: function (error) {
|
|
||||||
if (error.toString().includes("No such file") || error === 2) {
|
|
||||||
// Cache doesn't exist, fetch from API (only if popup is open)
|
|
||||||
if (root.visible) {
|
|
||||||
Qt.callLater(() => {
|
|
||||||
fetchAvailableSchemesFromAPI();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
color: Color.mSurface
|
color: Color.mSurface
|
||||||
radius: Style.radiusL
|
radius: Style.radiusL
|
||||||
@@ -135,10 +106,16 @@ Popup {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function loadSchemesFromCache() {
|
function loadSchemesFromCache() {
|
||||||
|
try {
|
||||||
const now = Time.timestamp;
|
const now = Time.timestamp;
|
||||||
|
const cacheData = ShellState.getColorSchemesList();
|
||||||
|
const cachedSchemes = cacheData.schemes || [];
|
||||||
|
const cachedTimestamp = cacheData.timestamp || 0;
|
||||||
|
|
||||||
// Check if cache is expired or missing
|
// Check if cache is expired or missing
|
||||||
if (!schemesCacheAdapter.timestamp || (now >= schemesCacheAdapter.timestamp + schemesCacheUpdateFrequency)) {
|
if (!cachedTimestamp || (now >= cachedTimestamp + schemesCacheUpdateFrequency)) {
|
||||||
|
// Migration is now handled in Settings.qml
|
||||||
|
|
||||||
// Only fetch from API if we haven't fetched recently (prevent rapid repeated calls)
|
// Only fetch from API if we haven't fetched recently (prevent rapid repeated calls)
|
||||||
const timeSinceLastFetch = now - lastApiFetchTime;
|
const timeSinceLastFetch = now - lastApiFetchTime;
|
||||||
if (timeSinceLastFetch >= minApiFetchInterval) {
|
if (timeSinceLastFetch >= minApiFetchInterval) {
|
||||||
@@ -148,8 +125,8 @@ Popup {
|
|||||||
} else {
|
} else {
|
||||||
// Use cached data even if expired, to avoid rate limits
|
// Use cached data even if expired, to avoid rate limits
|
||||||
Logger.d("ColorSchemeDownload", "Cache expired but recent API call detected, using cached data");
|
Logger.d("ColorSchemeDownload", "Cache expired but recent API call detected, using cached data");
|
||||||
if (schemesCacheAdapter.schemes && schemesCacheAdapter.schemes.length > 0) {
|
if (cachedSchemes.length > 0) {
|
||||||
availableSchemes = schemesCacheAdapter.schemes;
|
availableSchemes = cachedSchemes;
|
||||||
hasInitialData = true;
|
hasInitialData = true;
|
||||||
fetching = false;
|
fetching = false;
|
||||||
return;
|
return;
|
||||||
@@ -157,11 +134,11 @@ Popup {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const ageMinutes = Math.round((now - schemesCacheAdapter.timestamp) / 60);
|
const ageMinutes = Math.round((now - cachedTimestamp) / 60);
|
||||||
Logger.d("ColorSchemeDownload", "Loading cached schemes (age:", ageMinutes, "minutes)");
|
Logger.d("ColorSchemeDownload", "Loading cached schemes from ShellState (age:", ageMinutes, "minutes)");
|
||||||
|
|
||||||
if (schemesCacheAdapter.schemes && schemesCacheAdapter.schemes.length > 0) {
|
if (cachedSchemes.length > 0) {
|
||||||
availableSchemes = schemesCacheAdapter.schemes;
|
availableSchemes = cachedSchemes;
|
||||||
hasInitialData = true;
|
hasInitialData = true;
|
||||||
fetching = false;
|
fetching = false;
|
||||||
} else {
|
} else {
|
||||||
@@ -174,19 +151,22 @@ Popup {
|
|||||||
fetching = false;
|
fetching = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} catch (error) {
|
||||||
|
Logger.e("ColorSchemeDownload", "Failed to load schemes from cache:", error);
|
||||||
|
fetching = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function saveSchemesToCache() {
|
function saveSchemesToCache() {
|
||||||
schemesCacheAdapter.schemes = availableSchemes;
|
try {
|
||||||
schemesCacheAdapter.timestamp = Time.timestamp;
|
ShellState.setColorSchemesList({
|
||||||
|
schemes: availableSchemes,
|
||||||
// Ensure cache directory exists
|
timestamp: Time.timestamp
|
||||||
Quickshell.execDetached(["mkdir", "-p", Settings.cacheDir]);
|
|
||||||
|
|
||||||
Qt.callLater(() => {
|
|
||||||
schemesCacheFileView.writeAdapter();
|
|
||||||
Logger.d("ColorSchemeDownload", "Schemes list saved to cache");
|
|
||||||
});
|
});
|
||||||
|
Logger.d("ColorSchemeDownload", "Schemes list saved to ShellState");
|
||||||
|
} catch (error) {
|
||||||
|
Logger.e("ColorSchemeDownload", "Failed to save schemes to cache:", error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function fetchAvailableSchemes() {
|
function fetchAvailableSchemes() {
|
||||||
@@ -194,19 +174,11 @@ Popup {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Path is set when popup becomes visible, so FileView will start loading
|
// Try to load from ShellState cache first
|
||||||
// Try to load from cache first
|
if (typeof ShellState !== 'undefined' && ShellState.isLoaded) {
|
||||||
if (schemesCacheFileView.loaded) {
|
|
||||||
loadSchemesFromCache();
|
loadSchemesFromCache();
|
||||||
} else if (schemesCacheFileView.path) {
|
|
||||||
// Cache file path is set but not loaded yet, wait for it to load
|
|
||||||
// The FileView will trigger loadSchemesFromCache() when loaded
|
|
||||||
// But if it fails, we should fetch from API
|
|
||||||
if (!schemesCacheFileView.loading) {
|
|
||||||
schemesCacheFileView.reload();
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// No cache file path, fetch directly from API
|
// ShellState not ready, fetch directly from API
|
||||||
fetchAvailableSchemesFromAPI();
|
fetchAvailableSchemesFromAPI();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -725,7 +697,25 @@ Popup {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onAvailableSchemesChanged: preFetchSchemeColors()
|
onAvailableSchemesChanged: preFetchSchemeColors()
|
||||||
onVisibleChanged: preFetchSchemeColors()
|
onVisibleChanged: {
|
||||||
|
preFetchSchemeColors();
|
||||||
|
|
||||||
|
// Load schemes from ShellState when popup becomes visible
|
||||||
|
if (visible) {
|
||||||
|
if (typeof ShellState !== 'undefined' && ShellState.isLoaded) {
|
||||||
|
loadSchemesFromCache();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: typeof ShellState !== 'undefined' ? ShellState : null
|
||||||
|
function onIsLoadedChanged() {
|
||||||
|
if (root.visible && ShellState.isLoaded) {
|
||||||
|
loadSchemesFromCache();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
contentItem: ColumnLayout {
|
contentItem: ColumnLayout {
|
||||||
id: contentColumn
|
id: contentColumn
|
||||||
|
|||||||
@@ -37,7 +37,6 @@ ColumnLayout {
|
|||||||
LocationService.resetWeather();
|
LocationService.resetWeather();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Layout.maximumWidth: 420
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NText {
|
NText {
|
||||||
|
|||||||
@@ -74,6 +74,10 @@ ColumnLayout {
|
|||||||
{
|
{
|
||||||
"key": "bottom_right",
|
"key": "bottom_right",
|
||||||
"name": I18n.tr("options.launcher.position.bottom_right")
|
"name": I18n.tr("options.launcher.position.bottom_right")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "bar",
|
||||||
|
"name": I18n.tr("options.launcher.position.bar")
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
currentKey: Settings.data.notifications.location || "top_right"
|
currentKey: Settings.data.notifications.location || "top_right"
|
||||||
|
|||||||
@@ -63,6 +63,21 @@ ColumnLayout {
|
|||||||
Settings.data.sessionMenu.powerOptions = toSave;
|
Settings.data.sessionMenu.powerOptions = toSave;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function updateEntry(idx, properties) {
|
||||||
|
var newModel = entriesModel.slice();
|
||||||
|
newModel[idx] = Object.assign({}, newModel[idx], properties);
|
||||||
|
entriesModel = newModel;
|
||||||
|
saveEntries();
|
||||||
|
}
|
||||||
|
|
||||||
|
function reorderEntries(fromIndex, toIndex) {
|
||||||
|
var newModel = entriesModel.slice();
|
||||||
|
var item = newModel.splice(fromIndex, 1)[0];
|
||||||
|
newModel.splice(toIndex, 0, item);
|
||||||
|
entriesModel = newModel;
|
||||||
|
saveEntries();
|
||||||
|
}
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
entriesModel = [];
|
entriesModel = [];
|
||||||
|
|
||||||
@@ -213,9 +228,6 @@ ColumnLayout {
|
|||||||
clip: true
|
clip: true
|
||||||
model: entriesModel
|
model: entriesModel
|
||||||
|
|
||||||
// Store reference to root's saveEntries function
|
|
||||||
property var saveEntriesFunc: root.saveEntries
|
|
||||||
|
|
||||||
delegate: Item {
|
delegate: Item {
|
||||||
id: delegateItem
|
id: delegateItem
|
||||||
width: listView.width
|
width: listView.width
|
||||||
@@ -228,7 +240,6 @@ ColumnLayout {
|
|||||||
property int dragStartY: 0
|
property int dragStartY: 0
|
||||||
property int dragStartIndex: -1
|
property int dragStartIndex: -1
|
||||||
property int dragTargetIndex: -1
|
property int dragTargetIndex: -1
|
||||||
property var saveEntriesFunc: listView.saveEntriesFunc
|
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
@@ -310,11 +321,7 @@ ColumnLayout {
|
|||||||
onReleased: {
|
onReleased: {
|
||||||
preventStealing = false;
|
preventStealing = false;
|
||||||
if (delegateItem.dragStartIndex !== -1 && delegateItem.dragTargetIndex !== -1 && delegateItem.dragStartIndex !== delegateItem.dragTargetIndex) {
|
if (delegateItem.dragStartIndex !== -1 && delegateItem.dragTargetIndex !== -1 && delegateItem.dragStartIndex !== delegateItem.dragTargetIndex) {
|
||||||
var newModel = entriesModel.slice();
|
root.reorderEntries(delegateItem.dragStartIndex, delegateItem.dragTargetIndex);
|
||||||
var item = newModel.splice(delegateItem.dragStartIndex, 1)[0];
|
|
||||||
newModel.splice(delegateItem.dragTargetIndex, 0, item);
|
|
||||||
entriesModel = newModel;
|
|
||||||
root.saveEntries();
|
|
||||||
}
|
}
|
||||||
delegateItem.dragging = false;
|
delegateItem.dragging = false;
|
||||||
delegateItem.dragStartIndex = -1;
|
delegateItem.dragStartIndex = -1;
|
||||||
@@ -361,12 +368,9 @@ ColumnLayout {
|
|||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: {
|
onClicked: {
|
||||||
var newModel = entriesModel.slice();
|
root.updateEntry(index, {
|
||||||
newModel[index] = Object.assign({}, newModel[index], {
|
|
||||||
"enabled": !modelData.enabled
|
"enabled": !modelData.enabled
|
||||||
});
|
});
|
||||||
entriesModel = newModel;
|
|
||||||
delegateItem.saveEntriesFunc();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -395,12 +399,9 @@ ColumnLayout {
|
|||||||
NToggle {
|
NToggle {
|
||||||
checked: modelData.countdownEnabled !== undefined ? modelData.countdownEnabled : true
|
checked: modelData.countdownEnabled !== undefined ? modelData.countdownEnabled : true
|
||||||
onToggled: function (checked) {
|
onToggled: function (checked) {
|
||||||
var newModel = entriesModel.slice();
|
root.updateEntry(delegateItem.index, {
|
||||||
newModel[delegateItem.index] = Object.assign({}, newModel[delegateItem.index], {
|
|
||||||
"countdownEnabled": checked
|
"countdownEnabled": checked
|
||||||
});
|
});
|
||||||
entriesModel = newModel;
|
|
||||||
delegateItem.saveEntriesFunc();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -388,6 +388,7 @@ SmartPanel {
|
|||||||
Logger.i("SetupWizard", "Completing setup with selected options");
|
Logger.i("SetupWizard", "Completing setup with selected options");
|
||||||
isCompleting = true;
|
isCompleting = true;
|
||||||
|
|
||||||
|
if (typeof WallpaperService !== "undefined" && WallpaperService.refreshWallpapersList) {
|
||||||
if (selectedWallpaperDirectory !== Settings.data.wallpaper.directory) {
|
if (selectedWallpaperDirectory !== Settings.data.wallpaper.directory) {
|
||||||
Settings.data.wallpaper.directory = selectedWallpaperDirectory;
|
Settings.data.wallpaper.directory = selectedWallpaperDirectory;
|
||||||
WallpaperService.refreshWallpapersList();
|
WallpaperService.refreshWallpapersList();
|
||||||
@@ -396,6 +397,7 @@ SmartPanel {
|
|||||||
if (selectedWallpaper !== "") {
|
if (selectedWallpaper !== "") {
|
||||||
WallpaperService.changeWallpaper(selectedWallpaper, undefined);
|
WallpaperService.changeWallpaper(selectedWallpaper, undefined);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Settings.data.general.scaleRatio = selectedScaleRatio;
|
Settings.data.general.scaleRatio = selectedScaleRatio;
|
||||||
Settings.data.bar.position = selectedBarPosition;
|
Settings.data.bar.position = selectedBarPosition;
|
||||||
@@ -426,6 +428,7 @@ SmartPanel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function applyWallpaperSettings() {
|
function applyWallpaperSettings() {
|
||||||
|
if (typeof WallpaperService !== "undefined" && WallpaperService.refreshWallpapersList) {
|
||||||
if (selectedWallpaperDirectory !== Settings.data.wallpaper.directory) {
|
if (selectedWallpaperDirectory !== Settings.data.wallpaper.directory) {
|
||||||
Settings.data.wallpaper.directory = selectedWallpaperDirectory;
|
Settings.data.wallpaper.directory = selectedWallpaperDirectory;
|
||||||
WallpaperService.refreshWallpapersList();
|
WallpaperService.refreshWallpapersList();
|
||||||
@@ -435,6 +438,7 @@ SmartPanel {
|
|||||||
WallpaperService.changeWallpaper(selectedWallpaper, undefined);
|
WallpaperService.changeWallpaper(selectedWallpaper, undefined);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function applyUISettings() {
|
function applyUISettings() {
|
||||||
Settings.data.general.scaleRatio = selectedScaleRatio;
|
Settings.data.general.scaleRatio = selectedScaleRatio;
|
||||||
|
|||||||
@@ -100,9 +100,9 @@ Item {
|
|||||||
anchors.bottom: parent.bottom
|
anchors.bottom: parent.bottom
|
||||||
anchors.bottomMargin: Style.marginM
|
anchors.bottomMargin: Style.marginM
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.leftMargin: Style.marginM * 2
|
anchors.leftMargin: Style.marginM * 3
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.rightMargin: Style.marginM * 2
|
anchors.rightMargin: Style.marginM * 3
|
||||||
spacing: Style.marginL
|
spacing: Style.marginL
|
||||||
|
|
||||||
// Icon
|
// Icon
|
||||||
|
|||||||
@@ -134,7 +134,11 @@ Item {
|
|||||||
screen: root.screen
|
screen: root.screen
|
||||||
|
|
||||||
// Parse location setting
|
// Parse location setting
|
||||||
readonly property string location: Settings.data.notifications?.location || "top_right"
|
readonly property string location: {
|
||||||
|
if (Settings.data.notifications?.location == "bar")
|
||||||
|
return "top_right"
|
||||||
|
return Settings.data.notifications?.location || "top_right"
|
||||||
|
}
|
||||||
readonly property bool isTop: location.startsWith("top")
|
readonly property bool isTop: location.startsWith("top")
|
||||||
readonly property bool isBottom: location.startsWith("bottom")
|
readonly property bool isBottom: location.startsWith("bottom")
|
||||||
readonly property bool isLeft: location.endsWith("_left")
|
readonly property bool isLeft: location.endsWith("_left")
|
||||||
|
|||||||
@@ -32,21 +32,26 @@ Singleton {
|
|||||||
// Backend service loader
|
// Backend service loader
|
||||||
property var backend: null
|
property var backend: null
|
||||||
|
|
||||||
// Cache file path
|
|
||||||
property string displayCachePath: ""
|
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
// Setup cache path (needs Settings to be available)
|
// Load display scales from ShellState
|
||||||
Qt.callLater(() => {
|
Qt.callLater(() => {
|
||||||
if (typeof Settings !== 'undefined' && Settings.cacheDir) {
|
if (typeof ShellState !== 'undefined' && ShellState.isLoaded) {
|
||||||
displayCachePath = Settings.cacheDir + "display.json";
|
loadDisplayScalesFromState();
|
||||||
displayCacheFileView.path = displayCachePath;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
detectCompositor();
|
detectCompositor();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: typeof ShellState !== 'undefined' ? ShellState : null
|
||||||
|
function onIsLoadedChanged() {
|
||||||
|
if (ShellState.isLoaded) {
|
||||||
|
loadDisplayScalesFromState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function detectCompositor() {
|
function detectCompositor() {
|
||||||
const hyprlandSignature = Quickshell.env("HYPRLAND_INSTANCE_SIGNATURE");
|
const hyprlandSignature = Quickshell.env("HYPRLAND_INSTANCE_SIGNATURE");
|
||||||
const niriSocket = Quickshell.env("NIRI_SOCKET");
|
const niriSocket = Quickshell.env("NIRI_SOCKET");
|
||||||
@@ -99,28 +104,21 @@ Singleton {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cache FileView for display scales
|
// Load display scales from ShellState
|
||||||
FileView {
|
function loadDisplayScalesFromState() {
|
||||||
id: displayCacheFileView
|
try {
|
||||||
printErrors: false
|
const cached = ShellState.getDisplay();
|
||||||
watchChanges: false
|
if (cached && Object.keys(cached).length > 0) {
|
||||||
|
displayScales = cached;
|
||||||
adapter: JsonAdapter {
|
|
||||||
id: displayCacheAdapter
|
|
||||||
property var displays: ({})
|
|
||||||
}
|
|
||||||
|
|
||||||
onLoaded: {
|
|
||||||
// Load cached display scales
|
|
||||||
displayScales = displayCacheAdapter.displays || {};
|
|
||||||
displayScalesLoaded = true;
|
displayScalesLoaded = true;
|
||||||
// Logger.i("CompositorService", "Loaded display scales from cache:", JSON.stringify(displayScales))
|
Logger.d("CompositorService", "Loaded display scales from ShellState");
|
||||||
}
|
} else {
|
||||||
|
// Migration is now handled in Settings.qml
|
||||||
onLoadFailed: {
|
displayScalesLoaded = true;
|
||||||
// Cache doesn't exist yet, will be created on first update
|
}
|
||||||
|
} catch (error) {
|
||||||
|
Logger.e("CompositorService", "Failed to load display scales:", error);
|
||||||
displayScalesLoaded = true;
|
displayScalesLoaded = true;
|
||||||
// Logger.i("CompositorService", "No display cache found, will create on first update")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -234,12 +232,12 @@ Singleton {
|
|||||||
|
|
||||||
// Save display scales to cache
|
// Save display scales to cache
|
||||||
function saveDisplayScalesToCache() {
|
function saveDisplayScalesToCache() {
|
||||||
if (!displayCachePath) {
|
try {
|
||||||
return;
|
ShellState.setDisplay(displayScales);
|
||||||
|
Logger.d("CompositorService", "Saved display scales to ShellState");
|
||||||
|
} catch (error) {
|
||||||
|
Logger.e("CompositorService", "Failed to save display scales:", error);
|
||||||
}
|
}
|
||||||
|
|
||||||
displayCacheAdapter.displays = displayScales;
|
|
||||||
displayCacheFileView.writeAdapter();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Public function to get scale for a specific display
|
// Public function to get scale for a specific display
|
||||||
|
|||||||
@@ -283,7 +283,7 @@ Singleton {
|
|||||||
function increaseBrightness(): void {
|
function increaseBrightness(): void {
|
||||||
const value = !isNaN(monitor.queuedBrightness) ? monitor.queuedBrightness : monitor.brightness;
|
const value = !isNaN(monitor.queuedBrightness) ? monitor.queuedBrightness : monitor.brightness;
|
||||||
// Enforce minimum brightness if enabled
|
// Enforce minimum brightness if enabled
|
||||||
if (Settings.data.brightness.enforceMinimum && value <= minBrightnessValue) {
|
if (Settings.data.brightness.enforceMinimum && value < minBrightnessValue) {
|
||||||
setBrightnessDebounced(Math.max(stepSize, minBrightnessValue));
|
setBrightnessDebounced(Math.max(stepSize, minBrightnessValue));
|
||||||
} else {
|
} else {
|
||||||
// Normal brightness increase
|
// Normal brightness increase
|
||||||
|
|||||||
259
Services/Keyboard/EmojiService.qml
Normal file
259
Services/Keyboard/EmojiService.qml
Normal file
@@ -0,0 +1,259 @@
|
|||||||
|
pragma Singleton
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Io
|
||||||
|
import qs.Commons
|
||||||
|
|
||||||
|
// Manages emoji data loading, searching, and clipboard operations
|
||||||
|
Singleton {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property var emojis: []
|
||||||
|
property bool loaded: false
|
||||||
|
|
||||||
|
// Usage tracking for popular emojis
|
||||||
|
property var usageCounts: ({})
|
||||||
|
|
||||||
|
// File path for persisting usage data
|
||||||
|
readonly property string usageFilePath: Settings.cacheDir + "emoji_usage.json"
|
||||||
|
|
||||||
|
// Searches emojis based on query
|
||||||
|
function search(query) {
|
||||||
|
if (!loaded) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!query || query.trim() === "") {
|
||||||
|
// Return popular/recently used emojis, fallback to all emojis sorted by usage
|
||||||
|
return _getPopularEmojis(50);
|
||||||
|
}
|
||||||
|
|
||||||
|
const terms = query.toLowerCase().split(" ").filter(t => t);
|
||||||
|
const results = emojis.filter(emoji => {
|
||||||
|
for (let term of terms) {
|
||||||
|
const emojiMatch = emoji.emoji.toLowerCase().includes(term);
|
||||||
|
const nameMatch = (emoji.name || "").toLowerCase().includes(term);
|
||||||
|
const keywordMatch = (emoji.keywords || []).some(kw => kw.toLowerCase().includes(term));
|
||||||
|
const categoryMatch = (emoji.category || "").toLowerCase().includes(term);
|
||||||
|
|
||||||
|
if (!emojiMatch && !nameMatch && !keywordMatch && !categoryMatch) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get popular emojis sorted by usage count
|
||||||
|
function _getPopularEmojis(limit) {
|
||||||
|
// Create array of emojis with their usage counts
|
||||||
|
const emojisWithUsage = emojis.map(emoji => {
|
||||||
|
return {
|
||||||
|
emoji: emoji,
|
||||||
|
usageCount: usageCounts[emoji.emoji] || 0
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
// Sort by usage count (descending), then by name
|
||||||
|
emojisWithUsage.sort((a, b) => {
|
||||||
|
if (b.usageCount !== a.usageCount) {
|
||||||
|
return b.usageCount - a.usageCount;
|
||||||
|
}
|
||||||
|
return (a.emoji.name || "").localeCompare(b.emoji.name || "");
|
||||||
|
});
|
||||||
|
|
||||||
|
// Return the emoji objects limited by the specified count
|
||||||
|
return emojisWithUsage.slice(0, limit).map(item => item.emoji);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Record emoji usage
|
||||||
|
function recordUsage(emojiChar) {
|
||||||
|
if (emojiChar) {
|
||||||
|
const currentCount = usageCounts[emojiChar] || 0;
|
||||||
|
usageCounts[emojiChar] = currentCount + 1;
|
||||||
|
_saveUsageData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure usage file exists with default content
|
||||||
|
function _ensureUsageFileExists() {
|
||||||
|
Quickshell.execDetached(["sh", "-c", `mkdir -p "$(dirname "${root.usageFilePath}")" && echo '{}' > "${root.usageFilePath}"`]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// File paths
|
||||||
|
readonly property string userEmojiPath: Settings.configDir + "emoji.json"
|
||||||
|
readonly property string builtinEmojiPath: `${Quickshell.shellDir}/Assets/Launcher/emoji.json`
|
||||||
|
|
||||||
|
// Internal data
|
||||||
|
property var _userEmojiData: []
|
||||||
|
property var _builtinEmojiData: []
|
||||||
|
property int _pendingLoads: 0
|
||||||
|
|
||||||
|
// Initialize on component completion
|
||||||
|
Component.onCompleted: {
|
||||||
|
_loadUsageData();
|
||||||
|
_loadEmojis();
|
||||||
|
}
|
||||||
|
|
||||||
|
// File loaders
|
||||||
|
FileView {
|
||||||
|
id: userEmojiFile
|
||||||
|
path: root.userEmojiPath
|
||||||
|
printErrors: false
|
||||||
|
watchChanges: false
|
||||||
|
|
||||||
|
onLoaded: {
|
||||||
|
try {
|
||||||
|
const content = text();
|
||||||
|
if (content) {
|
||||||
|
const parsed = JSON.parse(content);
|
||||||
|
_userEmojiData = Array.isArray(parsed) ? parsed : [];
|
||||||
|
} else {
|
||||||
|
_userEmojiData = [];
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
_userEmojiData = [];
|
||||||
|
}
|
||||||
|
_onLoadComplete();
|
||||||
|
}
|
||||||
|
|
||||||
|
onLoadFailed: function (error) {
|
||||||
|
_userEmojiData = [];
|
||||||
|
_onLoadComplete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FileView {
|
||||||
|
id: builtinEmojiFile
|
||||||
|
path: root.builtinEmojiPath
|
||||||
|
printErrors: false
|
||||||
|
watchChanges: false
|
||||||
|
|
||||||
|
onLoaded: {
|
||||||
|
try {
|
||||||
|
const content = text();
|
||||||
|
if (content) {
|
||||||
|
const parsed = JSON.parse(content);
|
||||||
|
_builtinEmojiData = Array.isArray(parsed) ? parsed : [];
|
||||||
|
} else {
|
||||||
|
_builtinEmojiData = [];
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
_builtinEmojiData = [];
|
||||||
|
}
|
||||||
|
_onLoadComplete();
|
||||||
|
}
|
||||||
|
|
||||||
|
onLoadFailed: function (error) {
|
||||||
|
_builtinEmojiData = [];
|
||||||
|
_onLoadComplete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load emoji files
|
||||||
|
function _loadEmojis() {
|
||||||
|
_pendingLoads = 2;
|
||||||
|
userEmojiFile.reload();
|
||||||
|
builtinEmojiFile.reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called when one file finishes loading
|
||||||
|
function _onLoadComplete() {
|
||||||
|
_pendingLoads--;
|
||||||
|
if (_pendingLoads <= 0) {
|
||||||
|
_finalizeEmojis();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Merge and deduplicate emojis
|
||||||
|
function _finalizeEmojis() {
|
||||||
|
const emojiMap = new Map();
|
||||||
|
|
||||||
|
// Add built-in emojis first
|
||||||
|
for (const emoji of _builtinEmojiData) {
|
||||||
|
if (emoji && emoji.emoji) {
|
||||||
|
emojiMap.set(emoji.emoji, emoji);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add user emojis (override built-ins if duplicate)
|
||||||
|
for (const emoji of _userEmojiData) {
|
||||||
|
if (emoji && emoji.emoji) {
|
||||||
|
emojiMap.set(emoji.emoji, emoji);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
emojis = Array.from(emojiMap.values());
|
||||||
|
loaded = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FileView for usage data
|
||||||
|
FileView {
|
||||||
|
id: usageFile
|
||||||
|
path: root.usageFilePath
|
||||||
|
printErrors: false
|
||||||
|
watchChanges: false
|
||||||
|
|
||||||
|
onLoaded: {
|
||||||
|
try {
|
||||||
|
const content = text();
|
||||||
|
if (content && content.trim() !== "") {
|
||||||
|
const parsed = JSON.parse(content);
|
||||||
|
if (parsed && typeof parsed === 'object') {
|
||||||
|
root.usageCounts = parsed;
|
||||||
|
} else {
|
||||||
|
root.usageCounts = {};
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
root.usageCounts = {};
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
root.usageCounts = {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onLoadFailed: function (error) {
|
||||||
|
root.usageCounts = {};
|
||||||
|
Qt.callLater(_ensureUsageFileExists);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Timer for debouncing usage data saves
|
||||||
|
Timer {
|
||||||
|
id: saveTimer
|
||||||
|
interval: 1000
|
||||||
|
repeat: false
|
||||||
|
onTriggered: _doSaveUsageData()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load usage data
|
||||||
|
function _loadUsageData() {
|
||||||
|
usageFile.reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save usage data with debounce
|
||||||
|
function _saveUsageData() {
|
||||||
|
saveTimer.restart();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Actually save usage data to file
|
||||||
|
function _doSaveUsageData() {
|
||||||
|
try {
|
||||||
|
const content = JSON.stringify(root.usageCounts);
|
||||||
|
Quickshell.execDetached(["sh", "-c", `mkdir -p "$(dirname "${root.usageFilePath}")" && echo '${content}' > "${root.usageFilePath}"`]);
|
||||||
|
} catch (e) {
|
||||||
|
Logger.e("EmojiService", "Failed to save usage data: " + e.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copies emoji to clipboard
|
||||||
|
function copy(emojiChar) {
|
||||||
|
if (emojiChar) {
|
||||||
|
recordUsage(emojiChar); // Record usage before copying
|
||||||
|
Quickshell.execDetached(["sh", "-c", `echo -n "${emojiChar}" | wl-copy`]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -70,10 +70,10 @@ Singleton {
|
|||||||
root.lastBluetoothBlocked = root.blocked;
|
root.lastBluetoothBlocked = root.blocked;
|
||||||
if (bluetoothBlockedToggled) {
|
if (bluetoothBlockedToggled) {
|
||||||
checkWifiBlocked.running = true;
|
checkWifiBlocked.running = true;
|
||||||
} else if (adapter.enabled) {
|
} else if (adapter.state === BluetoothAdapterState.Enabled) {
|
||||||
ToastService.showNotice(I18n.tr("bluetooth.panel.title"), I18n.tr("toast.bluetooth.enabled"), "bluetooth");
|
ToastService.showNotice(I18n.tr("bluetooth.panel.title"), I18n.tr("toast.bluetooth.enabled"), "bluetooth");
|
||||||
discoveryTimer.running = true;
|
discoveryTimer.running = true;
|
||||||
} else {
|
} else if (adapter.state === BluetoothAdapterState.Disabled) {
|
||||||
ToastService.showNotice(I18n.tr("bluetooth.panel.title"), I18n.tr("toast.bluetooth.disabled"), "bluetooth-off");
|
ToastService.showNotice(I18n.tr("bluetooth.panel.title"), I18n.tr("toast.bluetooth.disabled"), "bluetooth-off");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -159,7 +159,7 @@ Singleton {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const type = remaining.substring(secondLastColonIdx + 1);
|
const type = remaining.substring(secondLastColonIdx + 1);
|
||||||
if (type !== "vpn") {
|
if (type !== "vpn" && type !== "wireguard") {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const remaining2 = remaining.substring(0, secondLastColonIdx);
|
const remaining2 = remaining.substring(0, secondLastColonIdx);
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ Singleton {
|
|||||||
readonly property bool isDevelopment: true
|
readonly property bool isDevelopment: true
|
||||||
readonly property string developmentSuffix: "-git"
|
readonly property string developmentSuffix: "-git"
|
||||||
readonly property string currentVersion: `v${!isDevelopment ? baseVersion : baseVersion + developmentSuffix}`
|
readonly property string currentVersion: `v${!isDevelopment ? baseVersion : baseVersion + developmentSuffix}`
|
||||||
readonly property string changelogStateFile: Quickshell.env("NOCTALIA_CHANGELOG_STATE_FILE") || (Settings.cacheDir + "changelog-state.json")
|
|
||||||
|
|
||||||
// URLs
|
// URLs
|
||||||
readonly property string discordUrl: "https://discord.noctalia.dev"
|
readonly property string discordUrl: "https://discord.noctalia.dev"
|
||||||
@@ -50,30 +49,21 @@ Singleton {
|
|||||||
|
|
||||||
initialized = true;
|
initialized = true;
|
||||||
Logger.i("UpdateService", "Version:", root.currentVersion);
|
Logger.i("UpdateService", "Version:", root.currentVersion);
|
||||||
|
|
||||||
|
// Load changelog state from ShellState
|
||||||
|
Qt.callLater(() => {
|
||||||
|
if (typeof ShellState !== 'undefined' && ShellState.isLoaded) {
|
||||||
|
loadChangelogState();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
FileView {
|
Connections {
|
||||||
id: changelogStateFileView
|
target: typeof ShellState !== 'undefined' ? ShellState : null
|
||||||
path: root.changelogStateFile
|
function onIsLoadedChanged() {
|
||||||
printErrors: false
|
if (ShellState.isLoaded) {
|
||||||
onLoaded: loadChangelogState()
|
loadChangelogState();
|
||||||
onLoadFailed: error => {
|
|
||||||
if (error === 2) {
|
|
||||||
// File doesn't exist, create it
|
|
||||||
debouncedSaveChangelogState();
|
|
||||||
} else {
|
|
||||||
Logger.e("UpdateService", "Failed to load changelog state file:", error);
|
|
||||||
}
|
}
|
||||||
changelogStateLoaded = true;
|
|
||||||
if (pendingShowRequest) {
|
|
||||||
pendingShowRequest = false;
|
|
||||||
Qt.callLater(root.showLatestChangelog);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
JsonAdapter {
|
|
||||||
id: changelogStateAdapter
|
|
||||||
property string lastSeenVersion: ""
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -326,12 +316,11 @@ Singleton {
|
|||||||
|
|
||||||
function loadChangelogState() {
|
function loadChangelogState() {
|
||||||
try {
|
try {
|
||||||
changelogLastSeenVersion = changelogStateAdapter.lastSeenVersion || "";
|
const changelog = ShellState.getChangelogState();
|
||||||
if (!changelogLastSeenVersion && Settings.data && Settings.data.changelog && Settings.data.changelog.lastSeenVersion) {
|
changelogLastSeenVersion = changelog.lastSeenVersion || "";
|
||||||
changelogLastSeenVersion = Settings.data.changelog.lastSeenVersion;
|
|
||||||
debouncedSaveChangelogState();
|
// Migration is now handled in Settings.qml
|
||||||
Logger.i("UpdateService", "Migrated changelog lastSeenVersion from settings to cache");
|
Logger.d("UpdateService", "Loaded changelog state from ShellState");
|
||||||
}
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
Logger.e("UpdateService", "Failed to load changelog state:", error);
|
Logger.e("UpdateService", "Failed to load changelog state:", error);
|
||||||
}
|
}
|
||||||
@@ -363,26 +352,16 @@ Singleton {
|
|||||||
saveInProgress = true;
|
saveInProgress = true;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
changelogStateAdapter.lastSeenVersion = changelogLastSeenVersion || "";
|
ShellState.setChangelogState({
|
||||||
|
lastSeenVersion: changelogLastSeenVersion || ""
|
||||||
// Ensure cache directory exists
|
});
|
||||||
Quickshell.execDetached(["mkdir", "-p", Settings.cacheDir]);
|
Logger.d("UpdateService", "Saved changelog state to ShellState");
|
||||||
|
|
||||||
// Small delay to ensure directory creation completes
|
|
||||||
Qt.callLater(() => {
|
|
||||||
try {
|
|
||||||
changelogStateFileView.writeAdapter();
|
|
||||||
saveInProgress = false;
|
saveInProgress = false;
|
||||||
|
|
||||||
// Check if another save was queued while we were saving
|
// Check if another save was queued while we were saving
|
||||||
if (pendingSave) {
|
if (pendingSave) {
|
||||||
Qt.callLater(executeSave);
|
Qt.callLater(executeSave);
|
||||||
}
|
}
|
||||||
} catch (writeError) {
|
|
||||||
Logger.e("UpdateService", "Failed to write changelog state:", writeError);
|
|
||||||
saveInProgress = false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
Logger.e("UpdateService", "Failed to save changelog state:", error);
|
Logger.e("UpdateService", "Failed to save changelog state:", error);
|
||||||
saveInProgress = false;
|
saveInProgress = false;
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ Singleton {
|
|||||||
property int maxVisible: 5
|
property int maxVisible: 5
|
||||||
property int maxHistory: 100
|
property int maxHistory: 100
|
||||||
property string historyFile: Quickshell.env("NOCTALIA_NOTIF_HISTORY_FILE") || (Settings.cacheDir + "notifications.json")
|
property string historyFile: Quickshell.env("NOCTALIA_NOTIF_HISTORY_FILE") || (Settings.cacheDir + "notifications.json")
|
||||||
property string stateFile: Settings.cacheDir + "notifications-state.json"
|
|
||||||
|
|
||||||
// State
|
// State
|
||||||
property real lastSeenTs: 0
|
property real lastSeenTs: 0
|
||||||
@@ -138,6 +137,22 @@ Singleton {
|
|||||||
if (Settings.isLoaded) {
|
if (Settings.isLoaded) {
|
||||||
updateNotificationServer();
|
updateNotificationServer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Load state from ShellState
|
||||||
|
Qt.callLater(() => {
|
||||||
|
if (typeof ShellState !== 'undefined' && ShellState.isLoaded) {
|
||||||
|
loadState();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: typeof ShellState !== 'undefined' ? ShellState : null
|
||||||
|
function onIsLoadedChanged() {
|
||||||
|
if (ShellState.isLoaded) {
|
||||||
|
loadState();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
@@ -471,23 +486,6 @@ Singleton {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Persistence - State
|
|
||||||
FileView {
|
|
||||||
id: stateFileView
|
|
||||||
path: stateFile
|
|
||||||
printErrors: false
|
|
||||||
onLoaded: loadState()
|
|
||||||
onLoadFailed: error => {
|
|
||||||
if (error === 2)
|
|
||||||
writeAdapter();
|
|
||||||
}
|
|
||||||
|
|
||||||
JsonAdapter {
|
|
||||||
id: stateAdapter
|
|
||||||
property real lastSeenTs: 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Timer {
|
Timer {
|
||||||
id: saveTimer
|
id: saveTimer
|
||||||
interval: 200
|
interval: 200
|
||||||
@@ -546,13 +544,11 @@ Singleton {
|
|||||||
|
|
||||||
function loadState() {
|
function loadState() {
|
||||||
try {
|
try {
|
||||||
root.lastSeenTs = stateAdapter.lastSeenTs || 0;
|
const notifState = ShellState.getNotificationsState();
|
||||||
|
root.lastSeenTs = notifState.lastSeenTs || 0;
|
||||||
|
|
||||||
if (root.lastSeenTs === 0 && Settings.data.notifications && Settings.data.notifications.lastSeenTs) {
|
// Migration is now handled in Settings.qml
|
||||||
root.lastSeenTs = Settings.data.notifications.lastSeenTs;
|
Logger.d("Notifications", "Loaded state from ShellState");
|
||||||
saveState();
|
|
||||||
Logger.i("Notifications", "Migrated lastSeenTs from settings to state file");
|
|
||||||
}
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
Logger.e("Notifications", "Load state failed:", e);
|
Logger.e("Notifications", "Load state failed:", e);
|
||||||
}
|
}
|
||||||
@@ -560,8 +556,10 @@ Singleton {
|
|||||||
|
|
||||||
function saveState() {
|
function saveState() {
|
||||||
try {
|
try {
|
||||||
stateAdapter.lastSeenTs = root.lastSeenTs;
|
ShellState.setNotificationsState({
|
||||||
stateFileView.writeAdapter();
|
lastSeenTs: root.lastSeenTs
|
||||||
|
});
|
||||||
|
Logger.d("Notifications", "Saved state to ShellState");
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
Logger.e("Notifications", "Save state failed:", e);
|
Logger.e("Notifications", "Save state failed:", e);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ Singleton {
|
|||||||
property bool gnomeCalendarAvailable: false
|
property bool gnomeCalendarAvailable: false
|
||||||
property bool spicetifyAvailable: false
|
property bool spicetifyAvailable: false
|
||||||
property bool telegramAvailable: false
|
property bool telegramAvailable: false
|
||||||
|
property bool cavaAvailable: false
|
||||||
|
|
||||||
// Discord client auto-detection
|
// Discord client auto-detection
|
||||||
property var availableDiscordClients: []
|
property var availableDiscordClients: []
|
||||||
@@ -183,7 +184,8 @@ Singleton {
|
|||||||
"codeAvailable": ["which", "code"],
|
"codeAvailable": ["which", "code"],
|
||||||
"gnomeCalendarAvailable": ["which", "gnome-calendar"],
|
"gnomeCalendarAvailable": ["which", "gnome-calendar"],
|
||||||
"spicetifyAvailable": ["which", "spicetify"],
|
"spicetifyAvailable": ["which", "spicetify"],
|
||||||
"telegramAvailable": ["which", "telegram-desktop"]
|
"telegramAvailable": ["which", "telegram-desktop"],
|
||||||
|
"cavaAvailable": ["which","cava"]
|
||||||
})
|
})
|
||||||
|
|
||||||
// Internal tracking
|
// Internal tracking
|
||||||
|
|||||||
@@ -213,6 +213,7 @@ Singleton {
|
|||||||
FileView {
|
FileView {
|
||||||
id: colorsWriter
|
id: colorsWriter
|
||||||
path: colorsJsonFilePath
|
path: colorsJsonFilePath
|
||||||
|
printErrors: false
|
||||||
onSaved:
|
onSaved:
|
||||||
|
|
||||||
// Logger.i("ColorScheme", "Colors saved")
|
// Logger.i("ColorScheme", "Colors saved")
|
||||||
|
|||||||
@@ -219,6 +219,18 @@ Singleton {
|
|||||||
"path": "~/.config/telegram-desktop/themes/noctalia.tdesktop-theme"
|
"path": "~/.config/telegram-desktop/themes/noctalia.tdesktop-theme"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cava",
|
||||||
|
"name": "Cava",
|
||||||
|
"category": "applications",
|
||||||
|
"input": "cava.ini",
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"path": "~/.config/cava/themes/noctalia"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"postProcess": () => `${colorsApplyScript} cava`
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -161,6 +161,7 @@ Singleton {
|
|||||||
"showAlbumArt": false,
|
"showAlbumArt": false,
|
||||||
"showArtistFirst": true,
|
"showArtistFirst": true,
|
||||||
"showVisualizer": false,
|
"showVisualizer": false,
|
||||||
|
"showProgressRing": true,
|
||||||
"visualizerType": "linear"
|
"visualizerType": "linear"
|
||||||
},
|
},
|
||||||
"Microphone": {
|
"Microphone": {
|
||||||
@@ -223,6 +224,7 @@ Singleton {
|
|||||||
"Workspace": {
|
"Workspace": {
|
||||||
"allowUserSettings": true,
|
"allowUserSettings": true,
|
||||||
"labelMode": "index",
|
"labelMode": "index",
|
||||||
|
"followFocusedScreen": false,
|
||||||
"hideUnoccupied": false,
|
"hideUnoccupied": false,
|
||||||
"characterCount": 2
|
"characterCount": 2
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -83,18 +83,38 @@ Singleton {
|
|||||||
|
|
||||||
translateModels();
|
translateModels();
|
||||||
|
|
||||||
// Rebuild cache from settings
|
// Load wallpapers from ShellState first (faster), then fall back to Settings
|
||||||
currentWallpapers = ({});
|
currentWallpapers = ({});
|
||||||
|
|
||||||
|
if (typeof ShellState !== 'undefined' && ShellState.isLoaded) {
|
||||||
|
var cachedWallpapers = ShellState.getWallpapers();
|
||||||
|
if (cachedWallpapers && Object.keys(cachedWallpapers).length > 0) {
|
||||||
|
currentWallpapers = cachedWallpapers;
|
||||||
|
Logger.d("Wallpaper", "Loaded wallpapers from ShellState");
|
||||||
|
} else {
|
||||||
|
// Fall back to Settings if ShellState is empty
|
||||||
|
loadFromSettings();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// ShellState not ready yet, load from Settings
|
||||||
|
loadFromSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
isInitialized = true;
|
||||||
|
Logger.d("Wallpaper", "Triggering initial wallpaper scan");
|
||||||
|
Qt.callLater(refreshWallpapersList);
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadFromSettings() {
|
||||||
var monitors = Settings.data.wallpaper.monitors || [];
|
var monitors = Settings.data.wallpaper.monitors || [];
|
||||||
for (var i = 0; i < monitors.length; i++) {
|
for (var i = 0; i < monitors.length; i++) {
|
||||||
if (monitors[i].name && monitors[i].wallpaper) {
|
if (monitors[i].name && monitors[i].wallpaper) {
|
||||||
currentWallpapers[monitors[i].name] = monitors[i].wallpaper;
|
currentWallpapers[monitors[i].name] = monitors[i].wallpaper;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Logger.d("Wallpaper", "Loaded wallpapers from Settings");
|
||||||
|
|
||||||
isInitialized = true;
|
// Migration is now handled in Settings.qml
|
||||||
Logger.d("Wallpaper", "Triggering initial wallpaper scan");
|
|
||||||
Qt.callLater(refreshWallpapersList);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------------------------------------------------
|
// -------------------------------------------------
|
||||||
@@ -270,32 +290,10 @@ Singleton {
|
|||||||
// Update cache directly
|
// Update cache directly
|
||||||
currentWallpapers[screenName] = path;
|
currentWallpapers[screenName] = path;
|
||||||
|
|
||||||
// Update Settings - still need immutable update for Settings persistence
|
// Save to ShellState (wallpaper paths now only stored here, not in Settings)
|
||||||
// The slice() ensures Settings detects the change and saves properly
|
if (typeof ShellState !== 'undefined' && ShellState.isLoaded) {
|
||||||
var monitors = Settings.data.wallpaper.monitors || [];
|
ShellState.setWallpapers(currentWallpapers);
|
||||||
var found = false;
|
|
||||||
|
|
||||||
var newMonitors = monitors.map(function (monitor) {
|
|
||||||
if (monitor.name === screenName) {
|
|
||||||
found = true;
|
|
||||||
return {
|
|
||||||
"name": screenName,
|
|
||||||
"directory": Settings.preprocessPath(monitor.directory) || getMonitorDirectory(screenName),
|
|
||||||
"wallpaper": path
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
return monitor;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!found) {
|
|
||||||
newMonitors.push({
|
|
||||||
"name": screenName,
|
|
||||||
"directory": getMonitorDirectory(screenName),
|
|
||||||
"wallpaper": path
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
Settings.data.wallpaper.monitors = newMonitors.slice();
|
|
||||||
|
|
||||||
// Emit signal for this specific wallpaper change
|
// Emit signal for this specific wallpaper change
|
||||||
root.wallpaperChanged(screenName, path);
|
root.wallpaperChanged(screenName, path);
|
||||||
|
|||||||
59
Shaders/frag/color_picker.frag
Normal file
59
Shaders/frag/color_picker.frag
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
#version 450
|
||||||
|
|
||||||
|
layout(location = 0) in vec2 qt_TexCoord0;
|
||||||
|
layout(location = 0) out vec4 fragColor;
|
||||||
|
|
||||||
|
layout(std140, binding = 0) uniform buf
|
||||||
|
{
|
||||||
|
mat4 qt_Matrix;
|
||||||
|
vec4 params; // x=opacity, y=fixedVal, z=mode, w=padding
|
||||||
|
};
|
||||||
|
|
||||||
|
// Compatibility function for GLSL ES to avoid % operator issues
|
||||||
|
int mod_compat(float a, float b) {
|
||||||
|
return int(a - floor(a / b) * b);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 hsv2rgb(vec3 c)
|
||||||
|
{
|
||||||
|
vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
|
||||||
|
vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
|
||||||
|
return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
float x = qt_TexCoord0.x;
|
||||||
|
float y = 1.0 - qt_TexCoord0.y; // Flip Y direction
|
||||||
|
|
||||||
|
// Unpack vector
|
||||||
|
float opacity = params.x;
|
||||||
|
float fixedVal = params.y;
|
||||||
|
float mode = params.z + 0.1; // +0.1 safely handles float rounding
|
||||||
|
|
||||||
|
// Build permutation matrices for swizzling
|
||||||
|
vec3 base = vec3(x, y, fixedVal);
|
||||||
|
|
||||||
|
// Determine Mode Logic
|
||||||
|
// 0: (Red/Hue) 1: (Green/Sat) 2: (Blue/Val)
|
||||||
|
int perm = mod_compat(mode, 3.0);
|
||||||
|
|
||||||
|
float isHSV = step(3.0, mode);
|
||||||
|
|
||||||
|
// Branchless Selection
|
||||||
|
vec3 mask = vec3(
|
||||||
|
float(perm == 0),
|
||||||
|
float(perm == 1),
|
||||||
|
float(perm == 2));
|
||||||
|
|
||||||
|
// Swizzle fo shizzle
|
||||||
|
// If perm 0: base.zxy -> (fixedVal, x, y)
|
||||||
|
// If perm 1: base.xzy -> (x, fixedVal, y)
|
||||||
|
// If perm 2: base.xyz -> (x, y, fixedVal)
|
||||||
|
vec3 rgb_base = (base.zxy * mask.x) + (base.xzy * mask.y) + (base.xyz * mask.z);
|
||||||
|
|
||||||
|
// Final mix
|
||||||
|
vec3 finalColor = mix(rgb_base, hsv2rgb(rgb_base), isHSV);
|
||||||
|
|
||||||
|
fragColor = vec4(finalColor, 1.0) * opacity;
|
||||||
|
}
|
||||||
BIN
Shaders/qsb/color_picker.frag.qsb
Normal file
BIN
Shaders/qsb/color_picker.frag.qsb
Normal file
Binary file not shown.
@@ -1,16 +1,35 @@
|
|||||||
|
import QtQml
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
import "../Helpers/ColorsConvert.js" as ColorsConvert
|
import "../Helpers/ColorList.js" as ColorList
|
||||||
import qs.Commons
|
import qs.Commons
|
||||||
|
import qs.Services.UI
|
||||||
import qs.Widgets
|
import qs.Widgets
|
||||||
|
|
||||||
Popup {
|
Popup {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property color selectedColor: Color.black
|
property color selectedColor: Color.black
|
||||||
property real currentHue: 0
|
|
||||||
property real currentSaturation: 0
|
enum EditMode {
|
||||||
|
R,
|
||||||
|
G,
|
||||||
|
B,
|
||||||
|
H,
|
||||||
|
S,
|
||||||
|
V
|
||||||
|
}
|
||||||
|
property int editMode: NColorPickerDialog.EditMode.R
|
||||||
|
|
||||||
|
// Code to deal with Hue when color is achromatic
|
||||||
|
property real stableHue: 0
|
||||||
|
onSelectedColorChanged: {
|
||||||
|
if (selectedColor.hsvHue >= 0) {
|
||||||
|
stableHue = selectedColor.hsvHue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
readonly property real displayHue: selectedColor.hsvHue < 0 ? stableHue : selectedColor.hsvHue
|
||||||
|
|
||||||
signal colorSelected(color color)
|
signal colorSelected(color color)
|
||||||
|
|
||||||
@@ -116,21 +135,249 @@ Popup {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hex input
|
// Main Box
|
||||||
|
NBox {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: controlsOutterColumn.implicitHeight + Style.marginL * 2
|
||||||
|
|
||||||
|
ButtonGroup {
|
||||||
|
id: colorValues
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: controlsOutterColumn
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: Style.marginL
|
||||||
|
spacing: Style.marginM
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
spacing: Style.marginL // Ensure nice gap between Left and Right groups
|
||||||
|
|
||||||
|
// SpinBoxes Column
|
||||||
|
ColumnLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.alignment: Qt.AlignTop
|
||||||
|
Layout.preferredWidth: 3
|
||||||
|
|
||||||
|
// --- RED ---
|
||||||
RowLayout {
|
RowLayout {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
spacing: Style.marginM
|
spacing: Style.marginM
|
||||||
|
|
||||||
NLabel {
|
NRadioButton {
|
||||||
label: I18n.tr("widgets.color-picker.hex.label")
|
ButtonGroup.group: colorValues
|
||||||
description: I18n.tr("widgets.color-picker.hex.description")
|
text: "R"
|
||||||
|
font.weight: Font.Bold
|
||||||
|
checked: true
|
||||||
|
onClicked: root.editMode = NColorPickerDialog.EditMode.R
|
||||||
|
Layout.fillWidth: false
|
||||||
|
}
|
||||||
|
|
||||||
|
NSpinBox {
|
||||||
|
id: redSpinBox
|
||||||
|
from: 0
|
||||||
|
to: 255
|
||||||
|
|
||||||
|
onValueChanged: {
|
||||||
|
if (!selectedSlider.pressed && !fieldMouse.pressed && !hexInput.activeFocus && value !== Math.round(root.selectedColor.r * 255)) {
|
||||||
|
root.selectedColor = Qt.rgba(value / 255, root.selectedColor.g, root.selectedColor.b, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Binding {
|
||||||
|
target: redSpinBox
|
||||||
|
property: "value"
|
||||||
|
value: Math.round(root.selectedColor.r * 255)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- GREEN ---
|
||||||
|
RowLayout {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
spacing: Style.marginM
|
||||||
|
|
||||||
|
NRadioButton {
|
||||||
|
ButtonGroup.group: colorValues
|
||||||
|
text: "G"
|
||||||
|
font.weight: Font.Bold
|
||||||
|
onClicked: root.editMode = NColorPickerDialog.EditMode.G
|
||||||
|
Layout.fillWidth: false
|
||||||
|
}
|
||||||
|
|
||||||
|
NSpinBox {
|
||||||
|
id: greenSpinBox
|
||||||
|
from: 0
|
||||||
|
to: 255
|
||||||
|
|
||||||
|
onValueChanged: {
|
||||||
|
if (!selectedSlider.pressed && !fieldMouse.pressed && !hexInput.activeFocus && value !== Math.round(root.selectedColor.g * 255)) {
|
||||||
|
root.selectedColor = Qt.rgba(root.selectedColor.r, value / 255, root.selectedColor.b, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Binding {
|
||||||
|
target: greenSpinBox
|
||||||
|
property: "value"
|
||||||
|
value: Math.round(root.selectedColor.g * 255)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- BLUE ---
|
||||||
|
RowLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
spacing: Style.marginM
|
||||||
|
|
||||||
|
NRadioButton {
|
||||||
|
ButtonGroup.group: colorValues
|
||||||
|
text: "B"
|
||||||
|
font.weight: Font.Bold
|
||||||
|
onClicked: root.editMode = NColorPickerDialog.EditMode.B
|
||||||
|
Layout.fillWidth: false
|
||||||
|
}
|
||||||
|
|
||||||
|
NSpinBox {
|
||||||
|
id: blueSpinBox
|
||||||
|
from: 0
|
||||||
|
to: 255
|
||||||
|
|
||||||
|
onValueChanged: {
|
||||||
|
if (!selectedSlider.pressed && !fieldMouse.pressed && !hexInput.activeFocus && value !== Math.round(root.selectedColor.b * 255)) {
|
||||||
|
root.selectedColor = Qt.rgba(root.selectedColor.r, root.selectedColor.g, value / 255, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Binding {
|
||||||
|
target: blueSpinBox
|
||||||
|
property: "value"
|
||||||
|
value: Math.round(root.selectedColor.b * 255)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Spacer
|
||||||
|
Item {
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- HUE ---
|
||||||
|
RowLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
spacing: Style.marginM
|
||||||
|
|
||||||
|
NRadioButton {
|
||||||
|
ButtonGroup.group: colorValues
|
||||||
|
text: "H"
|
||||||
|
font.weight: Font.Bold
|
||||||
|
checked: true
|
||||||
|
onClicked: root.editMode = NColorPickerDialog.EditMode.H
|
||||||
|
Layout.fillWidth: false
|
||||||
|
}
|
||||||
|
|
||||||
|
NSpinBox {
|
||||||
|
id: hueSpinBox
|
||||||
|
from: 0
|
||||||
|
to: 360
|
||||||
|
|
||||||
|
onValueChanged: {
|
||||||
|
if (!selectedSlider.pressed && !fieldMouse.pressed && !hexInput.activeFocus && value !== Math.round(root.displayHue * 360)) {
|
||||||
|
var newHue = value / 360;
|
||||||
|
root.selectedColor = Qt.hsva(newHue, root.selectedColor.hsvSaturation, root.selectedColor.hsvValue, 1);
|
||||||
|
root.stableHue = newHue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Binding {
|
||||||
|
target: hueSpinBox
|
||||||
|
property: "value"
|
||||||
|
value: Math.round(root.displayHue * 360)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- SATURATION ---
|
||||||
|
RowLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
spacing: Style.marginM
|
||||||
|
|
||||||
|
NRadioButton {
|
||||||
|
ButtonGroup.group: colorValues
|
||||||
|
text: "S"
|
||||||
|
font.weight: Font.Bold
|
||||||
|
onClicked: root.editMode = NColorPickerDialog.EditMode.S
|
||||||
|
Layout.fillWidth: false
|
||||||
|
}
|
||||||
|
|
||||||
|
NSpinBox {
|
||||||
|
id: satSpinBox
|
||||||
|
from: 0
|
||||||
|
to: 100
|
||||||
|
|
||||||
|
onValueChanged: {
|
||||||
|
if (!selectedSlider.pressed && !fieldMouse.pressed && !hexInput.activeFocus && value !== Math.round(root.selectedColor.hsvSaturation * 100)) {
|
||||||
|
root.selectedColor = Qt.hsva(root.selectedColor.hsvHue, value / 100, root.selectedColor.hsvValue, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Binding {
|
||||||
|
target: satSpinBox
|
||||||
|
property: "value"
|
||||||
|
value: Math.round(root.selectedColor.hsvSaturation * 100)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- VALUE ---
|
||||||
|
RowLayout {
|
||||||
|
spacing: Style.marginM
|
||||||
|
|
||||||
|
NRadioButton {
|
||||||
|
ButtonGroup.group: colorValues
|
||||||
|
text: "V"
|
||||||
|
font.weight: Font.Bold
|
||||||
|
onClicked: root.editMode = NColorPickerDialog.EditMode.V
|
||||||
|
Layout.fillWidth: false
|
||||||
|
}
|
||||||
|
|
||||||
|
NSpinBox {
|
||||||
|
id: valSpinBox
|
||||||
|
from: 0
|
||||||
|
to: 100
|
||||||
|
|
||||||
|
onValueChanged: {
|
||||||
|
if (!selectedSlider.pressed && !fieldMouse.pressed && !hexInput.activeFocus && value !== Math.round(root.selectedColor.hsvValue * 100)) {
|
||||||
|
root.selectedColor = Qt.hsva(root.selectedColor.hsvHue, root.selectedColor.hsvSaturation, value / 100, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Binding {
|
||||||
|
target: valSpinBox
|
||||||
|
property: "value"
|
||||||
|
value: Math.round(root.selectedColor.hsvValue * 100)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Spacer
|
||||||
|
Item {
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hex input
|
||||||
|
RowLayout {
|
||||||
|
id: hexInput
|
||||||
|
Layout.fillWidth: true
|
||||||
|
spacing: Style.marginM
|
||||||
|
|
||||||
|
NLabel {
|
||||||
|
label: "Hex:"
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
NTextInput {
|
NTextInput {
|
||||||
text: root.selectedColor.toString().toUpperCase()
|
text: root.selectedColor.toString().toUpperCase()
|
||||||
fontFamily: Settings.data.ui.fontFixed
|
fontFamily: Settings.data.ui.fontFixed
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
Layout.minimumWidth: implicitWidth
|
||||||
onEditingFinished: {
|
onEditingFinished: {
|
||||||
if (/^#[0-9A-F]{6}$/i.test(text)) {
|
if (/^#[0-9A-F]{6}$/i.test(text)) {
|
||||||
root.selectedColor = text;
|
root.selectedColor = text;
|
||||||
@@ -138,234 +385,339 @@ Popup {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// RGB sliders section
|
|
||||||
NBox {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.preferredHeight: slidersSection.implicitHeight + Style.marginL * 2
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
id: slidersSection
|
|
||||||
anchors.fill: parent
|
|
||||||
anchors.margins: Style.marginL
|
|
||||||
spacing: Style.marginM
|
|
||||||
|
|
||||||
NLabel {
|
|
||||||
label: I18n.tr("widgets.color-picker.rgb.label")
|
|
||||||
description: I18n.tr("widgets.color-picker.rgb.description")
|
|
||||||
Layout.fillWidth: true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
Layout.fillWidth: true
|
|
||||||
spacing: Style.marginM
|
|
||||||
|
|
||||||
NText {
|
|
||||||
text: "R"
|
|
||||||
font.weight: Font.Bold
|
|
||||||
Layout.preferredWidth: 20
|
|
||||||
}
|
|
||||||
|
|
||||||
NValueSlider {
|
|
||||||
id: redSlider
|
|
||||||
Layout.fillWidth: true
|
|
||||||
from: 0
|
|
||||||
to: 255
|
|
||||||
value: Math.round(root.selectedColor.r * 255)
|
|
||||||
onMoved: value => {
|
|
||||||
root.selectedColor = Qt.rgba(value / 255, root.selectedColor.g, root.selectedColor.b, 1);
|
|
||||||
var hsv = ColorsConvert.rgbToHsv(root.selectedColor.r * 255, root.selectedColor.g * 255, root.selectedColor.b * 255);
|
|
||||||
root.currentHue = hsv.h;
|
|
||||||
root.currentSaturation = hsv.s;
|
|
||||||
}
|
|
||||||
text: Math.round(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
spacing: Style.marginM
|
|
||||||
|
|
||||||
NText {
|
|
||||||
text: "G"
|
|
||||||
font.weight: Font.Bold
|
|
||||||
Layout.preferredWidth: 20
|
|
||||||
}
|
|
||||||
|
|
||||||
NValueSlider {
|
|
||||||
id: greenSlider
|
|
||||||
Layout.fillWidth: true
|
|
||||||
from: 0
|
|
||||||
to: 255
|
|
||||||
value: Math.round(root.selectedColor.g * 255)
|
|
||||||
onMoved: value => {
|
|
||||||
root.selectedColor = Qt.rgba(root.selectedColor.r, value / 255, root.selectedColor.b, 1);
|
|
||||||
// Update stored hue and saturation when RGB changes
|
|
||||||
var hsv = ColorsConvert.rgbToHsv(root.selectedColor.r * 255, root.selectedColor.g * 255, root.selectedColor.b * 255);
|
|
||||||
root.currentHue = hsv.h;
|
|
||||||
root.currentSaturation = hsv.s;
|
|
||||||
}
|
|
||||||
text: Math.round(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
spacing: Style.marginM
|
|
||||||
|
|
||||||
NText {
|
|
||||||
text: "B"
|
|
||||||
font.weight: Font.Bold
|
|
||||||
Layout.preferredWidth: 20
|
|
||||||
}
|
|
||||||
|
|
||||||
NValueSlider {
|
|
||||||
id: blueSlider
|
|
||||||
Layout.fillWidth: true
|
|
||||||
from: 0
|
|
||||||
to: 255
|
|
||||||
value: Math.round(root.selectedColor.b * 255)
|
|
||||||
onMoved: value => {
|
|
||||||
root.selectedColor = Qt.rgba(root.selectedColor.r, root.selectedColor.g, value / 255, 1);
|
|
||||||
// Update stored hue and saturation when RGB changes
|
|
||||||
var hsv = ColorsConvert.rgbToHsv(root.selectedColor.r * 255, root.selectedColor.g * 255, root.selectedColor.b * 255);
|
|
||||||
root.currentHue = hsv.h;
|
|
||||||
root.currentSaturation = hsv.s;
|
|
||||||
}
|
|
||||||
text: Math.round(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
spacing: Style.marginM
|
|
||||||
|
|
||||||
NText {
|
|
||||||
text: I18n.tr("widgets.color-picker.brightness")
|
|
||||||
font.weight: Font.Bold
|
|
||||||
Layout.preferredWidth: 80
|
|
||||||
}
|
|
||||||
|
|
||||||
NValueSlider {
|
|
||||||
id: brightnessSlider
|
|
||||||
Layout.fillWidth: true
|
|
||||||
from: 0
|
|
||||||
to: 100
|
|
||||||
value: {
|
|
||||||
var hsv = ColorsConvert.rgbToHsv(root.selectedColor.r * 255, root.selectedColor.g * 255, root.selectedColor.b * 255);
|
|
||||||
return hsv.v;
|
|
||||||
}
|
|
||||||
onMoved: value => {
|
|
||||||
var hue = root.currentHue;
|
|
||||||
var saturation = root.currentSaturation;
|
|
||||||
|
|
||||||
if (hue === 0 && saturation === 0) {
|
|
||||||
var hsv = ColorsConvert.rgbToHsv(root.selectedColor.r * 255, root.selectedColor.g * 255, root.selectedColor.b * 255);
|
|
||||||
hue = hsv.h;
|
|
||||||
saturation = hsv.s;
|
|
||||||
root.currentHue = hue;
|
|
||||||
root.currentSaturation = saturation;
|
|
||||||
}
|
|
||||||
|
|
||||||
var rgb = ColorsConvert.hsvToRgb(hue, saturation, value);
|
|
||||||
root.selectedColor = Qt.rgba(rgb.r / 255, rgb.g / 255, rgb.b / 255, 1);
|
|
||||||
}
|
|
||||||
text: Math.round(brightnessSlider.value) + "%"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
NBox {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.preferredHeight: themePalette.implicitHeight + Style.marginL * 2
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
id: themePalette
|
|
||||||
anchors.fill: parent
|
|
||||||
anchors.margins: Style.marginL
|
|
||||||
spacing: Style.marginS
|
|
||||||
|
|
||||||
NLabel {
|
|
||||||
label: I18n.tr("widgets.color-picker.theme-colors.label")
|
|
||||||
description: I18n.tr("widgets.color-picker.theme-colors.description")
|
|
||||||
Layout.fillWidth: true
|
|
||||||
}
|
|
||||||
|
|
||||||
Flow {
|
|
||||||
spacing: 6
|
|
||||||
Layout.fillWidth: true
|
|
||||||
flow: Flow.LeftToRight
|
|
||||||
|
|
||||||
Repeater {
|
|
||||||
model: [Color.mPrimary, Color.mSecondary, Color.mTertiary, Color.mError, Color.mSurface, Color.mSurfaceVariant, Color.mOutline, Color.white, Color.black]
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
width: 24
|
|
||||||
height: 24
|
|
||||||
radius: 4
|
|
||||||
color: modelData
|
|
||||||
border.color: root.selectedColor === modelData ? Color.mPrimary : Color.mOutline
|
|
||||||
border.width: root.selectedColor === modelData ? 2 : 1
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
anchors.fill: parent
|
|
||||||
cursorShape: Qt.PointingHandCursor
|
|
||||||
onClicked: {
|
|
||||||
root.selectedColor = modelData;
|
|
||||||
var hsv = ColorsConvert.rgbToHsv(root.selectedColor.r * 255, root.selectedColor.g * 255, root.selectedColor.b * 255);
|
|
||||||
root.currentHue = hsv.h;
|
|
||||||
root.currentSaturation = hsv.s;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
NBox {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.preferredHeight: genericPalette.implicitHeight + Style.marginL * 2
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
id: genericPalette
|
|
||||||
anchors.fill: parent
|
|
||||||
anchors.margins: Style.marginL
|
|
||||||
spacing: Style.marginS
|
|
||||||
|
|
||||||
NLabel {
|
|
||||||
label: I18n.tr("widgets.color-picker.palette.label")
|
|
||||||
description: I18n.tr("widgets.color-picker.palette.description")
|
|
||||||
Layout.fillWidth: true
|
|
||||||
}
|
|
||||||
|
|
||||||
Flow {
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
spacing: 6
|
Layout.preferredWidth: 5
|
||||||
flow: Flow.LeftToRight
|
Layout.alignment: Qt.AlignTop
|
||||||
|
spacing: Style.marginS
|
||||||
|
|
||||||
|
Layout.topMargin: Math.round(2 * Style.uiScaleRatio) //Shim to try and line up colorField with SpinBoxes
|
||||||
|
|
||||||
|
// --- SLIDER ---
|
||||||
|
NColorSlider {
|
||||||
|
id: selectedSlider
|
||||||
|
Layout.fillHeight: true
|
||||||
|
rainbowMode: root.editMode === "h"
|
||||||
|
topColor: {
|
||||||
|
if (rainbowMode)
|
||||||
|
return "transparent";
|
||||||
|
switch (root.editMode) {
|
||||||
|
case NColorPickerDialog.EditMode.R:
|
||||||
|
return "#FF0000";
|
||||||
|
case NColorPickerDialog.EditMode.G:
|
||||||
|
return "#00FF00";
|
||||||
|
case NColorPickerDialog.EditMode.B:
|
||||||
|
return "#0000FF";
|
||||||
|
default:
|
||||||
|
return "#FFFFFF";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Binding {
|
||||||
|
target: selectedSlider
|
||||||
|
property: "value"
|
||||||
|
when: !selectedSlider.pressed // Only update from model when NOT dragging
|
||||||
|
value: {
|
||||||
|
switch (root.editMode) {
|
||||||
|
case NColorPickerDialog.EditMode.R:
|
||||||
|
return root.selectedColor.r;
|
||||||
|
case NColorPickerDialog.EditMode.G:
|
||||||
|
return root.selectedColor.g;
|
||||||
|
case NColorPickerDialog.EditMode.B:
|
||||||
|
return root.selectedColor.b;
|
||||||
|
case NColorPickerDialog.EditMode.H:
|
||||||
|
return root.displayHue;
|
||||||
|
case NColorPickerDialog.EditMode.S:
|
||||||
|
return root.selectedColor.hsvSaturation;
|
||||||
|
case NColorPickerDialog.EditMode.V:
|
||||||
|
return root.selectedColor.hsvValue;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMoved: {
|
||||||
|
var v = value;
|
||||||
|
switch (root.editMode) {
|
||||||
|
case NColorPickerDialog.EditMode.R:
|
||||||
|
root.selectedColor = Qt.rgba(v, root.selectedColor.g, root.selectedColor.b, 1);
|
||||||
|
break;
|
||||||
|
case NColorPickerDialog.EditMode.G:
|
||||||
|
root.selectedColor = Qt.rgba(root.selectedColor.r, v, root.selectedColor.b, 1);
|
||||||
|
break;
|
||||||
|
case NColorPickerDialog.EditMode.B:
|
||||||
|
root.selectedColor = Qt.rgba(root.selectedColor.r, root.selectedColor.g, v, 1);
|
||||||
|
break;
|
||||||
|
case NColorPickerDialog.EditMode.H:
|
||||||
|
root.selectedColor = Qt.hsva(v, root.selectedColor.hsvSaturation, root.selectedColor.hsvValue, 1);
|
||||||
|
root.stableHue = v;
|
||||||
|
break;
|
||||||
|
case NColorPickerDialog.EditMode.S:
|
||||||
|
root.selectedColor = Qt.hsva(root.selectedColor.hsvHue, v, root.selectedColor.hsvValue, 1);
|
||||||
|
break;
|
||||||
|
case NColorPickerDialog.EditMode.V:
|
||||||
|
root.selectedColor = Qt.hsva(root.selectedColor.hsvHue, root.selectedColor.hsvSaturation, v, 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Color Field
|
||||||
|
Rectangle {
|
||||||
|
id: colorField
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
Layout.preferredHeight: width
|
||||||
|
Layout.alignment: Qt.AlignTop
|
||||||
|
|
||||||
|
radius: 0
|
||||||
|
border.color: Color.mOutline
|
||||||
|
border.width: Style.borderS
|
||||||
|
clip: true
|
||||||
|
|
||||||
|
ShaderEffect {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: 1 // Avoid drawing over the border
|
||||||
|
|
||||||
|
fragmentShader: "../Shaders/qsb/color_picker.frag.qsb"
|
||||||
|
|
||||||
|
// Pass which radio is selected
|
||||||
|
readonly property real fixedVal: {
|
||||||
|
switch (root.editMode) {
|
||||||
|
case NColorPickerDialog.EditMode.R:
|
||||||
|
return root.selectedColor.r;
|
||||||
|
case NColorPickerDialog.EditMode.G:
|
||||||
|
return root.selectedColor.g;
|
||||||
|
case NColorPickerDialog.EditMode.B:
|
||||||
|
return root.selectedColor.b;
|
||||||
|
case NColorPickerDialog.EditMode.H:
|
||||||
|
return root.displayHue;
|
||||||
|
case NColorPickerDialog.EditMode.S:
|
||||||
|
return root.selectedColor.hsvSaturation;
|
||||||
|
case NColorPickerDialog.EditMode.V:
|
||||||
|
return root.selectedColor.hsvValue;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send as one vector because GPUs are so damn picky
|
||||||
|
property vector4d params: Qt.vector4d(1.0, fixedVal, root.editMode, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: fieldMouse
|
||||||
|
anchors.fill: parent
|
||||||
|
cursorShape: Qt.CrossCursor
|
||||||
|
|
||||||
|
// Update color when clicking or dragging
|
||||||
|
function updateColor(mouse) {
|
||||||
|
// Normalize X and Y (0.0 to 1.0)
|
||||||
|
var xVal = Math.max(0, Math.min(1, mouse.x / width));
|
||||||
|
var yVal = Math.max(0, Math.min(1, 1.0 - (mouse.y / height))); // Flip Y (0 at bottom)
|
||||||
|
|
||||||
|
// Get the current fixed value (from the slider)
|
||||||
|
var fixed = 0.0;
|
||||||
|
|
||||||
|
switch (root.editMode) {
|
||||||
|
case NColorPickerDialog.EditMode.R:
|
||||||
|
fixed = root.selectedColor.r;
|
||||||
|
root.selectedColor = Qt.rgba(fixed, xVal, yVal, 1);
|
||||||
|
break;
|
||||||
|
case NColorPickerDialog.EditMode.G:
|
||||||
|
fixed = root.selectedColor.g;
|
||||||
|
root.selectedColor = Qt.rgba(xVal, fixed, yVal, 1);
|
||||||
|
break;
|
||||||
|
case NColorPickerDialog.EditMode.B:
|
||||||
|
fixed = root.selectedColor.b;
|
||||||
|
root.selectedColor = Qt.rgba(xVal, yVal, fixed, 1);
|
||||||
|
break;
|
||||||
|
case NColorPickerDialog.EditMode.H:
|
||||||
|
// Use stableHue to prevent flipping to -1
|
||||||
|
fixed = root.displayHue;
|
||||||
|
root.selectedColor = Qt.hsva(fixed, xVal, yVal, 1);
|
||||||
|
root.stableHue = fixed;
|
||||||
|
break;
|
||||||
|
case NColorPickerDialog.EditMode.S:
|
||||||
|
fixed = root.selectedColor.hsvSaturation;
|
||||||
|
root.selectedColor = Qt.hsva(xVal, fixed, yVal, 1);
|
||||||
|
// If we dragged Hue (xVal), update stableHue
|
||||||
|
root.stableHue = yVal;
|
||||||
|
break;
|
||||||
|
case NColorPickerDialog.EditMode.V:
|
||||||
|
fixed = root.selectedColor.hsvValue;
|
||||||
|
root.selectedColor = Qt.hsva(xVal, yVal, fixed, 1);
|
||||||
|
// If we dragged Hue (xVal), update stableHue
|
||||||
|
root.stableHue = xVal;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onPressed: mouse => updateColor(mouse)
|
||||||
|
onPositionChanged: mouse => updateColor(mouse)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Color Indicator
|
||||||
|
Rectangle {
|
||||||
|
width: 10
|
||||||
|
height: 10
|
||||||
|
radius: 5
|
||||||
|
color: "transparent"
|
||||||
|
border.color: root.selectedColor.hsvValue < 0.5 ? "white" : "black"
|
||||||
|
border.width: 1
|
||||||
|
|
||||||
|
// Find position based on the current color
|
||||||
|
readonly property point selectedPos: {
|
||||||
|
switch (root.editMode) {
|
||||||
|
case NColorPickerDialog.EditMode.R:
|
||||||
|
return Qt.point(root.selectedColor.g, root.selectedColor.b);
|
||||||
|
case NColorPickerDialog.EditMode.G:
|
||||||
|
return Qt.point(root.selectedColor.r, root.selectedColor.b);
|
||||||
|
case NColorPickerDialog.EditMode.B:
|
||||||
|
return Qt.point(root.selectedColor.r, root.selectedColor.g);
|
||||||
|
case NColorPickerDialog.EditMode.H:
|
||||||
|
return Qt.point(root.selectedColor.hsvSaturation, root.selectedColor.hsvValue);
|
||||||
|
case NColorPickerDialog.EditMode.S:
|
||||||
|
return Qt.point(root.displayHue, root.selectedColor.hsvValue);
|
||||||
|
case NColorPickerDialog.EditMode.V:
|
||||||
|
return Qt.point(root.displayHue, root.selectedColor.hsvSaturation);
|
||||||
|
default:
|
||||||
|
return Qt.point(0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert values to pixel position
|
||||||
|
x: (selectedPos.x * parent.width) - width / 2
|
||||||
|
y: ((1.0 - selectedPos.y) * parent.height) - height / 2
|
||||||
|
}
|
||||||
|
|
||||||
|
// Redraw the border in case Color Indicator is near the edge
|
||||||
|
Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
color: "transparent"
|
||||||
|
border.color: Color.mOutline
|
||||||
|
border.width: Style.borderS
|
||||||
|
antialiasing: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NCollapsible {
|
||||||
|
id: paletteCollapsible
|
||||||
|
label: I18n.tr("widgets.color-picker.palette.label")
|
||||||
|
description: I18n.tr("widgets.color-picker.palette.description")
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
contentSpacing: Style.marginS
|
||||||
|
|
||||||
|
GridLayout {
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
columns: 15
|
||||||
|
columnSpacing: 6
|
||||||
|
rowSpacing: 6
|
||||||
|
|
||||||
Repeater {
|
Repeater {
|
||||||
model: ["#F44336", "#E91E63", "#9C27B0", "#673AB7", "#3F51B5", "#2196F3", "#03A9F4", "#00BCD4", "#009688", "#4CAF50", "#8BC34A", "#CDDC39", "#FFEB3B", "#FFC107", "#FF9800", "#FF5722", "#795548", "#9E9E9E", "#E74C3C", "#E67E22", "#F1C40F", "#2ECC71", "#1ABC9C", "#3498DB", "#2980B9", "#9B59B6", "#34495E", "#2C3E50", "#95A5A6", "#7F8C8D",
|
model: ColorList.colors
|
||||||
Color.white, Color.black]
|
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: 24
|
width: 24
|
||||||
height: 24
|
height: 24
|
||||||
radius: Style.radiusXXS
|
radius: Style.radiusXXS
|
||||||
color: modelData
|
color: modelData.color
|
||||||
border.color: root.selectedColor === modelData ? Color.mPrimary : Color.mOutline
|
border.color: root.selectedColor.toString() === modelData.color.toString() ? Color.mPrimary : Color.mOutline
|
||||||
border.width: Math.max(1, root.selectedColor === modelData ? Style.borderM : Style.borderS)
|
border.width: Math.max(1, root.selectedColor.toString() === modelData.color.toString() ? Style.borderM : Style.borderS)
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
hoverEnabled: true
|
||||||
|
|
||||||
|
onEntered: {
|
||||||
|
TooltipService.show(parent, modelData.name + "\n" + parent.color.toString().toUpperCase());
|
||||||
|
}
|
||||||
|
onExited: {
|
||||||
|
TooltipService.hide();
|
||||||
|
}
|
||||||
onClicked: {
|
onClicked: {
|
||||||
root.selectedColor = modelData;
|
root.selectedColor = modelData.color;
|
||||||
var hsv = ColorsConvert.rgbToHsv(root.selectedColor.r * 255, root.selectedColor.g * 255, root.selectedColor.b * 255);
|
TooltipService.hide();
|
||||||
root.currentHue = hsv.h;
|
}
|
||||||
root.currentSaturation = hsv.s;
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NDivider {
|
||||||
|
Layout.columnSpan: 15
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.topMargin: Style.marginXS
|
||||||
|
Layout.bottomMargin: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
NLabel {
|
||||||
|
Layout.columnSpan: 15
|
||||||
|
Layout.fillWidth: true
|
||||||
|
description: I18n.tr("widgets.color-picker.palette.theme-colors")
|
||||||
|
}
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: [
|
||||||
|
{
|
||||||
|
name: "mPrimary",
|
||||||
|
color: Color.mPrimary
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "mSecondary",
|
||||||
|
color: Color.mSecondary
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "mTertiary",
|
||||||
|
color: Color.mTertiary
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "mError",
|
||||||
|
color: Color.mError
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "mSurface",
|
||||||
|
color: Color.mSurface
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "mSurfaceVariant",
|
||||||
|
color: Color.mSurfaceVariant
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "mOutline",
|
||||||
|
color: Color.mOutline
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: 24
|
||||||
|
height: 24
|
||||||
|
radius: 4
|
||||||
|
color: modelData.color
|
||||||
|
border.color: root.selectedColor.toString() === modelData.color.toString() ? Color.mPrimary : Color.mOutline
|
||||||
|
border.width: root.selectedColor.toString() === modelData.color.toString() ? 2 : 1
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
hoverEnabled: true
|
||||||
|
|
||||||
|
onEntered: {
|
||||||
|
TooltipService.show(screen, parent, modelData.name + "\n" + parent.color.toString().toUpperCase(), "auto");
|
||||||
|
}
|
||||||
|
onExited: {
|
||||||
|
TooltipService.hide();
|
||||||
|
}
|
||||||
|
onClicked: {
|
||||||
|
root.selectedColor = modelData.color;
|
||||||
|
TooltipService.hide();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -376,8 +728,8 @@ Popup {
|
|||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.topMargin: 20
|
//Layout.topMargin: 20
|
||||||
Layout.bottomMargin: 20
|
//Layout.bottomMargin: 20
|
||||||
spacing: 10
|
spacing: 10
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
|
|||||||
177
Widgets/NColorSlider.qml
Normal file
177
Widgets/NColorSlider.qml
Normal file
@@ -0,0 +1,177 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Effects
|
||||||
|
import qs.Commons
|
||||||
|
import qs.Services.UI
|
||||||
|
|
||||||
|
Slider {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property color fillColor: "transparent"
|
||||||
|
property var cutoutColor: Color.mSurface
|
||||||
|
property bool snapAlways: true
|
||||||
|
property real widthRatio: 0.7
|
||||||
|
property string tooltipText
|
||||||
|
property string tooltipDirection: "auto"
|
||||||
|
property bool hovering: false
|
||||||
|
property color topColor: "white"
|
||||||
|
property color bottomColor: "black"
|
||||||
|
property bool rainbowMode: false
|
||||||
|
|
||||||
|
readonly property real knobDiameter: Math.round((Style.baseWidgetSize * widthRatio * Style.uiScaleRatio) / 2) * 2
|
||||||
|
readonly property real trackWidth: Math.round((knobDiameter * 0.4 * Style.uiScaleRatio) / 2) * 2
|
||||||
|
readonly property real cutoutExtra: Math.round((Style.baseWidgetSize * 0.1 * Style.uiScaleRatio) / 2) * 2
|
||||||
|
|
||||||
|
//horizontal: false
|
||||||
|
orientation: Qt.Vertical
|
||||||
|
|
||||||
|
padding: cutoutExtra / 2
|
||||||
|
|
||||||
|
snapMode: snapAlways ? Slider.SnapAlways : Slider.SnapOnRelease
|
||||||
|
implicitWidth: Math.max(trackWidth, knobDiameter)
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
||||||
|
x: root.leftPadding + root.availableWidth / 2 - width / 2
|
||||||
|
|
||||||
|
height: root.availableHeight - root.knobDiameter
|
||||||
|
width: root.trackWidth
|
||||||
|
|
||||||
|
radius: width / 2
|
||||||
|
color: Qt.alpha(Color.mSurface, 0.5)
|
||||||
|
border.color: Qt.alpha(Color.mOutline, 0.5)
|
||||||
|
border.width: Style.borderS
|
||||||
|
|
||||||
|
// Gradients
|
||||||
|
gradient: root.rainbowMode ? rainbowGradient : standardGradient
|
||||||
|
Gradient {
|
||||||
|
id: standardGradient
|
||||||
|
orientation: Gradient.Vertical
|
||||||
|
GradientStop {
|
||||||
|
position: 0.0
|
||||||
|
color: root.topColor
|
||||||
|
}
|
||||||
|
GradientStop {
|
||||||
|
position: 1.0
|
||||||
|
color: root.bottomColor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Gradient {
|
||||||
|
id: rainbowGradient
|
||||||
|
orientation: Gradient.Vertical
|
||||||
|
GradientStop {
|
||||||
|
position: 0.000
|
||||||
|
color: "#FF0000"
|
||||||
|
}
|
||||||
|
GradientStop {
|
||||||
|
position: 0.167
|
||||||
|
color: "#FF00FF"
|
||||||
|
}
|
||||||
|
GradientStop {
|
||||||
|
position: 0.333
|
||||||
|
color: "#0000FF"
|
||||||
|
}
|
||||||
|
GradientStop {
|
||||||
|
position: 0.500
|
||||||
|
color: "#00FFFF"
|
||||||
|
}
|
||||||
|
GradientStop {
|
||||||
|
position: 0.667
|
||||||
|
color: "#00FF00"
|
||||||
|
}
|
||||||
|
GradientStop {
|
||||||
|
position: 0.833
|
||||||
|
color: "#FFFF00"
|
||||||
|
}
|
||||||
|
GradientStop {
|
||||||
|
position: 1.000
|
||||||
|
color: "#FF0000"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Circular cutout
|
||||||
|
Rectangle {
|
||||||
|
id: knobCutout
|
||||||
|
implicitWidth: root.knobDiameter + root.cutoutExtra
|
||||||
|
implicitHeight: root.knobDiameter + root.cutoutExtra
|
||||||
|
radius: width / 2
|
||||||
|
color: root.cutoutColor !== undefined ? root.cutoutColor : Color.mSurface
|
||||||
|
|
||||||
|
y: root.visualPosition * (root.availableHeight - root.knobDiameter) - ((root.knobDiameter + root.cutoutExtra) / 2)
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handle: Item {
|
||||||
|
implicitWidth: root.knobDiameter
|
||||||
|
implicitHeight: root.knobDiameter
|
||||||
|
y: root.topPadding + root.visualPosition * (root.availableHeight - height)
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: knob
|
||||||
|
implicitWidth: root.knobDiameter
|
||||||
|
implicitHeight: root.knobDiameter
|
||||||
|
radius: width / 2
|
||||||
|
color: {
|
||||||
|
if (root.rainbowMode) {
|
||||||
|
// Hue Logic: Map position (0.0 to 1.0) directly to Hue
|
||||||
|
return Qt.hsva(1 - root.visualPosition, 1, 1, 1);
|
||||||
|
} else {
|
||||||
|
// Linear Interpolation for Standard Gradients
|
||||||
|
// visualPosition 0.0 = Top, 1.0 = Bottom
|
||||||
|
var t = root.visualPosition;
|
||||||
|
var r = root.topColor.r * (1 - t) + root.bottomColor.r * t;
|
||||||
|
var g = root.topColor.g * (1 - t) + root.bottomColor.g * t;
|
||||||
|
var b = root.topColor.b * (1 - t) + root.bottomColor.b * t;
|
||||||
|
return Qt.rgba(r, g, b, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
border.color: root.pressed ? Color.mHover : Color.mPrimary
|
||||||
|
border.width: Style.borderL
|
||||||
|
anchors.centerIn: parent
|
||||||
|
|
||||||
|
Behavior on color {
|
||||||
|
ColorAnimation {
|
||||||
|
duration: Style.animationFast
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
enabled: true
|
||||||
|
anchors.fill: parent
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
hoverEnabled: true
|
||||||
|
acceptedButtons: Qt.NoButton // Don't accept any mouse buttons - only hover
|
||||||
|
propagateComposedEvents: true
|
||||||
|
|
||||||
|
onEntered: {
|
||||||
|
root.hovering = true;
|
||||||
|
if (root.tooltipText) {
|
||||||
|
TooltipService.show(screen, knob, root.tooltipText, root.tooltipDirection);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onExited: {
|
||||||
|
root.hovering = false;
|
||||||
|
if (root.tooltipText) {
|
||||||
|
TooltipService.hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hide tooltip when slider is pressed (anywhere on the slider)
|
||||||
|
Connections {
|
||||||
|
target: root
|
||||||
|
function onPressedChanged() {
|
||||||
|
if (root.pressed && root.tooltipText) {
|
||||||
|
TooltipService.hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -784,7 +784,7 @@ Popup {
|
|||||||
}
|
}
|
||||||
|
|
||||||
NButton {
|
NButton {
|
||||||
text: root.selectionMode === "folders" ? "Select Folder" : "Select File"
|
text: root.selectionMode === "folders" ? I18n.tr("widgets.file-picker.select-folder") : I18n.tr("widgets.file-picker.select-file")
|
||||||
icon: "filepicker-check"
|
icon: "filepicker-check"
|
||||||
enabled: filePickerPanel.currentSelection.length > 0
|
enabled: filePickerPanel.currentSelection.length > 0
|
||||||
onClicked: root.confirmSelection()
|
onClicked: root.confirmSelection()
|
||||||
|
|||||||
13
shell.qml
13
shell.qml
@@ -37,6 +37,7 @@ ShellRoot {
|
|||||||
|
|
||||||
property bool i18nLoaded: false
|
property bool i18nLoaded: false
|
||||||
property bool settingsLoaded: false
|
property bool settingsLoaded: false
|
||||||
|
property bool shellStateLoaded: false
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
Logger.i("Shell", "---------------------------");
|
Logger.i("Shell", "---------------------------");
|
||||||
@@ -63,8 +64,18 @@ ShellRoot {
|
|||||||
settingsLoaded = true;
|
settingsLoaded = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: typeof ShellState !== 'undefined' ? ShellState : null
|
||||||
|
function onIsLoadedChanged() {
|
||||||
|
if (ShellState.isLoaded) {
|
||||||
|
shellStateLoaded = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Loader {
|
Loader {
|
||||||
active: i18nLoaded && settingsLoaded
|
active: i18nLoaded && settingsLoaded && shellStateLoaded
|
||||||
|
|
||||||
sourceComponent: Item {
|
sourceComponent: Item {
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
|
|||||||
Reference in New Issue
Block a user