mirror of
https://github.com/noctalia-dev/noctalia-shell.git
synced 2026-05-11 17:08:27 +08:00
Merge branch 'main' into patch-2
This commit is contained in:
File diff suppressed because one or more lines are too long
+19
-10
@@ -527,6 +527,7 @@
|
||||
"close-app": "{app} schließen",
|
||||
"connect-vpn": "Mit {name} verbinden",
|
||||
"cycle-visualizer": "Zyklus-Visualisierer",
|
||||
"delete": "Löschen",
|
||||
"disable-bluetooth": "Bluetooth deaktivieren",
|
||||
"disable-dnd": "'Nicht stören' deaktivieren",
|
||||
"disable-wifi": "WLAN deaktivieren",
|
||||
@@ -735,10 +736,6 @@
|
||||
"top_left": "Oben links",
|
||||
"top_right": "Oben rechts"
|
||||
},
|
||||
"visualizer-quality": {
|
||||
"high": "Hoch",
|
||||
"low": "Niedrig"
|
||||
},
|
||||
"visualizer-types": {
|
||||
"linear": "Linear",
|
||||
"mirrored": "Gespiegelt",
|
||||
@@ -950,10 +947,6 @@
|
||||
"description": "Bevorzugte und ignorierte Medienanwendungen festlegen.",
|
||||
"label": "Medienplayer"
|
||||
},
|
||||
"visualizer-quality": {
|
||||
"description": "Hohe Qualität verbraucht mehr GPU-Ressourcen.",
|
||||
"label": "Visualisierungsqualität"
|
||||
},
|
||||
"visualizer-type": {
|
||||
"description": "Visualisierungstyp für Medienwiedergabe wählen.",
|
||||
"label": "Visualisierungstyp"
|
||||
@@ -1411,16 +1404,18 @@
|
||||
}
|
||||
},
|
||||
"edit-mode": {
|
||||
"add-widget": "Widget hinzufügen",
|
||||
"button": {
|
||||
"label": "Bearbeitungsmodus aktivieren"
|
||||
},
|
||||
"controls-explanation": "Linke Maustaste: Widget verschieben\nRechte Maustaste: Widget-Größe ändern",
|
||||
"controls-explanation": "Linksklick & Ziehen: Widget verschieben oder Größe ändern.\nRechtsklick: Kontextmenü-Optionen öffnen.",
|
||||
"description": "Aktiviere den Bearbeitungsmodus, um Desktop-Widgets zu verschieben und neu zu positionieren. Im aktivierten Zustand zeigen Widgets eine Ziehumrandung und können neu positioniert werden.",
|
||||
"exit-button": "Bearbeitungsmodus verlassen",
|
||||
"grid-snap": {
|
||||
"label": "Raster einrasten"
|
||||
},
|
||||
"label": "Bearbeitungsmodus"
|
||||
"label": "Bearbeitungsmodus",
|
||||
"open-settings": "Einstellungen öffnen"
|
||||
},
|
||||
"enabled": {
|
||||
"description": "Desktop-Widgets vollständig aktivieren oder deaktivieren.",
|
||||
@@ -1445,6 +1440,10 @@
|
||||
"description": "Zeige den Hintergrundcontainer für das Media-Player-Widget an.",
|
||||
"label": "Hintergrund anzeigen"
|
||||
},
|
||||
"show-buttons": {
|
||||
"description": "Mediensteuerungstasten (Wiedergabe/Pause, Zurück, Weiter) im Media-Player-Widget anzeigen.",
|
||||
"label": "Tasten anzeigen"
|
||||
},
|
||||
"visualizer-type": {
|
||||
"description": "Wählen Sie einen Visualisierungstyp für den Hintergrund des Desktop-Mediaplayers.",
|
||||
"label": "Visualisierungsart"
|
||||
@@ -1718,6 +1717,10 @@
|
||||
"placeholder": "z.B. notify-send \"Hintergrundbild\" \"Geändert\""
|
||||
}
|
||||
},
|
||||
"indicator": {
|
||||
"default-value": "Standard: {value}",
|
||||
"system-default": "Systemstandard"
|
||||
},
|
||||
"launcher": {
|
||||
"settings": {
|
||||
"clip-preview": {
|
||||
@@ -1740,6 +1743,10 @@
|
||||
"description": "Elemente in einem Raster statt in einer Liste anzeigen.",
|
||||
"label": "Rasteransicht"
|
||||
},
|
||||
"icon-mode": {
|
||||
"description": "Verwende native System-Icons anstelle von Tabler-Icons.",
|
||||
"label": "Native Icons verwenden"
|
||||
},
|
||||
"position": {
|
||||
"description": "Wählen Sie, wo das Starter-Panel erscheint.",
|
||||
"label": "Position"
|
||||
@@ -1865,6 +1872,7 @@
|
||||
},
|
||||
"network": {
|
||||
"bluetooth": {
|
||||
"description": "Bluetooth-Verwaltung aktivieren.",
|
||||
"label": "Bluetooth aktivieren"
|
||||
},
|
||||
"section": {
|
||||
@@ -1872,6 +1880,7 @@
|
||||
},
|
||||
"title": "Netzwerk",
|
||||
"wifi": {
|
||||
"description": "Drahtlosnetzwerke verwalten (erfordert NetworkManager).",
|
||||
"label": "WLAN aktivieren"
|
||||
}
|
||||
},
|
||||
|
||||
+19
-14
@@ -527,6 +527,7 @@
|
||||
"close-app": "Close {app}",
|
||||
"connect-vpn": "Connect to {name}",
|
||||
"cycle-visualizer": "Cycle visualizer",
|
||||
"delete": "Delete",
|
||||
"disable-bluetooth": "Disable Bluetooth",
|
||||
"disable-dnd": "Disable Do Not Disturb",
|
||||
"disable-wifi": "Disable Wi-Fi",
|
||||
@@ -735,10 +736,6 @@
|
||||
"top_left": "Top left",
|
||||
"top_right": "Top right"
|
||||
},
|
||||
"visualizer-quality": {
|
||||
"high": "High",
|
||||
"low": "Low"
|
||||
},
|
||||
"visualizer-types": {
|
||||
"linear": "Linear",
|
||||
"mirrored": "Mirrored",
|
||||
@@ -882,10 +879,6 @@
|
||||
"title": "Session Menu"
|
||||
},
|
||||
"settings": {
|
||||
"indicator": {
|
||||
"default-value": "Default: {value}",
|
||||
"system-default": "System Default"
|
||||
},
|
||||
"about": {
|
||||
"contributors": {
|
||||
"section": {
|
||||
@@ -954,10 +947,6 @@
|
||||
"description": "Set your preferred and ignored media applications.",
|
||||
"label": "Media players"
|
||||
},
|
||||
"visualizer-quality": {
|
||||
"description": "High quality use more GPU ressources.",
|
||||
"label": "Visualization quality"
|
||||
},
|
||||
"visualizer-type": {
|
||||
"description": "Choose a visualization type for media playback.",
|
||||
"label": "Visualization type"
|
||||
@@ -1415,16 +1404,18 @@
|
||||
}
|
||||
},
|
||||
"edit-mode": {
|
||||
"add-widget": "Add widget",
|
||||
"button": {
|
||||
"label": "Enter edit mode"
|
||||
},
|
||||
"controls-explanation": "Left mouse button: Move widget\nRight mouse button: Resize widget",
|
||||
"controls-explanation": "Left-click & drag: Move or resize the widget.\nRight-click: Open the context menu options.",
|
||||
"description": "Enable edit mode to move and reposition desktop widgets. When enabled, widgets show a drag outline and can be repositioned.",
|
||||
"exit-button": "Exit edit mode",
|
||||
"grid-snap": {
|
||||
"label": "Grid snap"
|
||||
},
|
||||
"label": "Edit mode"
|
||||
"label": "Edit mode",
|
||||
"open-settings": "Open settings"
|
||||
},
|
||||
"enabled": {
|
||||
"description": "Enable or disable desktop widgets entirely.",
|
||||
@@ -1449,6 +1440,10 @@
|
||||
"description": "Show the background container for the media player widget.",
|
||||
"label": "Show background"
|
||||
},
|
||||
"show-buttons": {
|
||||
"description": "Show media control buttons (play/pause, previous, next) on the media player widget.",
|
||||
"label": "Show buttons"
|
||||
},
|
||||
"visualizer-type": {
|
||||
"description": "Choose a visualization type for the desktop media player background.",
|
||||
"label": "Visualization type"
|
||||
@@ -1722,6 +1717,10 @@
|
||||
"placeholder": "e.g., notify-send \"Wallpaper\" \"Changed\""
|
||||
}
|
||||
},
|
||||
"indicator": {
|
||||
"default-value": "Default: {value}",
|
||||
"system-default": "System Default"
|
||||
},
|
||||
"launcher": {
|
||||
"settings": {
|
||||
"clip-preview": {
|
||||
@@ -1744,6 +1743,10 @@
|
||||
"description": "Display items in a grid layout instead of a list.",
|
||||
"label": "Grid view"
|
||||
},
|
||||
"icon-mode": {
|
||||
"description": "Use native system icons instead of Tabler icons.",
|
||||
"label": "Use native icons"
|
||||
},
|
||||
"position": {
|
||||
"description": "Choose where the launcher panel appears.",
|
||||
"label": "Position"
|
||||
@@ -1869,6 +1872,7 @@
|
||||
},
|
||||
"network": {
|
||||
"bluetooth": {
|
||||
"description": "Activate Bluetooth management.",
|
||||
"label": "Enable Bluetooth"
|
||||
},
|
||||
"section": {
|
||||
@@ -1876,6 +1880,7 @@
|
||||
},
|
||||
"title": "Network",
|
||||
"wifi": {
|
||||
"description": "Manage wireless networks (requires NetworkManager).",
|
||||
"label": "Enable Wi-Fi"
|
||||
}
|
||||
},
|
||||
|
||||
+19
-10
@@ -527,6 +527,7 @@
|
||||
"close-app": "Cerrar {app}",
|
||||
"connect-vpn": "Conectarse a {name}",
|
||||
"cycle-visualizer": "Visualizador de ciclos",
|
||||
"delete": "Borrar",
|
||||
"disable-bluetooth": "Desactivar Bluetooth",
|
||||
"disable-dnd": "Desactivar No molestar",
|
||||
"disable-wifi": "Desactivar Wi-Fi",
|
||||
@@ -735,10 +736,6 @@
|
||||
"top_left": "Superior izquierda",
|
||||
"top_right": "Superior derecha"
|
||||
},
|
||||
"visualizer-quality": {
|
||||
"high": "Alto/Alta",
|
||||
"low": "Bajo"
|
||||
},
|
||||
"visualizer-types": {
|
||||
"linear": "Lineal",
|
||||
"mirrored": "Espejado",
|
||||
@@ -950,10 +947,6 @@
|
||||
"description": "Establece tus aplicaciones multimedia preferidas e ignoradas.",
|
||||
"label": "Reproductores multimedia"
|
||||
},
|
||||
"visualizer-quality": {
|
||||
"description": "Alta calidad usa más recursos de la GPU.",
|
||||
"label": "Calidad de visualización"
|
||||
},
|
||||
"visualizer-type": {
|
||||
"description": "Elige un tipo de visualización para la reproducción de medios.",
|
||||
"label": "Tipo de visualización"
|
||||
@@ -1411,16 +1404,18 @@
|
||||
}
|
||||
},
|
||||
"edit-mode": {
|
||||
"add-widget": "Añadir widget",
|
||||
"button": {
|
||||
"label": "Entrar en modo de edición"
|
||||
},
|
||||
"controls-explanation": "Botón izquierdo del ratón: Mover widget\nBotón derecho del ratón: Redimensionar widget",
|
||||
"controls-explanation": "Clic izquierdo y arrastrar: Mover o redimensionar el widget.\nClic derecho: Abrir las opciones del menú contextual.",
|
||||
"description": "Habilita el modo de edición para mover y cambiar la posición de los widgets del escritorio. Cuando está habilitado, los widgets muestran un contorno de arrastre y se pueden reposicionar.",
|
||||
"exit-button": "Salir del modo de edición",
|
||||
"grid-snap": {
|
||||
"label": "Ajustar a cuadrícula"
|
||||
},
|
||||
"label": "Modo de edición"
|
||||
"label": "Modo de edición",
|
||||
"open-settings": "Abrir ajustes"
|
||||
},
|
||||
"enabled": {
|
||||
"description": "Activar o desactivar los widgets de escritorio por completo.",
|
||||
@@ -1445,6 +1440,10 @@
|
||||
"description": "Mostrar el contenedor de fondo para el widget del reproductor multimedia.",
|
||||
"label": "Mostrar fondo"
|
||||
},
|
||||
"show-buttons": {
|
||||
"description": "Mostrar botones de control de medios (reproducir/pausar, anterior, siguiente) en el widget del reproductor multimedia.",
|
||||
"label": "Mostrar botones"
|
||||
},
|
||||
"visualizer-type": {
|
||||
"description": "Elige un tipo de visualización para el fondo del reproductor multimedia de escritorio.",
|
||||
"label": "Tipo de visualización"
|
||||
@@ -1718,6 +1717,10 @@
|
||||
"placeholder": "ej., notify-send \"Fondo de pantalla\" \"Cambiado\""
|
||||
}
|
||||
},
|
||||
"indicator": {
|
||||
"default-value": "Predeterminado: {value}",
|
||||
"system-default": "Predeterminado del sistema"
|
||||
},
|
||||
"launcher": {
|
||||
"settings": {
|
||||
"clip-preview": {
|
||||
@@ -1740,6 +1743,10 @@
|
||||
"description": "Mostrar elementos en una cuadrícula en lugar de una lista.",
|
||||
"label": "Vista de cuadrícula"
|
||||
},
|
||||
"icon-mode": {
|
||||
"description": "Usa iconos nativos del sistema en lugar de iconos Tabler.",
|
||||
"label": "Usar iconos nativos"
|
||||
},
|
||||
"position": {
|
||||
"description": "Elige dónde aparece el panel del lanzador.",
|
||||
"label": "Posición"
|
||||
@@ -1865,6 +1872,7 @@
|
||||
},
|
||||
"network": {
|
||||
"bluetooth": {
|
||||
"description": "Activar la gestión de Bluetooth.",
|
||||
"label": "Activar Bluetooth"
|
||||
},
|
||||
"section": {
|
||||
@@ -1872,6 +1880,7 @@
|
||||
},
|
||||
"title": "Red",
|
||||
"wifi": {
|
||||
"description": "Administrar redes inalámbricas (requiere NetworkManager).",
|
||||
"label": "Activar Wi-Fi"
|
||||
}
|
||||
},
|
||||
|
||||
+19
-10
@@ -527,6 +527,7 @@
|
||||
"close-app": "Fermer {app}",
|
||||
"connect-vpn": "Se connecter à {name}",
|
||||
"cycle-visualizer": "Visualiseur de cycle",
|
||||
"delete": "Supprimer",
|
||||
"disable-bluetooth": "Désactiver le Bluetooth",
|
||||
"disable-dnd": "Désactiver le mode Ne pas déranger",
|
||||
"disable-wifi": "Désactiver le Wi-Fi",
|
||||
@@ -735,10 +736,6 @@
|
||||
"top_left": "En haut à gauche",
|
||||
"top_right": "En haut à droite"
|
||||
},
|
||||
"visualizer-quality": {
|
||||
"high": "Haut/Haute",
|
||||
"low": "Bas"
|
||||
},
|
||||
"visualizer-types": {
|
||||
"linear": "Linéaire",
|
||||
"mirrored": "Miroir",
|
||||
@@ -950,10 +947,6 @@
|
||||
"description": "Définissez vos applications multimédias préférées et ignorées.",
|
||||
"label": "Lecteurs multimédias"
|
||||
},
|
||||
"visualizer-quality": {
|
||||
"description": "L'utilisation de haute qualité nécessite plus de ressources GPU.",
|
||||
"label": "Qualité de la visualisation"
|
||||
},
|
||||
"visualizer-type": {
|
||||
"description": "Choisissez un type de visualisation pour la lecture multimédia.",
|
||||
"label": "Type de visualisation"
|
||||
@@ -1411,16 +1404,18 @@
|
||||
}
|
||||
},
|
||||
"edit-mode": {
|
||||
"add-widget": "Ajouter un widget",
|
||||
"button": {
|
||||
"label": "Entrer en mode édition"
|
||||
},
|
||||
"controls-explanation": "Bouton gauche de la souris : Déplacer le widget\nBouton droit de la souris : Redimensionner le widget",
|
||||
"controls-explanation": "Clic gauche et glisser-déposer : Déplacer ou redimensionner le widget.\nClic droit : Ouvrir les options du menu contextuel.",
|
||||
"description": "Activer le mode édition pour déplacer et repositionner les widgets du bureau. Une fois activé, les widgets affichent un contour de glissement et peuvent être repositionnés.",
|
||||
"exit-button": "Quitter le mode édition",
|
||||
"grid-snap": {
|
||||
"label": "Aligner sur la grille"
|
||||
},
|
||||
"label": "Mode édition"
|
||||
"label": "Mode édition",
|
||||
"open-settings": "Ouvrir les paramètres"
|
||||
},
|
||||
"enabled": {
|
||||
"description": "Activer ou désactiver complètement les widgets du bureau.",
|
||||
@@ -1445,6 +1440,10 @@
|
||||
"description": "Afficher le conteneur d'arrière-plan pour le widget lecteur multimédia.",
|
||||
"label": "Afficher l'arrière-plan"
|
||||
},
|
||||
"show-buttons": {
|
||||
"description": "Afficher les boutons de contrôle multimédia (lecture/pause, précédent, suivant) sur le widget lecteur multimédia.",
|
||||
"label": "Afficher les boutons"
|
||||
},
|
||||
"visualizer-type": {
|
||||
"description": "Choisissez un type de visualisation pour l'arrière-plan du lecteur multimédia de bureau.",
|
||||
"label": "Type de visualisation"
|
||||
@@ -1718,6 +1717,10 @@
|
||||
"placeholder": "ex: notify-send \"Fond d'écran\" \"Changé\""
|
||||
}
|
||||
},
|
||||
"indicator": {
|
||||
"default-value": "Par défaut : {value}",
|
||||
"system-default": "Par défaut du système"
|
||||
},
|
||||
"launcher": {
|
||||
"settings": {
|
||||
"clip-preview": {
|
||||
@@ -1740,6 +1743,10 @@
|
||||
"description": "Afficher les éléments dans une grille au lieu d'une liste.",
|
||||
"label": "Vue grille"
|
||||
},
|
||||
"icon-mode": {
|
||||
"description": "Utiliser les icônes natives du système au lieu des icônes Tabler.",
|
||||
"label": "Utiliser les icônes natives"
|
||||
},
|
||||
"position": {
|
||||
"description": "Choisissez où le panneau du lanceur apparaît.",
|
||||
"label": "Position"
|
||||
@@ -1865,6 +1872,7 @@
|
||||
},
|
||||
"network": {
|
||||
"bluetooth": {
|
||||
"description": "Activer la gestion Bluetooth.",
|
||||
"label": "Activer le Bluetooth"
|
||||
},
|
||||
"section": {
|
||||
@@ -1872,6 +1880,7 @@
|
||||
},
|
||||
"title": "Réseau",
|
||||
"wifi": {
|
||||
"description": "Gérer les réseaux sans fil (nécessite NetworkManager).",
|
||||
"label": "Activer le Wi-Fi"
|
||||
}
|
||||
},
|
||||
|
||||
+220
-211
@@ -47,9 +47,9 @@
|
||||
},
|
||||
"battery": {
|
||||
"device": {
|
||||
"default": "標準 (表示デバイス)",
|
||||
"description": "表示するバッテリーデバイスを選択してください。",
|
||||
"label": "電池デバイス"
|
||||
"default": "デフォルト(表示デバイス)",
|
||||
"description": "表示するバッテリーデバイスを選択します。",
|
||||
"label": "バッテリーデバイス"
|
||||
},
|
||||
"display-mode": {
|
||||
"description": "値の表示方法を選択します。",
|
||||
@@ -60,11 +60,11 @@
|
||||
"label": "低バッテリー警告の閾値"
|
||||
},
|
||||
"show-noctalia-performance": {
|
||||
"description": "バッテリーパネルにNoctaliaパフォーマンスモードのトグルを表示します。",
|
||||
"label": "Noctaliaパフォーマンストグルを表示"
|
||||
"description": "バッテリーパネルに Noctalia パフォーマンスモードのトグルを表示します。",
|
||||
"label": "Noctalia パフォーマンストグルを表示"
|
||||
},
|
||||
"show-power-profile": {
|
||||
"description": "バッテリーパネルに電源プロファイルの選択を表示します。",
|
||||
"description": "バッテリーパネルに電源プロファイルの選択項目を表示します。",
|
||||
"label": "電源プロファイルコントロールを表示"
|
||||
}
|
||||
},
|
||||
@@ -111,12 +111,12 @@
|
||||
"browse-file": "ファイルを参照",
|
||||
"browse-library": "ライブラリを参照",
|
||||
"color-selection": {
|
||||
"description": "アイコンにテーマの色を適用します。",
|
||||
"description": "アイコンに適用するテーマの色を選択します。",
|
||||
"label": "色の選択"
|
||||
},
|
||||
"enable-colorization": {
|
||||
"description": "コントロールセンターのアイコンの色付けを有効にし、テーマの色を適用します。",
|
||||
"label": "色の有効化"
|
||||
"description": "コントロールセンターのアイコンにテーマカラーを適用します。",
|
||||
"label": "テーマカラーの適用"
|
||||
},
|
||||
"icon": {
|
||||
"description": "ライブラリまたはカスタムファイルからアイコンを選択します。",
|
||||
@@ -142,10 +142,10 @@
|
||||
"dynamic-text": "動的テキスト",
|
||||
"hide-mode": {
|
||||
"alwaysExpanded": "常に展開",
|
||||
"description": "コマンドが出力を持っていない場合のウィジェットの表示を制御します。",
|
||||
"expandWithOutput": "出力があるときに展開",
|
||||
"description": "コマンドの出力がない場合のウィジェットの表示を制御します。",
|
||||
"expandWithOutput": "出力がある時のみ展開",
|
||||
"label": "非表示モード",
|
||||
"maxTransparent": "最大展開だが透過"
|
||||
"maxTransparent": "最大展開(透明)"
|
||||
},
|
||||
"icon": {
|
||||
"description": "ライブラリからアイコンを選択します。",
|
||||
@@ -312,8 +312,8 @@
|
||||
"label": "ディスクパス"
|
||||
},
|
||||
"gpu-temperature": {
|
||||
"description": "利用可能であれば、GPU温度の測定値を表示します。",
|
||||
"label": "GPU温度 (GPU ondo)"
|
||||
"description": "利用可能な場合、GPU 温度を表示します。",
|
||||
"label": "GPU 温度"
|
||||
},
|
||||
"memory-percentage": {
|
||||
"description": "メモリ使用量を絶対値ではなくパーセンテージで表示します。",
|
||||
@@ -346,7 +346,7 @@
|
||||
"label": "アイコンのスケーリング"
|
||||
},
|
||||
"max-width": {
|
||||
"description": "画面幅に対するタスクバーの最大幅の割合。",
|
||||
"description": "タスクバーの最大幅を、画面幅に対するパーセンテージで設定します。",
|
||||
"label": "最大幅"
|
||||
},
|
||||
"only-active-workspaces": {
|
||||
@@ -354,26 +354,26 @@
|
||||
"label": "アクティブなワークスペースのみ"
|
||||
},
|
||||
"only-same-monitor": {
|
||||
"description": "バーがあるモニターのアプリのみ表示する。",
|
||||
"label": "同じモニターからのみ"
|
||||
"description": "バーが配置されているディスプレイ上のアプリのみを表示します。",
|
||||
"label": "同じディスプレイのみ"
|
||||
},
|
||||
"show-pinned-apps": {
|
||||
"description": "ドックからピン留めされたアプリをタスクバーに表示する。",
|
||||
"label": "ピン留めされたアプリを表示"
|
||||
"description": "ドックにピン留めされたアプリをタスクバーに表示します。",
|
||||
"label": "ピン留めアプリを表示"
|
||||
},
|
||||
"show-title": {
|
||||
"description": "タスクバーにウィンドウタイトルを表示します。",
|
||||
"description-disabled": "縦型タスクバーはタイトル表示に対応していません。",
|
||||
"description-disabled": "垂直タスクバーではタイトルを表示できません。",
|
||||
"label": "タイトルを表示"
|
||||
},
|
||||
"smart-width": {
|
||||
"description": "エントリの数に基づいてエントリの幅を自動的に調整します。",
|
||||
"label": "スマート幅"
|
||||
"description": "項目の数に基づいて、項目の幅を自動的に調整します。",
|
||||
"label": "幅の自動調整"
|
||||
},
|
||||
"title-width": {
|
||||
"description": "タスクバーのウィンドウタイトルの幅を設定します(ピクセル単位)。",
|
||||
"label": "タイトル幅",
|
||||
"reset-tooltip": "タイトル幅をリセット"
|
||||
"label": "タイトルの幅",
|
||||
"reset-tooltip": "タイトルの幅をリセット"
|
||||
}
|
||||
},
|
||||
"tray": {
|
||||
@@ -386,8 +386,8 @@
|
||||
"label": "ドロワーを有効化"
|
||||
},
|
||||
"hide-passive": {
|
||||
"description": "有効にすると、非アクティブなステータスのトレイアイコンは非表示になります。",
|
||||
"label": "受動的なアイテムを隠す"
|
||||
"description": "有効にすると、ステータスが「パッシブ」のトレイ項目を非表示にします。",
|
||||
"label": "パッシブ項目を非表示"
|
||||
}
|
||||
},
|
||||
"volume": {
|
||||
@@ -402,8 +402,8 @@
|
||||
"label": "文字数"
|
||||
},
|
||||
"enable-scrollwheel": {
|
||||
"description": "マウスホイールでワークスペースを切り替える。",
|
||||
"label": "ワークスペースを切り替えるにはスクロールしてください"
|
||||
"description": "マウスホイールを使用してワークスペースを切り替えます。",
|
||||
"label": "スクロールでワークスペースを切り替え"
|
||||
},
|
||||
"follow-focused-screen": {
|
||||
"description": "バーが配置されている画面ではなく、現在フォーカスされている画面のワークスペースを表示します。",
|
||||
@@ -418,12 +418,12 @@
|
||||
"label": "ラベルモード"
|
||||
},
|
||||
"show-applications": {
|
||||
"description": "ワークスペース内にアプリケーションのアイコンを表示する。",
|
||||
"label": "アプリケーションを表示 (Apuriikeeshon o hyōji)"
|
||||
"description": "各ワークスペース内にアプリアイコンを表示します。",
|
||||
"label": "アプリアイコンを表示"
|
||||
},
|
||||
"show-labels-only-when-occupied": {
|
||||
"description": "ウィンドウを含む場合にのみ、ワークスペースのラベルを表示する。",
|
||||
"label": "占有されている場合のみラベルを表示する"
|
||||
"description": "ウィンドウが開いているワークスペースにのみラベルを表示します。",
|
||||
"label": "ウィンドウがある時のみラベルを表示"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -474,7 +474,7 @@
|
||||
"minutes": "分",
|
||||
"pause": "一時停止",
|
||||
"reset": "リセット",
|
||||
"resume": "履歴書 (Rirekisho)",
|
||||
"resume": "再開",
|
||||
"seconds": "秒",
|
||||
"start": "開始",
|
||||
"stopwatch": "ストップウォッチ",
|
||||
@@ -527,6 +527,7 @@
|
||||
"close-app": "{app} を閉じる",
|
||||
"connect-vpn": "{name} に接続",
|
||||
"cycle-visualizer": "ビジュアライザーを切り替える",
|
||||
"delete": "削除 (Sakujo)",
|
||||
"disable-bluetooth": "Bluetooth を無効化",
|
||||
"disable-dnd": "おやすみモードを無効化",
|
||||
"disable-wifi": "Wi-Fi を無効化",
|
||||
@@ -563,7 +564,7 @@
|
||||
"launcher": {
|
||||
"categories": {
|
||||
"all": "すべて",
|
||||
"audiovideo": "音声 & ビデオ",
|
||||
"audiovideo": "音声・ビデオ",
|
||||
"chat": "チャット",
|
||||
"development": "開発",
|
||||
"education": "教育",
|
||||
@@ -576,7 +577,7 @@
|
||||
"system": "システム",
|
||||
"webbrowser": "ウェブブラウザ"
|
||||
},
|
||||
"delete": "削除 (Sakujo)",
|
||||
"delete": "削除",
|
||||
"pin": "ピン留め",
|
||||
"unpin": "ピン留め解除"
|
||||
},
|
||||
@@ -598,7 +599,7 @@
|
||||
},
|
||||
"range": {
|
||||
"all": "すべて",
|
||||
"earlier": "以前",
|
||||
"earlier": "それ以前",
|
||||
"today": "今日",
|
||||
"yesterday": "昨日"
|
||||
},
|
||||
@@ -720,9 +721,9 @@
|
||||
"never": "スクロールしない"
|
||||
},
|
||||
"settings-panel-mode": {
|
||||
"attached": "棒に取り付けられたパネル",
|
||||
"centered": "中央パネル",
|
||||
"window": "別ウィンドウ"
|
||||
"attached": "バーに吸着",
|
||||
"centered": "中央に配置",
|
||||
"window": "独立したウィンドウ"
|
||||
},
|
||||
"shadow-direction": {
|
||||
"bottom": "下",
|
||||
@@ -735,10 +736,6 @@
|
||||
"top_left": "左上",
|
||||
"top_right": "右上"
|
||||
},
|
||||
"visualizer-quality": {
|
||||
"high": "高",
|
||||
"low": "低"
|
||||
},
|
||||
"visualizer-types": {
|
||||
"linear": "リニア",
|
||||
"mirrored": "ミラー",
|
||||
@@ -783,7 +780,7 @@
|
||||
"clipboard-clear-description": "クリップボード履歴をすべて消去",
|
||||
"clipboard-clear-description-full": "クリップボード履歴からすべての項目を削除します",
|
||||
"clipboard-clear-history": "クリップボード履歴を消去",
|
||||
"clipboard-delete": "クリップボードのエントリを削除",
|
||||
"clipboard-delete": "クリップボードの項目を削除",
|
||||
"clipboard-history-disabled": "クリップボード履歴は無効です",
|
||||
"clipboard-history-disabled-description": "設定で履歴を有効にするか、cliphist をインストールしてください",
|
||||
"clipboard-loading": "クリップボード履歴を読み込み中...",
|
||||
@@ -891,7 +888,7 @@
|
||||
}
|
||||
},
|
||||
"noctalia": {
|
||||
"git-commit": "Gitコミット:",
|
||||
"git-commit": "Git コミット:",
|
||||
"git-commit-loading": "読み込み中...",
|
||||
"installed-version": "現在のバージョン:",
|
||||
"latest-version": "最新のバージョン:",
|
||||
@@ -950,10 +947,6 @@
|
||||
"description": "優先するメディアアプリや、除外設定を行います。",
|
||||
"label": "メディアプレーヤー"
|
||||
},
|
||||
"visualizer-quality": {
|
||||
"description": "品質を高くすると、GPUリソースをより多く消費します。",
|
||||
"label": "ビジュアライザーの品質"
|
||||
},
|
||||
"visualizer-type": {
|
||||
"description": "メディア再生時の視覚効果(ビジュアライザー)の種類を選択します。",
|
||||
"label": "ビジュアライザーの種類"
|
||||
@@ -961,7 +954,7 @@
|
||||
},
|
||||
"panel": {
|
||||
"applications": {
|
||||
"empty": "現在、音声を再生しているアプリケーションはありません。"
|
||||
"empty": "現在音声を再生中のアプリケーションはありません"
|
||||
},
|
||||
"input": "入力",
|
||||
"output": "出力",
|
||||
@@ -1039,12 +1032,12 @@
|
||||
"label": "カプセルを表示"
|
||||
},
|
||||
"show-outline": {
|
||||
"description": "すべてのウィジェットの周りに目に見える境界線を表示します。",
|
||||
"label": "ウィジェットのアウトラインを表示"
|
||||
"description": "すべてのウィジェットの周囲に枠線を表示します。",
|
||||
"label": "ウィジェットの枠線を表示"
|
||||
},
|
||||
"transparent": {
|
||||
"description": "これにより、パネルがバーに取り付けられるのを防ぎます。",
|
||||
"label": "透明な背景"
|
||||
"description": "これを有効にすると、パネルはバーに吸着されなくなります。",
|
||||
"label": "背景の透明化"
|
||||
}
|
||||
},
|
||||
"monitors": {
|
||||
@@ -1066,7 +1059,7 @@
|
||||
},
|
||||
"widgets": {
|
||||
"section": {
|
||||
"description": "ウィジェットをドラッグして順番を変更します。右クリックメニューを使って、セクション間でウィジェットを移動したり、削除したりできます。",
|
||||
"description": "ウィジェットをドラッグして並べ替えます。右クリックメニューから、セクション間の移動や削除が行えます。",
|
||||
"label": "ウィジェットの配置"
|
||||
}
|
||||
}
|
||||
@@ -1155,75 +1148,75 @@
|
||||
},
|
||||
"templates": {
|
||||
"compositors": {
|
||||
"description": "コンポジタのテーマ設定。",
|
||||
"label": "合成者",
|
||||
"description": "コンポジタのテーマ設定",
|
||||
"label": "コンポジタ",
|
||||
"mango": {
|
||||
"description": "{filepath} を書き込みます。mango が必要です",
|
||||
"description-missing": "{app} がインストールされている必要があります"
|
||||
"description": "{filepath} を書き込みます。mango が必要です。",
|
||||
"description-missing": "{app} のインストールが必要です。"
|
||||
},
|
||||
"niri": {
|
||||
"description": "{filepath} を書き込みます。niri v25.11+ が必要です",
|
||||
"description-missing": "{app} がインストールされている必要があります"
|
||||
"description": "{filepath} を書き込みます。niri v25.11+ が必要です。",
|
||||
"description-missing": "{app} のインストールが必要です。"
|
||||
}
|
||||
},
|
||||
"misc": {
|
||||
"description": "独自のテンプレートを作成",
|
||||
"label": "詳細設定",
|
||||
"user-templates": {
|
||||
"description": "操作方法を理解している場合のみ有効にしてください、オンラインドキュメントを参照してください",
|
||||
"description": "操作方法を理解している場合のみ有効にしてください。オンラインドキュメントを参照してください。",
|
||||
"label": "ユーザーテンプレートを有効化"
|
||||
}
|
||||
},
|
||||
"programs": {
|
||||
"cava": {
|
||||
"description": "{filepath} を書き込みます",
|
||||
"description-missing": "{app} のインストールが必要です"
|
||||
"description": "{filepath} を書き込みます。",
|
||||
"description-missing": "{app} のインストールが必要です。"
|
||||
},
|
||||
"code": {
|
||||
"description": "{filepath} を書き込みます、HyprLuna テーマを手動でインストールして有効化する必要があります",
|
||||
"description-missing": "Code クライアントが検出されません。VSCode または VSCodium をインストールしてください"
|
||||
"description": "{filepath} を書き込みます。HyprLuna テーマを手動でインストールして有効化する必要があります。",
|
||||
"description-missing": "Code クライアントが検出されません。VSCode または VSCodium をインストールしてください。"
|
||||
},
|
||||
"description": "アプリケーションごとのテーマ設定",
|
||||
"discord": {
|
||||
"description": "{client} 用に {filepath} を書き込みます、HyprLuna テーマを手動でインストールして有効化する必要があります",
|
||||
"description-missing": "Discord クライアントが検出されません、vencord、vesktop、webcord、armcord、equibop、lightcord、dorion のいずれかをインストールしてください"
|
||||
"description": "{client} 用に {filepath} を書き込みます。HyprLuna テーマを手動でインストールして有効化する必要があります。",
|
||||
"description-missing": "Discord クライアントが検出されません。vencord、vesktop、webcord、armcord、equibop、lightcord、dorion のいずれかをインストールしてください。"
|
||||
},
|
||||
"emacs": {
|
||||
"description": "Doom: ~/.config/doom/themes/noctalia.el<br>標準: ~/.emacs.d/themes/noctalia.el<br>手動で適用: (load-theme 'noctalia t)",
|
||||
"description-missing": "{app} がインストールされている必要があります"
|
||||
"description-missing": "{app} のインストールが必要です。"
|
||||
},
|
||||
"fuzzel": {
|
||||
"description": "{filepath} を書き込み、リロードします",
|
||||
"description-missing": "{app} のインストールが必要です"
|
||||
"description": "{filepath} を書き込み、リロードします。",
|
||||
"description-missing": "{app} のインストールが必要です。"
|
||||
},
|
||||
"label": "プログラム",
|
||||
"pywalfox": {
|
||||
"description": "{filepath} を書き込み、pywalfox update を実行します",
|
||||
"description-missing": "{app} のインストールが必要です"
|
||||
"description": "{filepath} を書き込み、pywalfox update を実行します。",
|
||||
"description-missing": "{app} のインストールが必要です。"
|
||||
},
|
||||
"spicetify": {
|
||||
"description": "{filepath} を書き込みます、Comfy テーマを手動でインストールして有効化する必要があります",
|
||||
"description-missing": "{app} のインストールが必要です"
|
||||
"description": "{filepath} を書き込みます。Comfy テーマを手動でインストールして有効化する必要があります。",
|
||||
"description-missing": "{app} のインストールが必要です。"
|
||||
},
|
||||
"telegram": {
|
||||
"description": "{filepath} を書き込みます",
|
||||
"description-missing": "{app} のインストールが必要です"
|
||||
"description": "{filepath} を書き込みます。",
|
||||
"description-missing": "{app} のインストールが必要です。"
|
||||
},
|
||||
"vicinae": {
|
||||
"description": "{filepath} を書き込み、リロードします",
|
||||
"description-missing": "{app} のインストールが必要です"
|
||||
"description": "{filepath} を書き込み、リロードします。",
|
||||
"description-missing": "{app} のインストールが必要です。"
|
||||
},
|
||||
"walker": {
|
||||
"description": "{filepath} を書き込み、テーマを noctalia に設定します",
|
||||
"description-missing": "{app} のインストールが必要です"
|
||||
"description": "{filepath} を書き込み、テーマを noctalia に設定します。",
|
||||
"description-missing": "{app} のインストールが必要です。"
|
||||
},
|
||||
"yazi": {
|
||||
"description": "{filepath} を書き込み、flavor は手動で有効にする必要があります",
|
||||
"description-missing": "{app} がインストールされている必要があります"
|
||||
"description": "{filepath} を書き込みます。フレーバーは手動で有効化する必要があります。",
|
||||
"description-missing": "{app} のインストールが必要です。"
|
||||
},
|
||||
"zed": {
|
||||
"description": "{filepath} を書き込み、リロードします",
|
||||
"description-missing": "{app} のインストールが必要です"
|
||||
"description": "{filepath} を書き込み、リロードします。",
|
||||
"description-missing": "{app} のインストールが必要です。"
|
||||
}
|
||||
},
|
||||
"section": {
|
||||
@@ -1341,7 +1334,7 @@
|
||||
"cancel": "キャンセル"
|
||||
},
|
||||
"section": {
|
||||
"description": "ウィジェットをドラッグして順番を変更します。右クリックメニューを使って、セクション間でウィジェットを移動したり、削除したりできます。",
|
||||
"description": "ウィジェットをドラッグして並べ替えます。右クリックメニューから、セクション間の移動や削除が行えます。",
|
||||
"label": "ショートカットウィジェット"
|
||||
},
|
||||
"sectionLeft": "左",
|
||||
@@ -1362,28 +1355,28 @@
|
||||
"search-placeholder": "フォントを検索..."
|
||||
},
|
||||
"enabled": {
|
||||
"description": "デスクトップに時計ウィジェットを表示する。",
|
||||
"label": "時計ウィジェットを有効にする"
|
||||
"description": "デスクトップに時計ウィジェットを表示します。",
|
||||
"label": "時計ウィジェットを有効化"
|
||||
},
|
||||
"format": {
|
||||
"description": "ヒント: \\n を使用すると改行できます。",
|
||||
"label": "フォーマット"
|
||||
},
|
||||
"height": {
|
||||
"description": "時計ウィジェットの高さ(ピクセル単位)。",
|
||||
"description": "時計ウィジェットの高さをピクセル単位で設定します。",
|
||||
"label": "高さ"
|
||||
},
|
||||
"minimal-mode": {
|
||||
"description": "時刻と日付を含むミニマルな時計表示を使用します。",
|
||||
"description": "時刻と日付のみのミニマルな時計表示を使用します。",
|
||||
"label": "ミニマルモード"
|
||||
},
|
||||
"preview": "プレビュー",
|
||||
"show-background": {
|
||||
"description": "時計ウィジェットの背景コンテナを表示する。",
|
||||
"description": "時計ウィジェットの背景コンテナを表示します。",
|
||||
"label": "背景を表示"
|
||||
},
|
||||
"show-date": {
|
||||
"description": "現在時刻の下に今日の日付を表示しなさい。",
|
||||
"description": "時刻の下に現在の日付を表示します。",
|
||||
"label": "日付を表示"
|
||||
},
|
||||
"show-seconds": {
|
||||
@@ -1392,9 +1385,9 @@
|
||||
},
|
||||
"style": {
|
||||
"analog": "アナログ",
|
||||
"description": "時計の表示スタイルを選択してください。",
|
||||
"description": "時計の表示スタイルを選択します。",
|
||||
"digital": "デジタル",
|
||||
"label": "時計スタイル",
|
||||
"label": "時計のスタイル",
|
||||
"minimal": "ミニマル"
|
||||
},
|
||||
"use-custom-font": {
|
||||
@@ -1402,29 +1395,31 @@
|
||||
"label": "カスタムフォントを使用"
|
||||
},
|
||||
"use-primary-color": {
|
||||
"description": "有効にすると、強調のためにプライマリカラーが適用されます。",
|
||||
"description": "有効にすると、強調のためにプライマリカラーが適用します。",
|
||||
"label": "プライマリカラーを使用"
|
||||
},
|
||||
"width": {
|
||||
"description": "クロックウィジェットの幅(ピクセル単位)。",
|
||||
"description": "時計ウィジェットの幅をピクセル単位で設定します。",
|
||||
"label": "幅"
|
||||
}
|
||||
},
|
||||
"edit-mode": {
|
||||
"add-widget": "ウィジェットを追加",
|
||||
"button": {
|
||||
"label": "編集モードに入る"
|
||||
},
|
||||
"controls-explanation": "左マウスボタン: ウィジェットを移動\n右マウスボタン: ウィジェットのサイズを変更",
|
||||
"description": "デスクトップウィジェットを移動および再配置するには、編集モードを有効にしてください。有効にすると、ウィジェットにドラッグアウトラインが表示され、再配置できます。",
|
||||
"exit-button": "編集モードを終了します。",
|
||||
"controls-explanation": "左クリックでドラッグ:ウィジェットの移動またはサイズ変更。\n右クリック:コンテキストメニューオプションを開く。",
|
||||
"description": "編集モードを有効にして、デスクトップウィジェットの移動や配置変更を行います。有効にするとウィジェットに枠線が表示され、位置を変更できるようになります。",
|
||||
"exit-button": "編集モードを終了",
|
||||
"grid-snap": {
|
||||
"label": "グリッドスナップ"
|
||||
"label": "グリッドにスナップ"
|
||||
},
|
||||
"label": "編集モード"
|
||||
"label": "編集モード",
|
||||
"open-settings": "設定を開く"
|
||||
},
|
||||
"enabled": {
|
||||
"description": "デスクトップウィジェットを完全に有効または無効にします。",
|
||||
"label": "デスクトップウィジェットを有効にする"
|
||||
"description": "デスクトップウィジェット全体を有効または無効にします。",
|
||||
"label": "デスクトップウィジェットを有効化"
|
||||
},
|
||||
"general": {
|
||||
"section": {
|
||||
@@ -1434,34 +1429,38 @@
|
||||
},
|
||||
"media-player": {
|
||||
"enabled": {
|
||||
"description": "デスクトップにメディアプレーヤーウィジェットを表示する。",
|
||||
"label": "メディアプレーヤーウィジェットを有効にする"
|
||||
"description": "デスクトップにメディアプレーヤーウィジェットを表示します。",
|
||||
"label": "メディアプレーヤーウィジェットを有効化"
|
||||
},
|
||||
"hide-mode": {
|
||||
"description": "再生中のメディアがない場合のウィジェットの動作を制御します。",
|
||||
"label": "非表示モード"
|
||||
},
|
||||
"show-background": {
|
||||
"description": "メディアプレーヤーウィジェットの背景コンテナを表示する。",
|
||||
"description": "メディアプレーヤーウィジェットの背景コンテナを表示します。",
|
||||
"label": "背景を表示"
|
||||
},
|
||||
"show-buttons": {
|
||||
"description": "メディアプレーヤーウィジェットにメディアコントロールボタン(再生/一時停止、前へ、次へ)を表示します。",
|
||||
"label": "ボタンを表示"
|
||||
},
|
||||
"visualizer-type": {
|
||||
"description": "デスクトップメディアプレーヤーの背景の視覚化タイプを選択してください。",
|
||||
"label": "視覚化の種類"
|
||||
"description": "デスクトップメディアプレーヤーの背景に使用するビジュアライザーの種類を選択します。",
|
||||
"label": "ビジュアライザーの種類"
|
||||
},
|
||||
"visualizer-visibility": {
|
||||
"description": "オーディオビジュアライザーが表示されるタイミングを制御します。",
|
||||
"label": "オーディオビジュアライザーの表示"
|
||||
"description": "オーディオビジュアライザーを表示するタイミングを制御します。",
|
||||
"label": "ビジュアライザーの表示設定"
|
||||
}
|
||||
},
|
||||
"title": "デスクトップウィジェット",
|
||||
"weather": {
|
||||
"enabled": {
|
||||
"description": "デスクトップに天気ウィジェットを表示する。",
|
||||
"label": "天気ウィジェットを有効にする"
|
||||
"description": "デスクトップに天気ウィジェットを表示します。",
|
||||
"label": "天気ウィジェットを有効化"
|
||||
},
|
||||
"show-background": {
|
||||
"description": "天気ウィジェットの背景コンテナを表示する。",
|
||||
"description": "天気ウィジェットの背景コンテナを表示します。",
|
||||
"label": "背景を表示"
|
||||
}
|
||||
}
|
||||
@@ -1517,7 +1516,7 @@
|
||||
},
|
||||
"temperature": {
|
||||
"day": "日中",
|
||||
"day-description": "昼間の色温度を調整します。",
|
||||
"day-description": "日中の色温度を調整します。",
|
||||
"description": "日中と夜間の色温度(暖かさ)を設定します。",
|
||||
"label": "色温度",
|
||||
"night": "夜間",
|
||||
@@ -1541,8 +1540,8 @@
|
||||
"label": "テーマカラーの適用"
|
||||
},
|
||||
"dead-opacity": {
|
||||
"description": "実行されていないアプリのアイコンの不透明度を調整します。",
|
||||
"label": "死んだ不透明度"
|
||||
"description": "起動していないアプリアイコンの不透明度を調整します。",
|
||||
"label": "未実行時の不透明度"
|
||||
},
|
||||
"display": {
|
||||
"always-visible": "常に表示",
|
||||
@@ -1556,20 +1555,20 @@
|
||||
"label": "画面端からの距離"
|
||||
},
|
||||
"hide-show-speed": {
|
||||
"description": "ドックの表示/非表示アニメーションの速度を調整します。",
|
||||
"label": "表示/非表示の速度"
|
||||
"description": "ドックの表示・非表示アニメーションの速度を調整します。",
|
||||
"label": "表示・非表示の速度"
|
||||
},
|
||||
"icon-size": {
|
||||
"description": "ドック全体のサイズを調整します。",
|
||||
"label": "ドックのサイズ"
|
||||
},
|
||||
"inactive-indicators": {
|
||||
"description": "現在アクティブなアプリだけでなく、すべてのアプリのインジケーターピルを表示する。",
|
||||
"label": "実行指標"
|
||||
"description": "現在アクティブなアプリだけでなく、すべてのアプリにインジケーターを表示します。",
|
||||
"label": "実行インジケーター"
|
||||
},
|
||||
"pinned-static": {
|
||||
"description": "ピン留めされたアプリのアイコンは、常に静的な順序で左に寄せてください。",
|
||||
"label": "固定された静的アプリ"
|
||||
"description": "ピン留めされたアプリアイコンを常に左側に寄せて固定します。",
|
||||
"label": "ピン留めアプリの固定配置"
|
||||
},
|
||||
"section": {
|
||||
"description": "ドックの動作と外観をカスタマイズします。",
|
||||
@@ -1582,8 +1581,8 @@
|
||||
},
|
||||
"monitors": {
|
||||
"only-same-monitor": {
|
||||
"description": "ドックがあるモニターのアプリのみを表示する。",
|
||||
"label": "同じモニターのアプリのみ"
|
||||
"description": "ドックが配置されているディスプレイ上のアプリのみを表示します。",
|
||||
"label": "同じディスプレイのアプリのみ"
|
||||
},
|
||||
"section": {
|
||||
"description": "ドックを表示するディスプレイを選択します。未選択の場合は全てに表示されます。",
|
||||
@@ -1677,12 +1676,12 @@
|
||||
}
|
||||
},
|
||||
"performance-mode-disabled": {
|
||||
"description": "Noctaliaパフォーマンスモードが無効になった際に実行されるコマンド。",
|
||||
"description": "Noctalia パフォーマンスモードが無効になった際に実行されるコマンド。",
|
||||
"label": "パフォーマンスモード無効時",
|
||||
"placeholder": "例: notify-send \"Performance\" \"Mode Disabled\""
|
||||
},
|
||||
"performance-mode-enabled": {
|
||||
"description": "Noctaliaパフォーマンスモードが有効になった際に実行されるコマンド。",
|
||||
"description": "Noctalia パフォーマンスモードが有効になった際に実行されるコマンド。",
|
||||
"label": "パフォーマンスモード有効時",
|
||||
"placeholder": "例: notify-send \"Performance\" \"Mode Enabled\""
|
||||
},
|
||||
@@ -1692,7 +1691,7 @@
|
||||
"placeholder": "例: notify-send \"Screen\" \"Locked\""
|
||||
},
|
||||
"screen-unlock": {
|
||||
"description": "ロック画面が解除された際に実行されるコマンド。",
|
||||
"description": "画面ロックが解除された際に実行されるコマンド。",
|
||||
"label": "画面ロック解除時",
|
||||
"placeholder": "例: notify-send \"Screen\" \"Unlocked\""
|
||||
},
|
||||
@@ -1718,6 +1717,10 @@
|
||||
"placeholder": "例: notify-send \"Wallpaper\" \"Changed\""
|
||||
}
|
||||
},
|
||||
"indicator": {
|
||||
"default-value": "デフォルト: {value}",
|
||||
"system-default": "システムのデフォルト"
|
||||
},
|
||||
"launcher": {
|
||||
"settings": {
|
||||
"clip-preview": {
|
||||
@@ -1740,6 +1743,10 @@
|
||||
"description": "リスト形式の代わりに、グリッド(格子状)レイアウトで項目を表示します。",
|
||||
"label": "グリッド表示"
|
||||
},
|
||||
"icon-mode": {
|
||||
"description": "Tablerアイコンではなく、ネイティブのシステムアイコンを使用します。",
|
||||
"label": "ネイティブアイコンを使用"
|
||||
},
|
||||
"position": {
|
||||
"description": "ランチャーパネルの表示位置を選択します。",
|
||||
"label": "表示位置"
|
||||
@@ -1749,7 +1756,7 @@
|
||||
"label": "外観"
|
||||
},
|
||||
"show-categories": {
|
||||
"description": "アプリケーションをフィルタリングするためのカテゴリタブを表示します。",
|
||||
"description": "アプリケーションを絞り込むためのカテゴリタブを表示します。",
|
||||
"label": "カテゴリを表示"
|
||||
},
|
||||
"sort-by-usage": {
|
||||
@@ -1779,7 +1786,7 @@
|
||||
"label": "カレンダーヘッダー"
|
||||
},
|
||||
"month": {
|
||||
"label": "カレンダー月"
|
||||
"label": "カレンダー(月表示)"
|
||||
}
|
||||
},
|
||||
"date-time": {
|
||||
@@ -1858,13 +1865,14 @@
|
||||
"label": "ハイバネートを表示"
|
||||
},
|
||||
"show-session-buttons": {
|
||||
"description": "ロック画面から電源設定へのアクセスを許可します。",
|
||||
"label": "電源制御"
|
||||
"description": "ロック画面から電源操作を行えるようにします。",
|
||||
"label": "電源操作"
|
||||
},
|
||||
"title": "ロック画面"
|
||||
},
|
||||
"network": {
|
||||
"bluetooth": {
|
||||
"description": "Bluetooth の管理を有効にします。",
|
||||
"label": "Bluetooth を有効化"
|
||||
},
|
||||
"section": {
|
||||
@@ -1872,6 +1880,7 @@
|
||||
},
|
||||
"title": "ネットワーク",
|
||||
"wifi": {
|
||||
"description": "ワイヤレスネットワークを管理します(NetworkManager が必要です)。",
|
||||
"label": "Wi-Fi を有効化"
|
||||
}
|
||||
},
|
||||
@@ -1937,57 +1946,57 @@
|
||||
},
|
||||
"sounds": {
|
||||
"enabled": {
|
||||
"description": "着信通知の音声効果を有効にします。",
|
||||
"label": "通知音を有効にする"
|
||||
"description": "通知受信時のの効果音を有効にします。",
|
||||
"label": "通知音を有効化"
|
||||
},
|
||||
"excluded-apps": {
|
||||
"description": "独自の通知音を持つ特定のアプリケーションについて、設定された通知音の再生をスキップします。",
|
||||
"label": "除外アプリケーション",
|
||||
"description": "独自の通知音を持つ特定のアプリケーションについて、設定した通知音の再生をスキップします。",
|
||||
"label": "除外するアプリケーション",
|
||||
"placeholder": "discord,firefox,chrome,chromium,edge"
|
||||
},
|
||||
"files": {
|
||||
"critical": {
|
||||
"description": "緊急度の高い通知で再生される音声ファイルへのパス。",
|
||||
"label": "緊急通知音",
|
||||
"select-title": "緊急通知音ファイルを選択"
|
||||
"description": "緊急度「重大」の通知で再生する音声ファイルのパス。",
|
||||
"label": "緊急度「重大」の通知音",
|
||||
"select-title": "緊急度「重大」の通知音ファイルを選択"
|
||||
},
|
||||
"low": {
|
||||
"description": "緊急度の低い通知で再生される音声ファイルへのパス。",
|
||||
"label": "低優先度通知音",
|
||||
"select-title": "低優先度通知音ファイルを選択"
|
||||
"description": "緊急度「低」の通知で再生する音声ファイルのパス。",
|
||||
"label": "緊急度「低」の通知音",
|
||||
"select-title": "緊急度「低」の通知音ファイルを選択"
|
||||
},
|
||||
"normal": {
|
||||
"description": "通常優先度の通知で再生される音声ファイルへのパス。",
|
||||
"label": "通常優先度通知音",
|
||||
"select-title": "通常優先度通知音ファイルを選択"
|
||||
"description": "緊急度「通常」の通知で再生する音声ファイルのパス。",
|
||||
"label": "緊急度「通常」の通知音",
|
||||
"select-title": "緊急度「通常」の通知音ファイルを選択"
|
||||
},
|
||||
"placeholder": "音声ファイルへのパスを入力",
|
||||
"section": {
|
||||
"description": "通知の緊急度レベルに応じてカスタム音声ファイルを設定します。",
|
||||
"description": "通知の緊急度レベルごとにカスタム音声ファイルを設定します。",
|
||||
"label": "音声ファイル"
|
||||
},
|
||||
"select-file": "音声ファイルを選択",
|
||||
"unified": {
|
||||
"description": "通知で再生される音声ファイルへのパス。",
|
||||
"description": "通知で再生する音声ファイルへのパス。",
|
||||
"label": "通知音",
|
||||
"select-title": "通知音ファイルを選択"
|
||||
}
|
||||
},
|
||||
"section": {
|
||||
"description": "通知の音声効果と音量を設定します。",
|
||||
"label": "サウンド設定"
|
||||
"description": "通知音の効果と音量を設定します。",
|
||||
"label": "通知音設定"
|
||||
},
|
||||
"separate": {
|
||||
"description": "低、通常、緊急優先度の通知に異なる音声ファイルを使用します。",
|
||||
"label": "優先度ごとに異なる音を使用する"
|
||||
"description": "緊急度(低、通常、重大)ごとに異なる音声ファイルを使用します。",
|
||||
"label": "緊急度ごとに異なる通知音を使用"
|
||||
},
|
||||
"unavailable": {
|
||||
"description": "通知音を有効にするにはQt6マルチメディアをインストールしてください。",
|
||||
"label": "通知音は利用できません"
|
||||
"description": "通知音を有効にするには、Qt6 Multimedia をインストールしてください。",
|
||||
"label": "通知音を利用できません"
|
||||
},
|
||||
"volume": {
|
||||
"description": "通知音の音量レベルを調整します。",
|
||||
"label": "サウンド音量"
|
||||
"label": "通知音量"
|
||||
}
|
||||
},
|
||||
"title": "通知",
|
||||
@@ -2049,7 +2058,7 @@
|
||||
"label": "画面の明るさ"
|
||||
},
|
||||
"custom-text": {
|
||||
"description": "IPC からのカスタムテキストメッセージの OSD を表示します。",
|
||||
"description": "IPC 経由のカスタムテキストメッセージを OSD に表示します。",
|
||||
"label": "カスタムテキスト"
|
||||
},
|
||||
"input-volume": {
|
||||
@@ -2057,7 +2066,7 @@
|
||||
"label": "入力音量"
|
||||
},
|
||||
"lockkey": {
|
||||
"description": "Caps Lock、Num Lock、Scroll Lock の切り替え時にOSDを表示します。",
|
||||
"description": "Caps Lock、Num Lock、Scroll Lock の切り替え時に OSD を表示します。",
|
||||
"label": "ロックキー"
|
||||
},
|
||||
"section": {
|
||||
@@ -2072,32 +2081,32 @@
|
||||
},
|
||||
"plugins": {
|
||||
"available": {
|
||||
"description": "構成されたソースからプラグインをブラウズしてインストールします。",
|
||||
"label": "利用できるプラグイン",
|
||||
"description": "設定されたソースからプラグインを参照してインストールします。",
|
||||
"label": "利用可能なプラグイン",
|
||||
"no-plugins-description": "プラグインのソースを確認するか、リストを更新してください。",
|
||||
"no-plugins-label": "利用できるプラグインはありません"
|
||||
"no-plugins-label": "利用可能なプラグインはありません"
|
||||
},
|
||||
"filter": {
|
||||
"all": "すべて",
|
||||
"downloaded": "ダウンロード済み",
|
||||
"not-downloaded": "ダウンロードされていません"
|
||||
"not-downloaded": "未ダウンロード"
|
||||
},
|
||||
"hot-reload": {
|
||||
"description": "プラグインのファイルが変更されたときに、自動的にプラグインを再読み込みします。プラグイン開発に便利です。",
|
||||
"description": "ファイルの変更時にプラグインを自動的にリロードします。プラグイン開発に便利です。",
|
||||
"label": "ホットリロード(開発モード)"
|
||||
},
|
||||
"hot-reloaded": "プラグインを再読み込みしました: {name}",
|
||||
"hot-reloaded": "プラグインをリロードしました: {name}",
|
||||
"install": "インストール",
|
||||
"install-error": "インストールに失敗しました: {error}",
|
||||
"install-success": "{plugin} のインストールに成功しました。",
|
||||
"install-success": "{plugin} をインストールしました。",
|
||||
"installed": {
|
||||
"description": "ローカルにインストールされたすべてのプラグインを管理および設定します。",
|
||||
"description": "ローカルにインストールされたすべてのプラグインを管理・設定します。",
|
||||
"label": "インストール済みプラグイン",
|
||||
"no-plugins-description": "下の利用可能なプラグインのセクションからプラグインをインストールしてください。",
|
||||
"no-plugins-label": "プラグインはインストールされていません。"
|
||||
"no-plugins-description": "下の「利用可能なプラグイン」セクションからプラグインをインストールしてください。",
|
||||
"no-plugins-label": "インストールされているプラグインはありません"
|
||||
},
|
||||
"installing": "{plugin} をインストール中...",
|
||||
"plugin-settings-title": "{plugin} 設定",
|
||||
"plugin-settings-title": "{plugin} の設定",
|
||||
"refresh": {
|
||||
"refreshing": "プラグインリストを更新中...",
|
||||
"tooltip": "利用可能なプラグインを更新"
|
||||
@@ -2105,22 +2114,22 @@
|
||||
"settings": {
|
||||
"tooltip": "プラグイン設定"
|
||||
},
|
||||
"settings-error-not-loaded": "プラグインはロードされませんでした。",
|
||||
"settings-saved": "プラグイン設定を保存しました。",
|
||||
"settings-error-not-loaded": "プラグインは読み込まれていません",
|
||||
"settings-saved": "プラグイン設定を保存しました",
|
||||
"sources": {
|
||||
"add-custom": "カスタムリポジトリを追加",
|
||||
"add-dialog": {
|
||||
"description": "GitHubリポジトリをプラグインソースとして追加する。",
|
||||
"description": "プラグインソースとして GitHub リポジトリを追加します。",
|
||||
"error": "プラグインソースの追加に失敗しました",
|
||||
"name": "リポジトリ名",
|
||||
"name-placeholder": "私のカスタムプラグイン",
|
||||
"success": "プラグインソースが正常に追加されました。",
|
||||
"name-placeholder": "マイカスタムプラグイン",
|
||||
"success": "プラグインソースを追加しました",
|
||||
"title": "プラグインソースを追加",
|
||||
"url": "リポジトリのURL"
|
||||
"url": "リポジトリの URL"
|
||||
},
|
||||
"description": "プラグインリポジトリを管理する。",
|
||||
"description": "プラグインリポジトリを管理します。",
|
||||
"label": "ソース",
|
||||
"placeholder": "私のクールなリポジトリ",
|
||||
"placeholder": "マイリポジトリ",
|
||||
"remove": {
|
||||
"tooltip": "プラグインソースを削除"
|
||||
}
|
||||
@@ -2128,21 +2137,21 @@
|
||||
"title": "プラグイン",
|
||||
"uninstall": "アンインストール",
|
||||
"uninstall-dialog": {
|
||||
"description": "{plugin} をアンインストールしてもよろしいですか?プラグインのデータはすべて削除されます。",
|
||||
"title": "プラグインをアンインストールする"
|
||||
"description": "本当に {plugin} をアンインストールしますか?すべてのプラグインデータが削除されます。",
|
||||
"title": "プラグインのアンインストール"
|
||||
},
|
||||
"uninstall-error": "アンインストールに失敗しました: {error}",
|
||||
"uninstall-success": "{plugin} のアンインストールに成功しました。",
|
||||
"uninstalling": "{plugin} をアンインストールしています...",
|
||||
"update": "更新",
|
||||
"update-all": "すべて更新 ({count})",
|
||||
"update-all-success": "すべてのプラグインが正常に更新されました。",
|
||||
"update-available": "{count}個のプラグインアップデートがあります",
|
||||
"update-error": "プラグインの更新に失敗しました: {plugin}: {error}",
|
||||
"uninstall-success": "{plugin} をアンインストールしました。",
|
||||
"uninstalling": "{plugin} をアンインストール中...",
|
||||
"update": "アップデート",
|
||||
"update-all": "すべてアップデート ({count})",
|
||||
"update-all-success": "すべてのプラグインをアップデートしました。",
|
||||
"update-available": "{count} 件のプラグインアップデートがあります",
|
||||
"update-error": "プラグインのアップデートに失敗しました: {plugin}: {error}",
|
||||
"update-incompatible": "Noctalia v{version} 以降が必要です。",
|
||||
"update-success": "{plugin} を v{version} にアップデートしました。",
|
||||
"update-version": "v{現在} → v{新規}",
|
||||
"updating": "{plugin}を更新しています..."
|
||||
"update-version": "v{current} → v{new}",
|
||||
"updating": "{plugin} をアップデート中..."
|
||||
},
|
||||
"screen-recorder": {
|
||||
"audio": {
|
||||
@@ -2220,20 +2229,20 @@
|
||||
},
|
||||
"entry-settings": {
|
||||
"command": {
|
||||
"description": "このアクションのために実行するカスタムコマンド。空欄のままにすると、デフォルトのシステムコマンドが使用されます。",
|
||||
"description": "このアクションのために実行するカスタムコマンド。空欄にするとデフォルトのシステムコマンドを使用します。",
|
||||
"label": "コマンド",
|
||||
"placeholder": "systemctl poweroff"
|
||||
"placeholder": "例: systemctl poweroff"
|
||||
},
|
||||
"default-command": {
|
||||
"lock": "内部ロック画面(コマンドなし)",
|
||||
"logout": "内部ログアウト (コマンドなし)"
|
||||
"logout": "内部ログアウト(コマンドなし)"
|
||||
},
|
||||
"default-info": {
|
||||
"description": "カスタムコマンドが指定されていない場合、デフォルトのシステムコマンドが使用されます。",
|
||||
"label": "デフォルトのコマンド"
|
||||
},
|
||||
"title": "{entry} を構成する",
|
||||
"tooltip": "コマンドを構成する"
|
||||
"title": "「{entry}」の設定",
|
||||
"tooltip": "コマンドを設定"
|
||||
},
|
||||
"general": {
|
||||
"section": {
|
||||
@@ -2242,7 +2251,7 @@
|
||||
}
|
||||
},
|
||||
"large-buttons-style": {
|
||||
"description": "セッションメニューを大きなボタンでグリッドレイアウトで表示する。",
|
||||
"description": "セッションメニューを、グリッド配置の大きなボタンで表示します。",
|
||||
"label": "大きなボタンのスタイル"
|
||||
},
|
||||
"position": {
|
||||
@@ -2269,7 +2278,7 @@
|
||||
"label": "ストレージ使用量"
|
||||
},
|
||||
"enable-nvidia-gpu": {
|
||||
"description": "警告:これにより NVIDIA GPU が起動し、ハイブリッド グラフィックスを搭載したラップトップのバッテリー寿命に大きな影響を与える可能性があります。",
|
||||
"description": "警告: これにより NVIDIA GPU が起動状態になるため、ハイブリッドグラフィックス搭載のノート PC ではバッテリー駆動時間に大きな影響を与える可能性があります。",
|
||||
"label": "NVIDIA GPU モニタリングを有効にする"
|
||||
},
|
||||
"general": {
|
||||
@@ -2279,7 +2288,7 @@
|
||||
}
|
||||
},
|
||||
"gpu-section": {
|
||||
"label": "GPU温度 (GPU ondo)"
|
||||
"label": "GPU 温度"
|
||||
},
|
||||
"highlight-colors-section": {
|
||||
"label": "ハイライト色"
|
||||
@@ -2297,8 +2306,8 @@
|
||||
"label": "CPU 温度"
|
||||
},
|
||||
"threshold": {
|
||||
"critical": "臨界閾値 (Rinkai kīchi)",
|
||||
"warning": "警告しきい値"
|
||||
"critical": "危険閾値",
|
||||
"warning": "警告閾値"
|
||||
},
|
||||
"thresholds-section": {
|
||||
"description": "各システムメトリックの警告/危険しきい値とポーリング間隔を調整します。",
|
||||
@@ -2328,14 +2337,14 @@
|
||||
"reset": "アニメーション速度をリセット"
|
||||
},
|
||||
"box-border-radius": {
|
||||
"description": "サイドバー、カード、コンテンツパネルなど、主要なレイアウトセクションの角の丸みを調整します。",
|
||||
"label": "コンテナ半径",
|
||||
"reset": "コンテナ半径をリセット"
|
||||
"description": "サイドバー、カード、コンテンツパネルなど、主要なレイアウト部分の角の丸みを調整します。",
|
||||
"label": "コンテナの角の丸み",
|
||||
"reset": "コンテナの角の丸みをリセット"
|
||||
},
|
||||
"control-border-radius": {
|
||||
"description": "ボタン、トグル、テキストフィールドなどのインタラクティブな要素の曲率を制御します。",
|
||||
"label": "入力半径",
|
||||
"reset": "入力半径をリセット"
|
||||
"description": "ボタン、トグル、テキストフィールドなど、操作可能な要素の角の丸みを制御します。",
|
||||
"label": "入力要素の角の丸み",
|
||||
"reset": "入力要素の角の丸みをリセット"
|
||||
},
|
||||
"dim-desktop": {
|
||||
"description": "パネルやメニューが開いている間、デスクトップを暗くします。",
|
||||
@@ -2368,8 +2377,8 @@
|
||||
"label": "外観"
|
||||
},
|
||||
"settings-panel-mode": {
|
||||
"description": "設定レイアウトを選択 (再起動が必要な場合があります)",
|
||||
"label": "設定パネルモード"
|
||||
"description": "設定画面の表示形式を選択します (再起動が必要な場合があります)。",
|
||||
"label": "設定パネルの表示モード"
|
||||
},
|
||||
"shadows": {
|
||||
"description": "バーやパネルの下にドロップシャドウ(影)を表示します。",
|
||||
@@ -2449,7 +2458,7 @@
|
||||
"monitor-specific": {
|
||||
"description": "ディスプレイごとに異なる壁紙フォルダを設定します。",
|
||||
"label": "ディスプレイ別の壁紙フォルダ",
|
||||
"tooltip": "モニター壁紙フォルダ"
|
||||
"tooltip": "ディスプレイ別の壁紙フォルダ"
|
||||
},
|
||||
"recursive-search": {
|
||||
"description": "壁紙フォルダのサブフォルダ内も検索対象にします。",
|
||||
@@ -2580,7 +2589,7 @@
|
||||
"noctalia-performance": {
|
||||
"disabled": "パフォーマンスモード: 無効",
|
||||
"enabled": "パフォーマンスモード: 有効",
|
||||
"label": "Noctaliaパフォーマンス"
|
||||
"label": "Noctalia パフォーマンス"
|
||||
},
|
||||
"power-profile": {
|
||||
"changed": "電源プロファイルを変更しました",
|
||||
@@ -2626,8 +2635,8 @@
|
||||
"brightness-at": "画面の明るさ: {brightness}%",
|
||||
"cancel-timer": "タイマー",
|
||||
"clear-history": "履歴を消去",
|
||||
"click-to-start-recording": "画面録画 (録画開始)",
|
||||
"click-to-stop-recording": "画面録画 (録画停止)",
|
||||
"click-to-start-recording": "画面録画(録画開始)",
|
||||
"click-to-stop-recording": "画面録画(録画停止)",
|
||||
"close": "閉じる",
|
||||
"collapse": "サイドバーを折りたたむ",
|
||||
"connect-disconnect-devices": "Bluetooth デバイス",
|
||||
@@ -2642,7 +2651,7 @@
|
||||
"hidden-files-hide": "隠しファイル",
|
||||
"hidden-files-show": "隠しファイル",
|
||||
"home": "ホームディレクトリ",
|
||||
"input-muted": "入力をミュートに切り替え",
|
||||
"input-muted": "入力ミュート切り替え",
|
||||
"keep-awake": "スリープ防止",
|
||||
"keyboard-layout": "キーボードレイアウト: {layout}",
|
||||
"list-view": "リスト表示",
|
||||
@@ -2658,7 +2667,7 @@
|
||||
"night-light-disabled": "夜間モード",
|
||||
"night-light-enabled": "夜間モード",
|
||||
"night-light-forced": "夜間モード",
|
||||
"night-light-not-installed": "夜間モード (利用不可)",
|
||||
"night-light-not-installed": "夜間モード(利用不可)",
|
||||
"noctalia-performance-disabled": "Noctalia パフォーマンスモード",
|
||||
"noctalia-performance-enabled": "Noctalia パフォーマンスモード",
|
||||
"open-control-center": "コントロールセンター",
|
||||
@@ -2667,7 +2676,7 @@
|
||||
"open-settings": "設定",
|
||||
"open-tray-dropdown": "システムトレイ",
|
||||
"open-wallpaper-selector": "壁紙選択パネル",
|
||||
"output-muted": "出力のミュートを切り替え",
|
||||
"output-muted": "出力ミュート切り替え",
|
||||
"pause": "一時停止",
|
||||
"play": "再生",
|
||||
"power-profile": "電源プロファイル: {profile}",
|
||||
@@ -2678,7 +2687,7 @@
|
||||
"refresh-wallhaven": "Wallhaven の検索結果を更新",
|
||||
"refresh-wallpaper-list": "壁紙リストを更新",
|
||||
"remove-widget": "ウィジェットを削除",
|
||||
"screen-recorder-not-installed": "スクリーンレコーダー (未インストール)",
|
||||
"screen-recorder-not-installed": "スクリーンレコーダー(未インストール)",
|
||||
"search": "検索",
|
||||
"search-close": "検索を閉じる",
|
||||
"session-menu": "セッションメニュー",
|
||||
|
||||
+19
-10
@@ -527,6 +527,7 @@
|
||||
"close-app": "Sluit {app}",
|
||||
"connect-vpn": "Verbinding maken met {name}",
|
||||
"cycle-visualizer": "Cyclusvisualisatie",
|
||||
"delete": "Verwijderen",
|
||||
"disable-bluetooth": "Bluetooth uitschakelen",
|
||||
"disable-dnd": "Niet Storen uitschakelen",
|
||||
"disable-wifi": "Wi-Fi uitschakelen",
|
||||
@@ -735,10 +736,6 @@
|
||||
"top_left": "Linksboven",
|
||||
"top_right": "Rechtsboven"
|
||||
},
|
||||
"visualizer-quality": {
|
||||
"high": "Hoog",
|
||||
"low": "Laag"
|
||||
},
|
||||
"visualizer-types": {
|
||||
"linear": "Lineair",
|
||||
"mirrored": "Gespiegeld",
|
||||
@@ -950,10 +947,6 @@
|
||||
"description": "Stel je favoriete en genegeerde media-applicaties in.",
|
||||
"label": "Mediaspelers"
|
||||
},
|
||||
"visualizer-quality": {
|
||||
"description": "Een hogere kwaliteit gebruikt meer GPU-resources.",
|
||||
"label": "Visualisatiekwaliteit"
|
||||
},
|
||||
"visualizer-type": {
|
||||
"description": "Kies een visualisatietype voor mediaweergave.",
|
||||
"label": "Type visualisatie"
|
||||
@@ -1411,16 +1404,18 @@
|
||||
}
|
||||
},
|
||||
"edit-mode": {
|
||||
"add-widget": "Widget toevoegen",
|
||||
"button": {
|
||||
"label": "Ga naar de bewerkingsmodus"
|
||||
},
|
||||
"controls-explanation": "Linkermuisknop: Widget verplaatsen\nRechtermuisknop: Widget vergroten/verkleinen",
|
||||
"controls-explanation": "Linksklikken en slepen: Widget verplaatsen of het formaat wijzigen.\nRechtermuisknop: Het contextmenu openen.",
|
||||
"description": "Schakel de bewerkingsmodus in om desktopwidgets te verplaatsen en te herpositioneren. In de ingeschakelde modus tonen widgets een sleepomtrek en kunnen ze worden verplaatst.",
|
||||
"exit-button": "Bewerkingsmodus verlaten",
|
||||
"grid-snap": {
|
||||
"label": "Raster uitlijnen"
|
||||
},
|
||||
"label": "Bewerkingsmodus"
|
||||
"label": "Bewerkingsmodus",
|
||||
"open-settings": "Instellingen openen"
|
||||
},
|
||||
"enabled": {
|
||||
"description": "Desktopwidgets volledig in- of uitschakelen.",
|
||||
@@ -1445,6 +1440,10 @@
|
||||
"description": "Toon de achtergrondcontainer voor de mediaspeler-widget.",
|
||||
"label": "Toon achtergrond"
|
||||
},
|
||||
"show-buttons": {
|
||||
"description": "Toon mediabesturingsknoppen (afspelen/pauzeren, vorige, volgende) op de mediaspeler-widget.",
|
||||
"label": "Knoppen weergeven"
|
||||
},
|
||||
"visualizer-type": {
|
||||
"description": "Kies een visualisatietype voor de achtergrond van de desktop mediaspeler.",
|
||||
"label": "Visualisatietype"
|
||||
@@ -1718,6 +1717,10 @@
|
||||
"placeholder": "bijv. notify-send \"Achtergrond\" \"Gewijzigd\""
|
||||
}
|
||||
},
|
||||
"indicator": {
|
||||
"default-value": "Standaard: {value}",
|
||||
"system-default": "Systeemstandaard"
|
||||
},
|
||||
"launcher": {
|
||||
"settings": {
|
||||
"clip-preview": {
|
||||
@@ -1740,6 +1743,10 @@
|
||||
"description": "Items in een raster weergeven in plaats van een lijst.",
|
||||
"label": "Rasterweergave"
|
||||
},
|
||||
"icon-mode": {
|
||||
"description": "Gebruik native systeemiconen in plaats van Tabler-iconen.",
|
||||
"label": "Gebruik native iconen"
|
||||
},
|
||||
"position": {
|
||||
"description": "Kies waar het launcher-paneel verschijnt.",
|
||||
"label": "Positie"
|
||||
@@ -1865,6 +1872,7 @@
|
||||
},
|
||||
"network": {
|
||||
"bluetooth": {
|
||||
"description": "Bluetooth-beheer activeren.",
|
||||
"label": "Bluetooth inschakelen"
|
||||
},
|
||||
"section": {
|
||||
@@ -1872,6 +1880,7 @@
|
||||
},
|
||||
"title": "Netwerk",
|
||||
"wifi": {
|
||||
"description": "Draadloze netwerken beheren (vereist NetworkManager).",
|
||||
"label": "Wi-Fi inschakelen"
|
||||
}
|
||||
},
|
||||
|
||||
+19
-10
@@ -527,6 +527,7 @@
|
||||
"close-app": "Fechar {app}",
|
||||
"connect-vpn": "Conectar-se a {name}",
|
||||
"cycle-visualizer": "Visualizador de ciclo",
|
||||
"delete": "Apagar",
|
||||
"disable-bluetooth": "Desativar Bluetooth",
|
||||
"disable-dnd": "Desativar o Não Perturbe",
|
||||
"disable-wifi": "Desativar Wi-Fi",
|
||||
@@ -735,10 +736,6 @@
|
||||
"top_left": "Superior esquerda",
|
||||
"top_right": "Superior direita"
|
||||
},
|
||||
"visualizer-quality": {
|
||||
"high": "Alto(a)",
|
||||
"low": "Baixo(a)"
|
||||
},
|
||||
"visualizer-types": {
|
||||
"linear": "Linear",
|
||||
"mirrored": "Espelhado",
|
||||
@@ -950,10 +947,6 @@
|
||||
"description": "Defina seus aplicativos de mídia preferidos e ignorados.",
|
||||
"label": "Reprodutores de mídia"
|
||||
},
|
||||
"visualizer-quality": {
|
||||
"description": "Alta qualidade usa mais recursos da GPU.",
|
||||
"label": "Qualidade da visualização"
|
||||
},
|
||||
"visualizer-type": {
|
||||
"description": "Escolha um tipo de visualização para a reprodução de mídia",
|
||||
"label": "Tipo de visualização"
|
||||
@@ -1411,16 +1404,18 @@
|
||||
}
|
||||
},
|
||||
"edit-mode": {
|
||||
"add-widget": "Adicionar widget",
|
||||
"button": {
|
||||
"label": "Entrar no modo de edição"
|
||||
},
|
||||
"controls-explanation": "Botão esquerdo do mouse: Mover widget\nBotão direito do mouse: Redimensionar widget",
|
||||
"controls-explanation": "Clique esquerdo e arraste: Mover ou redimensionar o widget.\nClique direito: Abrir as opções do menu de contexto.",
|
||||
"description": "Ative o modo de edição para mover e reposicionar widgets da área de trabalho. Quando ativado, os widgets exibem um contorno de arrastar e podem ser reposicionados.",
|
||||
"exit-button": "Sair do modo de edição",
|
||||
"grid-snap": {
|
||||
"label": "Alinhar à grade"
|
||||
},
|
||||
"label": "Modo de edição"
|
||||
"label": "Modo de edição",
|
||||
"open-settings": "Abrir configurações"
|
||||
},
|
||||
"enabled": {
|
||||
"description": "Ativar ou desativar widgets da área de trabalho completamente.",
|
||||
@@ -1445,6 +1440,10 @@
|
||||
"description": "Mostrar o contêiner de fundo para o widget do reprodutor de mídia.",
|
||||
"label": "Mostrar plano de fundo"
|
||||
},
|
||||
"show-buttons": {
|
||||
"description": "Mostrar botões de controle de mídia (reproduzir/pausar, anterior, próximo) no widget do reprodutor de mídia.",
|
||||
"label": "Mostrar botões"
|
||||
},
|
||||
"visualizer-type": {
|
||||
"description": "Escolha um tipo de visualização para o fundo do reprodutor de mídia na área de trabalho.",
|
||||
"label": "Tipo de visualização"
|
||||
@@ -1718,6 +1717,10 @@
|
||||
"placeholder": "ex., notify-send \"Papel de parede\" \"Alterado\""
|
||||
}
|
||||
},
|
||||
"indicator": {
|
||||
"default-value": "Padrão: {value}",
|
||||
"system-default": "Padrão do sistema"
|
||||
},
|
||||
"launcher": {
|
||||
"settings": {
|
||||
"clip-preview": {
|
||||
@@ -1740,6 +1743,10 @@
|
||||
"description": "Exibir itens em uma grade em vez de uma lista.",
|
||||
"label": "Visualização em grade"
|
||||
},
|
||||
"icon-mode": {
|
||||
"description": "Use ícones nativos do sistema em vez de ícones Tabler.",
|
||||
"label": "Usar ícones nativos"
|
||||
},
|
||||
"position": {
|
||||
"description": "Escolha onde o painel do lançador aparece.",
|
||||
"label": "Posição"
|
||||
@@ -1865,6 +1872,7 @@
|
||||
},
|
||||
"network": {
|
||||
"bluetooth": {
|
||||
"description": "Ativar gerenciamento de Bluetooth.",
|
||||
"label": "Ativar Bluetooth"
|
||||
},
|
||||
"section": {
|
||||
@@ -1872,6 +1880,7 @@
|
||||
},
|
||||
"title": "Rede",
|
||||
"wifi": {
|
||||
"description": "Gerenciar redes sem fio (requer NetworkManager).",
|
||||
"label": "Ativar Wi-Fi"
|
||||
}
|
||||
},
|
||||
|
||||
+19
-10
@@ -527,6 +527,7 @@
|
||||
"close-app": "Закрыть {app}",
|
||||
"connect-vpn": "Подключиться к {name}",
|
||||
"cycle-visualizer": "Визуализатор циклов",
|
||||
"delete": "Удалить",
|
||||
"disable-bluetooth": "Отключить Bluetooth",
|
||||
"disable-dnd": "Отключить режим \"Не беспокоить\"",
|
||||
"disable-wifi": "Отключить Wi-Fi",
|
||||
@@ -735,10 +736,6 @@
|
||||
"top_left": "Вверху слева",
|
||||
"top_right": "Вверху справа"
|
||||
},
|
||||
"visualizer-quality": {
|
||||
"high": "Высокое",
|
||||
"low": "Низкое"
|
||||
},
|
||||
"visualizer-types": {
|
||||
"linear": "Линейный",
|
||||
"mirrored": "Зеркальный",
|
||||
@@ -950,10 +947,6 @@
|
||||
"description": "Установите предпочитаемые и игнорируемые медиа-приложения.",
|
||||
"label": "Медиаплееры"
|
||||
},
|
||||
"visualizer-quality": {
|
||||
"description": "Высокое качество использует больше ресурсов GPU.",
|
||||
"label": "Качество визуализации"
|
||||
},
|
||||
"visualizer-type": {
|
||||
"description": "Выберите тип визуализации для воспроизведения медиа.",
|
||||
"label": "Тип визуализации"
|
||||
@@ -1411,16 +1404,18 @@
|
||||
}
|
||||
},
|
||||
"edit-mode": {
|
||||
"add-widget": "Добавить виджет",
|
||||
"button": {
|
||||
"label": "Войти в режим редактирования"
|
||||
},
|
||||
"controls-explanation": "Левая кнопка мыши: Перемещать виджет\nПравая кнопка мыши: Изменять размер виджета",
|
||||
"controls-explanation": "Левый клик и перетаскивание: Переместить или изменить размер виджета.\nПравый клик: Открыть контекстное меню.",
|
||||
"description": "Включите режим редактирования, чтобы перемещать и изменять положение виджетов рабочего стола. В этом режиме виджеты отображаются с контуром перетаскивания и могут быть перемещены.",
|
||||
"exit-button": "Выйти из режима редактирования",
|
||||
"grid-snap": {
|
||||
"label": "Привязка к сетке"
|
||||
},
|
||||
"label": "Режим редактирования"
|
||||
"label": "Режим редактирования",
|
||||
"open-settings": "Открыть настройки"
|
||||
},
|
||||
"enabled": {
|
||||
"description": "Включить или отключить виджеты рабочего стола полностью.",
|
||||
@@ -1445,6 +1440,10 @@
|
||||
"description": "Показать фоновый контейнер для виджета медиаплеера.",
|
||||
"label": "Показать фон"
|
||||
},
|
||||
"show-buttons": {
|
||||
"description": "Показать кнопки управления медиа (воспроизведение/пауза, предыдущий, следующий) на виджете медиаплеера.",
|
||||
"label": "Показать кнопки"
|
||||
},
|
||||
"visualizer-type": {
|
||||
"description": "Выберите тип визуализации для фона медиаплеера.",
|
||||
"label": "Тип визуализации"
|
||||
@@ -1718,6 +1717,10 @@
|
||||
"placeholder": "например, notify-send \"Wallpaper\" \"Changed\""
|
||||
}
|
||||
},
|
||||
"indicator": {
|
||||
"default-value": "По умолчанию: {value}",
|
||||
"system-default": "По умолчанию"
|
||||
},
|
||||
"launcher": {
|
||||
"settings": {
|
||||
"clip-preview": {
|
||||
@@ -1740,6 +1743,10 @@
|
||||
"description": "Показывать элементы в виде сетки вместо списка.",
|
||||
"label": "Вид сетки"
|
||||
},
|
||||
"icon-mode": {
|
||||
"description": "Использовать нативные системные иконки вместо иконок Tabler.",
|
||||
"label": "Использовать нативные иконки"
|
||||
},
|
||||
"position": {
|
||||
"description": "Выберите, где появляется панель запуска.",
|
||||
"label": "Положение"
|
||||
@@ -1865,6 +1872,7 @@
|
||||
},
|
||||
"network": {
|
||||
"bluetooth": {
|
||||
"description": "Включить управление Bluetooth.",
|
||||
"label": "Включить Bluetooth"
|
||||
},
|
||||
"section": {
|
||||
@@ -1872,6 +1880,7 @@
|
||||
},
|
||||
"title": "Сеть",
|
||||
"wifi": {
|
||||
"description": "Управление беспроводными сетями (требуется NetworkManager).",
|
||||
"label": "Включить Wi-Fi"
|
||||
}
|
||||
},
|
||||
|
||||
+19
-10
@@ -527,6 +527,7 @@
|
||||
"close-app": "{app}'i kapat",
|
||||
"connect-vpn": "{name} bağlantısına bağlan",
|
||||
"cycle-visualizer": "Döngü görselleştirici",
|
||||
"delete": "Sil",
|
||||
"disable-bluetooth": "Bluetooth'u kapat",
|
||||
"disable-dnd": "Rahatsız Etmeyin'i Kapat",
|
||||
"disable-wifi": "Kablosuz Bağlantıyı kapat",
|
||||
@@ -735,10 +736,6 @@
|
||||
"top_left": "Sol üst",
|
||||
"top_right": "Sağ üst"
|
||||
},
|
||||
"visualizer-quality": {
|
||||
"high": "Yüksek",
|
||||
"low": "Düşük"
|
||||
},
|
||||
"visualizer-types": {
|
||||
"linear": "Doğrusal",
|
||||
"mirrored": "Yansıtılmış",
|
||||
@@ -950,10 +947,6 @@
|
||||
"description": "Tercih ettiğiniz ve yoksayacağınız ortam uygulamalarını ayarlayın.",
|
||||
"label": "Ortam oynatıcıları"
|
||||
},
|
||||
"visualizer-quality": {
|
||||
"description": "Yüksek kalite ekran kartını daha fazla kullanır.",
|
||||
"label": "Görselleştirme kalitesi"
|
||||
},
|
||||
"visualizer-type": {
|
||||
"description": "Ortam oynatımı için bir görselleştirme türü seçin",
|
||||
"label": "Görselleştirme türü"
|
||||
@@ -1411,16 +1404,18 @@
|
||||
}
|
||||
},
|
||||
"edit-mode": {
|
||||
"add-widget": "Araç ekle",
|
||||
"button": {
|
||||
"label": "Düzenleme moduna gir"
|
||||
},
|
||||
"controls-explanation": "Sol fare tuşu: Araç taşı\nSağ fare tuşu: Araç yeniden boyutlandır",
|
||||
"controls-explanation": "Sol tıklayıp sürükleyin: Bileşeni taşıyın veya yeniden boyutlandırın.\nSağ tıklayın: Bağlam menüsü seçeneklerini açın.",
|
||||
"description": "Masaüstü araçlarını taşımak ve yeniden konumlandırmak için düzenleme modunu etkinleştirin. Etkinleştirildiğinde, araçlar bir sürükleme ana hattı gösterir ve yeniden konumlandırılabilir.",
|
||||
"exit-button": "Düzenleme modundan çık",
|
||||
"grid-snap": {
|
||||
"label": "Izgaraya hizala"
|
||||
},
|
||||
"label": "Düzenleme modu"
|
||||
"label": "Düzenleme modu",
|
||||
"open-settings": "Ayarları aç"
|
||||
},
|
||||
"enabled": {
|
||||
"description": "Masaüstü araçlarını tamamen etkinleştir veya devre dışı bırak.",
|
||||
@@ -1445,6 +1440,10 @@
|
||||
"description": "Medya oynatıcı widget'ı için arka plan konteynerini göster.",
|
||||
"label": "Arka planı göster"
|
||||
},
|
||||
"show-buttons": {
|
||||
"description": "Medya oynatıcı widget'ında medya kontrol düğmelerini (oynat/durdur, önceki, sonraki) göster.",
|
||||
"label": "Düğmeleri göster"
|
||||
},
|
||||
"visualizer-type": {
|
||||
"description": "Masaüstü medya oynatıcı arka planı için bir görselleştirme türü seçin.",
|
||||
"label": "Görselleştirme türü"
|
||||
@@ -1718,6 +1717,10 @@
|
||||
"placeholder": "örn., notify-send \"Duvar Kâğıdı\" \"Değişti\""
|
||||
}
|
||||
},
|
||||
"indicator": {
|
||||
"default-value": "Varsayılan: {value}",
|
||||
"system-default": "Sistem Varsayılanı"
|
||||
},
|
||||
"launcher": {
|
||||
"settings": {
|
||||
"clip-preview": {
|
||||
@@ -1740,6 +1743,10 @@
|
||||
"description": "Öğeleri liste yerine ızgara düzeninde görüntüle.",
|
||||
"label": "Izgara görünümü"
|
||||
},
|
||||
"icon-mode": {
|
||||
"description": "Tabler simgeleri yerine yerel sistem simgelerini kullanın.",
|
||||
"label": "Yerel simgeleri kullan"
|
||||
},
|
||||
"position": {
|
||||
"description": "Başlatıcı panelinin nerede görüneceğini seçin.",
|
||||
"label": "Konum"
|
||||
@@ -1865,6 +1872,7 @@
|
||||
},
|
||||
"network": {
|
||||
"bluetooth": {
|
||||
"description": "Bluetooth yönetimini etkinleştir.",
|
||||
"label": "Bluetooth'u Etkinleştir"
|
||||
},
|
||||
"section": {
|
||||
@@ -1872,6 +1880,7 @@
|
||||
},
|
||||
"title": "Ağ",
|
||||
"wifi": {
|
||||
"description": "Kablosuz ağları yönet (NetworkManager gerektirir).",
|
||||
"label": "Kablosuz Bağlantıyı Etkinleştir"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -527,6 +527,7 @@
|
||||
"close-app": "Закрити {app}",
|
||||
"connect-vpn": "Підключитися до {name}",
|
||||
"cycle-visualizer": "Візуалізатор циклів",
|
||||
"delete": "Видалити",
|
||||
"disable-bluetooth": "Вимкнути Bluetooth",
|
||||
"disable-dnd": "Вимкнути режим \"Не турбувати\"",
|
||||
"disable-wifi": "Вимкнути Wi-Fi",
|
||||
@@ -735,10 +736,6 @@
|
||||
"top_left": "Згори ліворуч",
|
||||
"top_right": "Згори праворуч"
|
||||
},
|
||||
"visualizer-quality": {
|
||||
"high": "Високий",
|
||||
"low": "Низький"
|
||||
},
|
||||
"visualizer-types": {
|
||||
"linear": "Лінійний",
|
||||
"mirrored": "Дзеркальний",
|
||||
@@ -950,10 +947,6 @@
|
||||
"description": "Встановіть бажані та ігноровані медіазастосунки.",
|
||||
"label": "Медіаплеєри"
|
||||
},
|
||||
"visualizer-quality": {
|
||||
"description": "Висока якість використовує більше ресурсів GPU.",
|
||||
"label": "Якість візуалізації"
|
||||
},
|
||||
"visualizer-type": {
|
||||
"description": "Виберіть тип візуалізації для відтворення медіа",
|
||||
"label": "Тип візуалізації"
|
||||
@@ -1411,16 +1404,18 @@
|
||||
}
|
||||
},
|
||||
"edit-mode": {
|
||||
"add-widget": "Додати віджет",
|
||||
"button": {
|
||||
"label": "Увійти в режим редагування"
|
||||
},
|
||||
"controls-explanation": "Ліва кнопка миші: Переміщувати віджет\nПрава кнопка миші: Змінювати розмір віджета",
|
||||
"controls-explanation": "Клацання лівою кнопкою миші та перетягування: Перемістити або змінити розмір віджета.\nКлацання правою кнопкою миші: Відкрити контекстне меню з опціями.",
|
||||
"description": "Увімкніть режим редагування, щоб переміщувати та змінювати розташування віджетів робочого столу. У ввімкненому стані віджети відображають контур перетягування, і їх можна переміщувати.",
|
||||
"exit-button": "Вийти з режиму редагування",
|
||||
"grid-snap": {
|
||||
"label": "Прив'язка до сітки"
|
||||
},
|
||||
"label": "Режим редагування"
|
||||
"label": "Режим редагування",
|
||||
"open-settings": "Відкрити налаштування"
|
||||
},
|
||||
"enabled": {
|
||||
"description": "Увімкнути або вимкнути віджети робочого столу повністю.",
|
||||
@@ -1445,6 +1440,10 @@
|
||||
"description": "Показати фоновий контейнер для віджета медіаплеєра.",
|
||||
"label": "Показати фон"
|
||||
},
|
||||
"show-buttons": {
|
||||
"description": "Показати кнопки керування медіа (відтворення/пауза, попередній, наступний) на віджеті медіаплеєра.",
|
||||
"label": "Показати кнопки"
|
||||
},
|
||||
"visualizer-type": {
|
||||
"description": "Оберіть тип візуалізації для фону медіаплеєра на робочому столі.",
|
||||
"label": "Тип візуалізації"
|
||||
@@ -1718,6 +1717,10 @@
|
||||
"placeholder": "напр., notify-send \"Шпалери\" \"Змінено\""
|
||||
}
|
||||
},
|
||||
"indicator": {
|
||||
"default-value": "За замовчуванням: {value}",
|
||||
"system-default": "Системні налаштування за замовчуванням"
|
||||
},
|
||||
"launcher": {
|
||||
"settings": {
|
||||
"clip-preview": {
|
||||
@@ -1740,6 +1743,10 @@
|
||||
"description": "Показувати елементи у вигляді сітки замість списку.",
|
||||
"label": "Режим сітки"
|
||||
},
|
||||
"icon-mode": {
|
||||
"description": "Використовувати нативні системні іконки замість іконок Tabler.",
|
||||
"label": "Використовувати нативні іконки"
|
||||
},
|
||||
"position": {
|
||||
"description": "Виберіть, де з'являється панель лаунчера.",
|
||||
"label": "Положення"
|
||||
@@ -1865,6 +1872,7 @@
|
||||
},
|
||||
"network": {
|
||||
"bluetooth": {
|
||||
"description": "Активувати керування Bluetooth.",
|
||||
"label": "Увімкнути Bluetooth"
|
||||
},
|
||||
"section": {
|
||||
@@ -1872,6 +1880,7 @@
|
||||
},
|
||||
"title": "Мережа",
|
||||
"wifi": {
|
||||
"description": "Керування бездротовими мережами (потребує NetworkManager).",
|
||||
"label": "Увімкнути Wi-Fi"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -527,6 +527,7 @@
|
||||
"close-app": "关闭 {app}",
|
||||
"connect-vpn": "连接 {name}",
|
||||
"cycle-visualizer": "切换可视化器样式",
|
||||
"delete": "删除",
|
||||
"disable-bluetooth": "禁用蓝牙",
|
||||
"disable-dnd": "关闭勿扰模式",
|
||||
"disable-wifi": "禁用Wi-Fi",
|
||||
@@ -735,10 +736,6 @@
|
||||
"top_left": "左上",
|
||||
"top_right": "右上"
|
||||
},
|
||||
"visualizer-quality": {
|
||||
"high": "高",
|
||||
"low": "低"
|
||||
},
|
||||
"visualizer-types": {
|
||||
"linear": "线性",
|
||||
"mirrored": "镜像",
|
||||
@@ -950,10 +947,6 @@
|
||||
"description": "设置您首选和要忽略的媒体应用程序。",
|
||||
"label": "媒体播放器"
|
||||
},
|
||||
"visualizer-quality": {
|
||||
"description": "高质量使用更多 GPU 资源。",
|
||||
"label": "可视化质量"
|
||||
},
|
||||
"visualizer-type": {
|
||||
"description": "选择媒体播放的可视化类型",
|
||||
"label": "可视化类型"
|
||||
@@ -1411,16 +1404,18 @@
|
||||
}
|
||||
},
|
||||
"edit-mode": {
|
||||
"add-widget": "添加小部件",
|
||||
"button": {
|
||||
"label": "进入编辑模式"
|
||||
},
|
||||
"controls-explanation": "鼠标左键:移动小组件\n鼠标右键:调整小组件大小",
|
||||
"controls-explanation": "左键点击并拖动:移动或调整小部件大小。\n右键点击:打开上下文菜单选项。",
|
||||
"description": "启用编辑模式以移动和重新定位桌面小组件。启用后,小组件会显示拖动轮廓,并且可以重新定位。",
|
||||
"exit-button": "退出编辑模式",
|
||||
"grid-snap": {
|
||||
"label": "网格对齐"
|
||||
},
|
||||
"label": "编辑模式"
|
||||
"label": "编辑模式",
|
||||
"open-settings": "打开设置"
|
||||
},
|
||||
"enabled": {
|
||||
"description": "完全启用或禁用桌面小部件。",
|
||||
@@ -1445,6 +1440,10 @@
|
||||
"description": "显示媒体播放器小部件的背景容器。",
|
||||
"label": "显示背景"
|
||||
},
|
||||
"show-buttons": {
|
||||
"description": "在媒体播放器小部件上显示媒体控制按钮(播放/暂停、上一个、下一个)。",
|
||||
"label": "显示按钮"
|
||||
},
|
||||
"visualizer-type": {
|
||||
"description": "选择桌面媒体播放器的背景可视化类型。",
|
||||
"label": "可视化类型"
|
||||
@@ -1718,6 +1717,10 @@
|
||||
"placeholder": "例如:notify-send \"壁纸\" \"已更改\""
|
||||
}
|
||||
},
|
||||
"indicator": {
|
||||
"default-value": "默认值:{value}",
|
||||
"system-default": "系统默认"
|
||||
},
|
||||
"launcher": {
|
||||
"settings": {
|
||||
"clip-preview": {
|
||||
@@ -1740,6 +1743,10 @@
|
||||
"description": "以网格布局而非列表显示项目。",
|
||||
"label": "网格视图"
|
||||
},
|
||||
"icon-mode": {
|
||||
"description": "使用本地系统图标而不是 Tabler 图标。",
|
||||
"label": "使用本地图标"
|
||||
},
|
||||
"position": {
|
||||
"description": "选择启动器面板出现的位置。",
|
||||
"label": "位置"
|
||||
@@ -1865,6 +1872,7 @@
|
||||
},
|
||||
"network": {
|
||||
"bluetooth": {
|
||||
"description": "启用蓝牙管理。",
|
||||
"label": "启用蓝牙"
|
||||
},
|
||||
"section": {
|
||||
@@ -1872,6 +1880,7 @@
|
||||
},
|
||||
"title": "网络",
|
||||
"wifi": {
|
||||
"description": "管理无线网络(需要 NetworkManager)。",
|
||||
"label": "启用 Wi-Fi"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -179,7 +179,8 @@
|
||||
"customLaunchPrefixEnabled": false,
|
||||
"customLaunchPrefix": "",
|
||||
"viewMode": "list",
|
||||
"showCategories": true
|
||||
"showCategories": true,
|
||||
"iconMode": "tabler"
|
||||
},
|
||||
"controlCenter": {
|
||||
"position": "close_to_bar_button",
|
||||
@@ -353,7 +354,6 @@
|
||||
"volumeOverdrive": false,
|
||||
"cavaFrameRate": 30,
|
||||
"visualizerType": "linear",
|
||||
"visualizerQuality": "high",
|
||||
"mprisBlacklist": [],
|
||||
"preferredPlayer": "",
|
||||
"externalMixer": "pwvucontrol || pavucontrol"
|
||||
@@ -418,7 +418,6 @@
|
||||
},
|
||||
"desktopWidgets": {
|
||||
"enabled": false,
|
||||
"editMode": false,
|
||||
"gridSnap": false,
|
||||
"monitorWidgets": []
|
||||
}
|
||||
|
||||
@@ -387,6 +387,8 @@ Singleton {
|
||||
// View mode: "list" or "grid"
|
||||
property string viewMode: "list"
|
||||
property bool showCategories: true
|
||||
// Icon mode: "tabler" or "native"
|
||||
property string iconMode: "tabler"
|
||||
}
|
||||
|
||||
// control center
|
||||
@@ -575,7 +577,6 @@ Singleton {
|
||||
property bool volumeOverdrive: false
|
||||
property int cavaFrameRate: 30
|
||||
property string visualizerType: "linear"
|
||||
property string visualizerQuality: "high"
|
||||
property list<string> mprisBlacklist: []
|
||||
property string preferredPlayer: ""
|
||||
property string externalMixer: "pwvucontrol || pavucontrol"
|
||||
@@ -651,7 +652,6 @@ Singleton {
|
||||
// desktop widgets
|
||||
property JsonObject desktopWidgets: JsonObject {
|
||||
property bool enabled: false
|
||||
property bool editMode: false
|
||||
property bool gridSnap: false
|
||||
property list<var> monitorWidgets: []
|
||||
// Format: [{ "name": "DP-1", "widgets": [...] }, { "name": "HDMI-1", "widgets": [...] }]
|
||||
|
||||
@@ -212,6 +212,7 @@ Singleton {
|
||||
barVisible: BarService.isVisible,
|
||||
lockScreenActive: PanelService.lockScreen?.active || false,
|
||||
wallpapers: WallpaperService.currentWallpapers || {},
|
||||
desktopWidgetsEditMode: DesktopWidgetRegistry.editMode || false,
|
||||
// -------------
|
||||
display: shellStateData.display || {},
|
||||
notificationsState: shellStateData.notificationsState || {},
|
||||
|
||||
@@ -29,23 +29,6 @@ Item {
|
||||
return (item && item.visible) ? Math.round(item[prop]) : 0;
|
||||
}
|
||||
|
||||
// Create a dummy pluginApi that returns empty strings to avoid undefined warnings
|
||||
property var _dummyApi: QtObject {
|
||||
property var pluginSettings: ({})
|
||||
property var manifest: ({
|
||||
metadata: {
|
||||
defaultSettings: {}
|
||||
}
|
||||
})
|
||||
|
||||
function tr(key) {
|
||||
return "";
|
||||
}
|
||||
function trp(key, count) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
// Only load if widget exists in registry
|
||||
function checkWidgetExists(): bool {
|
||||
return root.widgetId !== "" && BarWidgetRegistry.hasWidget(root.widgetId);
|
||||
@@ -84,13 +67,6 @@ Item {
|
||||
if (!item)
|
||||
return;
|
||||
|
||||
// Inject dummy API immediately for plugin widgets before any other code runs
|
||||
if (BarWidgetRegistry.isPluginWidget(widgetId) && item.hasOwnProperty("pluginApi")) {
|
||||
if (!item.pluginApi) {
|
||||
item.pluginApi = root._dummyApi;
|
||||
}
|
||||
}
|
||||
|
||||
Logger.d("BarWidgetLoader", "Loading widget", widgetId, "on screen:", widgetScreen.name);
|
||||
|
||||
// Apply properties to loaded widget
|
||||
@@ -113,11 +89,11 @@ Item {
|
||||
}
|
||||
|
||||
// Inject plugin API for plugin widgets
|
||||
// The API is fully populated (settings/translations already loaded) by PluginService
|
||||
if (BarWidgetRegistry.isPluginWidget(widgetId)) {
|
||||
var pluginId = widgetId.replace("plugin:", "");
|
||||
var api = PluginService.getPluginAPI(pluginId);
|
||||
if (api && item.hasOwnProperty("pluginApi")) {
|
||||
// Inject API into widget
|
||||
item.pluginApi = api;
|
||||
Logger.d("BarWidgetLoader", "Injected plugin API for", widgetId);
|
||||
}
|
||||
|
||||
@@ -55,6 +55,23 @@ Item {
|
||||
|
||||
readonly property bool shouldShow: (currentVisualizerType !== "" && currentVisualizerType !== "none") && (!hideWhenIdle || MediaService.isPlaying)
|
||||
|
||||
// Register/unregister with CavaService based on visibility
|
||||
readonly property string cavaComponentId: "bar:audiovisualizer:" + root.screen.name + ":" + root.section + ":" + root.sectionWidgetIndex
|
||||
|
||||
onShouldShowChanged: {
|
||||
if (root.shouldShow) {
|
||||
CavaService.registerComponent(root.cavaComponentId);
|
||||
} else {
|
||||
CavaService.unregisterComponent(root.cavaComponentId);
|
||||
}
|
||||
}
|
||||
|
||||
Component.onDestruction: {
|
||||
if (root.shouldShow) {
|
||||
CavaService.unregisterComponent(root.cavaComponentId);
|
||||
}
|
||||
}
|
||||
|
||||
implicitWidth: !shouldShow ? 0 : isVerticalBar ? Style.capsuleHeight : visualizerWidth
|
||||
implicitHeight: !shouldShow ? 0 : isVerticalBar ? visualizerWidth : Style.capsuleHeight
|
||||
visible: shouldShow
|
||||
|
||||
@@ -66,6 +66,24 @@ Item {
|
||||
return showArtistFirst ? (artist ? `${artist} - ${track}` : track) : (artist ? `${track} - ${artist}` : track);
|
||||
}
|
||||
|
||||
// CavaService registration for visualizer
|
||||
readonly property string cavaComponentId: "bar:mediamini:" + root.screen.name + ":" + root.section + ":" + root.sectionWidgetIndex
|
||||
readonly property bool needsCava: root.showVisualizer && root.visualizerType !== "" && root.visualizerType !== "none"
|
||||
|
||||
onNeedsCavaChanged: {
|
||||
if (root.needsCava) {
|
||||
CavaService.registerComponent(root.cavaComponentId);
|
||||
} else {
|
||||
CavaService.unregisterComponent(root.cavaComponentId);
|
||||
}
|
||||
}
|
||||
|
||||
Component.onDestruction: {
|
||||
if (root.needsCava) {
|
||||
CavaService.unregisterComponent(root.cavaComponentId);
|
||||
}
|
||||
}
|
||||
|
||||
readonly property string tooltipText: {
|
||||
var text = title;
|
||||
var controls = [];
|
||||
|
||||
@@ -632,7 +632,7 @@ Item {
|
||||
anchors.fill: parent
|
||||
|
||||
Loader {
|
||||
active: (labelMode !== "none") && (!root.showLabelsOnlyWhenOccupied || model.isOccupied)
|
||||
active: (labelMode !== "none") && (!root.showLabelsOnlyWhenOccupied || model.isOccupied || model.isFocused)
|
||||
sourceComponent: Component {
|
||||
NText {
|
||||
x: (pillVertical.width - width) / 2
|
||||
@@ -662,7 +662,7 @@ Item {
|
||||
if (model.isOccupied)
|
||||
return Color.mOnSecondary;
|
||||
|
||||
return Color.mOnSurface;
|
||||
return Color.mOnSecondary;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -677,7 +677,7 @@ Item {
|
||||
if (model.isOccupied)
|
||||
return Color.mSecondary;
|
||||
|
||||
return Color.mOutline;
|
||||
return Qt.alpha(Color.mSecondary, 0.3);
|
||||
}
|
||||
z: 0
|
||||
|
||||
|
||||
@@ -15,6 +15,27 @@ NBox {
|
||||
// Track whether we have an active media player
|
||||
readonly property bool hasActivePlayer: MediaService.currentPlayer && MediaService.canPlay
|
||||
|
||||
// CavaService registration for visualizer
|
||||
readonly property bool needsCava: Settings.data.audio.visualizerType !== "" && Settings.data.audio.visualizerType !== "none"
|
||||
|
||||
onNeedsCavaChanged: {
|
||||
if (root.needsCava) {
|
||||
CavaService.registerComponent("mediacard");
|
||||
} else {
|
||||
CavaService.unregisterComponent("mediacard");
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
if (root.needsCava) {
|
||||
CavaService.registerComponent("mediacard");
|
||||
}
|
||||
}
|
||||
|
||||
Component.onDestruction: {
|
||||
CavaService.unregisterComponent("mediacard");
|
||||
}
|
||||
|
||||
property string wallpaper: WallpaperService.getWallpaper(screen.name)
|
||||
|
||||
// External state management
|
||||
|
||||
@@ -4,6 +4,7 @@ import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Wayland
|
||||
import qs.Commons
|
||||
import qs.Modules.Panels.Settings
|
||||
import qs.Services.Compositor
|
||||
import qs.Services.Noctalia
|
||||
import qs.Services.Power
|
||||
@@ -70,6 +71,57 @@ Variants {
|
||||
Logger.d("DesktopWidgets", "Created panel window for", screen?.name);
|
||||
}
|
||||
|
||||
// Add a new widget to the current screen
|
||||
function addWidgetToCurrentScreen(widgetId) {
|
||||
var monitorName = window.screen.name;
|
||||
var newWidget = {
|
||||
"id": widgetId
|
||||
};
|
||||
|
||||
// Load default metadata if available
|
||||
var metadata = DesktopWidgetRegistry.widgetMetadata[widgetId];
|
||||
if (metadata) {
|
||||
Object.keys(metadata).forEach(function (key) {
|
||||
if (key !== "allowUserSettings") {
|
||||
newWidget[key] = metadata[key];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Place at screen center
|
||||
newWidget.x = (window.screen.width / 2) - 100;
|
||||
newWidget.y = (window.screen.height / 2) - 100;
|
||||
newWidget.scale = 1.0;
|
||||
|
||||
// Get current widgets and add new one
|
||||
var monitorWidgets = Settings.data.desktopWidgets.monitorWidgets || [];
|
||||
var newMonitorWidgets = monitorWidgets.slice();
|
||||
var found = false;
|
||||
|
||||
for (var i = 0; i < newMonitorWidgets.length; i++) {
|
||||
if (newMonitorWidgets[i].name === monitorName) {
|
||||
var widgets = (newMonitorWidgets[i].widgets || []).slice();
|
||||
widgets.push(newWidget);
|
||||
newMonitorWidgets[i] = {
|
||||
"name": monitorName,
|
||||
"widgets": widgets
|
||||
};
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
newMonitorWidgets.push({
|
||||
"name": monitorName,
|
||||
"widgets": [newWidget]
|
||||
});
|
||||
}
|
||||
|
||||
Settings.data.desktopWidgets.monitorWidgets = newMonitorWidgets;
|
||||
Logger.i("DesktopWidgets", "Added widget", widgetId, "to", monitorName);
|
||||
}
|
||||
|
||||
Item {
|
||||
id: widgetsContainer
|
||||
anchors.fill: parent
|
||||
@@ -78,7 +130,7 @@ Variants {
|
||||
// Using Loader to properly unload Canvas when not needed
|
||||
Loader {
|
||||
id: gridOverlayLoader
|
||||
active: Settings.data.desktopWidgets.editMode && Settings.data.desktopWidgets.enabled && Settings.data.desktopWidgets.gridSnap
|
||||
active: DesktopWidgetRegistry.editMode && Settings.data.desktopWidgets.enabled && Settings.data.desktopWidgets.gridSnap
|
||||
anchors.fill: parent
|
||||
z: -1 // Behind widgets but above background
|
||||
asynchronous: false
|
||||
@@ -197,6 +249,10 @@ Variants {
|
||||
gridOverlay.requestPaint();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: DesktopWidgetRegistry
|
||||
function onEditModeChanged() {
|
||||
if (gridOverlayLoader.active) {
|
||||
gridOverlay.requestPaint();
|
||||
@@ -245,140 +301,188 @@ Variants {
|
||||
}
|
||||
}
|
||||
|
||||
// Background for edit mode controls
|
||||
// Edit mode controls panel
|
||||
Rectangle {
|
||||
id: editModeControlsBackground
|
||||
visible: Settings.data.desktopWidgets.editMode && Settings.data.desktopWidgets.enabled
|
||||
id: editModeControlsPanel
|
||||
visible: DesktopWidgetRegistry.editMode && Settings.data.desktopWidgets.enabled
|
||||
|
||||
readonly property string barPos: Settings.data.bar.position || "top"
|
||||
readonly property bool barFloating: Settings.data.bar.floating || false
|
||||
// Calculate offset from bar based on position and floating state
|
||||
|
||||
readonly property int barOffsetTop: {
|
||||
if (barPos !== "top")
|
||||
return Style.marginXL * Style.uiScaleRatio;
|
||||
return Style.marginM;
|
||||
const floatMarginV = barFloating ? Math.ceil(Settings.data.bar.marginVertical * Style.marginXL) : 0;
|
||||
return Style.barHeight + floatMarginV + Style.marginM + (Style.marginXL * Style.uiScaleRatio);
|
||||
return Style.barHeight + floatMarginV + Style.marginM;
|
||||
}
|
||||
readonly property int barOffsetRight: {
|
||||
if (barPos !== "right")
|
||||
return Style.marginXL * Style.uiScaleRatio;
|
||||
return Style.marginM;
|
||||
const floatMarginH = barFloating ? Math.ceil(Settings.data.bar.marginHorizontal * Style.marginXL) : 0;
|
||||
return Style.barHeight + floatMarginH + Style.marginM + (Style.marginXL * Style.uiScaleRatio);
|
||||
return Style.barHeight + floatMarginH + Style.marginM;
|
||||
}
|
||||
|
||||
anchors {
|
||||
top: parent.top
|
||||
right: parent.right
|
||||
topMargin: barOffsetTop
|
||||
rightMargin: barOffsetRight
|
||||
// Internal state for drag tracking (session-only, resets on restart)
|
||||
QtObject {
|
||||
id: panelInternal
|
||||
property bool isDragging: false
|
||||
property real dragOffsetX: 0
|
||||
property real dragOffsetY: 0
|
||||
// Default position: top-right corner accounting for bar
|
||||
property real baseX: widgetsContainer.width - editModeControlsPanel.width - editModeControlsPanel.barOffsetRight
|
||||
property real baseY: editModeControlsPanel.barOffsetTop
|
||||
}
|
||||
|
||||
// Calculate width to accommodate all controls
|
||||
width: {
|
||||
var buttonWidth = editModeButton.visible ? editModeButton.implicitWidth : 0;
|
||||
var explanationWidth = controlsExplanation.visible ? controlsExplanation.width : 0;
|
||||
var checkboxWidth = gridSnapCheckbox.visible ? gridSnapCheckbox.implicitWidth : 0;
|
||||
return Math.max(buttonWidth, explanationWidth, checkboxWidth, 200) + (Style.marginXL * 2);
|
||||
// Reset position when bar position changes
|
||||
Connections {
|
||||
target: Settings.data.bar
|
||||
function onPositionChanged() {
|
||||
panelInternal.baseX = widgetsContainer.width - editModeControlsPanel.width - editModeControlsPanel.barOffsetRight;
|
||||
panelInternal.baseY = editModeControlsPanel.barOffsetTop;
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate height to cover all controls with spacing
|
||||
height: {
|
||||
var buttonHeight = editModeButton.visible ? editModeButton.height : 0;
|
||||
var explanationHeight = controlsExplanation.visible ? controlsExplanation.height : 0;
|
||||
var checkboxHeight = gridSnapCheckbox.visible ? gridSnapCheckbox.height : 0;
|
||||
return buttonHeight + Style.marginXL + explanationHeight + Style.marginXL + checkboxHeight + (Style.marginXL * 2);
|
||||
}
|
||||
x: panelInternal.isDragging ? panelInternal.dragOffsetX : panelInternal.baseX
|
||||
y: panelInternal.isDragging ? panelInternal.dragOffsetY : panelInternal.baseY
|
||||
|
||||
width: controlsLayout.implicitWidth + (Style.marginXL * 2)
|
||||
height: controlsLayout.implicitHeight + (Style.marginXL * 2)
|
||||
|
||||
color: Qt.rgba(Color.mSurface.r, Color.mSurface.g, Color.mSurface.b, 0.85)
|
||||
radius: Style.radiusL
|
||||
border {
|
||||
width: 1
|
||||
color: Qt.alpha(Color.mOutline, 0.2)
|
||||
width: Style.borderS
|
||||
color: Color.mOutline
|
||||
}
|
||||
z: 9999
|
||||
}
|
||||
|
||||
// Exit edit mode button
|
||||
NButton {
|
||||
id: editModeButton
|
||||
visible: Settings.data.desktopWidgets.editMode && Settings.data.desktopWidgets.enabled
|
||||
// Drag area for relocating the panel
|
||||
MouseArea {
|
||||
id: dragArea
|
||||
anchors.fill: parent
|
||||
cursorShape: panelInternal.isDragging ? Qt.ClosedHandCursor : Qt.OpenHandCursor
|
||||
|
||||
readonly property string barPos: Settings.data.bar.position || "top"
|
||||
readonly property bool barFloating: Settings.data.bar.floating || false
|
||||
// Calculate offset from bar based on position and floating state
|
||||
readonly property int barOffsetTop: {
|
||||
if (barPos !== "top")
|
||||
return Style.marginXL * Style.uiScaleRatio;
|
||||
const floatMarginV = barFloating ? Math.ceil(Settings.data.bar.marginVertical * Style.marginXL) : 0;
|
||||
return Style.barHeight + floatMarginV + Style.marginM + (Style.marginXL * Style.uiScaleRatio);
|
||||
}
|
||||
readonly property int barOffsetRight: {
|
||||
if (barPos !== "right")
|
||||
return Style.marginXL * Style.uiScaleRatio;
|
||||
const floatMarginH = barFloating ? Math.ceil(Settings.data.bar.marginHorizontal * Style.marginXL) : 0;
|
||||
return Style.barHeight + floatMarginH + Style.marginM + (Style.marginXL * Style.uiScaleRatio);
|
||||
property point pressPos: Qt.point(0, 0)
|
||||
|
||||
onPressed: mouse => {
|
||||
pressPos = mapToItem(widgetsContainer, mouse.x, mouse.y);
|
||||
panelInternal.dragOffsetX = editModeControlsPanel.x;
|
||||
panelInternal.dragOffsetY = editModeControlsPanel.y;
|
||||
panelInternal.isDragging = true;
|
||||
}
|
||||
|
||||
onPositionChanged: mouse => {
|
||||
if (panelInternal.isDragging && pressed) {
|
||||
var currentPos = mapToItem(widgetsContainer, mouse.x, mouse.y);
|
||||
var deltaX = currentPos.x - pressPos.x;
|
||||
var deltaY = currentPos.y - pressPos.y;
|
||||
|
||||
var newX = panelInternal.baseX + deltaX;
|
||||
var newY = panelInternal.baseY + deltaY;
|
||||
|
||||
// Boundary clamping
|
||||
newX = Math.max(0, Math.min(newX, widgetsContainer.width - editModeControlsPanel.width));
|
||||
newY = Math.max(0, Math.min(newY, widgetsContainer.height - editModeControlsPanel.height));
|
||||
|
||||
panelInternal.dragOffsetX = newX;
|
||||
panelInternal.dragOffsetY = newY;
|
||||
}
|
||||
}
|
||||
|
||||
onReleased: {
|
||||
if (panelInternal.isDragging) {
|
||||
panelInternal.baseX = panelInternal.dragOffsetX;
|
||||
panelInternal.baseY = panelInternal.dragOffsetY;
|
||||
panelInternal.isDragging = false;
|
||||
}
|
||||
}
|
||||
|
||||
onCanceled: {
|
||||
panelInternal.isDragging = false;
|
||||
}
|
||||
}
|
||||
|
||||
anchors {
|
||||
top: editModeControlsBackground.top
|
||||
right: editModeControlsBackground.right
|
||||
topMargin: Style.marginXL
|
||||
rightMargin: Style.marginXL
|
||||
}
|
||||
text: I18n.tr("settings.desktop-widgets.edit-mode.exit-button")
|
||||
icon: "logout"
|
||||
//backgroundColor: Color.mSurface
|
||||
//textColor: Color.mOnSurface
|
||||
//hoverColor: Color.mSurfaceVariant
|
||||
outlined: false
|
||||
fontSize: Style.fontSizeM * 1.1
|
||||
iconSize: Style.fontSizeL * 1.1
|
||||
z: 10000
|
||||
onClicked: Settings.data.desktopWidgets.editMode = false
|
||||
}
|
||||
ColumnLayout {
|
||||
id: controlsLayout
|
||||
anchors {
|
||||
fill: parent
|
||||
margins: Style.marginXL
|
||||
}
|
||||
spacing: Style.marginL
|
||||
|
||||
// Controls explanation text
|
||||
NText {
|
||||
id: controlsExplanation
|
||||
visible: Settings.data.desktopWidgets.editMode && Settings.data.desktopWidgets.enabled
|
||||
anchors {
|
||||
top: editModeButton.bottom
|
||||
right: editModeControlsBackground.right
|
||||
topMargin: Style.marginXL
|
||||
rightMargin: Style.marginXL
|
||||
}
|
||||
text: I18n.tr("settings.desktop-widgets.edit-mode.controls-explanation")
|
||||
pointSize: Style.fontSizeS
|
||||
color: Color.mOnSurfaceVariant
|
||||
horizontalAlignment: Text.AlignRight
|
||||
wrapMode: Text.WordWrap
|
||||
width: Math.min(implicitWidth, 300 * Style.uiScaleRatio)
|
||||
z: 10000
|
||||
}
|
||||
RowLayout {
|
||||
Layout.alignment: Qt.AlignRight
|
||||
spacing: Style.marginS
|
||||
|
||||
// Grid snap checkbox
|
||||
RowLayout {
|
||||
id: gridSnapCheckbox
|
||||
visible: Settings.data.desktopWidgets.editMode && Settings.data.desktopWidgets.enabled
|
||||
anchors {
|
||||
top: controlsExplanation.bottom
|
||||
right: editModeControlsBackground.right
|
||||
topMargin: Style.marginXL
|
||||
rightMargin: Style.marginXL
|
||||
}
|
||||
spacing: Style.marginS
|
||||
z: 10000
|
||||
NIconButton {
|
||||
id: addWidgetButton
|
||||
icon: "layout-grid-add"
|
||||
tooltipText: I18n.tr("settings.desktop-widgets.edit-mode.add-widget")
|
||||
onClicked: {
|
||||
var popupMenuWindow = PanelService.getPopupMenuWindow(window.screen);
|
||||
if (popupMenuWindow) {
|
||||
// Build menu items from registry
|
||||
var items = [];
|
||||
var widgets = DesktopWidgetRegistry.widgets;
|
||||
for (var id in widgets) {
|
||||
items.push({
|
||||
action: id,
|
||||
text: DesktopWidgetRegistry.getWidgetDisplayName(id),
|
||||
icon: "layout-grid-add"
|
||||
});
|
||||
}
|
||||
var globalPos = addWidgetButton.mapToItem(null, 0, addWidgetButton.height + Style.marginS);
|
||||
popupMenuWindow.showDynamicContextMenu(items, globalPos.x, globalPos.y, function (widgetId) {
|
||||
addWidgetToCurrentScreen(widgetId);
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NText {
|
||||
text: I18n.tr("settings.desktop-widgets.edit-mode.grid-snap.label")
|
||||
pointSize: Style.fontSizeS
|
||||
color: Color.mOnSurfaceVariant
|
||||
horizontalAlignment: Text.AlignRight
|
||||
}
|
||||
NIconButton {
|
||||
icon: "grid-4x4"
|
||||
tooltipText: I18n.tr("settings.desktop-widgets.edit-mode.grid-snap.label")
|
||||
colorBg: Settings.data.desktopWidgets.gridSnap ? Color.mPrimary : Color.mSurfaceVariant
|
||||
colorFg: Settings.data.desktopWidgets.gridSnap ? Color.mOnPrimary : Color.mPrimary
|
||||
onClicked: Settings.data.desktopWidgets.gridSnap = !Settings.data.desktopWidgets.gridSnap
|
||||
}
|
||||
|
||||
NCheckbox {
|
||||
checked: Settings.data.desktopWidgets.gridSnap
|
||||
onToggled: checked => Settings.data.desktopWidgets.gridSnap = checked
|
||||
NIconButton {
|
||||
icon: "settings"
|
||||
tooltipText: I18n.tr("settings.desktop-widgets.edit-mode.open-settings")
|
||||
onClicked: {
|
||||
if (Settings.data.ui.settingsPanelMode === "window") {
|
||||
SettingsPanelService.toggleWindow(SettingsPanel.Tab.DesktopWidgets);
|
||||
} else {
|
||||
var settingsPanel = PanelService.getPanel("settingsPanel", screenLoader.modelData);
|
||||
if (settingsPanel) {
|
||||
settingsPanel.requestedTab = SettingsPanel.Tab.DesktopWidgets;
|
||||
settingsPanel.toggle();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NButton {
|
||||
text: I18n.tr("settings.desktop-widgets.edit-mode.exit-button")
|
||||
icon: "logout"
|
||||
outlined: false
|
||||
fontSize: Style.fontSizeS
|
||||
iconSize: Style.fontSizeM
|
||||
onClicked: DesktopWidgetRegistry.editMode = false
|
||||
}
|
||||
}
|
||||
|
||||
NText {
|
||||
Layout.alignment: Qt.AlignRight
|
||||
Layout.maximumWidth: 300 * Style.uiScaleRatio
|
||||
text: I18n.tr("settings.desktop-widgets.edit-mode.controls-explanation")
|
||||
pointSize: Style.fontSizeS
|
||||
color: Color.mOnSurfaceVariant
|
||||
horizontalAlignment: Text.AlignRight
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,9 @@ import QtQuick
|
||||
import QtQuick.Effects
|
||||
import Quickshell
|
||||
import qs.Commons
|
||||
import qs.Services.Noctalia
|
||||
import qs.Services.UI
|
||||
import qs.Widgets
|
||||
|
||||
Item {
|
||||
id: root
|
||||
@@ -26,6 +29,7 @@ Item {
|
||||
|
||||
readonly property real scaleSensitivity: 0.0015
|
||||
readonly property real scaleUpdateThreshold: 0.015
|
||||
readonly property real cornerScaleSensitivity: 0.0003 // Much lower sensitivity for corner handles
|
||||
|
||||
// Grid size ensures lines pass through screen center on both axes
|
||||
readonly property int gridSize: {
|
||||
@@ -99,6 +103,8 @@ Item {
|
||||
property real lastScale: 1.0
|
||||
// Locks operation type to prevent switching between drag/scale mid-operation
|
||||
property string operationType: "" // "drag" or "scale" or ""
|
||||
property real previousWidth: 0
|
||||
property real centerX: baseX + (previousWidth > 0 ? previousWidth / 2 : root.width / 2)
|
||||
}
|
||||
|
||||
function snapToGrid(coord) {
|
||||
@@ -131,6 +137,105 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
function removeWidget() {
|
||||
if (widgetIndex < 0 || !screen || !screen.name) {
|
||||
return;
|
||||
}
|
||||
|
||||
var monitorWidgets = Settings.data.desktopWidgets.monitorWidgets || [];
|
||||
var newMonitorWidgets = monitorWidgets.slice();
|
||||
|
||||
for (var i = 0; i < newMonitorWidgets.length; i++) {
|
||||
if (newMonitorWidgets[i].name === screen.name) {
|
||||
var widgets = (newMonitorWidgets[i].widgets || []).slice();
|
||||
if (widgetIndex >= 0 && widgetIndex < widgets.length) {
|
||||
widgets.splice(widgetIndex, 1);
|
||||
newMonitorWidgets[i] = Object.assign({}, newMonitorWidgets[i], {
|
||||
"widgets": widgets
|
||||
});
|
||||
Settings.data.desktopWidgets.monitorWidgets = newMonitorWidgets;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function openWidgetSettings() {
|
||||
if (!widgetData || !widgetData.id || !screen) {
|
||||
return;
|
||||
}
|
||||
|
||||
var widgetId = widgetData.id;
|
||||
var hasSettings = false;
|
||||
|
||||
// Check if widget has settings
|
||||
if (DesktopWidgetRegistry.isPluginWidget(widgetId)) {
|
||||
var pluginId = widgetId.replace("plugin:", "");
|
||||
var manifest = PluginRegistry.getPluginManifest(pluginId);
|
||||
if (manifest && manifest.entryPoints && manifest.entryPoints.settings) {
|
||||
hasSettings = true;
|
||||
}
|
||||
} else {
|
||||
hasSettings = DesktopWidgetRegistry.widgetSettingsMap[widgetId] !== undefined;
|
||||
}
|
||||
|
||||
if (!hasSettings) {
|
||||
Logger.w("DraggableDesktopWidget", "Widget does not have settings:", widgetId);
|
||||
return;
|
||||
}
|
||||
|
||||
var popupMenuWindow = PanelService.getPopupMenuWindow(screen);
|
||||
if (!popupMenuWindow) {
|
||||
Logger.e("DraggableDesktopWidget", "No popup menu window found for screen");
|
||||
return;
|
||||
}
|
||||
|
||||
// Hide the dynamic context menu (popup window stays open for the dialog)
|
||||
if (popupMenuWindow.hideDynamicMenu) {
|
||||
popupMenuWindow.hideDynamicMenu();
|
||||
}
|
||||
|
||||
var component = Qt.createComponent(Quickshell.shellDir + "/Modules/Panels/Settings/DesktopWidgets/DesktopWidgetSettingsDialog.qml");
|
||||
|
||||
function instantiateAndOpen() {
|
||||
var dialog = component.createObject(popupMenuWindow.dialogParent, {
|
||||
"widgetIndex": widgetIndex,
|
||||
"widgetData": widgetData,
|
||||
"widgetId": widgetId,
|
||||
"sectionId": screen.name
|
||||
});
|
||||
|
||||
if (dialog) {
|
||||
dialog.updateWidgetSettings.connect((sec, idx, settings) => {
|
||||
root.updateWidgetData(settings);
|
||||
});
|
||||
popupMenuWindow.hasDialog = true;
|
||||
dialog.closed.connect(() => {
|
||||
popupMenuWindow.hasDialog = false;
|
||||
popupMenuWindow.close();
|
||||
dialog.destroy();
|
||||
});
|
||||
dialog.open();
|
||||
} else {
|
||||
Logger.e("DraggableDesktopWidget", "Failed to create widget settings dialog");
|
||||
}
|
||||
}
|
||||
|
||||
if (component.status === Component.Ready) {
|
||||
instantiateAndOpen();
|
||||
} else if (component.status === Component.Error) {
|
||||
Logger.e("DraggableDesktopWidget", "Error loading settings dialog component:", component.errorString());
|
||||
} else {
|
||||
component.statusChanged.connect(() => {
|
||||
if (component.status === Component.Ready) {
|
||||
instantiateAndOpen();
|
||||
} else if (component.status === Component.Error) {
|
||||
Logger.e("DraggableDesktopWidget", "Error loading settings dialog component:", component.errorString());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
x: internal.isDragging ? internal.dragOffsetX : internal.baseX
|
||||
y: internal.isDragging ? internal.dragOffsetY : internal.baseY
|
||||
|
||||
@@ -138,6 +243,22 @@ Item {
|
||||
scale: widgetScale
|
||||
transformOrigin: Item.TopLeft
|
||||
|
||||
// Adjust position when width changes to maintain center position
|
||||
onWidthChanged: {
|
||||
if (!internal.isDragging && !internal.isScaling && internal.previousWidth > 0 && width > 0) {
|
||||
var widthDelta = width - internal.previousWidth;
|
||||
// Adjust baseX to keep center position constant
|
||||
internal.baseX = internal.baseX - widthDelta / 2;
|
||||
internal.centerX = internal.baseX + width / 2;
|
||||
}
|
||||
internal.previousWidth = width;
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
internal.previousWidth = width;
|
||||
internal.centerX = internal.baseX + width / 2;
|
||||
}
|
||||
|
||||
onWidgetDataChanged: {
|
||||
if (!internal.isDragging) {
|
||||
internal.baseX = (widgetData && widgetData.x !== undefined) ? widgetData.x : defaultX;
|
||||
@@ -145,6 +266,9 @@ Item {
|
||||
if (widgetData && widgetData.scale !== undefined) {
|
||||
widgetScale = widgetData.scale;
|
||||
}
|
||||
// Update centerX and previousWidth when widget data changes
|
||||
internal.previousWidth = width;
|
||||
internal.centerX = internal.baseX + width / 2;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,10 +276,10 @@ Item {
|
||||
id: decorationRect
|
||||
anchors.fill: parent
|
||||
anchors.margins: -Style.marginS
|
||||
color: Settings.data.desktopWidgets.editMode ? Qt.rgba(Color.mPrimary.r, Color.mPrimary.g, Color.mPrimary.b, 0.1) : Color.transparent
|
||||
border.color: (Settings.data.desktopWidgets.editMode || internal.isDragging) ? (internal.isDragging ? Color.mOutline : Color.mPrimary) : Color.transparent
|
||||
border.width: Settings.data.desktopWidgets.editMode ? 3 : (internal.isDragging ? 2 : 0)
|
||||
radius: Style.radiusL + Style.marginS
|
||||
color: DesktopWidgetRegistry.editMode ? Qt.rgba(Color.mPrimary.r, Color.mPrimary.g, Color.mPrimary.b, 0.1) : Color.transparent
|
||||
border.color: (DesktopWidgetRegistry.editMode || internal.isDragging) ? (internal.isDragging ? Color.mOutline : Color.mPrimary) : Color.transparent
|
||||
border.width: DesktopWidgetRegistry.editMode ? 3 : 0
|
||||
radius: Style.radiusL
|
||||
z: -1
|
||||
}
|
||||
|
||||
@@ -189,26 +313,59 @@ Item {
|
||||
z: 1
|
||||
}
|
||||
|
||||
// Drag and Scale MouseArea - handles both dragging (left-click) and scaling (right-click)
|
||||
// Context menu model and handler - menu is created dynamically in PopupMenuWindow
|
||||
property var contextMenuModel: {
|
||||
var hasSettings = false;
|
||||
if (widgetData && widgetData.id) {
|
||||
var widgetId = widgetData.id;
|
||||
if (DesktopWidgetRegistry.isPluginWidget(widgetId)) {
|
||||
var pluginId = widgetId.replace("plugin:", "");
|
||||
var manifest = PluginRegistry.getPluginManifest(pluginId);
|
||||
hasSettings = manifest && manifest.entryPoints && manifest.entryPoints.settings;
|
||||
} else {
|
||||
hasSettings = DesktopWidgetRegistry.widgetSettingsMap[widgetId] !== undefined;
|
||||
}
|
||||
}
|
||||
|
||||
var items = [];
|
||||
if (hasSettings) {
|
||||
items.push({
|
||||
"label": I18n.tr("context-menu.widget-settings"),
|
||||
"action": "widget-settings",
|
||||
"icon": "settings"
|
||||
});
|
||||
}
|
||||
items.push({
|
||||
"label": I18n.tr("context-menu.delete"),
|
||||
"action": "delete",
|
||||
"icon": "trash"
|
||||
});
|
||||
return items;
|
||||
}
|
||||
|
||||
function handleContextMenuAction(action) {
|
||||
if (action === "widget-settings") {
|
||||
// Don't close - openWidgetSettings will use the popup window for the dialog
|
||||
root.openWidgetSettings();
|
||||
return true; // Signal that we're handling close ourselves
|
||||
} else if (action === "delete") {
|
||||
root.removeWidget();
|
||||
return false; // Let caller close the popup
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Drag MouseArea - handles dragging (left-click)
|
||||
MouseArea {
|
||||
id: interactionArea
|
||||
id: dragArea
|
||||
anchors.fill: parent
|
||||
z: 1000
|
||||
visible: Settings.data.desktopWidgets.editMode
|
||||
cursorShape: {
|
||||
if (internal.isDragging)
|
||||
return Qt.ClosedHandCursor;
|
||||
if (internal.isScaling)
|
||||
return Qt.SizeAllCursor;
|
||||
// Change cursor based on which button user is likely to press
|
||||
// Right mouse button for scaling, left for dragging
|
||||
return Qt.OpenHandCursor;
|
||||
}
|
||||
visible: DesktopWidgetRegistry.editMode
|
||||
cursorShape: internal.isDragging ? Qt.ClosedHandCursor : Qt.OpenHandCursor
|
||||
hoverEnabled: true
|
||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||
acceptedButtons: Qt.LeftButton
|
||||
|
||||
property point pressPos: Qt.point(0, 0)
|
||||
property real initialScale: 1.0
|
||||
|
||||
onPressed: mouse => {
|
||||
// Prevent starting new operation if one is already in progress
|
||||
@@ -217,21 +374,10 @@ Item {
|
||||
}
|
||||
|
||||
pressPos = Qt.point(mouse.x, mouse.y);
|
||||
|
||||
if (mouse.button === Qt.LeftButton) {
|
||||
internal.operationType = "drag";
|
||||
internal.dragOffsetX = root.x;
|
||||
internal.dragOffsetY = root.y;
|
||||
internal.isDragging = true;
|
||||
} else if (mouse.button === Qt.RightButton) {
|
||||
internal.operationType = "scale";
|
||||
internal.isScaling = true;
|
||||
internal.initialWidth = root.width;
|
||||
internal.initialHeight = root.height;
|
||||
internal.initialMousePos = Qt.point(mouse.x, mouse.y);
|
||||
internal.initialScale = root.widgetScale;
|
||||
internal.lastScale = root.widgetScale;
|
||||
}
|
||||
internal.operationType = "drag";
|
||||
internal.dragOffsetX = root.x;
|
||||
internal.dragOffsetY = root.y;
|
||||
internal.isDragging = true;
|
||||
}
|
||||
|
||||
onPositionChanged: mouse => {
|
||||
@@ -242,7 +388,6 @@ Item {
|
||||
var deltaX = globalCurrentPos.x - globalPressPos.x;
|
||||
var deltaY = globalCurrentPos.y - globalPressPos.y;
|
||||
|
||||
// Calculate new position based on original position when drag started
|
||||
var newX = internal.dragOffsetX + deltaX;
|
||||
var newY = internal.dragOffsetY + deltaY;
|
||||
|
||||
@@ -266,24 +411,6 @@ Item {
|
||||
|
||||
internal.dragOffsetX = newX;
|
||||
internal.dragOffsetY = newY;
|
||||
} else if (internal.isScaling && pressed && internal.operationType === "scale") {
|
||||
var dx = mouse.x - internal.initialMousePos.x;
|
||||
var dy = mouse.y - internal.initialMousePos.y;
|
||||
|
||||
// Use primary direction of movement to determine scale change
|
||||
var primaryMovement = (Math.abs(dx) > Math.abs(dy)) ? dx : dy;
|
||||
|
||||
// Scale change relative to initial widget size ensures consistent behavior
|
||||
var scaleChange = primaryMovement * root.scaleSensitivity;
|
||||
|
||||
// Add to last applied scale (not initial) to allow smooth continuous scaling
|
||||
var newScale = Math.max(minScale, Math.min(maxScale, internal.lastScale + scaleChange));
|
||||
|
||||
// Apply smoothing threshold to prevent rapid changes
|
||||
if (Math.abs(root.widgetScale - newScale) > root.scaleUpdateThreshold && !isNaN(newScale) && newScale > 0) {
|
||||
root.widgetScale = newScale;
|
||||
internal.lastScale = newScale;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -296,25 +423,188 @@ Item {
|
||||
|
||||
internal.baseX = internal.dragOffsetX;
|
||||
internal.baseY = internal.dragOffsetY;
|
||||
internal.centerX = internal.baseX + root.width / 2;
|
||||
internal.isDragging = false;
|
||||
internal.operationType = "";
|
||||
} else if (internal.isScaling && internal.operationType === "scale") {
|
||||
root.updateWidgetData({
|
||||
"scale": root.widgetScale
|
||||
});
|
||||
|
||||
internal.isScaling = false;
|
||||
internal.operationType = "";
|
||||
internal.lastScale = root.widgetScale;
|
||||
}
|
||||
}
|
||||
|
||||
onCanceled: {
|
||||
internal.isDragging = false;
|
||||
internal.isScaling = false;
|
||||
internal.operationType = "";
|
||||
// Sync lastScale when operation is canceled to prevent drift
|
||||
internal.lastScale = root.widgetScale;
|
||||
}
|
||||
}
|
||||
|
||||
// Right-click MouseArea for context menu
|
||||
MouseArea {
|
||||
id: contextMenuArea
|
||||
anchors.fill: parent
|
||||
z: 1001
|
||||
visible: DesktopWidgetRegistry.editMode
|
||||
acceptedButtons: Qt.RightButton
|
||||
hoverEnabled: true
|
||||
|
||||
onPressed: mouse => {
|
||||
if (mouse.button === Qt.RightButton) {
|
||||
var popupMenuWindow = PanelService.getPopupMenuWindow(root.screen);
|
||||
if (popupMenuWindow) {
|
||||
// Map click position to screen coordinates
|
||||
var globalPos = root.mapToItem(null, mouse.x, mouse.y);
|
||||
// Use dynamic context menu (created in PopupMenuWindow's Top layer)
|
||||
// This ensures input events work correctly for desktop widgets (Bottom layer)
|
||||
popupMenuWindow.showDynamicContextMenu(root.contextMenuModel, globalPos.x, globalPos.y, root.handleContextMenuAction);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Corner handles for scaling - using Repeater to avoid code duplication
|
||||
readonly property real cornerHandleSize: 8
|
||||
readonly property real outlineMargin: Style.marginS
|
||||
readonly property color colorHandle: Color.mSecondary
|
||||
|
||||
// Corner handle model: defines position, opposite corner, cursor, and triangle points for each corner
|
||||
// xMult/yMult: multipliers for position (0 = left/top edge, 1 = right/bottom edge)
|
||||
// oppXMult/oppYMult: multipliers for opposite corner calculation
|
||||
// cursor: resize cursor type (FDiag for TL-BR diagonal, BDiag for TR-BL diagonal)
|
||||
// points: triangle vertices as [x, y] pairs normalized to cornerHandleSize
|
||||
readonly property var cornerHandleModel: [
|
||||
{
|
||||
xMult: 0,
|
||||
yMult: 0,
|
||||
oppXMult: 1,
|
||||
oppYMult: 1,
|
||||
cursor: Qt.SizeFDiagCursor,
|
||||
points: [[0, 0], [1, 0], [0, 1]]
|
||||
},
|
||||
{
|
||||
xMult: 1,
|
||||
yMult: 0,
|
||||
oppXMult: 0,
|
||||
oppYMult: 1,
|
||||
cursor: Qt.SizeBDiagCursor,
|
||||
points: [[1, 0], [1, 1], [0, 0]]
|
||||
},
|
||||
{
|
||||
xMult: 0,
|
||||
yMult: 1,
|
||||
oppXMult: 1,
|
||||
oppYMult: 0,
|
||||
cursor: Qt.SizeBDiagCursor,
|
||||
points: [[0, 1], [0, 0], [1, 1]]
|
||||
},
|
||||
{
|
||||
xMult: 1,
|
||||
yMult: 1,
|
||||
oppXMult: 0,
|
||||
oppYMult: 0,
|
||||
cursor: Qt.SizeFDiagCursor,
|
||||
points: [[1, 1], [1, 0], [0, 1]]
|
||||
}
|
||||
]
|
||||
|
||||
Repeater {
|
||||
model: root.cornerHandleModel
|
||||
|
||||
delegate: Canvas {
|
||||
id: cornerHandle
|
||||
required property var modelData
|
||||
required property int index
|
||||
|
||||
visible: DesktopWidgetRegistry.editMode && !internal.isDragging
|
||||
x: modelData.xMult * (root.width + outlineMargin) - (modelData.xMult === 0 ? outlineMargin : cornerHandleSize)
|
||||
y: modelData.yMult * (root.height + outlineMargin) - (modelData.yMult === 0 ? outlineMargin : cornerHandleSize)
|
||||
width: cornerHandleSize
|
||||
height: cornerHandleSize
|
||||
z: 2000
|
||||
|
||||
onPaint: {
|
||||
var ctx = getContext("2d");
|
||||
ctx.reset();
|
||||
ctx.fillStyle = colorHandle;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(modelData.points[0][0] * cornerHandleSize, modelData.points[0][1] * cornerHandleSize);
|
||||
ctx.lineTo(modelData.points[1][0] * cornerHandleSize, modelData.points[1][1] * cornerHandleSize);
|
||||
ctx.lineTo(modelData.points[2][0] * cornerHandleSize, modelData.points[2][1] * cornerHandleSize);
|
||||
ctx.closePath();
|
||||
ctx.fill();
|
||||
}
|
||||
|
||||
Component.onCompleted: requestPaint()
|
||||
onVisibleChanged: if (visible)
|
||||
requestPaint()
|
||||
|
||||
Connections {
|
||||
target: root
|
||||
function onWidthChanged() {
|
||||
if (cornerHandle.visible)
|
||||
cornerHandle.requestPaint();
|
||||
}
|
||||
function onHeightChanged() {
|
||||
if (cornerHandle.visible)
|
||||
cornerHandle.requestPaint();
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: scaleMouseArea
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.LeftButton
|
||||
cursorShape: cornerHandle.modelData.cursor
|
||||
property point pressPos: Qt.point(0, 0)
|
||||
// Capture opposite corner at press time to avoid feedback loop during scaling
|
||||
property real oppositeCornerX: 0
|
||||
property real oppositeCornerY: 0
|
||||
property real initialDistance: 0
|
||||
|
||||
onPressed: mouse => {
|
||||
if (internal.operationType !== "") {
|
||||
return;
|
||||
}
|
||||
pressPos = mapToItem(root.parent, mouse.x, mouse.y);
|
||||
// Calculate and store opposite corner position using initial scale
|
||||
oppositeCornerX = root.x + cornerHandle.modelData.oppXMult * root.width * root.widgetScale;
|
||||
oppositeCornerY = root.y + cornerHandle.modelData.oppYMult * root.height * root.widgetScale;
|
||||
initialDistance = Math.sqrt(Math.pow(pressPos.x - oppositeCornerX, 2) + Math.pow(pressPos.y - oppositeCornerY, 2));
|
||||
internal.operationType = "scale";
|
||||
internal.isScaling = true;
|
||||
internal.initialScale = root.widgetScale;
|
||||
internal.lastScale = root.widgetScale;
|
||||
}
|
||||
|
||||
onPositionChanged: mouse => {
|
||||
if (internal.isScaling && pressed && internal.operationType === "scale" && initialDistance > 0) {
|
||||
var currentPos = mapToItem(root.parent, mouse.x, mouse.y);
|
||||
// Use the fixed opposite corner position captured at press time
|
||||
var currentDistance = Math.sqrt(Math.pow(currentPos.x - oppositeCornerX, 2) + Math.pow(currentPos.y - oppositeCornerY, 2));
|
||||
|
||||
var scaleRatio = currentDistance / initialDistance;
|
||||
var newScale = Math.max(minScale, Math.min(maxScale, internal.initialScale * scaleRatio));
|
||||
|
||||
if (!isNaN(newScale) && newScale > 0) {
|
||||
root.widgetScale = newScale;
|
||||
internal.lastScale = newScale;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onReleased: mouse => {
|
||||
if (internal.isScaling && internal.operationType === "scale") {
|
||||
root.updateWidgetData({
|
||||
"scale": root.widgetScale
|
||||
});
|
||||
internal.isScaling = false;
|
||||
internal.operationType = "";
|
||||
internal.lastScale = root.widgetScale;
|
||||
}
|
||||
}
|
||||
|
||||
onCanceled: {
|
||||
internal.isScaling = false;
|
||||
internal.operationType = "";
|
||||
internal.lastScale = root.widgetScale;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import Quickshell
|
||||
import qs.Commons
|
||||
import qs.Modules.DesktopWidgets
|
||||
import qs.Services.Media
|
||||
import qs.Services.UI
|
||||
import qs.Widgets
|
||||
import qs.Widgets.AudioSpectrum
|
||||
|
||||
@@ -15,18 +16,40 @@ DraggableDesktopWidget {
|
||||
|
||||
// Widget settings
|
||||
readonly property string hideMode: (widgetData.hideMode !== undefined) ? widgetData.hideMode : "visible"
|
||||
readonly property bool showButtons: (widgetData.showButtons !== undefined) ? widgetData.showButtons : true
|
||||
readonly property bool hasPlayer: MediaService.currentPlayer !== null
|
||||
readonly property bool isPlaying: MediaService.isPlaying
|
||||
|
||||
// State
|
||||
readonly property bool shouldHideIdle: (hideMode === "idle") && !isPlaying
|
||||
readonly property bool shouldHideEmpty: !hasPlayer && hideMode === "hidden"
|
||||
readonly property bool isHidden: (shouldHideIdle || shouldHideEmpty) && !Settings.data.desktopWidgets.editMode
|
||||
readonly property bool isHidden: (shouldHideIdle || shouldHideEmpty) && !DesktopWidgetRegistry.editMode
|
||||
visible: !isHidden
|
||||
|
||||
// CavaService registration for visualizer
|
||||
readonly property string cavaComponentId: "desktopmediaplayer:" + (root.screen ? root.screen.name : "unknown")
|
||||
|
||||
onShouldShowVisualizerChanged: {
|
||||
if (root.shouldShowVisualizer) {
|
||||
CavaService.registerComponent(root.cavaComponentId);
|
||||
} else {
|
||||
CavaService.unregisterComponent(root.cavaComponentId);
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
if (root.shouldShowVisualizer) {
|
||||
CavaService.registerComponent(root.cavaComponentId);
|
||||
}
|
||||
}
|
||||
|
||||
Component.onDestruction: {
|
||||
CavaService.unregisterComponent(root.cavaComponentId);
|
||||
}
|
||||
|
||||
readonly property bool showPrev: hasPlayer && MediaService.canGoPrevious
|
||||
readonly property bool showNext: hasPlayer && MediaService.canGoNext
|
||||
readonly property int visibleButtonCount: 1 + (showPrev ? 1 : 0) + (showNext ? 1 : 0)
|
||||
readonly property int visibleButtonCount: root.showButtons ? (1 + (showPrev ? 1 : 0) + (showNext ? 1 : 0)) : 0
|
||||
readonly property int baseWidth: 400 * Style.uiScaleRatio
|
||||
readonly property int buttonWidth: 32 * Style.uiScaleRatio
|
||||
readonly property int buttonSpacing: Style.marginXS
|
||||
@@ -192,6 +215,7 @@ DraggableDesktopWidget {
|
||||
id: controlsRow
|
||||
spacing: Style.marginXS
|
||||
z: 10
|
||||
visible: root.showButtons
|
||||
|
||||
NIconButton {
|
||||
visible: showPrev
|
||||
|
||||
@@ -24,6 +24,25 @@ Loader {
|
||||
id: root
|
||||
active: false
|
||||
|
||||
// Track if the visualizer should be shown (lockscreen active + media playing + non-compact mode)
|
||||
readonly property bool needsCava: root.active && !Settings.data.general.compactLockScreen && Settings.data.audio.visualizerType !== "" && Settings.data.audio.visualizerType !== "none"
|
||||
|
||||
onActiveChanged: {
|
||||
if (root.active && root.needsCava) {
|
||||
CavaService.registerComponent("lockscreen");
|
||||
} else {
|
||||
CavaService.unregisterComponent("lockscreen");
|
||||
}
|
||||
}
|
||||
|
||||
onNeedsCavaChanged: {
|
||||
if (root.needsCava) {
|
||||
CavaService.registerComponent("lockscreen");
|
||||
} else {
|
||||
CavaService.unregisterComponent("lockscreen");
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
// Register with panel service
|
||||
PanelService.lockScreen = this;
|
||||
|
||||
@@ -3,6 +3,7 @@ import Quickshell
|
||||
import Quickshell.Wayland
|
||||
import qs.Commons
|
||||
import qs.Services.UI
|
||||
import qs.Widgets
|
||||
|
||||
// Generic full-screen popup window for menus and context menus
|
||||
// This is a top-level PanelWindow (sibling to MainScreen, not nested inside it)
|
||||
@@ -20,6 +21,9 @@ PanelWindow {
|
||||
// Expose the trayMenu Loader directly (for backward compatibility)
|
||||
readonly property alias trayMenuLoader: trayMenuLoader
|
||||
|
||||
// Dynamic context menu callback for items in other windows (e.g., desktop widgets)
|
||||
property var dynamicMenuCallback: null
|
||||
|
||||
anchors.top: true
|
||||
anchors.left: true
|
||||
anchors.right: true
|
||||
@@ -55,6 +59,26 @@ PanelWindow {
|
||||
}
|
||||
}
|
||||
|
||||
// Dynamic context menu - created as child of this window (Top layer) so input works correctly
|
||||
// Used for items in other windows like desktop widgets (bottom layer)
|
||||
NPopupContextMenu {
|
||||
id: dynamicMenu
|
||||
visible: false
|
||||
screen: root.screen
|
||||
|
||||
onTriggered: (action, item) => {
|
||||
if (root.dynamicMenuCallback) {
|
||||
// Callback returns true if it will handle closing (e.g., opening a dialog)
|
||||
var handled = root.dynamicMenuCallback(action);
|
||||
if (!handled) {
|
||||
root.close();
|
||||
}
|
||||
} else {
|
||||
root.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function open() {
|
||||
visible = true;
|
||||
}
|
||||
@@ -67,6 +91,35 @@ PanelWindow {
|
||||
}
|
||||
}
|
||||
|
||||
// Show a dynamic context menu with model and callback at screen coordinates
|
||||
// Used for items in other window layers (e.g., desktop widgets in bottom layer)
|
||||
function showDynamicContextMenu(model, screenX, screenY, callback) {
|
||||
dynamicMenu.model = model;
|
||||
dynamicMenuCallback = callback;
|
||||
|
||||
// Use the anchor point item for positioning at absolute coordinates
|
||||
dynamicMenu.anchorItem = anchorPoint;
|
||||
anchorPoint.x = screenX;
|
||||
anchorPoint.y = screenY;
|
||||
|
||||
contentItem = dynamicMenu;
|
||||
dynamicMenu.visible = true;
|
||||
open();
|
||||
}
|
||||
|
||||
// Invisible anchor point for dynamic menu positioning
|
||||
Item {
|
||||
id: anchorPoint
|
||||
width: 1
|
||||
height: 1
|
||||
}
|
||||
|
||||
// Hide just the dynamic menu without closing the popup window
|
||||
// Used when transitioning from context menu to a dialog
|
||||
function hideDynamicMenu() {
|
||||
dynamicMenu.visible = false;
|
||||
}
|
||||
|
||||
function close() {
|
||||
visible = false;
|
||||
// Call close/hide method on current content
|
||||
@@ -77,6 +130,9 @@ PanelWindow {
|
||||
contentItem.close();
|
||||
}
|
||||
}
|
||||
// Hide dynamic menu
|
||||
dynamicMenu.visible = false;
|
||||
dynamicMenuCallback = null;
|
||||
// Restore TrayMenu as default content
|
||||
if (trayMenuLoader.item) {
|
||||
contentItem = trayMenuLoader.item;
|
||||
|
||||
@@ -917,6 +917,23 @@ SmartPanel {
|
||||
active: visible
|
||||
|
||||
sourceComponent: Component {
|
||||
Loader {
|
||||
anchors.fill: parent
|
||||
sourceComponent: Settings.data.appLauncher.iconMode === "tabler" && modelData.isTablerIcon ? tablerIconComponent : systemIconComponent
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: tablerIconComponent
|
||||
NIcon {
|
||||
icon: modelData.icon
|
||||
pointSize: Style.fontSizeXXXL
|
||||
visible: modelData.icon && !modelData.emojiChar
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: systemIconComponent
|
||||
IconImage {
|
||||
anchors.fill: parent
|
||||
source: modelData.icon ? ThemeIcons.iconFromName(modelData.icon, "application-x-executable") : ""
|
||||
@@ -1268,6 +1285,23 @@ SmartPanel {
|
||||
active: visible
|
||||
|
||||
sourceComponent: Component {
|
||||
Loader {
|
||||
anchors.fill: parent
|
||||
sourceComponent: Settings.data.appLauncher.iconMode === "tabler" && modelData.isTablerIcon ? gridTablerIconComponent : gridSystemIconComponent
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: gridTablerIconComponent
|
||||
NIcon {
|
||||
icon: modelData.icon
|
||||
pointSize: Style.fontSizeXXXL
|
||||
visible: modelData.icon && !modelData.emojiChar
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: gridSystemIconComponent
|
||||
IconImage {
|
||||
anchors.fill: parent
|
||||
source: modelData.icon ? ThemeIcons.iconFromName(modelData.icon, "application-x-executable") : ""
|
||||
|
||||
@@ -5,6 +5,7 @@ import qs.Commons
|
||||
Item {
|
||||
property var launcher: null
|
||||
property string name: I18n.tr("plugins.calculator")
|
||||
property string iconMode: Settings.data.appLauncher.iconMode
|
||||
|
||||
function handleCommand(query) {
|
||||
// Handle >calc command or direct math expressions after >
|
||||
@@ -16,7 +17,8 @@ Item {
|
||||
{
|
||||
"name": ">calc",
|
||||
"description": I18n.tr("plugins.calculator-description"),
|
||||
"icon": "accessories-calculator",
|
||||
"icon": iconMode === "tabler" ? "calculator" : "accessories-calculator",
|
||||
"isTablerIcon": true,
|
||||
"isImage": false,
|
||||
"onActivate": function () {
|
||||
launcher.setSearchText(">calc ");
|
||||
@@ -41,7 +43,8 @@ Item {
|
||||
{
|
||||
"name": I18n.tr("plugins.calculator-name"),
|
||||
"description": I18n.tr("plugins.calculator-enter-expression"),
|
||||
"icon": "accessories-calculator",
|
||||
"icon": iconMode === "tabler" ? "calculator" : "accessories-calculator",
|
||||
"isTablerIcon": true,
|
||||
"isImage": false,
|
||||
"onActivate": function () {}
|
||||
}
|
||||
@@ -55,7 +58,8 @@ Item {
|
||||
{
|
||||
"name": AdvancedMath.formatResult(result),
|
||||
"description": `${expression} = ${result}`,
|
||||
"icon": "accessories-calculator",
|
||||
"icon": iconMode === "tabler" ? "calculator" : "accessories-calculator",
|
||||
"isTablerIcon": true,
|
||||
"isImage": false,
|
||||
"onActivate": function () {
|
||||
// TODO: copy entry to clipboard via ClipHist
|
||||
@@ -68,7 +72,8 @@ Item {
|
||||
{
|
||||
"name": I18n.tr("plugins.calculator-error"),
|
||||
"description": error.message || "Invalid expression",
|
||||
"icon": "dialog-error",
|
||||
"icon": iconMode === "tabler" ? "circle-x" : "dialog-error",
|
||||
"isTablerIcon": true,
|
||||
"isImage": false,
|
||||
"onActivate": function () {}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ Item {
|
||||
// Plugin metadata
|
||||
property string name: I18n.tr("plugins.clipboard")
|
||||
property var launcher: null
|
||||
property string iconMode: Settings.data.appLauncher.iconMode
|
||||
|
||||
// Plugin capabilities
|
||||
property bool handleSearch: false // Don't handle regular search
|
||||
@@ -70,7 +71,8 @@ Item {
|
||||
{
|
||||
"name": ">clip",
|
||||
"description": I18n.tr("plugins.clipboard-search-description"),
|
||||
"icon": "text-x-generic",
|
||||
"icon": iconMode === "tabler" ? "clipboard" : "text-x-generic",
|
||||
"isTablerIcon": true,
|
||||
"isImage": false,
|
||||
"onActivate": function () {
|
||||
launcher.setSearchText(">clip ");
|
||||
@@ -79,7 +81,8 @@ Item {
|
||||
{
|
||||
"name": ">clip clear",
|
||||
"description": I18n.tr("plugins.clipboard-clear-description"),
|
||||
"icon": "text-x-generic",
|
||||
"icon": iconMode === "tabler" ? "trash" : "delete_sweep",
|
||||
"isTablerIcon": true,
|
||||
"isImage": false,
|
||||
"onActivate": function () {
|
||||
ClipboardService.wipeAll();
|
||||
@@ -105,7 +108,8 @@ Item {
|
||||
{
|
||||
"name": I18n.tr("plugins.clipboard-history-disabled"),
|
||||
"description": I18n.tr("plugins.clipboard-history-disabled-description"),
|
||||
"icon": "view-refresh",
|
||||
"icon": iconMode === "tabler" ? "refresh" : "view-refresh",
|
||||
"isTablerIcon": true,
|
||||
"isImage": false,
|
||||
"onActivate": function () {}
|
||||
}
|
||||
@@ -118,7 +122,8 @@ Item {
|
||||
{
|
||||
"name": I18n.tr("plugins.clipboard-clear-history"),
|
||||
"description": I18n.tr("plugins.clipboard-clear-description-full"),
|
||||
"icon": "delete_sweep",
|
||||
"icon": iconMode === "tabler" ? "trash" : "delete_sweep",
|
||||
"isTablerIcon": true,
|
||||
"isImage": false,
|
||||
"onActivate": function () {
|
||||
ClipboardService.wipeAll();
|
||||
@@ -134,7 +139,8 @@ Item {
|
||||
{
|
||||
"name": I18n.tr("plugins.clipboard-loading"),
|
||||
"description": I18n.tr("plugins.clipboard-loading-description"),
|
||||
"icon": "view-refresh",
|
||||
"icon": iconMode === "tabler" ? "refresh" : "view-refresh",
|
||||
"isTablerIcon": true,
|
||||
"isImage": false,
|
||||
"onActivate": function () {}
|
||||
}
|
||||
@@ -152,7 +158,8 @@ Item {
|
||||
{
|
||||
"name": I18n.tr("plugins.clipboard-loading"),
|
||||
"description": I18n.tr("plugins.clipboard-loading-description"),
|
||||
"icon": "view-refresh",
|
||||
"icon": iconMode === "tabler" ? "refresh" : "view-refresh",
|
||||
"isTablerIcon": true,
|
||||
"isImage": false,
|
||||
"onActivate": function () {}
|
||||
}
|
||||
@@ -193,7 +200,8 @@ Item {
|
||||
results.push({
|
||||
"name": searchTerm ? "No matching clipboard items" : "Clipboard is empty",
|
||||
"description": searchTerm ? `No items containing "${query}"` : "Copy something to see it here",
|
||||
"icon": "text-x-generic",
|
||||
"icon": iconMode === "tabler" ? "clipboard" : "text-x-generic",
|
||||
"isTablerIcon": true,
|
||||
"isImage": false,
|
||||
"onActivate": function () {// Do nothing
|
||||
}
|
||||
@@ -210,7 +218,8 @@ Item {
|
||||
return {
|
||||
"name": meta ? `Image ${meta.w}×${meta.h}` : "Image",
|
||||
"description": meta ? `${meta.fmt} • ${meta.size}` : item.mime || "Image data",
|
||||
"icon": "image",
|
||||
"icon": iconMode === "tabler" ? "photo" : "image",
|
||||
"isTablerIcon": true,
|
||||
"isImage": true,
|
||||
"imageWidth": meta ? meta.w : 0,
|
||||
"imageHeight": meta ? meta.h : 0,
|
||||
@@ -244,7 +253,8 @@ Item {
|
||||
return {
|
||||
"name": title,
|
||||
"description": description,
|
||||
"icon": "text-x-generic",
|
||||
"icon": iconMode === "tabler" ? "clipboard" : "text-x-generic",
|
||||
"isTablerIcon": true,
|
||||
"isImage": false,
|
||||
"clipboardId": item.id,
|
||||
"preview": preview
|
||||
|
||||
@@ -5,6 +5,7 @@ import qs.Commons
|
||||
Item {
|
||||
property var launcher: null
|
||||
property string name: I18n.tr("plugins.command")
|
||||
property string iconMode: Settings.data.appLauncher.iconMode
|
||||
|
||||
function handleCommand(query) {
|
||||
return query.startsWith(">cmd");
|
||||
@@ -15,7 +16,8 @@ Item {
|
||||
{
|
||||
"name": ">cmd",
|
||||
"description": I18n.tr("plugins.command-description"),
|
||||
"icon": "utilities-terminal",
|
||||
"icon": iconMode === "tabler" ? "terminal" : "utilities-terminal",
|
||||
"isTablerIcon": true,
|
||||
"isImage": false,
|
||||
"onActivate": function () {
|
||||
launcher.setSearchText(">cmd ");
|
||||
@@ -33,7 +35,8 @@ Item {
|
||||
{
|
||||
"name": I18n.tr("plugins.command-name"),
|
||||
"description": I18n.tr("plugins.command-description"),
|
||||
"icon": "utilities-terminal",
|
||||
"icon": iconMode === "tabler" ? "terminal" : "utilities-terminal",
|
||||
"isTablerIcon": true,
|
||||
"isImage": false,
|
||||
"onActivate": function () {
|
||||
launcher.close();
|
||||
|
||||
@@ -9,6 +9,7 @@ Item {
|
||||
// Plugin metadata
|
||||
property string name: I18n.tr("plugins.emoji")
|
||||
property var launcher: null
|
||||
property string iconMode: Settings.data.appLauncher.iconMode
|
||||
property bool handleSearch: false
|
||||
|
||||
property string selectedCategory: "recent"
|
||||
@@ -67,7 +68,8 @@ Item {
|
||||
{
|
||||
"name": ">emoji",
|
||||
"description": I18n.tr("plugins.emoji-search-description"),
|
||||
"icon": "face-smile",
|
||||
"icon": iconMode === "tabler" ? "mood-smile" : "face-smile",
|
||||
"isTablerIcon": true,
|
||||
"isImage": false,
|
||||
"onActivate": function () {
|
||||
launcher.setSearchText(">emoji ");
|
||||
@@ -87,7 +89,8 @@ Item {
|
||||
{
|
||||
"name": I18n.tr("plugins.emoji-loading"),
|
||||
"description": I18n.tr("plugins.emoji-loading-description"),
|
||||
"icon": "view-refresh",
|
||||
"icon": iconMode === "tabler" ? "refresh" : "view-refresh",
|
||||
"isTablerIcon": true,
|
||||
"isImage": false,
|
||||
"onActivate": function () {}
|
||||
}
|
||||
|
||||
@@ -64,34 +64,11 @@ SmartPanel {
|
||||
id: pluginContentItem
|
||||
anchors.fill: parent
|
||||
|
||||
// Plugin content loader, pluginApi is injected synchronously in loadPluginPanel()
|
||||
Loader {
|
||||
id: pluginContentLoader
|
||||
anchors.fill: parent
|
||||
active: false
|
||||
|
||||
// Create a dummy pluginApi that returns empty strings to avoid undefined warnings
|
||||
property var _dummyApi: QtObject {
|
||||
property var pluginSettings: ({})
|
||||
property var manifest: ({
|
||||
metadata: {
|
||||
defaultSettings: {}
|
||||
}
|
||||
})
|
||||
|
||||
function tr(key) {
|
||||
return "";
|
||||
}
|
||||
function trp(key, count) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
onLoaded: {
|
||||
// Inject the dummy API immediately to prevent undefined warnings
|
||||
if (item && item.hasOwnProperty("pluginApi") && !item.pluginApi) {
|
||||
item.pluginApi = _dummyApi;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ ColumnLayout {
|
||||
property string valueVisualizerType: widgetData.visualizerType !== undefined ? widgetData.visualizerType : widgetMetadata.visualizerType
|
||||
property string valueHideMode: widgetData.hideMode !== undefined ? widgetData.hideMode : widgetMetadata.hideMode
|
||||
property string valueVisualizerVisibility: widgetData.visualizerVisibility !== undefined ? widgetData.visualizerVisibility : (widgetMetadata.visualizerVisibility !== undefined ? widgetMetadata.visualizerVisibility : "always")
|
||||
property bool valueShowButtons: widgetData.showButtons !== undefined ? widgetData.showButtons : (widgetMetadata.showButtons !== undefined ? widgetMetadata.showButtons : true)
|
||||
|
||||
function saveSettings() {
|
||||
var settings = Object.assign({}, widgetData || {});
|
||||
@@ -22,6 +23,7 @@ ColumnLayout {
|
||||
settings.visualizerType = valueVisualizerType;
|
||||
settings.hideMode = valueHideMode;
|
||||
settings.visualizerVisibility = valueVisualizerVisibility;
|
||||
settings.showButtons = valueShowButtons;
|
||||
return settings;
|
||||
}
|
||||
|
||||
@@ -33,6 +35,14 @@ ColumnLayout {
|
||||
onToggled: checked => valueShowBackground = checked
|
||||
}
|
||||
|
||||
NToggle {
|
||||
Layout.fillWidth: true
|
||||
label: I18n.tr("settings.desktop-widgets.media-player.show-buttons.label")
|
||||
description: I18n.tr("settings.desktop-widgets.media-player.show-buttons.description")
|
||||
checked: valueShowButtons
|
||||
onToggled: checked => valueShowButtons = checked
|
||||
}
|
||||
|
||||
NComboBox {
|
||||
Layout.fillWidth: true
|
||||
label: I18n.tr("settings.desktop-widgets.media-player.visualizer-type.label")
|
||||
|
||||
@@ -383,25 +383,6 @@ ColumnLayout {
|
||||
onSelected: key => Settings.data.audio.visualizerType = key
|
||||
}
|
||||
|
||||
NComboBox {
|
||||
label: I18n.tr("settings.audio.media.visualizer-quality.label")
|
||||
description: I18n.tr("settings.audio.media.visualizer-quality.description")
|
||||
model: [
|
||||
{
|
||||
"key": "low",
|
||||
"name": I18n.tr("options.visualizer-quality.low")
|
||||
},
|
||||
{
|
||||
"key": "high",
|
||||
"name": I18n.tr("options.visualizer-quality.high")
|
||||
}
|
||||
]
|
||||
currentKey: Settings.data.audio.visualizerQuality
|
||||
isSettings: true
|
||||
defaultValue: Settings.getDefaultValue("audio.visualizerQuality")
|
||||
onSelected: key => Settings.data.audio.visualizerQuality = key
|
||||
}
|
||||
|
||||
NComboBox {
|
||||
label: I18n.tr("settings.audio.media.frame-rate.label")
|
||||
description: I18n.tr("settings.audio.media.frame-rate.description")
|
||||
|
||||
@@ -45,11 +45,11 @@ ColumnLayout {
|
||||
NButton {
|
||||
visible: Settings.data.desktopWidgets.enabled
|
||||
Layout.fillWidth: true
|
||||
text: Settings.data.desktopWidgets.editMode ? I18n.tr("settings.desktop-widgets.edit-mode.exit-button") : I18n.tr("settings.desktop-widgets.edit-mode.button.label")
|
||||
text: DesktopWidgetRegistry.editMode ? I18n.tr("settings.desktop-widgets.edit-mode.exit-button") : I18n.tr("settings.desktop-widgets.edit-mode.button.label")
|
||||
icon: "edit"
|
||||
onClicked: {
|
||||
Settings.data.desktopWidgets.editMode = !Settings.data.desktopWidgets.editMode;
|
||||
if (Settings.data.desktopWidgets.editMode && Settings.data.ui.settingsPanelMode !== "window") {
|
||||
DesktopWidgetRegistry.editMode = !DesktopWidgetRegistry.editMode;
|
||||
if (DesktopWidgetRegistry.editMode && Settings.data.ui.settingsPanelMode !== "window") {
|
||||
var item = root.parent;
|
||||
while (item) {
|
||||
if (item.closeRequested !== undefined) {
|
||||
|
||||
@@ -93,6 +93,12 @@ ColumnLayout {
|
||||
onToggled: checked => Settings.data.appLauncher.sortByMostUsed = checked
|
||||
}
|
||||
|
||||
NToggle {
|
||||
label: I18n.tr("settings.launcher.settings.icon-mode.label")
|
||||
description: I18n.tr("settings.launcher.settings.icon-mode.description")
|
||||
checked: Settings.data.appLauncher.iconMode === "native"
|
||||
onToggled: checked => Settings.data.appLauncher.iconMode = checked ? "native" : "tabler"
|
||||
}
|
||||
NToggle {
|
||||
label: I18n.tr("settings.launcher.settings.use-app2unit.label")
|
||||
description: I18n.tr("settings.launcher.settings.use-app2unit.description")
|
||||
|
||||
@@ -4,6 +4,7 @@ import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import qs.Commons
|
||||
import qs.Services.Networking
|
||||
import qs.Services.System
|
||||
import qs.Widgets
|
||||
|
||||
ColumnLayout {
|
||||
@@ -16,12 +17,15 @@ ColumnLayout {
|
||||
|
||||
NToggle {
|
||||
label: I18n.tr("settings.network.wifi.label")
|
||||
checked: Settings.data.network.wifiEnabled
|
||||
description: I18n.tr("settings.network.wifi.description")
|
||||
checked: ProgramCheckerService.nmcliAvailable && Settings.data.network.wifiEnabled
|
||||
onToggled: checked => NetworkService.setWifiEnabled(checked)
|
||||
enabled: ProgramCheckerService.nmcliAvailable
|
||||
}
|
||||
|
||||
NToggle {
|
||||
label: I18n.tr("settings.network.bluetooth.label")
|
||||
description: I18n.tr("settings.network.bluetooth.description")
|
||||
checked: BluetoothService.enabled
|
||||
onToggled: checked => BluetoothService.setBluetoothEnabled(checked)
|
||||
}
|
||||
|
||||
@@ -363,11 +363,11 @@ ColumnLayout {
|
||||
Layout.preferredWidth: 30 * Style.uiScaleRatio
|
||||
Layout.preferredHeight: 30 * Style.uiScaleRatio
|
||||
|
||||
NIconButton {
|
||||
icon: "restore"
|
||||
baseSize: Style.baseWidgetSize * 0.8
|
||||
tooltipText: I18n.tr("settings.notifications.duration.reset")
|
||||
onClicked: Settings.data.notifications.lowUrgencyDuration = 3
|
||||
NIconButton {
|
||||
icon: "restore"
|
||||
baseSize: Style.baseWidgetSize * 0.8
|
||||
tooltipText: I18n.tr("settings.notifications.duration.reset")
|
||||
onClicked: Settings.data.notifications.lowUrgencyDuration = 3
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
@@ -398,11 +398,11 @@ ColumnLayout {
|
||||
Layout.preferredWidth: 30 * Style.uiScaleRatio
|
||||
Layout.preferredHeight: 30 * Style.uiScaleRatio
|
||||
|
||||
NIconButton {
|
||||
icon: "restore"
|
||||
baseSize: Style.baseWidgetSize * 0.8
|
||||
tooltipText: I18n.tr("settings.notifications.duration.reset")
|
||||
onClicked: Settings.data.notifications.normalUrgencyDuration = 8
|
||||
NIconButton {
|
||||
icon: "restore"
|
||||
baseSize: Style.baseWidgetSize * 0.8
|
||||
tooltipText: I18n.tr("settings.notifications.duration.reset")
|
||||
onClicked: Settings.data.notifications.normalUrgencyDuration = 8
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
@@ -432,11 +432,11 @@ ColumnLayout {
|
||||
Layout.preferredWidth: 30 * Style.uiScaleRatio
|
||||
Layout.preferredHeight: 30 * Style.uiScaleRatio
|
||||
|
||||
NIconButton {
|
||||
icon: "restore"
|
||||
baseSize: Style.baseWidgetSize * 0.8
|
||||
tooltipText: I18n.tr("settings.notifications.duration.reset")
|
||||
onClicked: Settings.data.notifications.criticalUrgencyDuration = 15
|
||||
NIconButton {
|
||||
icon: "restore"
|
||||
baseSize: Style.baseWidgetSize * 0.8
|
||||
tooltipText: I18n.tr("settings.notifications.duration.reset")
|
||||
onClicked: Settings.data.notifications.criticalUrgencyDuration = 15
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import "../../../../Helpers/FuzzySort.js" as Fuzzysort
|
||||
import qs.Commons
|
||||
import qs.Services.Noctalia
|
||||
import qs.Services.UI
|
||||
@@ -98,9 +99,10 @@ ColumnLayout {
|
||||
for (var i = 0; i < allIds.length; i++) {
|
||||
var manifest = PluginRegistry.getPluginManifest(allIds[i]);
|
||||
if (manifest) {
|
||||
// Create a copy of manifest and include update info
|
||||
// Create a copy of manifest and include update info and enabled state
|
||||
var pluginData = JSON.parse(JSON.stringify(manifest));
|
||||
pluginData._updateInfo = PluginService.pluginUpdates[allIds[i]];
|
||||
pluginData._enabled = PluginRegistry.isPluginEnabled(allIds[i]);
|
||||
plugins.push(pluginData);
|
||||
}
|
||||
}
|
||||
@@ -208,6 +210,16 @@ ColumnLayout {
|
||||
}
|
||||
}
|
||||
|
||||
NIconButton {
|
||||
icon: "trash"
|
||||
tooltipText: I18n.tr("settings.plugins.uninstall")
|
||||
baseSize: Style.baseWidgetSize * 0.7
|
||||
onClicked: {
|
||||
uninstallDialog.pluginToUninstall = modelData;
|
||||
uninstallDialog.open();
|
||||
}
|
||||
}
|
||||
|
||||
NButton {
|
||||
id: updateButton
|
||||
property string pluginId: modelData.id
|
||||
@@ -251,7 +263,7 @@ ColumnLayout {
|
||||
}
|
||||
|
||||
NToggle {
|
||||
checked: PluginRegistry.isPluginEnabled(modelData.id)
|
||||
checked: modelData._enabled
|
||||
baseSize: Style.baseWidgetSize * 0.7
|
||||
onToggled: function (checked) {
|
||||
if (checked) {
|
||||
@@ -378,6 +390,7 @@ ColumnLayout {
|
||||
RowLayout {
|
||||
spacing: Style.marginM
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: Style.marginM
|
||||
Layout.bottomMargin: Style.marginM
|
||||
|
||||
NTabBar {
|
||||
@@ -428,25 +441,18 @@ ColumnLayout {
|
||||
}
|
||||
}
|
||||
|
||||
// Timer to check for updates after refresh starts
|
||||
Timer {
|
||||
id: checkUpdatesTimer
|
||||
interval: 100
|
||||
onTriggered: {
|
||||
PluginService.checkForUpdates();
|
||||
}
|
||||
}
|
||||
|
||||
// Timer to recheck updates after available plugins are updated
|
||||
Timer {
|
||||
id: recheckUpdatesTimer
|
||||
interval: 50
|
||||
onTriggered: {
|
||||
PluginService.checkForUpdates();
|
||||
}
|
||||
}
|
||||
|
||||
property string pluginFilter: "all"
|
||||
property string pluginSearchText: ""
|
||||
|
||||
// Search input
|
||||
NTextInput {
|
||||
placeholderText: I18n.tr("placeholders.search")
|
||||
inputIconName: "search"
|
||||
text: root.pluginSearchText
|
||||
onTextChanged: root.pluginSearchText = text
|
||||
Layout.fillWidth: true
|
||||
Layout.bottomMargin: Style.marginL
|
||||
}
|
||||
|
||||
// Available plugins list
|
||||
ColumnLayout {
|
||||
@@ -460,6 +466,7 @@ ColumnLayout {
|
||||
var all = PluginService.availablePlugins || [];
|
||||
var filtered = [];
|
||||
|
||||
// First apply download filter
|
||||
for (var i = 0; i < all.length; i++) {
|
||||
var plugin = all[i];
|
||||
var downloaded = plugin.downloaded || false;
|
||||
@@ -473,106 +480,135 @@ ColumnLayout {
|
||||
}
|
||||
}
|
||||
|
||||
// Then apply fuzzy search if there's search text
|
||||
var query = root.pluginSearchText.trim();
|
||||
if (query !== "") {
|
||||
var results = Fuzzysort.go(query, filtered, {
|
||||
"keys": ["name", "description"],
|
||||
"threshold": 0.35,
|
||||
"limit": 50
|
||||
});
|
||||
filtered = [];
|
||||
for (var j = 0; j < results.length; j++) {
|
||||
filtered.push(results[j].obj);
|
||||
}
|
||||
} else {
|
||||
// Sort by lastUpdated (most recent first) when not searching
|
||||
filtered.sort(function (a, b) {
|
||||
var dateA = a.lastUpdated ? new Date(a.lastUpdated).getTime() : 0;
|
||||
var dateB = b.lastUpdated ? new Date(b.lastUpdated).getTime() : 0;
|
||||
return dateB - dateA;
|
||||
});
|
||||
}
|
||||
|
||||
return filtered;
|
||||
}
|
||||
|
||||
delegate: NBox {
|
||||
id: pluginBox
|
||||
property bool isHovered: hoverHandler.hovered
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: Style.borderS
|
||||
Layout.rightMargin: Style.borderS
|
||||
implicitHeight: Math.round(contentRow.implicitHeight + Style.marginL * 2)
|
||||
implicitHeight: Math.round(contentColumn.implicitHeight + Style.marginL * 2)
|
||||
color: Color.mSurface
|
||||
|
||||
RowLayout {
|
||||
id: contentRow
|
||||
Behavior on implicitHeight {
|
||||
NumberAnimation {
|
||||
duration: 150
|
||||
easing.type: Easing.OutCubic
|
||||
}
|
||||
}
|
||||
|
||||
HoverHandler {
|
||||
id: hoverHandler
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: contentColumn
|
||||
anchors.fill: parent
|
||||
anchors.margins: Style.marginL
|
||||
spacing: Style.marginM
|
||||
spacing: Style.marginS
|
||||
|
||||
NIcon {
|
||||
icon: "plugin"
|
||||
pointSize: Style.fontSizeXL
|
||||
color: Color.mOnSurface
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 2
|
||||
RowLayout {
|
||||
spacing: Style.marginM
|
||||
Layout.fillWidth: true
|
||||
|
||||
NIcon {
|
||||
icon: "plugin"
|
||||
pointSize: Style.fontSizeL
|
||||
color: Color.mOnSurface
|
||||
}
|
||||
|
||||
NText {
|
||||
text: modelData.name
|
||||
font.weight: Font.Medium
|
||||
color: Color.mOnSurface
|
||||
elide: Text.ElideRight
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
// Downloaded indicator
|
||||
NIcon {
|
||||
icon: "circle-check"
|
||||
pointSize: Style.fontSizeL
|
||||
color: Color.mPrimary
|
||||
visible: modelData.downloaded === true
|
||||
}
|
||||
|
||||
// Install/Uninstall button
|
||||
NIconButton {
|
||||
icon: modelData.downloaded ? "trash" : "download"
|
||||
baseSize: Style.baseWidgetSize * 0.7
|
||||
tooltipText: modelData.downloaded ? I18n.tr("settings.plugins.uninstall") : I18n.tr("settings.plugins.install")
|
||||
onClicked: {
|
||||
if (modelData.downloaded) {
|
||||
uninstallDialog.pluginToUninstall = modelData;
|
||||
uninstallDialog.open();
|
||||
} else {
|
||||
installPlugin(modelData);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Description - visible on hover
|
||||
NText {
|
||||
visible: pluginBox.isHovered && modelData.description
|
||||
text: modelData.description || ""
|
||||
font.pointSize: Style.fontSizeXS
|
||||
color: Color.mOnSurface
|
||||
wrapMode: Text.WordWrap
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
// Details row - visible on hover
|
||||
RowLayout {
|
||||
visible: pluginBox.isHovered
|
||||
spacing: Style.marginS
|
||||
Layout.fillWidth: true
|
||||
|
||||
NText {
|
||||
text: modelData.description
|
||||
text: "v" + modelData.version
|
||||
font.pointSize: Style.fontSizeXS
|
||||
color: Color.mOnSurfaceVariant
|
||||
wrapMode: Text.WordWrap
|
||||
maximumLineCount: 2
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
|
||||
NText {
|
||||
text: "•"
|
||||
font.pointSize: Style.fontSizeXS
|
||||
color: Color.mOnSurfaceVariant
|
||||
}
|
||||
|
||||
NText {
|
||||
text: stripAuthorEmail(modelData.author)
|
||||
font.pointSize: Style.fontSizeXS
|
||||
color: Color.mOnSurfaceVariant
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
spacing: Style.marginS
|
||||
|
||||
NText {
|
||||
text: "v" + modelData.version
|
||||
font.pointSize: Style.fontSizeXXS
|
||||
color: Color.mOnSurfaceVariant
|
||||
}
|
||||
|
||||
NText {
|
||||
text: "•"
|
||||
font.pointSize: Style.fontSizeXXS
|
||||
color: Color.mOnSurfaceVariant
|
||||
}
|
||||
|
||||
NText {
|
||||
text: stripAuthorEmail(modelData.author)
|
||||
font.pointSize: Style.fontSizeXXS
|
||||
color: Color.mOnSurfaceVariant
|
||||
}
|
||||
|
||||
NText {
|
||||
text: "•"
|
||||
font.pointSize: Style.fontSizeXXS
|
||||
color: Color.mOnSurfaceVariant
|
||||
}
|
||||
|
||||
NText {
|
||||
text: modelData.source?.name || "Unknown"
|
||||
font.pointSize: Style.fontSizeXS
|
||||
color: Color.mOnSurfaceVariant
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Downloaded indicator
|
||||
NIcon {
|
||||
icon: "circle-check"
|
||||
pointSize: Style.fontSizeXL
|
||||
color: Color.mPrimary
|
||||
visible: modelData.downloaded === true
|
||||
}
|
||||
|
||||
// Install/Uninstall button
|
||||
NIconButton {
|
||||
icon: modelData.downloaded ? "trash" : "download"
|
||||
baseSize: Style.baseWidgetSize * 0.7
|
||||
tooltipText: modelData.downloaded ? I18n.tr("settings.plugins.uninstall") : I18n.tr("settings.plugins.install")
|
||||
onClicked: {
|
||||
if (modelData.downloaded) {
|
||||
uninstallDialog.pluginToUninstall = modelData;
|
||||
uninstallDialog.open();
|
||||
} else {
|
||||
installPlugin(modelData);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -599,6 +635,7 @@ ColumnLayout {
|
||||
// Add source dialog
|
||||
Popup {
|
||||
id: addSourceDialog
|
||||
parent: Overlay.overlay
|
||||
modal: true
|
||||
dim: false
|
||||
anchors.centerIn: parent
|
||||
@@ -672,6 +709,7 @@ ColumnLayout {
|
||||
// Uninstall confirmation dialog
|
||||
Popup {
|
||||
id: uninstallDialog
|
||||
parent: Overlay.overlay
|
||||
modal: true
|
||||
dim: false
|
||||
anchors.centerIn: parent
|
||||
@@ -733,6 +771,23 @@ ColumnLayout {
|
||||
showToastOnSave: true
|
||||
}
|
||||
|
||||
// Timer to check for updates after refresh starts
|
||||
Timer {
|
||||
id: checkUpdatesTimer
|
||||
interval: 100
|
||||
onTriggered: {
|
||||
PluginService.checkForUpdates();
|
||||
}
|
||||
}
|
||||
|
||||
// Timer to recheck updates after available plugins are updated
|
||||
Timer {
|
||||
id: recheckUpdatesTimer
|
||||
interval: 50
|
||||
onTriggered: {
|
||||
PluginService.checkForUpdates();
|
||||
}
|
||||
}
|
||||
// ------------------------------
|
||||
// Functions
|
||||
// ------------------------------
|
||||
@@ -783,21 +838,8 @@ ColumnLayout {
|
||||
target: PluginRegistry
|
||||
|
||||
function onPluginsChanged() {
|
||||
// Force model refresh for installed plugins
|
||||
installedPluginsRepeater.model = undefined;
|
||||
Qt.callLater(function () {
|
||||
installedPluginsRepeater.model = Qt.binding(function () {
|
||||
var allIds = PluginRegistry.getAllInstalledPluginIds();
|
||||
var plugins = [];
|
||||
for (var i = 0; i < allIds.length; i++) {
|
||||
var manifest = PluginRegistry.getPluginManifest(allIds[i]);
|
||||
if (manifest) {
|
||||
plugins.push(manifest);
|
||||
}
|
||||
}
|
||||
return plugins;
|
||||
});
|
||||
});
|
||||
// Force model refresh for installed plugins by incrementing counter
|
||||
root.installedPluginsRefreshCounter++;
|
||||
|
||||
// Force model refresh for plugin sources
|
||||
pluginSourcesRepeater.model = undefined;
|
||||
|
||||
@@ -8,7 +8,11 @@
|
||||
|
||||
<p align="center">
|
||||
<a href="https://docs.noctalia.dev/getting-started/installation">
|
||||
<img src="https://img.shields.io/badge/⚡_QUICK_INSTALL-Get_Started_Now-A8AEFF?style=for-the-badge&logoColor=FFFFFF&labelColor=0C0D11" alt="Quick Install" style="height: 50px" />
|
||||
<img
|
||||
src="https://img.shields.io/badge/🌙_Install_Noctalia-A8AEFF?style=for-the-badge&labelColor=0C0D11"
|
||||
alt="Install Noctalia"
|
||||
style="height: 50px"
|
||||
/>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
|
||||
@@ -489,5 +489,8 @@ Item {
|
||||
function enable() {
|
||||
Settings.data.desktopWidgets.enabled = true;
|
||||
}
|
||||
function edit() {
|
||||
DesktopWidgetRegistry.editMode = !DesktopWidgetRegistry.editMode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,31 +9,30 @@ import qs.Services.UI
|
||||
Singleton {
|
||||
id: root
|
||||
|
||||
/**
|
||||
* Cava runs if:
|
||||
* - Bar has an audio visualizer
|
||||
* - LockScreen is opened
|
||||
* - A control center is open
|
||||
* - Desktop media player has a visualizer enabled
|
||||
*/
|
||||
readonly property bool hasDesktopMediaVisualizer: (function () {
|
||||
var monitorWidgets = Settings.data.desktopWidgets.monitorWidgets;
|
||||
if (!monitorWidgets)
|
||||
return false;
|
||||
for (var i = 0; i < monitorWidgets.length; i++) {
|
||||
var widgets = monitorWidgets[i].widgets;
|
||||
if (!widgets)
|
||||
continue;
|
||||
for (var j = 0; j < widgets.length; j++) {
|
||||
var widget = widgets[j];
|
||||
if (widget.id === "MediaPlayer" && widget.visualizerType && widget.visualizerType !== "" && widget.visualizerType !== "none") {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
})()
|
||||
property bool shouldRun: BarService.hasAudioVisualizer || PanelService.lockScreen?.active || (PanelService.openedPanel && PanelService.openedPanel.objectName.startsWith("controlCenterPanel")) || hasDesktopMediaVisualizer
|
||||
// Register a component that needs audio data, call this when a visualizer becomes active.
|
||||
// Pass a unique identifier (e.g., "lockscreen", "controlcenter:screen1", "plugin:fancy-audiovisualizer")
|
||||
function registerComponent(componentId) {
|
||||
root.registeredComponents[componentId] = true;
|
||||
root.registeredComponents = Object.assign({}, root.registeredComponents);
|
||||
Logger.d("Cava", "Component registered:", componentId, "- total:", root.registeredCount);
|
||||
}
|
||||
|
||||
// Unregister a component when it no longer needs audio data.
|
||||
function unregisterComponent(componentId) {
|
||||
delete root.registeredComponents[componentId];
|
||||
root.registeredComponents = Object.assign({}, root.registeredComponents);
|
||||
Logger.d("Cava", "Component unregistered:", componentId, "- total:", root.registeredCount);
|
||||
}
|
||||
|
||||
// Check if a component is registered
|
||||
function isRegistered(componentId) {
|
||||
return root.registeredComponents[componentId] === true;
|
||||
}
|
||||
|
||||
// Component registration - any component needing audio data registers here
|
||||
property var registeredComponents: ({})
|
||||
readonly property int registeredCount: Object.keys(registeredComponents).length
|
||||
property bool shouldRun: registeredCount > 0
|
||||
|
||||
property var values: []
|
||||
property int barsCount: 32
|
||||
|
||||
@@ -4,6 +4,7 @@ import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import qs.Commons
|
||||
import qs.Services.System
|
||||
import qs.Services.UI
|
||||
|
||||
Singleton {
|
||||
@@ -68,8 +69,21 @@ Singleton {
|
||||
|
||||
Component.onCompleted: {
|
||||
Logger.i("Network", "Service started");
|
||||
syncWifiState();
|
||||
scan();
|
||||
if (ProgramCheckerService.nmcliAvailable) {
|
||||
syncWifiState();
|
||||
scan();
|
||||
}
|
||||
}
|
||||
|
||||
// Start initial checks when nmcli becomes available
|
||||
Connections {
|
||||
target: ProgramCheckerService
|
||||
function onNmcliAvailableChanged() {
|
||||
if (ProgramCheckerService.nmcliAvailable) {
|
||||
syncWifiState();
|
||||
scan();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Save cache with debounce
|
||||
@@ -91,37 +105,41 @@ Singleton {
|
||||
}
|
||||
|
||||
// Ethernet check timer
|
||||
// Always running every 30s
|
||||
// Runs every 30s if nmcli is available
|
||||
Timer {
|
||||
id: ethernetCheckTimer
|
||||
interval: 30000
|
||||
running: true
|
||||
running: ProgramCheckerService.nmcliAvailable
|
||||
repeat: true
|
||||
onTriggered: ethernetStateProcess.running = true
|
||||
}
|
||||
|
||||
// Internet connectivity check timer
|
||||
// Always running every 15s
|
||||
// Runs every 15s if nmcli is available
|
||||
Timer {
|
||||
id: connectivityCheckTimer
|
||||
interval: 15000
|
||||
running: true
|
||||
running: ProgramCheckerService.nmcliAvailable
|
||||
repeat: true
|
||||
onTriggered: connectivityCheckProcess.running = true
|
||||
}
|
||||
|
||||
// Core functions
|
||||
function syncWifiState() {
|
||||
if (!ProgramCheckerService.nmcliAvailable)
|
||||
return;
|
||||
wifiStateProcess.running = true;
|
||||
}
|
||||
|
||||
function setWifiEnabled(enabled) {
|
||||
if (!ProgramCheckerService.nmcliAvailable)
|
||||
return;
|
||||
Settings.data.network.wifiEnabled = enabled;
|
||||
wifiStateEnableProcess.running = true;
|
||||
}
|
||||
|
||||
function scan() {
|
||||
if (!Settings.data.network.wifiEnabled)
|
||||
if (!ProgramCheckerService.nmcliAvailable || !Settings.data.network.wifiEnabled)
|
||||
return;
|
||||
if (scanning) {
|
||||
// Mark current scan results to be ignored and schedule a new scan
|
||||
@@ -141,7 +159,7 @@ Singleton {
|
||||
}
|
||||
|
||||
function connect(ssid, password = "") {
|
||||
if (connecting)
|
||||
if (!ProgramCheckerService.nmcliAvailable || connecting)
|
||||
return;
|
||||
connecting = true;
|
||||
connectingTo = ssid;
|
||||
@@ -162,12 +180,16 @@ Singleton {
|
||||
}
|
||||
|
||||
function disconnect(ssid) {
|
||||
if (!ProgramCheckerService.nmcliAvailable)
|
||||
return;
|
||||
disconnectingFrom = ssid;
|
||||
disconnectProcess.ssid = ssid;
|
||||
disconnectProcess.running = true;
|
||||
}
|
||||
|
||||
function forget(ssid) {
|
||||
if (!ProgramCheckerService.nmcliAvailable)
|
||||
return;
|
||||
forgettingNetwork = ssid;
|
||||
|
||||
// Remove from cache
|
||||
@@ -239,7 +261,7 @@ Singleton {
|
||||
// Processes
|
||||
Process {
|
||||
id: ethernetStateProcess
|
||||
running: true
|
||||
running: ProgramCheckerService.nmcliAvailable
|
||||
command: ["nmcli", "-t", "-f", "DEVICE,TYPE,STATE", "device"]
|
||||
|
||||
stdout: StdioCollector {
|
||||
|
||||
@@ -115,6 +115,9 @@ Singleton {
|
||||
}
|
||||
}
|
||||
|
||||
// Track pending plugin loads for init completion
|
||||
property int _pendingPluginLoads: 0
|
||||
|
||||
function init() {
|
||||
if (root.initialized) {
|
||||
Logger.d("PluginService", "Already initialized, skipping");
|
||||
@@ -133,12 +136,12 @@ Singleton {
|
||||
var enabledIds = PluginRegistry.getEnabledPluginIds();
|
||||
Logger.i("PluginService", "Found", enabledIds.length, "enabled plugins:", JSON.stringify(enabledIds));
|
||||
|
||||
// Count plugins that need to be loaded
|
||||
var pluginsToLoad = [];
|
||||
for (var i = 0; i < enabledIds.length; i++) {
|
||||
Logger.d("PluginService", "Attempting to load plugin:", enabledIds[i]);
|
||||
var manifest = PluginRegistry.getPluginManifest(enabledIds[i]);
|
||||
if (manifest) {
|
||||
Logger.d("PluginService", "Manifest found for", enabledIds[i]);
|
||||
loadPlugin(enabledIds[i]);
|
||||
pluginsToLoad.push(enabledIds[i]);
|
||||
} else {
|
||||
Logger.w("PluginService", "Plugin", enabledIds[i], "is enabled but not found on disk - cleaning up");
|
||||
// Plugin was deleted from disk but still marked as enabled
|
||||
@@ -149,13 +152,38 @@ Singleton {
|
||||
}
|
||||
}
|
||||
|
||||
// Mark plugins as fully loaded
|
||||
root.pluginsFullyLoaded = true;
|
||||
Logger.i("PluginService", "All plugins loaded");
|
||||
root.allPluginsLoaded();
|
||||
// If no plugins to load, mark as complete immediately
|
||||
if (pluginsToLoad.length === 0) {
|
||||
root.pluginsFullyLoaded = true;
|
||||
Logger.i("PluginService", "No plugins to load");
|
||||
root.allPluginsLoaded();
|
||||
refreshAvailablePlugins();
|
||||
return;
|
||||
}
|
||||
|
||||
// Fetch available plugins from all sources
|
||||
refreshAvailablePlugins();
|
||||
// Track pending loads
|
||||
root._pendingPluginLoads = pluginsToLoad.length;
|
||||
|
||||
// Load all plugins (async - they will call _onPluginLoadComplete when done)
|
||||
for (var j = 0; j < pluginsToLoad.length; j++) {
|
||||
Logger.d("PluginService", "Attempting to load plugin:", pluginsToLoad[j]);
|
||||
loadPlugin(pluginsToLoad[j]);
|
||||
}
|
||||
}
|
||||
|
||||
// Called when a plugin finishes loading (success or failure)
|
||||
function _onPluginLoadComplete() {
|
||||
root._pendingPluginLoads--;
|
||||
|
||||
if (root._pendingPluginLoads <= 0) {
|
||||
// All plugins finished loading
|
||||
root.pluginsFullyLoaded = true;
|
||||
Logger.i("PluginService", "All plugins loaded");
|
||||
root.allPluginsLoaded();
|
||||
|
||||
// Fetch available plugins from all sources
|
||||
refreshAvailablePlugins();
|
||||
}
|
||||
}
|
||||
|
||||
// Refresh available plugins from all sources
|
||||
@@ -223,13 +251,20 @@ Singleton {
|
||||
}
|
||||
|
||||
Logger.i("PluginService", "Loaded", registry.plugins.length, "plugins from", source.name);
|
||||
|
||||
// Remove from active fetches BEFORE emitting signal so handler sees correct count
|
||||
delete activeFetches[source.url];
|
||||
fetchProcess.destroy();
|
||||
|
||||
root.availablePluginsUpdated();
|
||||
return;
|
||||
}
|
||||
} catch (e) {
|
||||
Logger.e("PluginService", "Failed to parse registry from", source.name, ":", e);
|
||||
Logger.e("PluginService", "Response was:", response ? response.substring(0, 200) : "null");
|
||||
}
|
||||
|
||||
// Clean up on error or empty response
|
||||
delete activeFetches[source.url];
|
||||
fetchProcess.destroy();
|
||||
});
|
||||
@@ -487,6 +522,19 @@ Singleton {
|
||||
return changed;
|
||||
}
|
||||
|
||||
// Load plugin settings and translations before instantiating components
|
||||
// This ensures pluginApi is fully populated before being passed to createObject()
|
||||
function loadPluginData(pluginId, manifest, callback) {
|
||||
// Load settings first
|
||||
loadPluginSettings(pluginId, function (settings) {
|
||||
// Then load translations
|
||||
loadPluginTranslationsAsync(pluginId, manifest, I18n.langCode, function (translations) {
|
||||
// Both ready - call back with complete data
|
||||
callback(settings, translations);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Load a plugin
|
||||
function loadPlugin(pluginId) {
|
||||
if (root.loadedPlugins[pluginId]) {
|
||||
@@ -504,97 +552,98 @@ Singleton {
|
||||
|
||||
Logger.i("PluginService", "Loading plugin:", pluginId);
|
||||
|
||||
// Create plugin API object
|
||||
var pluginApi = createPluginAPI(pluginId, manifest);
|
||||
// Load settings and translations FIRST, then create API and instantiate components
|
||||
loadPluginData(pluginId, manifest, function (settings, translations) {
|
||||
// Create plugin API object with pre-loaded data
|
||||
var pluginApi = createPluginAPI(pluginId, manifest, settings, translations);
|
||||
|
||||
// Initialize plugin entry with API and manifest
|
||||
root.loadedPlugins[pluginId] = {
|
||||
barWidget: null,
|
||||
desktopWidget: null,
|
||||
mainInstance: null,
|
||||
api: pluginApi,
|
||||
manifest: manifest
|
||||
};
|
||||
// Initialize plugin entry with API and manifest
|
||||
root.loadedPlugins[pluginId] = {
|
||||
barWidget: null,
|
||||
desktopWidget: null,
|
||||
mainInstance: null,
|
||||
api: pluginApi,
|
||||
manifest: manifest
|
||||
};
|
||||
|
||||
// Clear any previous errors for this plugin
|
||||
root.clearPluginError(pluginId);
|
||||
// Clear any previous errors for this plugin
|
||||
root.clearPluginError(pluginId);
|
||||
|
||||
// Load Main.qml entry point if it exists
|
||||
if (manifest.entryPoints && manifest.entryPoints.main) {
|
||||
var mainPath = pluginDir + "/" + manifest.entryPoints.main;
|
||||
var loadVersion = PluginRegistry.pluginLoadVersions[pluginId] || 0;
|
||||
var mainComponent = Qt.createComponent("file://" + mainPath + "?v=" + loadVersion);
|
||||
// Load Main.qml entry point if it exists
|
||||
if (manifest.entryPoints && manifest.entryPoints.main) {
|
||||
var mainPath = pluginDir + "/" + manifest.entryPoints.main;
|
||||
var loadVersion = PluginRegistry.pluginLoadVersions[pluginId] || 0;
|
||||
var mainComponent = Qt.createComponent("file://" + mainPath + "?v=" + loadVersion);
|
||||
|
||||
if (mainComponent.status === Component.Ready) {
|
||||
// Get the plugin container from shell.qml (must be in graphics scene)
|
||||
if (!root.pluginContainer) {
|
||||
Logger.e("PluginService", "Plugin container not set. Shell must set PluginService.pluginContainer.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Instantiate Main.qml with container as parent (places it in graphics scene)
|
||||
var mainInstance = mainComponent.createObject(root.pluginContainer);
|
||||
|
||||
if (mainInstance) {
|
||||
// Set pluginApi property after creation
|
||||
if (mainInstance.hasOwnProperty('pluginApi')) {
|
||||
mainInstance.pluginApi = pluginApi;
|
||||
} else {
|
||||
Logger.w("PluginService", "Main.qml for", pluginId, "should declare 'property var pluginApi: null'");
|
||||
if (mainComponent.status === Component.Ready) {
|
||||
// Get the plugin container from shell.qml (must be in graphics scene)
|
||||
if (!root.pluginContainer) {
|
||||
Logger.e("PluginService", "Plugin container not set. Shell must set PluginService.pluginContainer.");
|
||||
return;
|
||||
}
|
||||
|
||||
root.loadedPlugins[pluginId].mainInstance = mainInstance;
|
||||
pluginApi.mainInstance = mainInstance;
|
||||
Logger.i("PluginService", "Loaded Main.qml for plugin:", pluginId);
|
||||
} else {
|
||||
root.recordPluginError(pluginId, "main", "Failed to instantiate Main.qml");
|
||||
// Instantiate Main.qml with pluginApi passed directly in createObject
|
||||
var mainInstance = mainComponent.createObject(root.pluginContainer, {
|
||||
pluginApi: pluginApi
|
||||
});
|
||||
|
||||
if (mainInstance) {
|
||||
root.loadedPlugins[pluginId].mainInstance = mainInstance;
|
||||
pluginApi.mainInstance = mainInstance;
|
||||
Logger.i("PluginService", "Loaded Main.qml for plugin:", pluginId);
|
||||
} else {
|
||||
root.recordPluginError(pluginId, "main", "Failed to instantiate Main.qml");
|
||||
}
|
||||
} else if (mainComponent.status === Component.Error) {
|
||||
root.recordPluginError(pluginId, "main", mainComponent.errorString());
|
||||
}
|
||||
} else if (mainComponent.status === Component.Error) {
|
||||
root.recordPluginError(pluginId, "main", mainComponent.errorString());
|
||||
}
|
||||
}
|
||||
|
||||
// Load bar widget component if provided (don't instantiate - BarWidgetRegistry will do that)
|
||||
if (manifest.entryPoints && manifest.entryPoints.barWidget) {
|
||||
var widgetPath = pluginDir + "/" + manifest.entryPoints.barWidget;
|
||||
var widgetLoadVersion = PluginRegistry.pluginLoadVersions[pluginId] || 0;
|
||||
var widgetComponent = Qt.createComponent("file://" + widgetPath + "?v=" + widgetLoadVersion);
|
||||
// Load bar widget component if provided (don't instantiate - BarWidgetRegistry will do that)
|
||||
if (manifest.entryPoints && manifest.entryPoints.barWidget) {
|
||||
var widgetPath = pluginDir + "/" + manifest.entryPoints.barWidget;
|
||||
var widgetLoadVersion = PluginRegistry.pluginLoadVersions[pluginId] || 0;
|
||||
var widgetComponent = Qt.createComponent("file://" + widgetPath + "?v=" + widgetLoadVersion);
|
||||
|
||||
if (widgetComponent.status === Component.Ready) {
|
||||
root.loadedPlugins[pluginId].barWidget = widgetComponent;
|
||||
pluginApi.barWidget = widgetComponent;
|
||||
if (widgetComponent.status === Component.Ready) {
|
||||
root.loadedPlugins[pluginId].barWidget = widgetComponent;
|
||||
pluginApi.barWidget = widgetComponent;
|
||||
|
||||
// Register with BarWidgetRegistry
|
||||
BarWidgetRegistry.registerPluginWidget(pluginId, widgetComponent, manifest.metadata);
|
||||
Logger.i("PluginService", "Loaded bar widget for plugin:", pluginId);
|
||||
} else if (widgetComponent.status === Component.Error) {
|
||||
root.recordPluginError(pluginId, "barWidget", widgetComponent.errorString());
|
||||
// Register with BarWidgetRegistry
|
||||
BarWidgetRegistry.registerPluginWidget(pluginId, widgetComponent, manifest.metadata);
|
||||
Logger.i("PluginService", "Loaded bar widget for plugin:", pluginId);
|
||||
} else if (widgetComponent.status === Component.Error) {
|
||||
root.recordPluginError(pluginId, "barWidget", widgetComponent.errorString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Load desktop widget component if provided (don't instantiate - DesktopWidgetRegistry will do that)
|
||||
if (manifest.entryPoints && manifest.entryPoints.desktopWidget) {
|
||||
var desktopWidgetPath = pluginDir + "/" + manifest.entryPoints.desktopWidget;
|
||||
var desktopWidgetLoadVersion = PluginRegistry.pluginLoadVersions[pluginId] || 0;
|
||||
var desktopWidgetComponent = Qt.createComponent("file://" + desktopWidgetPath + "?v=" + desktopWidgetLoadVersion);
|
||||
// Load desktop widget component if provided (don't instantiate - DesktopWidgetRegistry will do that)
|
||||
if (manifest.entryPoints && manifest.entryPoints.desktopWidget) {
|
||||
var desktopWidgetPath = pluginDir + "/" + manifest.entryPoints.desktopWidget;
|
||||
var desktopWidgetLoadVersion = PluginRegistry.pluginLoadVersions[pluginId] || 0;
|
||||
var desktopWidgetComponent = Qt.createComponent("file://" + desktopWidgetPath + "?v=" + desktopWidgetLoadVersion);
|
||||
|
||||
if (desktopWidgetComponent.status === Component.Ready) {
|
||||
root.loadedPlugins[pluginId].desktopWidget = desktopWidgetComponent;
|
||||
pluginApi.desktopWidget = desktopWidgetComponent;
|
||||
if (desktopWidgetComponent.status === Component.Ready) {
|
||||
root.loadedPlugins[pluginId].desktopWidget = desktopWidgetComponent;
|
||||
pluginApi.desktopWidget = desktopWidgetComponent;
|
||||
|
||||
// Register with DesktopWidgetRegistry
|
||||
DesktopWidgetRegistry.registerPluginWidget(pluginId, desktopWidgetComponent, manifest.metadata);
|
||||
Logger.i("PluginService", "Loaded desktop widget for plugin:", pluginId);
|
||||
} else if (desktopWidgetComponent.status === Component.Error) {
|
||||
root.recordPluginError(pluginId, "desktopWidget", desktopWidgetComponent.errorString());
|
||||
// Register with DesktopWidgetRegistry
|
||||
DesktopWidgetRegistry.registerPluginWidget(pluginId, desktopWidgetComponent, manifest.metadata);
|
||||
Logger.i("PluginService", "Loaded desktop widget for plugin:", pluginId);
|
||||
} else if (desktopWidgetComponent.status === Component.Error) {
|
||||
root.recordPluginError(pluginId, "desktopWidget", desktopWidgetComponent.errorString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Logger.i("PluginService", "Plugin loaded:", pluginId);
|
||||
root.pluginLoaded(pluginId);
|
||||
Logger.i("PluginService", "Plugin loaded:", pluginId);
|
||||
root.pluginLoaded(pluginId);
|
||||
|
||||
// Set up hot reload watcher if enabled
|
||||
setupPluginFileWatcher(pluginId);
|
||||
// Set up hot reload watcher if enabled
|
||||
setupPluginFileWatcher(pluginId);
|
||||
|
||||
// Notify that this plugin finished loading (for init tracking)
|
||||
root._onPluginLoadComplete();
|
||||
});
|
||||
}
|
||||
|
||||
// Unload a plugin
|
||||
@@ -635,10 +684,9 @@ Singleton {
|
||||
Logger.i("PluginService", "Unloaded plugin:", pluginId);
|
||||
}
|
||||
|
||||
// Create plugin API object
|
||||
function createPluginAPI(pluginId, manifest) {
|
||||
// Create plugin API object with pre-loaded settings and translations
|
||||
function createPluginAPI(pluginId, manifest, settings, translations) {
|
||||
var pluginDir = PluginRegistry.getPluginDir(pluginId);
|
||||
var settingsFile = PluginRegistry.getPluginSettingsFile(pluginId);
|
||||
|
||||
var api = Qt.createQmlObject(`
|
||||
import QtQuick
|
||||
@@ -679,15 +727,9 @@ Singleton {
|
||||
// Set current language (can't use binding in Qt.createQmlObject string)
|
||||
api.currentLanguage = I18n.langCode;
|
||||
|
||||
// Load plugin settings
|
||||
loadPluginSettings(pluginId, function (settings) {
|
||||
api.pluginSettings = settings;
|
||||
});
|
||||
|
||||
// Load plugin translations for current language
|
||||
loadPluginTranslationsAsync(pluginId, manifest, I18n.langCode, function (translations) {
|
||||
api.pluginTranslations = translations;
|
||||
});
|
||||
// Set pre-loaded settings and translations (available immediately!)
|
||||
api.pluginSettings = settings || {};
|
||||
api.pluginTranslations = translations || {};
|
||||
|
||||
// ----------------------------------------
|
||||
// Helper function to get nested property by dot notation
|
||||
|
||||
@@ -34,6 +34,7 @@ Singleton {
|
||||
property bool zedAvailable: false
|
||||
property bool niriAvailable: false
|
||||
property bool mangoAvailable: false
|
||||
property bool nmcliAvailable: false
|
||||
|
||||
// Discord client auto-detection
|
||||
property var availableDiscordClients: []
|
||||
@@ -195,7 +196,8 @@ Singleton {
|
||||
"zedAvailable": ["which", "zeditor"],
|
||||
"emacsAvailable": ["sh", "-c", "test -d \"$HOME/.config/doom\" || test -d \"$HOME/.emacs.d\""],
|
||||
"niriAvailable": ["which", "niri"],
|
||||
"mangoAvailable": ["which", "mmsg"]
|
||||
"mangoAvailable": ["which", "mmsg"],
|
||||
"nmcliAvailable": ["which", "nmcli"]
|
||||
})
|
||||
|
||||
// Internal tracking
|
||||
|
||||
@@ -28,6 +28,8 @@ Singleton {
|
||||
property var diskPercents: ({})
|
||||
property real rxSpeed: 0
|
||||
property real txSpeed: 0
|
||||
property real zfsArcSizeKb: 0 // ZFS ARC cache size in KB
|
||||
property real zfsArcCminKb: 0 // ZFS ARC minimum (non-reclaimable) size in KB
|
||||
|
||||
// Internal state for CPU calculation
|
||||
property var prevCpuStats: null
|
||||
@@ -66,6 +68,9 @@ Singleton {
|
||||
|
||||
// Kickoff the gpu sensor detection for temperature
|
||||
gpuTempNameReader.checkNext();
|
||||
|
||||
// Check for ZFS ARC stats on startup
|
||||
zfsArcStatsFile.reload();
|
||||
}
|
||||
|
||||
// Re-run GPU detection when NVIDIA opt-in setting changes
|
||||
@@ -134,7 +139,10 @@ Singleton {
|
||||
restart();
|
||||
}
|
||||
}
|
||||
onTriggered: memInfoFile.reload()
|
||||
onTriggered: {
|
||||
memInfoFile.reload();
|
||||
zfsArcStatsFile.reload();
|
||||
}
|
||||
}
|
||||
|
||||
// Timer for disk usage
|
||||
@@ -202,6 +210,19 @@ Singleton {
|
||||
onLoaded: calculateNetworkSpeed(text())
|
||||
}
|
||||
|
||||
// ZFS ARC stats file (only exists on ZFS systems)
|
||||
FileView {
|
||||
id: zfsArcStatsFile
|
||||
path: "/proc/spl/kstat/zfs/arcstats"
|
||||
printErrors: false
|
||||
onLoaded: parseZfsArcStats(text())
|
||||
onLoadFailed: {
|
||||
// File doesn't exist (non-ZFS system), set ARC values to 0
|
||||
root.zfsArcSizeKb = 0;
|
||||
root.zfsArcCminKb = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------------
|
||||
// Process to fetch disk usage in percent
|
||||
// Uses 'df' aka 'disk free'
|
||||
@@ -448,6 +469,49 @@ Singleton {
|
||||
}
|
||||
|
||||
// -------------------------------------------------------
|
||||
// -------------------------------------------------------
|
||||
// Parse ZFS ARC stats from /proc/spl/kstat/zfs/arcstats
|
||||
function parseZfsArcStats(text) {
|
||||
if (!text)
|
||||
return;
|
||||
const lines = text.split('\n');
|
||||
|
||||
// The file format is: name type data
|
||||
// We need to find the lines with "size" and "c_min" and extract the values (third column)
|
||||
let foundSize = false;
|
||||
let foundCmin = false;
|
||||
|
||||
for (const line of lines) {
|
||||
const parts = line.trim().split(/\s+/);
|
||||
if (parts.length >= 3) {
|
||||
if (parts[0] === 'size') {
|
||||
// The value is in bytes, convert to KB
|
||||
const arcSizeBytes = parseInt(parts[2]) || 0;
|
||||
root.zfsArcSizeKb = Math.floor(arcSizeBytes / 1024);
|
||||
foundSize = true;
|
||||
} else if (parts[0] === 'c_min') {
|
||||
// The value is in bytes, convert to KB
|
||||
const arcCminBytes = parseInt(parts[2]) || 0;
|
||||
root.zfsArcCminKb = Math.floor(arcCminBytes / 1024);
|
||||
foundCmin = true;
|
||||
}
|
||||
|
||||
// If we found both, we can return early
|
||||
if (foundSize && foundCmin) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If fields not found, set to 0
|
||||
if (!foundSize) {
|
||||
root.zfsArcSizeKb = 0;
|
||||
}
|
||||
if (!foundCmin) {
|
||||
root.zfsArcCminKb = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------
|
||||
// Parse memory info from /proc/meminfo
|
||||
function parseMemoryInfo(text) {
|
||||
@@ -466,7 +530,11 @@ Singleton {
|
||||
}
|
||||
|
||||
if (memTotal > 0) {
|
||||
const usageKb = memTotal - memAvailable;
|
||||
// Calculate usage, adjusting for ZFS ARC cache if present
|
||||
let usageKb = memTotal - memAvailable;
|
||||
if (root.zfsArcSizeKb > 0) {
|
||||
usageKb = Math.max(0, usageKb - root.zfsArcSizeKb + root.zfsArcCminKb);
|
||||
}
|
||||
root.memGb = (usageKb / 1048576).toFixed(1); // 1024*1024 = 1048576
|
||||
root.memPercent = Math.round((usageKb / memTotal) * 100);
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ import qs.Commons
|
||||
Singleton {
|
||||
id: root
|
||||
|
||||
property bool hasAudioVisualizer: false
|
||||
property bool isVisible: true
|
||||
property var readyBars: ({})
|
||||
|
||||
@@ -18,33 +17,6 @@ Singleton {
|
||||
signal activeWidgetsChanged
|
||||
signal barReadyChanged(string screenName)
|
||||
|
||||
// onHasAudioVisualizerChanged: {
|
||||
// Logger.d("BarService", "hasAudioVisualizer", hasAudioVisualizer)
|
||||
// }
|
||||
|
||||
// Simple timer that run once when the widget structure has changed
|
||||
// and determine if any MediaMini widget has the visualizer on
|
||||
Timer {
|
||||
id: timerCheckVisualizer
|
||||
interval: 100
|
||||
repeat: false
|
||||
onTriggered: {
|
||||
hasAudioVisualizer = false;
|
||||
if (getAllWidgetInstances("AudioVisualizer").length > 0) {
|
||||
hasAudioVisualizer = true;
|
||||
return;
|
||||
}
|
||||
const widgets = getAllWidgetInstances("MediaMini");
|
||||
for (var i = 0; i < widgets.length; i++) {
|
||||
const widget = widgets[i];
|
||||
if (widget.showVisualizer) {
|
||||
hasAudioVisualizer = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
Logger.i("BarService", "Service started");
|
||||
}
|
||||
@@ -75,8 +47,6 @@ Singleton {
|
||||
"instance": instance
|
||||
};
|
||||
|
||||
timerCheckVisualizer.restart();
|
||||
|
||||
Logger.d("BarService", "Registered widget:", key);
|
||||
root.activeWidgetsChanged();
|
||||
}
|
||||
|
||||
@@ -4,10 +4,14 @@ import QtQuick
|
||||
import Quickshell
|
||||
import qs.Commons
|
||||
import qs.Modules.DesktopWidgets.Widgets
|
||||
import qs.Services.Noctalia
|
||||
|
||||
Singleton {
|
||||
id: root
|
||||
|
||||
// Transient state - not persisted, resets on shell restart
|
||||
property bool editMode: false
|
||||
|
||||
// Signal emitted when plugin widgets are registered/unregistered
|
||||
signal pluginWidgetRegistryUpdated
|
||||
|
||||
@@ -62,7 +66,8 @@ Singleton {
|
||||
"allowUserSettings": true,
|
||||
"showBackground": true,
|
||||
"visualizerType": "linear",
|
||||
"hideMode": "visible"
|
||||
"hideMode": "visible",
|
||||
"showButtons": true
|
||||
},
|
||||
"Weather": {
|
||||
"allowUserSettings": true,
|
||||
@@ -110,6 +115,17 @@ Singleton {
|
||||
return Object.keys(pluginWidgets);
|
||||
}
|
||||
|
||||
// Get display name for a widget ID
|
||||
function getWidgetDisplayName(widgetId) {
|
||||
if (widgetId.startsWith("plugin:")) {
|
||||
var pluginId = widgetId.replace("plugin:", "");
|
||||
var manifest = PluginRegistry.getPluginManifest(pluginId);
|
||||
return manifest ? manifest.name : pluginId;
|
||||
}
|
||||
// Core widgets - return as-is (Clock, MediaPlayer, Weather)
|
||||
return widgetId;
|
||||
}
|
||||
|
||||
// Register a plugin desktop widget
|
||||
function registerPluginWidget(pluginId, component, metadata) {
|
||||
if (!pluginId || !component) {
|
||||
|
||||
@@ -30,7 +30,8 @@ RowLayout {
|
||||
|
||||
readonly property bool isValueChanged: isSettings && (currentKey !== defaultValue)
|
||||
readonly property string indicatorTooltip: {
|
||||
if (!isSettings) return "";
|
||||
if (!isSettings)
|
||||
return "";
|
||||
var displayValue = "";
|
||||
if (defaultValue === "") {
|
||||
// Try to find the display name for empty key in the model
|
||||
@@ -304,5 +305,4 @@ RowLayout {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -62,38 +62,10 @@ Popup {
|
||||
color: Color.mOutline
|
||||
}
|
||||
|
||||
// Settings loader
|
||||
// Settings loader - pluginApi is passed via setSource() in openPluginSettings()
|
||||
Loader {
|
||||
id: settingsLoader
|
||||
Layout.fillWidth: true
|
||||
|
||||
// Create a dummy pluginApi that returns empty strings to avoid undefined warnings
|
||||
property var _dummyApi: QtObject {
|
||||
property var pluginSettings: ({})
|
||||
property var manifest: ({
|
||||
metadata: {
|
||||
defaultSettings: {}
|
||||
}
|
||||
})
|
||||
|
||||
function tr(key) {
|
||||
return "";
|
||||
}
|
||||
|
||||
function trp(key, count) {
|
||||
return "";
|
||||
}
|
||||
|
||||
function saveSettings() {
|
||||
}
|
||||
}
|
||||
|
||||
onLoaded: {
|
||||
// Inject dummy API immediately to prevent undefined warnings during initialization
|
||||
if (item && item.hasOwnProperty("pluginApi") && !item.pluginApi) {
|
||||
item.pluginApi = _dummyApi;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Action buttons
|
||||
|
||||
@@ -118,6 +118,22 @@ PopupWindow {
|
||||
}
|
||||
anchor.rect.y: {
|
||||
if (anchorItem && screen) {
|
||||
// Check if using absolute positioning (small anchor point item)
|
||||
const isAbsolutePosition = anchorItem.width <= 1 && anchorItem.height <= 1;
|
||||
|
||||
if (isAbsolutePosition) {
|
||||
// For absolute positioning, show menu directly at anchor Y
|
||||
// Only adjust if menu would clip at bottom
|
||||
const anchorGlobalPos = anchorItem.mapToItem(null, 0, 0);
|
||||
const menuBottom = anchorGlobalPos.y + implicitHeight;
|
||||
|
||||
if (menuBottom > screen.height - Style.marginM) {
|
||||
// Position above the click point instead
|
||||
return -implicitHeight;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
const anchorCenterY = anchorItem.height / 2;
|
||||
|
||||
// Calculate base Y position based on bar orientation
|
||||
|
||||
@@ -31,7 +31,8 @@ RowLayout {
|
||||
|
||||
readonly property bool isValueChanged: isSettings && (currentKey !== defaultValue)
|
||||
readonly property string indicatorTooltip: {
|
||||
if (!isSettings) return "";
|
||||
if (!isSettings)
|
||||
return "";
|
||||
var displayValue = "";
|
||||
if (defaultValue === "") {
|
||||
// Try to find the display name for empty key in the model
|
||||
@@ -387,5 +388,4 @@ RowLayout {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -45,4 +45,3 @@ Rectangle {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -42,8 +42,8 @@ RowLayout {
|
||||
|
||||
readonly property bool isValueChanged: isSettings && (value !== defaultValue)
|
||||
readonly property string indicatorTooltip: isSettings ? I18n.tr("settings.indicator.default-value", {
|
||||
"value": String(defaultValue)
|
||||
}) : ""
|
||||
"value": String(defaultValue)
|
||||
}) : ""
|
||||
|
||||
Timer {
|
||||
id: repeatTimer
|
||||
@@ -415,5 +415,4 @@ RowLayout {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -32,8 +32,8 @@ ColumnLayout {
|
||||
|
||||
readonly property bool isValueChanged: isSettings && (text !== defaultValue)
|
||||
readonly property string indicatorTooltip: isSettings ? I18n.tr("settings.indicator.default-value", {
|
||||
"value": defaultValue === "" ? "(empty)" : String(defaultValue)
|
||||
}) : ""
|
||||
"value": defaultValue === "" ? "(empty)" : String(defaultValue)
|
||||
}) : ""
|
||||
|
||||
NLabel {
|
||||
label: root.label
|
||||
@@ -230,5 +230,4 @@ ColumnLayout {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+2
-3
@@ -28,8 +28,8 @@ RowLayout {
|
||||
|
||||
readonly property bool isValueChanged: isSettings && (checked !== defaultValue)
|
||||
readonly property string indicatorTooltip: isSettings ? I18n.tr("settings.indicator.default-value", {
|
||||
"value": typeof defaultValue === "boolean" ? (defaultValue ? "true" : "false") : String(defaultValue)
|
||||
}) : ""
|
||||
"value": typeof defaultValue === "boolean" ? (defaultValue ? "true" : "false") : String(defaultValue)
|
||||
}) : ""
|
||||
|
||||
NLabel {
|
||||
label: root.label
|
||||
@@ -107,5 +107,4 @@ RowLayout {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -32,14 +32,15 @@ ColumnLayout {
|
||||
|
||||
readonly property bool isValueChanged: isSettings && (value !== defaultValue)
|
||||
readonly property string indicatorTooltip: {
|
||||
if (!isSettings) return "";
|
||||
if (!isSettings)
|
||||
return "";
|
||||
var defaultVal = defaultValue;
|
||||
if (typeof defaultVal === "number") {
|
||||
// If it's a decimal between 0 and 1, format as percentage
|
||||
if (defaultVal > 0 && defaultVal <= 1 && from >= 0 && from < 1) {
|
||||
return I18n.tr("settings.indicator.default-value", {
|
||||
"value": Math.floor(defaultVal * 100) + "%"
|
||||
});
|
||||
"value": Math.floor(defaultVal * 100) + "%"
|
||||
});
|
||||
}
|
||||
return I18n.tr("settings.indicator.default-value", {
|
||||
"value": String(defaultVal)
|
||||
|
||||
@@ -143,18 +143,6 @@ ShellRoot {
|
||||
PluginService.screenDetector = screenDetector;
|
||||
}
|
||||
}
|
||||
|
||||
// Listen for when available plugins are fetched, then check for updates
|
||||
Connections {
|
||||
target: PluginService
|
||||
property bool hasCheckedOnStartup: false
|
||||
function onAvailablePluginsUpdated() {
|
||||
if (!hasCheckedOnStartup) {
|
||||
hasCheckedOnStartup = true;
|
||||
PluginService.checkForUpdates();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user