Merge branch 'main' into fix/NScrollText

This commit is contained in:
yuzujr
2026-01-28 22:36:33 +08:00
committed by GitHub
179 changed files with 12102 additions and 4915 deletions
+31 -52
View File
@@ -153,68 +153,46 @@ body {
--purple-5: hsl(267deg, 83%, 70%);
}
.divider__908e2:not(.isUnread__908e2) {
border-color: var(--bg-2);
/* Dividers (Channel list and messages) */
[class*="divider"]:not([class*="isUnread"]) {
border-color: var(--bg-2) !important;
}
.bannerButton_de8008, .bannerColor_fb7f94 {
color: var(--text-1);
/* Profile Banners */
[class*="bannerButton"],
[class*="bannerColor"] {
color: var(--text-1) !important;
}
/* Prevent scaling animation on DMS icon */
.wrapper__6e9f8[data-list-item-id='guildsnav___home']:hover > .childWrapper__6e9f8::before,
.wrapper__6e9f8[data-list-item-id='guildsnav___home'].selected__6e9f8 > .childWrapper__6e9f8::before {
/* Prevent scaling animation on Home/DMS icon */
[class*="guildsnav"] [class*="wrapper"][data-list-item-id='guildsnav___home']:hover > [class*="childWrapper"]::before,
[class*="guildsnav"] [class*="wrapper"][data-list-item-id='guildsnav___home'][class*="selected"] > [class*="childWrapper"]::before {
transform: rotate(-360deg) scale(1) !important;
}
/* Make timestamps use lighter text color */
.timestamp_c19a55,
.timestampInline_c19a55,
.timestamp_c19a55 time,
.timestampInline_c19a55 time {
color: var(--text-4) !important;
}
/* Make timestamps more readable by overriding inline styles */
/* Timestamps */
[class*="timestamp"],
[class*="timestampInline"],
[class*="timestamp"] time {
color: var(--text-4) !important;
}
.postTitleText_faa96b {
/* Forum/Thread Titles */
[class*="postTitleText"],
[class*="postTitleText"] [class*="highlight"] {
color: var(--text-2) !important;
}
.postTitleText_faa96b .highlight {
color: var(--text-2) !important;
}
/* Make thread titles more readable by overriding inline styles */
[class*="headerText"] [class*="postTitleText"],
[class*="headerText"] h3[class*="postTitleText"] {
color: var(--text-2) !important;
}
.messageContent_c19a55,
.messageContent_faa96b {
color: var(--text-3) !important;
}
.messageContent_c19a55 .highlight,
.messageContent_faa96b .highlight {
color: var(--text-2) !important;
}
/* Make message content more readable by overriding inline styles */
/* Message Content */
[class*="messageContent"] {
color: var(--text-3) !important;
}
[class*="messageContent"] .highlight {
[class*="messageContent"] [class*="highlight"] {
color: var(--text-2) !important;
}
/* Make slowmode/cooldown text more readable by overriding inline styles */
/* Slowmode/Cooldown Text */
[class*="cooldownText"] {
color: var(--text-4) !important;
}
@@ -279,8 +257,8 @@ body {
[class*="radioSelection"][data-selected="true"] [class*="innerDotRadio"],
[class*="radioSelection"][aria-checked="true"] [class*="outerRadioFill"],
[class*="radioSelection"][aria-checked="true"] [class*="innerDotRadio"],
.parentSelected__6e9f8 [class*="outerRadioFill"],
.parentSelected__6e9f8 [class*="innerDotRadio"] {
[class*="parentSelected"] [class*="outerRadioFill"],
[class*="parentSelected"] [class*="innerDotRadio"] {
opacity: 1 !important;
}
@@ -311,35 +289,36 @@ body {
border-color: var(--accent-1) /* keep background consistent on hover */
}
._714a9a7abaf0392a-checkboxOption[data-selected="true"]:hover ._714a9a7abaf0392a-checkStroke,
._714a9a7abaf0392a-checkboxOption[data-selected="true"]:hover ._714a9a7abaf0392a-dot {
color: var(--accent-1) !important; /* keep checkmark accent color on hover */
}
/* Fix hover color for channel mentions */
.channelMention:hover {
color: var(--text-1) !important;
}
[class*="unreadTop"],
[class*="unreadBottom"],
[class*="unreadMentionsIndicator"] {
background-color: transparent !important;
}
[class*="unreadBar"],
[class*="unreadMentionsBar"] {
background-color: var(--accent-1) !important;
border: 1px solid var(--border);
}
[class*="unreadBar"][class*="mention"],
[class*="unreadMentionsBar"][class*="mention"] {
background-color: var(--accent-1) !important;
}
[class*="unreadBar"][class*="unread"],
[class*="unreadMentionsBar"][class*="unread"] {
background-color: var(--bg-1) !important;
border-color: var(--accent-1) !important;
}
[class*="unreadBar"] [class*="text"],
[class*="unreadMentionsBar"] [class*="text"] {
color: var(--text-0) !important;
color: var(--text-1) !important;
font-weight: 700;
}
[class*="unreadMentionsIndicator"] [class*="bar"] {
background-color: var(--accent-1) !important;
}
+1 -1
View File
@@ -1,4 +1,4 @@
;;; noctalia-theme.el --- Theme using Template SCSS variables
;;; noctalia-theme.el --- Theme using Template SCSS variables -*- lexical-binding: t -*-
;; Copyright (C) 2025
+35 -2
View File
@@ -182,7 +182,9 @@
"hide-widget-when-zero-unread-description": "Benachrichtigungssymbol ausblenden, wenn keine ungelesenen Benachrichtigungen vorhanden sind.",
"hide-widget-when-zero-unread-label": "Symbol bei keinen ungelesenen Benachrichtigungen ausblenden",
"show-unread-badge-description": "Badge mit der Anzahl ungelesener Benachrichtigungen anzeigen.",
"show-unread-badge-label": "Badge für ungelesene Nachrichten anzeigen"
"show-unread-badge-label": "Badge für ungelesene Nachrichten anzeigen",
"unread-badge-color-description": "Wähle die Farbe für das Abzeichen ungelesener Benachrichtigungen.",
"unread-badge-color-label": "Farbe des ungelesenen Abzeichens"
},
"section-editor": {
"placeholder": "Widget auswählen...",
@@ -254,8 +256,12 @@
"workspace": {
"character-count-description": "Anzahl der Zeichen, die von Arbeitsbereichsnamen angezeigt werden (1-10).",
"character-count-label": "Zeichenanzahl",
"empty-color-description": "Lege die Hintergrundfarbe für leere Workspaces fest.",
"empty-color-label": "Farbe für leere Workspace",
"enable-scrollwheel-description": "Zwischen Arbeitsbereichen mit dem Mausrad wechseln.",
"enable-scrollwheel-label": "Zum Wechseln der Arbeitsbereiche scrollen",
"focused-color-description": "Lege die Hintergrundfarbe für den fokussierten Workspace fest.",
"focused-color-label": "Farbe des fokussierten Workspace",
"follow-focused-screen-description": "Zeige Arbeitsbereiche vom aktuell fokussierten Bildschirm an, statt vom Bildschirm, auf dem sich die Leiste befindet.",
"follow-focused-screen-label": "Fokussiertem Bildschirm folgen",
"grouped-border-opacity-description": "Legen Sie die Deckkraft für Workspace-Container-Rahmen fest.",
@@ -264,8 +270,14 @@
"hide-unoccupied-label": "Unbesetzte ausblenden",
"label-mode-description": "Wählen Sie, wie Arbeitsbereichs-Beschriftungen angezeigt werden.",
"label-mode-label": "Beschriftungsmodus",
"occupied-color-description": "Lege die Hintergrundfarbe für belegte Workspaces fest.",
"occupied-color-label": "Farbe für belegte Arbeitsfläche",
"reverse-scrolling-description": "Die Richtung des Arbeitsbereichswechsels beim Scrollen umkehren.",
"reverse-scrolling-label": "Scrollen umkehren",
"show-applications-description": "Anwendungssymbole in jedem Arbeitsbereich anzeigen.",
"show-applications-label": "Anwendungen anzeigen",
"show-badge-description": "Zeige die Arbeitsbereichsnummern-Plakette im gruppierten Modus an.",
"show-badge-label": "Workspace-Badge anzeigen",
"show-labels-only-when-occupied-description": "Arbeitsbereichsbezeichnungen nur anzeigen, wenn sie Fenster enthalten.",
"show-labels-only-when-occupied-label": "Beschriftungen nur anzeigen, wenn belegt",
"unfocused-icons-opacity-description": "Transparenzgrad für nicht fokussierte App-Symbole festlegen.",
@@ -416,6 +428,7 @@
"not-found": "Nicht gefunden",
"notifications": "Benachrichtigungen",
"official": "Offiziell",
"on-surface": "Auf Oberfläche",
"output": "Ausgabe",
"pair": "Koppeln",
"paired": "Gekoppelt",
@@ -428,6 +441,7 @@
"polling": "Abfrageintervall",
"position": "Position",
"previous": "Zurück",
"primary": "Primär",
"random": "Zufällig",
"reboot": "Neu starten",
"refresh": "Aktualisieren",
@@ -440,6 +454,7 @@
"scanning": "Scannen...",
"screen-corners": "Bildschirmecken",
"search": "Suche",
"secondary": "Sekundär",
"security": "Sicherheit",
"select": "Auswählen",
"shortcuts": "Kurzbefehle",
@@ -451,6 +466,7 @@
"stop": "Stopp",
"suspend": "Energie sparen",
"templates": "Vorlagen",
"tertiary": "Tertiär",
"test": "Test",
"thresholds": "Schwellenwerte",
"title": "Titel",
@@ -579,7 +595,10 @@
"density-compact": "Kompakt",
"density-default": "Standard",
"density-mini": "Mini",
"density-spacious": "Geräumig"
"density-spacious": "Geräumig",
"type-floating": "Schwebend",
"type-framed": "Gerahmt",
"type-simple": "Einfach"
},
"control-center": {
"quick-settings-style-classic": "Klassisch",
@@ -707,6 +726,10 @@
"appearance-desc": "Erscheinungsbild und Position der Statusleiste anpassen.",
"appearance-floating-description": "Statusleiste als schwebende 'Pille' anzeigen.",
"appearance-floating-label": "Schwebende Statusleiste",
"appearance-frame-radius": "Innenradius",
"appearance-frame-settings-description": "Rahmenstärke und inneren Eckenradius anpassen",
"appearance-frame-settings-label": "Rahmeneinstellungen",
"appearance-frame-thickness": "Stärke",
"appearance-hide-on-overview-description": "Die Leiste ausblenden und Panels schließen, wenn die Compositor-Übersicht aktiv ist.",
"appearance-hide-on-overview-label": "Leiste in der Übersicht ausblenden",
"appearance-margins-description": "Ränder um die schwebende Statusleiste anpassen.",
@@ -721,6 +744,8 @@
"appearance-show-capsule-label": "Kapsel anzeigen",
"appearance-show-outline-description": "Zeige eine sichtbare Umrandung um jedes Widget an.",
"appearance-show-outline-label": "Widget-Umrisse anzeigen",
"appearance-type-description": "Wähle den Stil der Leiste: Einfach, Schwebend oder Gerahmt",
"appearance-type-label": "Leistentyp",
"appearance-use-separate-opacity-description": "Ermöglicht die Verwendung eines separaten Deckkraftwerts für den Leistenhintergrund.",
"appearance-use-separate-opacity-label": "Separate Leisten-Deckkraft",
"monitor-configure-widgets": "Widgets konfigurieren",
@@ -1027,6 +1052,10 @@
"settings-clip-wrap-text-label": "Zwischenablage-Text umbrechen",
"settings-clipboard-history-description": "Zugriff auf zuvor kopierte Elemente über den Launcher.",
"settings-clipboard-history-label": "Zwischenablageverlauf aktivieren",
"settings-clipboard-watch-image-description": "Vollständige Befehlszeichenfolge, die an wl-paste für Bildänderungen übergeben wird. (Neustart erforderlich)",
"settings-clipboard-watch-image-label": "Bildüberwachungsbefehl",
"settings-clipboard-watch-text-description": "Vollständiger Befehlsstring, der an wl-paste für Textänderungen übergeben wird. (Neustart erforderlich)",
"settings-clipboard-watch-text-label": "Textüberwachungsbefehl",
"settings-custom-launch-prefix-description": "Befehle mit einem benutzerdefinierten Launcher präfixieren (z.B. 'runapp' für systemd-Integration).",
"settings-custom-launch-prefix-enabled-description": "Verwenden Sie ein benutzerdefiniertes Präfix zum Starten von Anwendungen anstelle der Standardmethode.",
"settings-custom-launch-prefix-enabled-label": "Benutzerdefiniertes Start-Präfix aktivieren",
@@ -1090,6 +1119,10 @@
"weather-show-in-calendar-label": "Wetter im Kalender anzeigen"
},
"lock-screen": {
"allow-password-with-fprintd-description": "Wenn fprintd (Fingerabdruck-Authentifizierung) aktiv ist, können Sie mit dieser Option weiterhin Ihr Passwort anstelle eines Fingerabdrucks verwenden.",
"allow-password-with-fprintd-label": "Passwort-Anmeldung mit fprintd erlauben",
"auto-start-auth-description": "z. B. startet die Fingerabdruckauthentifizierung automatisch, ohne dass ein Tastendruck oder ein Klick erforderlich ist.",
"auto-start-auth-label": "Automatische Authentifizierung beim Start",
"compact-lockscreen-description": "Zeige nur die Login-Eingabe und Systemsteuerung, blende Wetter- und Medien-Widgets aus.",
"compact-lockscreen-label": "Kompakter Sperrbildschirm",
"lock-on-suspend-description": "Den Bildschirm beim Energiesparen automatisch sperren.",
+35 -2
View File
@@ -182,7 +182,9 @@
"hide-widget-when-zero-unread-description": "Hide the notification icon when there are no unread notifications.",
"hide-widget-when-zero-unread-label": "Hide icon when no unread notifications",
"show-unread-badge-description": "Display a badge showing the number of unread notifications.",
"show-unread-badge-label": "Show unread badge"
"show-unread-badge-label": "Show unread badge",
"unread-badge-color-description": "Select the color for the unread notification badge.",
"unread-badge-color-label": "Unread badge color"
},
"section-editor": {
"placeholder": "Select a widget to add...",
@@ -254,8 +256,12 @@
"workspace": {
"character-count-description": "Number of characters to display from workspace names (1-10).",
"character-count-label": "Character count",
"empty-color-description": "Set the background color for empty workspaces.",
"empty-color-label": "Empty workspace color",
"enable-scrollwheel-description": "Switch between workspaces using the mouse scroll wheel.",
"enable-scrollwheel-label": "Scroll to switch workspaces",
"focused-color-description": "Set the background color for the focused workspace.",
"focused-color-label": "Focused workspace color",
"follow-focused-screen-description": "Display workspaces from the currently focused screen, rather than the screen where the bar is located.",
"follow-focused-screen-label": "Follow focused screen",
"grouped-border-opacity-description": "Set the opacity level for workspace container borders.",
@@ -264,8 +270,14 @@
"hide-unoccupied-label": "Hide unoccupied",
"label-mode-description": "Choose how workspace labels are displayed.",
"label-mode-label": "Label mode",
"occupied-color-description": "Set the background color for occupied workspaces.",
"occupied-color-label": "Occupied workspace color",
"reverse-scrolling-description": "Reverse the direction of workspace switching when scrolling.",
"reverse-scrolling-label": "Reverse scrolling",
"show-applications-description": "Display application icons inside each workspace.",
"show-applications-label": "Show applications",
"show-badge-description": "Show the workspace number badge in grouped mode.",
"show-badge-label": "Show workspace badge",
"show-labels-only-when-occupied-description": "Only show workspace labels when they contain windows.",
"show-labels-only-when-occupied-label": "Show labels only when occupied",
"unfocused-icons-opacity-description": "Set the opacity level for unfocused app icons.",
@@ -416,6 +428,7 @@
"not-found": "Not found",
"notifications": "Notifications",
"official": "Official",
"on-surface": "On Surface",
"output": "Output",
"pair": "Pair",
"paired": "Paired",
@@ -428,6 +441,7 @@
"polling": "Polling",
"position": "Position",
"previous": "Previous",
"primary": "Primary",
"random": "Random",
"reboot": "Reboot",
"refresh": "Refresh",
@@ -440,6 +454,7 @@
"scanning": "Scanning...",
"screen-corners": "Screen Corners",
"search": "Search",
"secondary": "Secondary",
"security": "Security",
"select": "Select",
"shortcuts": "Shortcuts",
@@ -451,6 +466,7 @@
"stop": "Stop",
"suspend": "Suspend",
"templates": "Templates",
"tertiary": "Tertiary",
"test": "Test",
"thresholds": "Thresholds",
"title": "Title",
@@ -579,7 +595,10 @@
"density-compact": "Compact",
"density-default": "Default",
"density-mini": "Mini",
"density-spacious": "Spacious"
"density-spacious": "Spacious",
"type-floating": "Floating",
"type-framed": "Framed",
"type-simple": "Simple"
},
"control-center": {
"quick-settings-style-classic": "Classic",
@@ -707,6 +726,10 @@
"appearance-desc": "Customize the bar's appearance and position.",
"appearance-floating-description": "Display the bar as a floating 'pill'.",
"appearance-floating-label": "Floating bar",
"appearance-frame-radius": "Inner Radius",
"appearance-frame-settings-description": "Adjust frame thickness and inner corner radius",
"appearance-frame-settings-label": "Frame Settings",
"appearance-frame-thickness": "Thickness",
"appearance-hide-on-overview-description": "Hide the bar and close panels when the compositor overview is active.",
"appearance-hide-on-overview-label": "Hide bar on overview",
"appearance-margins-description": "Adjust the margins around the floating bar.",
@@ -721,6 +744,8 @@
"appearance-show-capsule-label": "Show capsule",
"appearance-show-outline-description": "Displays a visible border around every widget.",
"appearance-show-outline-label": "Show widget outlines",
"appearance-type-description": "Choose the style of the bar: Simple, Floating or Framed",
"appearance-type-label": "Bar Type",
"appearance-use-separate-opacity-description": "Enable to use a separate opacity value for the bar background.",
"appearance-use-separate-opacity-label": "Use separate bar opacity",
"monitor-configure-widgets": "Configure widgets",
@@ -1027,6 +1052,10 @@
"settings-clip-wrap-text-label": "Wrap clipboard text",
"settings-clipboard-history-description": "Access previously copied items from the launcher.",
"settings-clipboard-history-label": "Enable clipboard history",
"settings-clipboard-watch-image-description": "Full command string passed to wl-paste for image changes. (requires restart)",
"settings-clipboard-watch-image-label": "Image watch command",
"settings-clipboard-watch-text-description": "Full command string passed to wl-paste for text changes. (requires restart)",
"settings-clipboard-watch-text-label": "Text watch command",
"settings-custom-launch-prefix-description": "Prefix commands with a custom launcher (e.g., 'runapp' for systemd integration).",
"settings-custom-launch-prefix-enabled-description": "Use a custom prefix for launching applications instead of the default method.",
"settings-custom-launch-prefix-enabled-label": "Enable custom launch prefix",
@@ -1090,6 +1119,10 @@
"weather-show-in-calendar-label": "Display weather in calendar"
},
"lock-screen": {
"allow-password-with-fprintd-description": "When fprintd (fingerprint authentication) is active, this option lets you still login using your password instead of a fingerprint",
"allow-password-with-fprintd-label": "Allow password login with fprintd",
"auto-start-auth-description": "e.g., automatically starts fingerprint authentication without requiring a key press or button click.",
"auto-start-auth-label": "Auto-start authentication",
"compact-lockscreen-description": "Show only the login input and system controls, hiding weather and media widgets.",
"compact-lockscreen-label": "Compact lock screen",
"lock-on-suspend-description": "Automatically lock the screen when suspending the system.",
+90 -57
View File
@@ -26,8 +26,8 @@
"widget-settings": "Configuración del widget"
},
"authentication": {
"error": "Error de autenticación",
"failed": "Autenticación fallida"
"error": "Error de autentificación",
"failed": "Autentificación fallida"
},
"bar": {
"active-window": {
@@ -60,7 +60,7 @@
"show-power-profile-label": "Mostrar controles de perfil de energía"
},
"clock": {
"clock-display-description": "Personaliza la visualización de tu reloj añadiendo tokens de la lista de abajo. Para usar el formato de 12 horas, debes incluir el token 'AP'.",
"clock-display-description": "Personaliza la visualización del reloj añadiendo tokens de la lista de abajo. Para usar el formato de 12 horas, debes incluir el token 'AP'.",
"clock-display-label": "Visualización del reloj",
"custom-font-description": "Seleccionar una fuente personalizada para la visualización del reloj.",
"custom-font-label": "Fuente personalizada",
@@ -69,13 +69,13 @@
"horizontal-bar-description": "Consejo: Usa \\n para crear un salto de línea.",
"horizontal-bar-label": "Barra horizontal",
"preview": "Vista previa",
"tooltip-format-description": "Cadena de formato para la información sobre herramientas que se muestra al pasar el cursor sobre el reloj. Déjelo vacío para usar la información sobre herramientas predeterminada.",
"tooltip-format-label": "Formato de información sobre herramientas",
"use-custom-font-description": "Anular la selección de fuente predeterminada con una fuente personalizada para el reloj.",
"tooltip-format-description": "Formato de la información mostrada al pasar el cursor sobre el reloj. Déjalo vacío para usar el formato por defecto.",
"tooltip-format-label": "Formato del emergente",
"use-custom-font-description": "Anular la fuente predeterminada del sistema con una fuente específica para el reloj.",
"use-custom-font-label": "Usar fuente personalizada",
"use-monospaced-font-description": "Cuando está activado, el reloj usará la fuente monoespaciada.",
"use-monospaced-font-description": "Cuando está activada, el reloj usará la fuente monoespaciada.",
"use-monospaced-font-label": "Usar fuente monoespaciada",
"use-primary-color-description": "Cuando está activado, se aplica el color primario para dar énfasis.",
"use-primary-color-description": "Cuando está activado, se aplica el color primario para dar énfasis.",
"use-primary-color-label": "Usar color primario",
"vertical-bar-description": "Usa un espacio para separar cada parte en una nueva línea.",
"vertical-bar-label": "Barra vertical"
@@ -83,8 +83,8 @@
"control-center": {
"browse-file": "Explorar archivo",
"browse-library": "Explorar biblioteca",
"color-selection-description": "Aplica colores del tema a los íconos.",
"enable-colorization-description": "Habilita la coloración para el ícono del centro de control, aplicando los colores del tema.",
"color-selection-description": "Aplica colores del tema a los iconos.",
"enable-colorization-description": "Habilita el coloreado del icono del centro de control, aplicando los colores del tema.",
"icon-description": "Selecciona un icono de la biblioteca o un archivo personalizado.",
"select-custom-icon": "Seleccionar un icono personalizado",
"use-distro-logo-description": "Usar el logo de tu distribución en lugar de un icono personalizado.",
@@ -95,19 +95,19 @@
"collapse-condition-label": "Condición de colapso",
"color-selection-description": "Aplicar colores del tema al icono y al texto.",
"color-selection-label": "Seleccionar color",
"display-command-output-description": "Ingresa un comando para ejecutar a intervalos regulares. La primera línea de su salida se mostrará como texto.",
"display-command-output-label": "Mostrar salida de comando",
"display-command-output-description": "Introduce un comando para ejecutar a intervalos regulares. La primera línea de su salida se mostrará como texto.",
"display-command-output-label": "Mostrar salida del comando",
"display-command-output-stream-description": "Introduce un comando para ejecutar continuamente.",
"dynamic-text": "Texto dinámico",
"enable-colorization-description": "Habilitar la coloración para el icono y el texto del botón personalizado, aplicando los colores del tema.",
"enable-colorization-label": "Habilitar coloración",
"enable-colorization-description": "Habilitar el coloreado para el icono y el texto del botón personalizado, aplicando los colores del tema.",
"enable-colorization-label": "Habilitar coloreado",
"hide-mode-always-expanded": "Siempre expandido",
"hide-mode-description": "Controla la visibilidad del widget cuando el comando no tiene salida.",
"hide-mode-expand-with-output": "Expandir cuando tiene salida",
"hide-mode-label": "Modo de ocultar",
"hide-mode-label": "Modo de ocultación",
"hide-mode-max-transparent": "Máximo expandido pero transparente",
"icon-description": "Selecciona un icono de la biblioteca.",
"ipc-identifier-description": "Identificador único para comandos IPC. Utilice este identificador con 'qs -c noctalia-shell ipc call cb [acción] [identificador]' para controlar este botón a través de IPC.",
"ipc-identifier-description": "Identificador único para comandos IPC. Utiliza este identificador con 'qs -c noctalia-shell ipc call cb [acción] [identificador]' para controlar este botón a través de IPC.",
"ipc-identifier-label": "Identificador IPC",
"left-click-description": "Comando a ejecutar cuando se hace clic izquierdo en el botón.",
"left-click-label": "Clic izquierdo",
@@ -116,11 +116,11 @@
"max-text-length-horizontal-label": "Longitud máxima de texto (horizontal)",
"max-text-length-vertical-description": "Número máximo de caracteres a mostrar en la barra vertical (0 para ocultar el texto).",
"max-text-length-vertical-label": "Longitud máxima de texto (vertical)",
"middle-click-description": "Comando a ejecutar cuando se hace clic medio en el botón.",
"middle-click-label": "Clic medio",
"middle-click-description": "Comando a ejecutar cuando se hace clic central en el botón.",
"middle-click-label": "Clic central",
"middle-click-update-text": "Actualizar el texto mostrado al hacer clic con el botón central",
"parse-json-description": "Analizar la salida del comando como un objeto JSON para establecer dinámicamente el texto y el ícono.",
"parse-json-label": "Analizar salida como JSON",
"parse-json-label": "Interpretar salida como JSON",
"refresh-interval-description": "Intervalo en milisegundos.",
"refresh-interval-label": "Intervalo de actualización",
"right-click-description": "Comando a ejecutar cuando se hace clic derecho en el botón.",
@@ -129,7 +129,7 @@
"show-icon-description": "Alterna la visibilidad del icono del widget.",
"show-icon-label": "Mostrar icono",
"text-stream-description": "Las líneas transmitidas desde el comando se mostrarán como texto en el botón.",
"text-stream-label": "Transmisión",
"text-stream-label": "Salida del comando",
"wheel-description": "Comando a ejecutar cuando se usa la rueda de desplazamiento.<br>Usa $delta para el delta de la rueda de desplazamiento en el comando.",
"wheel-down-description": "Comando a ejecutar cuando la rueda de desplazamiento se desplaza hacia abajo.",
"wheel-down-label": "Comando de rueda hacia abajo",
@@ -154,19 +154,19 @@
"show-scroll-lock-label": "Bloq Despl"
},
"media-mini": {
"compact-mode-description": "Habilitar un diseño que ahorre espacio para el panel del reproductor multimedia.",
"compact-mode-description": "Habilitar un diseño compacto para el panel del reproductor multimedia.",
"compact-mode-label": "Modo compacto",
"hide-mode-description": "Controla el comportamiento del widget cuando no se está reproduciendo ningún medio.",
"max-width-description": "Establece el tamaño horizontal máximo del widget. El widget se reducirá para adaptarse a contenido más corto.",
"no-active-player": "Sin reproductor activo",
"panel-section-description": "Configurar la apariencia y el comportamiento del panel del reproductor multimedia.",
"panel-section-label": "Panel del reproductor multimedia",
"scrolling-mode-description": "Controlar cuándo está habilitado el desplazamiento de texto para títulos de pista largos.",
"scrolling-mode-description": "Controla cuándo está habilitado el desplazamiento de texto para títulos de pista largos.",
"scrolling-mode-label": "Modo de desplazamiento",
"show-album-art-description": "Mostrar la portada del álbum de la pista que se está reproduciendo actualmente.",
"show-album-art-description": "Mostrar la portada del álbum de la pista que se está reproduciendo.",
"show-album-art-label": "Mostrar arte del álbum",
"show-artist-first-description": "Mostrar artista - título en lugar de título - artista.",
"show-artist-first-label": "Mostrar primero al artista",
"show-artist-first-description": "Mostrar 'artista - título' en lugar de 'título - artista'.",
"show-artist-first-label": "Mostrar primero el artista",
"show-progress-ring-description": "Mostrar un indicador de progreso circular que muestre el progreso de la pista.",
"show-progress-ring-label": "Mostrar anillo de progreso",
"show-visualizer-description": "Mostrar un visualizador de audio cuando se reproduce música.",
@@ -179,10 +179,12 @@
"notification-history": {
"hide-widget-when-zero-description": "Ocultar el icono de notificaciones cuando no haya notificaciones.",
"hide-widget-when-zero-label": "Ocultar icono sin notificaciones",
"hide-widget-when-zero-unread-description": "Ocultar el icono de notificaciones cuando no haya notificaciones sin leer.",
"hide-widget-when-zero-unread-label": "Ocultar icono sin notificaciones sin leer",
"hide-widget-when-zero-unread-description": "Ocultar el icono de notificaciones cuando no haya notificaciones pendientes de leer.",
"hide-widget-when-zero-unread-label": "Ocultar icono sin notificaciones pendientes",
"show-unread-badge-description": "Mostrar una insignia con el número de notificaciones no leídas.",
"show-unread-badge-label": "Mostrar insignia de no leídos"
"show-unread-badge-label": "Mostrar insignia de no leídos",
"unread-badge-color-description": "Selecciona el color para la insignia de notificación no leída.",
"unread-badge-color-label": "Color de la insignia de no leídos"
},
"section-editor": {
"placeholder": "Selecciona un widget para añadir...",
@@ -254,9 +256,13 @@
"workspace": {
"character-count-description": "Número de caracteres a mostrar de los nombres de espacios de trabajo (1-10).",
"character-count-label": "Número de caracteres",
"empty-color-description": "Establecer el color de fondo para los Workspaces vacíos.",
"empty-color-label": "Color de espacio de trabajo vacío",
"enable-scrollwheel-description": "Cambia entre espacios de trabajo usando la rueda de desplazamiento del ratón.",
"enable-scrollwheel-label": "Desplázate para cambiar de espacio de trabajo",
"follow-focused-screen-description": "Mostrar espacios de trabajo de la pantalla actualmente enfocada, en lugar de la pantalla donde se encuentra la barra.",
"focused-color-description": "Establece el color de fondo para el Workspace enfocado.",
"focused-color-label": "Color del espacio de trabajo enfocado",
"follow-focused-screen-description": "Mostrar espacios de trabajo de la pantalla enfocada, en lugar de la pantalla donde se encuentra la barra.",
"follow-focused-screen-label": "Seguir pantalla enfocada",
"grouped-border-opacity-description": "Establecer el nivel de opacidad para los bordes del contenedor del espacio de trabajo.",
"grouped-border-opacity-label": "Opacidad del borde",
@@ -264,12 +270,18 @@
"hide-unoccupied-label": "Ocultar desocupados",
"label-mode-description": "Elegir cómo se muestran las etiquetas de los espacios de trabajo.",
"label-mode-label": "Modo de etiqueta",
"occupied-color-description": "Establecer el color de fondo para los Workspaces ocupados.",
"occupied-color-label": "Color del espacio de trabajo ocupado",
"reverse-scrolling-description": "Invertir la dirección del cambio de espacios de trabajo al desplazarse.",
"reverse-scrolling-label": "Desplazamiento inverso",
"show-applications-description": "Mostrar los iconos de las aplicaciones dentro de cada espacio de trabajo.",
"show-applications-label": "Mostrar aplicaciones",
"show-badge-description": "Mostrar la insignia del número de espacio de trabajo en modo agrupado.",
"show-badge-label": "Mostrar insignia del espacio de trabajo",
"show-labels-only-when-occupied-description": "Mostrar solo las etiquetas del espacio de trabajo cuando contengan ventanas.",
"show-labels-only-when-occupied-label": "Mostrar etiquetas solo cuando esté ocupado",
"show-labels-only-when-occupied-label": "Mostrar etiquetas solo cuando haya contenido",
"unfocused-icons-opacity-description": "Establecer el nivel de opacidad para los iconos de aplicaciones no enfocadas.",
"unfocused-icons-opacity-label": "Opacidad de iconos desenfocados"
"unfocused-icons-opacity-label": "Opacidad de iconos de aplicaciones no enfocadas"
}
},
"battery": {
@@ -301,7 +313,7 @@
"pairing-mode": "Asegúrate de que tu dispositivo esté en modo de emparejamiento.",
"scanning": "Buscando dispositivos...",
"signal-text-excellent": "Señal: Excelente",
"signal-text-fair": "Señal: Regular",
"signal-text-fair": "Señal: Razonable",
"signal-text-good": "Señal: Buena",
"signal-text-poor": "Señal: Débil",
"signal-text-unknown": "Señal: Desconocida",
@@ -316,11 +328,11 @@
"panel": {
"buttons-discord": "Únete a nuestro Discord",
"buttons-dismiss": "OK",
"buttons-feedback": "Dar retroalimentación",
"buttons-feedback": "Dejar comentario",
"empty": "Las notas de la versión aún no están disponibles.",
"highlight-title": "Cambios destacados",
"released": "Publicado el {date}",
"subtitle-fresh": "Gracias por instalar Noctalia. Esto es lo que incluye esta compilación.",
"subtitle-fresh": "¡Gracias por instalar Noctalia! Esto es lo que incluye esta compilación.",
"subtitle-updated": "Actualizado desde {previousVersion}",
"title": "Novedades en {version}",
"version": "Versión {version}",
@@ -329,7 +341,7 @@
},
"colors": {
"error": "Error",
"on-surface": "En la superficie",
"on-surface": "Sobre la superficie",
"primary": "Primario",
"secondary": "Secundario",
"tertiary": "Terciario"
@@ -408,7 +420,7 @@
"monitors": "Monitores",
"network": "Red",
"next": "Siguiente",
"night-light": "Luz de noche",
"night-light": "Luz nocturna",
"no": "No",
"no-results": "Sin resultados",
"no-summary": "Sin resumen",
@@ -416,6 +428,7 @@
"not-found": "No encontrado",
"notifications": "Notificaciones",
"official": "Oficial",
"on-surface": "En superficie",
"output": "Salida",
"pair": "Emparejar",
"paired": "Emparejado",
@@ -428,6 +441,7 @@
"polling": "Sondeo",
"position": "Posición",
"previous": "Anterior",
"primary": "Primario",
"random": "Aleatorio",
"reboot": "Reiniciar",
"refresh": "Refrescar",
@@ -440,6 +454,7 @@
"scanning": "Escaneando...",
"screen-corners": "Esquinas de la pantalla",
"search": "Buscar",
"secondary": "Secundario",
"security": "Seguridad",
"select": "Seleccionar",
"shortcuts": "Atajos",
@@ -448,13 +463,14 @@
"sound": "Sonido",
"sources": "Fuentes",
"start": "Comienzo",
"stop": "Pare",
"stop": "Parar",
"suspend": "Suspender",
"templates": "Plantillas",
"tertiary": "Terciario",
"test": "Prueba",
"thresholds": "Umbrales",
"title": "Título",
"toast": "Toast",
"toast": "Emergente",
"trusted": "De confianza",
"uninstall": "Desinstalar",
"unknown": "Desconocido",
@@ -486,16 +502,16 @@
}
},
"display-modes": {
"always-hide": "Siempre esconderse",
"always-show": "Siempre mostrar",
"force-open": "Forzar la apertura",
"on-hover": "Al pasar"
"always-hide": "Ocultar siempre",
"always-show": "Mostrar siempre",
"force-open": "Forzar abierto",
"on-hover": "Al pasar por encima"
},
"hide-modes": {
"hidden": "Ocultar cuando esté vacío",
"idle": "Ocultar cuando esté inactivo",
"transparent": "Transparente cuando esté vacío",
"visible": "Siempre visible"
"visible": "Mostrar siempre"
},
"launcher": {
"categories": {
@@ -525,10 +541,10 @@
"providers": {
"applications": "Aplicaciones",
"calculator": "Calculadora",
"calculator-deprecated": "El comando >calc está obsoleto y será eliminado pronto. Por favor, escribe expresiones matemáticas directamente en la búsqueda en su lugar.",
"calculator-deprecated": "El comando >calc está obsoleto y será eliminado pronto. Por favor, en su lugar escribe expresiones matemáticas directamente en la búsqueda.",
"calculator-description": "Calculadora — evalúa expresiones matemáticas",
"calculator-enter-expression": "Ingresa una expresión matemática",
"calculator-press-enter-to-copy": "Presione Enter para copiar el resultado",
"calculator-enter-expression": "Introduce una expresión matemática",
"calculator-press-enter-to-copy": "Pulsa Enter para copiar el resultado",
"clipboard": "Historial del portapapeles",
"clipboard-clear-description": "Limpiar todo el historial del portapapeles",
"clipboard-clear-description-full": "Eliminar todos los elementos del historial del portapapeles",
@@ -542,12 +558,12 @@
"emoji": "Selector de emojis",
"emoji-loading": "Cargando emojis...",
"emoji-loading-description": "Por favor espera",
"emoji-no-recent": "Aún no hay emojis recientes",
"emoji-no-recent": "No hay emojis recientes",
"emoji-search-description": "Buscar y copiar emojis"
}
},
"lock-screen": {
"authenticating": "Autenticando...",
"authenticating": "Autentificando...",
"password": "Ingresa tu contraseña...",
"restart": "Reiniciar",
"shut-down": "Apagar"
@@ -579,7 +595,10 @@
"density-compact": "Compacta",
"density-default": "Predeterminada",
"density-mini": "Mini",
"density-spacious": "Espacioso/a"
"density-spacious": "Espaciosa",
"type-floating": "Flotante",
"type-framed": "Con marco",
"type-simple": "Simple"
},
"control-center": {
"quick-settings-style-classic": "Clásico",
@@ -589,7 +608,7 @@
"scrolling-modes": {
"always": "Desplazar siempre",
"hover": "Desplazar al pasar",
"never": "Nunca desplazar"
"never": "No desplazar nunca"
},
"session-menu-grid-layout": {
"grid": "Cuadrícula",
@@ -656,9 +675,9 @@
},
"audio": {
"devices-desc": "Configura los dispositivos de entrada y salida de audio disponibles.",
"devices-input-device-description": "Selecciona el dispositivo de entrada de audio deseado.",
"devices-input-device-description": "Selecciona el dispositivo preferido de entrada de audio.",
"devices-input-device-label": "Dispositivo de entrada",
"devices-output-device-description": "Selecciona el dispositivo de salida de audio deseado.",
"devices-output-device-description": "Selecciona el dispositivo preferido de salida de audio.",
"devices-output-device-label": "Dispositivo de salida",
"devices-title": "Dispositivos de audio",
"external-mixer-description": "Ingrese el comando o la ruta de la aplicación para ejecutar al activar la función de mezclador de audio externo.",
@@ -670,7 +689,7 @@
"media-excluded-player-placeholder": "escribe una subcadena y presiona +",
"media-frame-rate-description": "Tasas más altas son más fluidas pero usan más recursos.",
"media-frame-rate-label": "Tasa de fotogramas",
"media-primary-player-description": "Ingresa una palabra clave para identificar tu reproductor principal.",
"media-primary-player-description": "Introduce una palabra clave para identificar tu reproductor principal.",
"media-primary-player-label": "Reproductor principal",
"media-primary-player-placeholder": "ej. spotify, vlc, mpv",
"media-scrolling-speed-description": "Tiempo en segundos para que el título se desplace del inicio al final.",
@@ -690,10 +709,10 @@
"volumes-mute-output-description": "Silencia la salida de audio principal del sistema.",
"volumes-mute-output-label": "Silenciar salida de audio",
"volumes-output-volume-description": "Nivel de volumen de todo el sistema.",
"volumes-step-size-description": "Ajusta el tamaño del paso para los cambios de volumen (rueda de desplazamiento, atajos de teclado).",
"volumes-step-size-description": "Ajusta el tamaño del paso para los cambios de volumen (rueda del ratón, atajos de teclado).",
"volumes-step-size-label": "Tamaño del paso de volumen",
"volumes-volume-feedback-description": "Reproducir un sonido de retroalimentación al ajustar el volumen",
"volumes-volume-feedback-label": "Reproducir sonido de retroalimentación de volumen",
"volumes-volume-feedback-description": "Reproducir un sonido cuando se ajusta el volumen",
"volumes-volume-feedback-label": "Reproducir sonido de cambio de volumen",
"volumes-volume-overdrive-description": "Permite subir el volumen por encima del 100 %. Es posible que no sea compatible con todo el hardware.",
"volumes-volume-overdrive-label": "Permitir sobreamplificación de volumen"
},
@@ -707,6 +726,10 @@
"appearance-desc": "Personaliza la apariencia y posición de la barra.",
"appearance-floating-description": "Muestra la barra como una 'píldora' flotante.",
"appearance-floating-label": "Barra flotante",
"appearance-frame-radius": "Radio interno",
"appearance-frame-settings-description": "Ajustar el grosor del marco y el radio de la esquina interior",
"appearance-frame-settings-label": "Ajustes del marco",
"appearance-frame-thickness": "Grosor",
"appearance-hide-on-overview-description": "Ocultar la barra y cerrar los paneles cuando la vista general del compositor esté activa.",
"appearance-hide-on-overview-label": "Ocultar barra en la vista general",
"appearance-margins-description": "Ajusta los márgenes alrededor de la barra flotante.",
@@ -721,6 +744,8 @@
"appearance-show-capsule-label": "Mostrar cápsula",
"appearance-show-outline-description": "Muestra un borde visible alrededor de cada widget.",
"appearance-show-outline-label": "Mostrar contornos de widgets",
"appearance-type-description": "Elige el estilo de la barra: Simple, Flotante o Con marco",
"appearance-type-label": "Tipo de barra",
"appearance-use-separate-opacity-description": "Permitir usar un valor de opacidad separado para el fondo de la barra.",
"appearance-use-separate-opacity-label": "Usar opacidad de barra separada",
"monitor-configure-widgets": "Configurar widgets",
@@ -786,7 +811,7 @@
"templates-filter-label": "Filtrar por categoría",
"templates-misc-description": "Crea tus propias plantillas.",
"templates-misc-label": "Avanzado",
"templates-misc-user-templates-description": "Solo habilita si sabes lo que estás haciendo — consulta nuestra documentación en línea.",
"templates-misc-user-templates-description": "Habilita sólamente si sabes lo que estás haciendo — consulta nuestra documentación en línea.",
"templates-misc-user-templates-label": "Habilitar plantillas de usuario",
"templates-none-detected": "Ninguno detectado",
"templates-write-path": "Escribe: {filepath}",
@@ -1027,6 +1052,10 @@
"settings-clip-wrap-text-label": "Ajustar texto del portapapeles",
"settings-clipboard-history-description": "Accede a los elementos copiados anteriormente desde el lanzador.",
"settings-clipboard-history-label": "Activar historial del portapapeles",
"settings-clipboard-watch-image-description": "Cadena de comando completa pasada a wl-paste para cambios de imagen. (requiere reinicio)",
"settings-clipboard-watch-image-label": "Comando de Vigilancia de Imágenes",
"settings-clipboard-watch-text-description": "Cadena de comando completa pasada a wl-paste para cambios de texto. (requiere reinicio)",
"settings-clipboard-watch-text-label": "Comando de vigilancia de texto",
"settings-custom-launch-prefix-description": "Prefijar comandos con un lanzador personalizado (ej. 'runapp' para integración con systemd).",
"settings-custom-launch-prefix-enabled-description": "Usar un prefijo personalizado para lanzar aplicaciones en lugar del método predeterminado.",
"settings-custom-launch-prefix-enabled-label": "Habilitar prefijo de lanzamiento personalizado",
@@ -1090,6 +1119,10 @@
"weather-show-in-calendar-label": "Mostrar el clima en el calendario"
},
"lock-screen": {
"allow-password-with-fprintd-description": "Cuando fprintd (autenticación por huella dactilar) está activo, esta opción te permite iniciar sesión con tu contraseña en lugar de una huella dactilar.",
"allow-password-with-fprintd-label": "Permitir inicio de sesión con contraseña usando fprintd",
"auto-start-auth-description": "p. ej., inicia automáticamente la autenticación de huellas dactilares sin requerir una pulsación de tecla o un clic de botón.",
"auto-start-auth-label": "Autentificación de inicio automática",
"compact-lockscreen-description": "Mostrar solo el campo de inicio de sesión y los controles del sistema, ocultando los widgets del clima y multimedia.",
"compact-lockscreen-label": "Pantalla de bloqueo compacta",
"lock-on-suspend-description": "Bloquear la pantalla automáticamente al suspender el sistema.",
+35 -2
View File
@@ -182,7 +182,9 @@
"hide-widget-when-zero-unread-description": "Masquer licône de notification lorsquil ny a aucune notification non lue.",
"hide-widget-when-zero-unread-label": "Masquer licône sans notifications non lues",
"show-unread-badge-description": "Afficher un badge montrant le nombre de notifications non lues.",
"show-unread-badge-label": "Afficher le badge non lu"
"show-unread-badge-label": "Afficher le badge non lu",
"unread-badge-color-description": "Sélectionnez la couleur pour le badge de notification non lue.",
"unread-badge-color-label": "Couleur du badge non lu"
},
"section-editor": {
"placeholder": "Sélectionnez un widget à ajouter...",
@@ -254,8 +256,12 @@
"workspace": {
"character-count-description": "Nombre de caractères à afficher des noms d'espaces de travail (1-10).",
"character-count-label": "Nombre de caractères",
"empty-color-description": "Définir la couleur d'arrière-plan pour les Workspaces vides.",
"empty-color-label": "Couleur d'espace de travail vide",
"enable-scrollwheel-description": "Basculer entre les espaces de travail avec la molette de la souris.",
"enable-scrollwheel-label": "Faites défiler pour changer d'espace de travail",
"focused-color-description": "Définir la couleur d'arrière-plan pour l'espace de travail sélectionné.",
"focused-color-label": "Couleur de l'espace de travail sélectionné",
"follow-focused-screen-description": "Afficher les espaces de travail de l'écran actuellement ciblé, plutôt que de l'écran où se trouve la barre.",
"follow-focused-screen-label": "Suivre l'écran ciblé",
"grouped-border-opacity-description": "Définir le niveau d'opacité des bordures des conteneurs d'espace de travail.",
@@ -264,8 +270,14 @@
"hide-unoccupied-label": "Masquer les inoccupés",
"label-mode-description": "Choisir comment les étiquettes d'espace de travail sont affichées.",
"label-mode-label": "Mode d'étiquette",
"occupied-color-description": "Définir la couleur d'arrière-plan pour les Workspaces occupés.",
"occupied-color-label": "Couleur de l'espace de travail occupé",
"reverse-scrolling-description": "Inverser la direction du changement d'espace de travail lors du défilement.",
"reverse-scrolling-label": "Défilement inversé",
"show-applications-description": "Afficher les icônes des applications dans chaque espace de travail.",
"show-applications-label": "Afficher les applications",
"show-badge-description": "Afficher le badge du numéro d'espace de travail en mode groupé.",
"show-badge-label": "Afficher le badge de l'espace de travail",
"show-labels-only-when-occupied-description": "Afficher uniquement les étiquettes d'espace de travail lorsqu'elles contiennent des fenêtres.",
"show-labels-only-when-occupied-label": "Afficher les étiquettes uniquement lorsque le champ est occupé",
"unfocused-icons-opacity-description": "Définir le niveau d'opacité des icônes d'applications non focalisées.",
@@ -416,6 +428,7 @@
"not-found": "Non trouvé",
"notifications": "Notifications",
"official": "Officiel",
"on-surface": "Sur la surface",
"output": "Sortie",
"pair": "Appairer",
"paired": "Apparié",
@@ -428,6 +441,7 @@
"polling": "Interrogation",
"position": "Position",
"previous": "Précédent",
"primary": "Primaire",
"random": "Aléatoire",
"reboot": "Redémarrer",
"refresh": "Actualiser",
@@ -440,6 +454,7 @@
"scanning": "Analyse en cours...",
"screen-corners": "Coins d'écran",
"search": "Rechercher",
"secondary": "Secondaire",
"security": "Sécurité",
"select": "Sélectionner",
"shortcuts": "Raccourcis",
@@ -451,6 +466,7 @@
"stop": "Arrêtez",
"suspend": "Mettre en veille",
"templates": "Modèles",
"tertiary": "Tertiaire",
"test": "Test",
"thresholds": "Seuils",
"title": "Titre",
@@ -579,7 +595,10 @@
"density-compact": "Compact",
"density-default": "Défaut",
"density-mini": "Mini",
"density-spacious": "Spacieux"
"density-spacious": "Spacieux",
"type-floating": "Flottante",
"type-framed": "Encadrée",
"type-simple": "Simple"
},
"control-center": {
"quick-settings-style-classic": "Classique",
@@ -707,6 +726,10 @@
"appearance-desc": "Personnalisez l'apparence et la position de la barre.",
"appearance-floating-description": "Afficher la barre sous forme de 'pilule' flottante.",
"appearance-floating-label": "Barre flottante",
"appearance-frame-radius": "Rayon interne",
"appearance-frame-settings-description": "Ajuster l'épaisseur du cadre et le rayon interne",
"appearance-frame-settings-label": "Paramètres du cadre",
"appearance-frame-thickness": "Épaisseur",
"appearance-hide-on-overview-description": "Masquer la barre et fermer les panneaux lorsque l'aperçu du compositeur est actif.",
"appearance-hide-on-overview-label": "Masquer la barre dans l'aperçu",
"appearance-margins-description": "Ajustez les marges autour de la barre flottante.",
@@ -721,6 +744,8 @@
"appearance-show-capsule-label": "Afficher la capsule",
"appearance-show-outline-description": "Affiche une bordure visible autour de chaque widget.",
"appearance-show-outline-label": "Afficher les contours des widgets",
"appearance-type-description": "Choisissez le style de la barre : Simple, Flottante ou Encadrée",
"appearance-type-label": "Type de barre",
"appearance-use-separate-opacity-description": "Activer l'utilisation d'une valeur d'opacité distincte pour l'arrière-plan de la barre.",
"appearance-use-separate-opacity-label": "Utiliser l'opacité des barres séparément",
"monitor-configure-widgets": "Configurer les widgets",
@@ -1027,6 +1052,10 @@
"settings-clip-wrap-text-label": "Envelopper le texte du presse-papiers",
"settings-clipboard-history-description": "Accédez aux éléments précédemment copiés depuis le lanceur.",
"settings-clipboard-history-label": "Activer l'historique du presse-papiers",
"settings-clipboard-watch-image-description": "Chaîne de commande complète passée à wl-paste pour les changements d'image. (redémarrage requis)",
"settings-clipboard-watch-image-label": "Commande de Surveillance d'Images",
"settings-clipboard-watch-text-description": "Chaîne de commande complète passée à wl-paste pour les changements de texte. (nécessite un redémarrage)",
"settings-clipboard-watch-text-label": "Commande de surveillance de texte",
"settings-custom-launch-prefix-description": "Préfixer les commandes avec un lanceur personnalisé (ex. 'runapp' pour l'intégration systemd).",
"settings-custom-launch-prefix-enabled-description": "Utiliser un préfixe personnalisé pour lancer les applications au lieu de la méthode par défaut.",
"settings-custom-launch-prefix-enabled-label": "Activer le préfixe de lancement personnalisé",
@@ -1090,6 +1119,10 @@
"weather-show-in-calendar-label": "Afficher la météo dans le calendrier"
},
"lock-screen": {
"allow-password-with-fprintd-description": "Lorsque fprintd (authentification par empreinte digitale) est actif, cette option vous permet de vous connecter avec votre mot de passe au lieu d'une empreinte digitale.",
"allow-password-with-fprintd-label": "Autoriser la connexion par mot de passe avec fprintd",
"auto-start-auth-description": "par exemple, démarre automatiquement l'authentification par empreinte digitale sans nécessiter de pression sur une touche ou un clic de bouton.",
"auto-start-auth-label": "Authentification au démarrage automatique",
"compact-lockscreen-description": "Afficher uniquement le champ de saisie de connexion et les commandes système, en masquant les widgets météo et multimédia.",
"compact-lockscreen-label": "Écran de verrouillage compact",
"lock-on-suspend-description": "Verrouiller automatiquement l'écran lors de la mise en veille du système.",
+24
View File
@@ -1,4 +1,28 @@
{
"bar": {
"workspace": {
"empty-color-description": "खाली कार्यस्थानों के लिए पृष्ठभूमि रंग सेट करें।",
"empty-color-label": "खाली कार्यस्थान रंग",
"focused-color-description": "फोकस गरिएको कार्यक्षेत्रको लागि पृष्ठभूमि रङ सेट गर्नुहोस्।",
"focused-color-label": "फोकस किए गए कार्यक्षेत्र का रंग",
"occupied-color-description": "काम में लिए गए कार्यस्थानों के लिए पृष्ठभूमि रंग सेट करें।",
"occupied-color-label": "अधिकृत कार्यक्षेत्र रंग",
"show-badge-description": "समूहीकृत मोड में कार्यस्थान संख्या बैज दिखाएँ।",
"show-badge-label": "कार्यस्थान बैज देखाउनुहोस्"
}
},
"common": {
"on-surface": "सतह पर",
"primary": "प्राइमरी",
"secondary": "माध्यमिक",
"tertiary": "तृतीयक"
},
"panels": {
"lock-screen": {
"auto-start-auth-description": "उदाहरण के लिए, बिना किसी कुंजी प्रेस या बटन क्लिक की आवश्यकता के स्वचालित रूप से फिंगरप्रिंट प्रमाणीकरण शुरू करता है।",
"auto-start-auth-label": "स्वतः प्रमाणन शुरू करें"
}
},
"toast": {
"donation-opened": "दान पृष्ठ आपके ब्राउज़र में खुला"
}
+35 -2
View File
@@ -182,7 +182,9 @@
"hide-widget-when-zero-unread-description": "Az értesítési ikon elrejtése, ha nincs olvasatlan értesítés.",
"hide-widget-when-zero-unread-label": "Ikon elrejtése, amikor nincs olvasatlan értesítés",
"show-unread-badge-description": "Jelenítsen meg egy jelvényt, amely mutatja az olvasatlan értesítések számát.",
"show-unread-badge-label": "Olvasatlan jelvény megjelenítése"
"show-unread-badge-label": "Olvasatlan jelvény megjelenítése",
"unread-badge-color-description": "Válaszd ki az olvasatlan értesítési jelvény színét.",
"unread-badge-color-label": "Olvasatlan jelvény színe"
},
"section-editor": {
"placeholder": "Válasszon hozzáadandó widgetet...",
@@ -254,8 +256,12 @@
"workspace": {
"character-count-description": "A munkaterületnevekből megjelenítendő karakterek száma (1-10).",
"character-count-label": "Karakterek száma",
"empty-color-description": "Üres Workspaces háttérszínének beállítása.",
"empty-color-label": "Üres munkafelület színe",
"enable-scrollwheel-description": "Váltás a munkaterületek között az egérgörgővel.",
"enable-scrollwheel-label": "Görgetés a munkaterületek váltásához",
"focused-color-description": "Állítsd be a fókuszált Workspace háttérszínét.",
"focused-color-label": "Fókuszált munkaterület színe",
"follow-focused-screen-description": "Munkaterületek megjelenítése az aktuálisan fókuszált képernyőről, nem arról a képernyőről, ahol a sáv található.",
"follow-focused-screen-label": "Fókuszált képernyő követése",
"grouped-border-opacity-description": "A munkaterület-konténerek szegélyeinek átlátszósági szintjének beállítása.",
@@ -264,8 +270,14 @@
"hide-unoccupied-label": "Foglalatlan elrejtése",
"label-mode-description": "Válassza ki, hogyan jelenjenek meg a munkaterület címkéi.",
"label-mode-label": "Címke mód",
"occupied-color-description": "A foglalt Workspaces háttérszínének beállítása.",
"occupied-color-label": "Foglalt munkaterület színe",
"reverse-scrolling-description": "Görgetéskor fordított irányba váltson a munkaterületek között.",
"reverse-scrolling-label": "Görgetés megfordítása",
"show-applications-description": "Alkalmazásikonok megjelenítése minden munkaterületen belül.",
"show-applications-label": "Alkalmazások megjelenítése",
"show-badge-description": "Munkaterület számának jelzése csoportosított módban.",
"show-badge-label": "Munkafelület jelvény megjelenítése",
"show-labels-only-when-occupied-description": "Csak akkor jelenítse meg a munkaterület címkéket, ha azok tartalmaznak ablakokat.",
"show-labels-only-when-occupied-label": "Címkék megjelenítése csak foglalt állapotban",
"unfocused-icons-opacity-description": "Állítsa be a nem fókuszált alkalmazásikonok átlátszóságának szintjét.",
@@ -416,6 +428,7 @@
"not-found": "Nem található",
"notifications": "Értesítések",
"official": "Hivatalos",
"on-surface": "Felületen",
"output": "Kimenet",
"pair": "Párosítás",
"paired": "Párosítva",
@@ -428,6 +441,7 @@
"polling": "Lekérdezés",
"position": "Pozíció",
"previous": "Előző",
"primary": "Elsődleges",
"random": "Véletlen",
"reboot": "Újraindítás",
"refresh": "Frissítés",
@@ -440,6 +454,7 @@
"scanning": "Szkennelés...",
"screen-corners": "Képernyő sarkok",
"search": "Keresés",
"secondary": "Másodlagos",
"security": "Biztonság",
"select": "Kiválasztás",
"shortcuts": "Gyorsbillentyűk",
@@ -451,6 +466,7 @@
"stop": "Állj",
"suspend": "Felfüggesztés",
"templates": "Sablonok",
"tertiary": "Harmadlagos",
"test": "Teszt",
"thresholds": "Küszöbértékek",
"title": "Cím",
@@ -579,7 +595,10 @@
"density-compact": "Kompakt",
"density-default": "Alapértelmezett",
"density-mini": "Mini",
"density-spacious": "Tágas"
"density-spacious": "Tágas",
"type-floating": "Lebegő",
"type-framed": "Keretes",
"type-simple": "Egyszerű"
},
"control-center": {
"quick-settings-style-classic": "Klasszikus",
@@ -707,6 +726,10 @@
"appearance-desc": "A sáv megjelenésének és pozíciójának testreszabása.",
"appearance-floating-description": "Megjeleníti a sávot lebegő „pirulaként”.",
"appearance-floating-label": "Lebegő sáv",
"appearance-frame-radius": "Belső sugár",
"appearance-frame-settings-description": "Állítsa be a keret vastagságát és a belső sarok sugarát",
"appearance-frame-settings-label": "Keret beállításai",
"appearance-frame-thickness": "Vastagság",
"appearance-hide-on-overview-description": "A sáv elrejtése és a panelek bezárása, amikor a kompozitor áttekintése aktív.",
"appearance-hide-on-overview-label": "Sáv elrejtése az áttekintésben",
"appearance-margins-description": "Beállítja a lebegő sáv körüli margókat.",
@@ -721,6 +744,8 @@
"appearance-show-capsule-label": "Kapszula megjelenítése",
"appearance-show-outline-description": "Látható szegély megjelenítése minden widget körül.",
"appearance-show-outline-label": "Widget körvonalak megjelenítése",
"appearance-type-description": "Válassza ki a sáv stílusát: Egyszerű, Lebegő vagy Keretes",
"appearance-type-label": "Sáv típusa",
"appearance-use-separate-opacity-description": "Lehetővé teszi egy külön átlátszatlansági érték használatát a sáv hátteréhez.",
"appearance-use-separate-opacity-label": "Külön oszlop átlátszóság használata",
"monitor-configure-widgets": "Widgetek konfigurálása",
@@ -1027,6 +1052,10 @@
"settings-clip-wrap-text-label": "Clipboard szöveg becsomagolása",
"settings-clipboard-history-description": "Hozzáférés a korábban másolt elemekhez az indítóból.",
"settings-clipboard-history-label": "Vágólapelőzmények engedélyezése",
"settings-clipboard-watch-image-description": "Teljes parancssor, ami a wl-paste-nek kerül átadásra a képváltozásokhoz. (újraindítás szükséges)",
"settings-clipboard-watch-image-label": "Képfigyelő Parancs",
"settings-clipboard-watch-text-description": "A wl-paste-nek átadott teljes parancssor a szövegváltozásokhoz. (újraindítást igényel)",
"settings-clipboard-watch-text-label": "Szövegfigyelő parancs",
"settings-custom-launch-prefix-description": "Előtagolja a parancsokat egyéni indítóval (pl. 'runapp' a systemd integrációhoz).",
"settings-custom-launch-prefix-enabled-description": "Használjon egyéni előtagot az alkalmazások indításához az alapértelmezett módszer helyett.",
"settings-custom-launch-prefix-enabled-label": "Egyéni indítási előtag engedélyezése",
@@ -1090,6 +1119,10 @@
"weather-show-in-calendar-label": "Időjárás megjelenítése a naptárban"
},
"lock-screen": {
"allow-password-with-fprintd-description": "Ha az fprintd (ujjlenyomat-hitelesítés) aktív, ez az opció lehetővé teszi, hogy továbbra is jelszóval jelentkezzen be ujjlenyomat helyett.",
"allow-password-with-fprintd-label": "Jelszavas bejelentkezés engedélyezése fprintd-vel",
"auto-start-auth-description": "pl. automatikusan elindítja az ujjlenyomat-hitelesítést anélkül, hogy billentyűt kellene megnyomni vagy gombra kellene kattintani.",
"auto-start-auth-label": "Automatikus hitelesítés indításkor",
"compact-lockscreen-description": "Csak a bejelentkezési beviteli mező és a rendszervezérlők megjelenítése, elrejtve az időjárás és média widgeteket.",
"compact-lockscreen-label": "Kompakt zárolási képernyő",
"lock-on-suspend-description": "A képernyő automatikus zárolása a rendszer felfüggesztésekor.",
+35 -2
View File
@@ -182,7 +182,9 @@
"hide-widget-when-zero-unread-description": "未読の通知がない場合に通知アイコンを非表示にします。",
"hide-widget-when-zero-unread-label": "未読なしでアイコンを非表示",
"show-unread-badge-description": "未読通知の数を表示するバッジを表示します。",
"show-unread-badge-label": "未読バッジを表示"
"show-unread-badge-label": "未読バッジを表示",
"unread-badge-color-description": "未読の通知バッジの色を選択してください。",
"unread-badge-color-label": "未読バッジの色"
},
"section-editor": {
"placeholder": "追加するウィジェットを選択...",
@@ -254,8 +256,12 @@
"workspace": {
"character-count-description": "ワークスペース名から表示する文字数 (1-10)。",
"character-count-label": "文字数",
"empty-color-description": "空のWorkspaceの背景色を設定します。",
"empty-color-label": "空のワークスペースの色",
"enable-scrollwheel-description": "マウスホイールを使用してワークスペースを切り替えます。",
"enable-scrollwheel-label": "スクロールでワークスペースを切り替え",
"focused-color-description": "フォーカスされたWorkspaceの背景色を設定します。",
"focused-color-label": "フォーカスされたワークスペースの色",
"follow-focused-screen-description": "バーが配置されている画面ではなく、現在フォーカスされている画面のワークスペースを表示します。",
"follow-focused-screen-label": "フォーカス中の画面に追従",
"grouped-border-opacity-description": "ワークスペースコンテナのボーダーの不透明度を設定します。",
@@ -264,8 +270,14 @@
"hide-unoccupied-label": "空のワークスペースを隠す",
"label-mode-description": "ワークスペースラベルの表示方法を選択します。",
"label-mode-label": "ラベルモード",
"occupied-color-description": "使用中のWorkspaceの背景色を設定します。",
"occupied-color-label": "使用中のワークスペースの色",
"reverse-scrolling-description": "スクロール時のワークスペース切り替え方向を反転する。",
"reverse-scrolling-label": "スクロール方向を反転",
"show-applications-description": "各ワークスペース内にアプリアイコンを表示します。",
"show-applications-label": "アプリアイコンを表示",
"show-badge-description": "グループ化モードでワークスペース番号バッジを表示します。",
"show-badge-label": "ワークスペースバッジを表示",
"show-labels-only-when-occupied-description": "ウィンドウが開いているワークスペースにのみラベルを表示します。",
"show-labels-only-when-occupied-label": "ウィンドウがある時のみラベルを表示",
"unfocused-icons-opacity-description": "フォーカスされていないアプリのアイコンの不透明度を設定します。",
@@ -416,6 +428,7 @@
"not-found": "見つかりませんでした",
"notifications": "通知",
"official": "公式",
"on-surface": "表面上",
"output": "出力",
"pair": "ペアリング",
"paired": "ペアリング済み",
@@ -428,6 +441,7 @@
"polling": "ポーリング",
"position": "位置",
"previous": "前へ",
"primary": "プライマリ",
"random": "ランダム",
"reboot": "再起動",
"refresh": "更新",
@@ -440,6 +454,7 @@
"scanning": "スキャン中...",
"screen-corners": "画面の隅",
"search": "検索",
"secondary": "セカンダリー",
"security": "セキュリティ",
"select": "選択",
"shortcuts": "ショートカット",
@@ -451,6 +466,7 @@
"stop": "停止",
"suspend": "サスペンド",
"templates": "テンプレート",
"tertiary": "第三",
"test": "テスト",
"thresholds": "しきい値",
"title": "タイトル",
@@ -579,7 +595,10 @@
"density-compact": "コンパクト",
"density-default": "標準",
"density-mini": "ミニ",
"density-spacious": "広々とした"
"density-spacious": "広々とした",
"type-floating": "フローティング",
"type-framed": "フレーム",
"type-simple": "シンプル"
},
"control-center": {
"quick-settings-style-classic": "クラシック",
@@ -707,6 +726,10 @@
"appearance-desc": "バーの外観や位置をカスタマイズします。",
"appearance-floating-description": "バーを浮かせて、カプセル型で表示します。",
"appearance-floating-label": "フローティングバー",
"appearance-frame-radius": "内側の半径",
"appearance-frame-settings-description": "フレームの太さと内側の半径を調整します",
"appearance-frame-settings-label": "フレーム設定",
"appearance-frame-thickness": "太さ",
"appearance-hide-on-overview-description": "コンポジタの概要がアクティブなときは、バーを非表示にしてパネルを閉じます。",
"appearance-hide-on-overview-label": "概要のバーを非表示にする",
"appearance-margins-description": "フローティングバーの周囲の余白を調整します。",
@@ -721,6 +744,8 @@
"appearance-show-capsule-label": "カプセルを表示",
"appearance-show-outline-description": "すべてのウィジェットの周囲に枠線を表示します。",
"appearance-show-outline-label": "ウィジェットの枠線を表示",
"appearance-type-description": "バーのスタイルを選択:シンプル、フローティング、またはフレーム",
"appearance-type-label": "バーの種類",
"appearance-use-separate-opacity-description": "バーの背景に別の不透明度の値を使用できるようにする。",
"appearance-use-separate-opacity-label": "バーの不透明度を個別に設定",
"monitor-configure-widgets": "ウィジェットを設定",
@@ -1027,6 +1052,10 @@
"settings-clip-wrap-text-label": "クリップボードのテキストを折り返す",
"settings-clipboard-history-description": "以前コピーした項目にランチャーからアクセスできるようにします。",
"settings-clipboard-history-label": "クリップボード履歴を有効化",
"settings-clipboard-watch-image-description": "画像変更のためにwl-pasteに渡される完全なコマンド文字列。(再起動が必要)",
"settings-clipboard-watch-image-label": "画像監視コマンド",
"settings-clipboard-watch-text-description": "テキスト変更のために wl-paste に渡される完全なコマンド文字列。(再起動が必要)",
"settings-clipboard-watch-text-label": "テキスト監視コマンド",
"settings-custom-launch-prefix-description": "コマンドの先頭にカスタムランチャーを付与します(例: systemd 連携用の 'runapp')。",
"settings-custom-launch-prefix-enabled-description": "デフォルトの方法の代わりに、カスタムプレフィックスを使用してアプリを起動します。",
"settings-custom-launch-prefix-enabled-label": "カスタム起動プレフィックスを有効化",
@@ -1090,6 +1119,10 @@
"weather-show-in-calendar-label": "カレンダー"
},
"lock-screen": {
"allow-password-with-fprintd-description": "fprintd(指紋認証)が有効な場合、このオプションを使用すると、指紋の代わりにパスワードでログインできます。",
"allow-password-with-fprintd-label": "fprintd でパスワードログインを許可する",
"auto-start-auth-description": "たとえば、キーを押したりボタンをクリックしたりしなくても、指紋認証が自動的に開始されます。",
"auto-start-auth-label": "自動認証を開始",
"compact-lockscreen-description": "天気やメディアウィジェットを隠し、ログイン入力とシステムコントロールのみを表示します。",
"compact-lockscreen-label": "コンパクトなロック画面",
"lock-on-suspend-description": "システムのサスペンド時に、自動的に画面をロックします。",
File diff suppressed because it is too large Load Diff
+35 -2
View File
@@ -182,7 +182,9 @@
"hide-widget-when-zero-unread-description": "Dema ku ti agahdarîya nexwendî tune be, îkona agahdarîyê veşêre.",
"hide-widget-when-zero-unread-label": "Îkonê bê nexwendî veşêre",
"show-unread-badge-description": "Nîşaneyekê nîşan bide ku hejmara agahiyên nehatine xwendin nîşan dide.",
"show-unread-badge-label": "Nîşana ne xwendî nîşan bide"
"show-unread-badge-label": "Nîşana ne xwendî nîşan bide",
"unread-badge-color-description": "Rengê nîşana agahdariya nexwendî hilbijêre.",
"unread-badge-color-label": "Rengê nîşana nexwendî"
},
"section-editor": {
"placeholder": "Widgetekek hilbijêre ku lê zêde bike...",
@@ -254,8 +256,12 @@
"workspace": {
"character-count-description": "Hejmara tîpên ku ji navên qada xebatê bên nîşandan (1-10).",
"character-count-label": "Hejmara tîpan",
"empty-color-description": "Rengê paşxaneya ji bo Workspaceyên val destnîşan bike.",
"empty-color-label": "Rengê cîhê kar ê vala",
"enable-scrollwheel-description": "Bi tekerê mişkê di navbera cîhên xebatê de biguhere.",
"enable-scrollwheel-label": "Bişkoka gerokê bizivirîne da ku cîhên kar biguherî",
"focused-color-description": "Rengê paşperdeyê ji bo cîhê karê ku hatiye fokus kirin destnîşan bike.",
"focused-color-label": "Rengê cîhê kar ê fokusdayî",
"follow-focused-screen-description": "Dîmên xebatê ji dîmendera niha ya ku balê dikişîne, ne ji dîmendera ku bar lê ye, nîşan bide.",
"follow-focused-screen-label": "Şopandina ekrana fokusê",
"grouped-border-opacity-description": "Ayarê asta zelalbûna sînorên konteynerê cîhê kar.",
@@ -264,8 +270,14 @@
"hide-unoccupied-label": "Veşartî ne dagirkirî",
"label-mode-description": "Çawa nîşaneyên cîhê kar têne nîşandan hilbijêre.",
"label-mode-label": "Moda labelê",
"occupied-color-description": "Rengê paşxaneyê ji bo Workspaceên dagirkirî destnîşan bike.",
"occupied-color-label": "Rengê qada xebatê ya dagirkirî",
"reverse-scrolling-description": "Dema ku hûn digerînin, rêgeha guhertina cîhê kar berevajî bike.",
"reverse-scrolling-label": "گەڕاندنەوەی گەڕان",
"show-applications-description": "Nîşaneyên sepanan di hundirê her cîhê xebatê de nîşan bide.",
"show-applications-label": "Nîşan sepanan bide",
"show-badge-description": "Di moda komkirî de nîşana jimareya cîhê kar nîşan bide.",
"show-badge-label": "Nîşana qada xebatê nîşan bide",
"show-labels-only-when-occupied-description": "Tenê etîketên cîhê xebatê nîşan bide dema ku ew pencereyan dihewînin.",
"show-labels-only-when-occupied-label": "Tenê dema dagirkirî etîketan nîşan bide",
"unfocused-icons-opacity-description": "Ayarê asta zelalbûna îkonên sepanên ne di fokusê de bike.",
@@ -416,6 +428,7 @@
"not-found": "Nehat dîtin",
"notifications": "Agahdarî",
"official": "Fermî",
"on-surface": "Li ser rûyê erdê",
"output": "Derket",
"pair": "Hevpar",
"paired": "Hevpar bûyî",
@@ -428,6 +441,7 @@
"polling": "Lêpirsîn",
"position": "Helwest",
"previous": "Berê",
"primary": "Serekî",
"random": "Bêserûber",
"reboot": "Restart",
"refresh": "Nûkirin",
@@ -440,6 +454,7 @@
"scanning": "Lêgerîn...",
"screen-corners": "Kişekanên dîmenderê",
"search": "Lêgerîn",
"secondary": "Duyemîn",
"security": "Ewlehî",
"select": "Hilbijêre",
"shortcuts": "Kurterê",
@@ -451,6 +466,7 @@
"stop": "Raweste",
"suspend": "Rawestîne",
"templates": "Şablonan",
"tertiary": "Sêyemîn",
"test": "Test",
"thresholds": "Asteyan",
"title": "Sernav",
@@ -579,7 +595,10 @@
"density-compact": "Kompakt",
"density-default": "Berdest",
"density-mini": "Piçûk",
"density-spacious": "Berfireh"
"density-spacious": "Berfireh",
"type-floating": "Herikbar",
"type-framed": "Çarçovekirî",
"type-simple": "Sade"
},
"control-center": {
"quick-settings-style-classic": "Klasîk",
@@ -707,6 +726,10 @@
"appearance-desc": "Xuyakirina xuyabûn û pozîsyona bar.",
"appearance-floating-description": "Bariş wekî 'heb'eke herikbar nîşan bide.",
"appearance-floating-label": "Barê avjen",
"appearance-frame-radius": "Nîvçapa Hundirîn",
"appearance-frame-settings-description": "Stûrahiya çarçoveyê û nîvçapa goşeyê hundirîn sererast bike",
"appearance-frame-settings-label": "Mîhengên Çarçoveyê",
"appearance-frame-thickness": "Stûrahî",
"appearance-hide-on-overview-description": "Dema ku pêşdîtina berhevker çalak be, bar veşêre û panelan bigire.",
"appearance-hide-on-overview-label": "Li ser dîtinê bar veşêre",
"appearance-margins-description": "Marginên dora bara herikbar eyar bike.",
@@ -721,6 +744,8 @@
"appearance-show-capsule-label": "Pêşkêşkirina kapsulê",
"appearance-show-outline-description": "Dîwarê xuya li dora her widgetê nîşan dide.",
"appearance-show-outline-label": "Nîşandan xêzên widgetê",
"appearance-type-description": "Stîla darikê hilbijêre: Sade, Herikbar an Çarçovekirî",
"appearance-type-label": "Cureyê Darikê",
"appearance-use-separate-opacity-description": "Destûrê bide ku ji bo paşxaneya bar nirxek opakbûna cuda were bikaranîn.",
"appearance-use-separate-opacity-label": "Şefafiya bara cuda bikar bîne",
"monitor-configure-widgets": "Widgetan saz bike",
@@ -1027,6 +1052,10 @@
"settings-clip-wrap-text-label": "Pêça nivîsa clipboardê",
"settings-clipboard-history-description": "Gihîştina tiştên ku berê hatine kopîkirin ji destpêkerê.",
"settings-clipboard-history-label": "Dîroka clipboardê çalak bike",
"settings-clipboard-watch-image-description": "ڕستەی فەرمانی تەواو کە بۆ wl-paste تێدەپەڕێنرێت بۆ گۆڕانکارییەکانی وێنە. (پێویستی بە دووبارە دەستپێکردنەوە هەیە)",
"settings-clipboard-watch-image-label": "فەرمانی چاودێریکردنی وێنە",
"settings-clipboard-watch-text-description": "تەواوی زنجیرەی فەرمان کە بۆ wl-paste تێپەڕێنراوە بۆ گۆڕانکارییەکانی دەق. (پێویستی بە دووبارە دەستپێکردنەوە هەیە)",
"settings-clipboard-watch-text-label": "فەرمانی چاودێری دەق",
"settings-custom-launch-prefix-description": "Fermanên pêşgiran bi destpêkerek xwerû pêşda bikin (mînak, 'runapp' ji bo entegrasyona systemd).",
"settings-custom-launch-prefix-enabled-description": "Bi awayekî xwerû destpêkê ji bo destpêkirina sepanan bikar bîne li şûna rêbaza standard.",
"settings-custom-launch-prefix-enabled-label": "Pêşgira destpêkirinê ya xwerû çalak bike",
@@ -1090,6 +1119,10 @@
"weather-show-in-calendar-label": "Nîşandanîna hewayê di salnameyê de"
},
"lock-screen": {
"allow-password-with-fprintd-description": "Dema ku fprintd (nasnameya tiliyan) çalak e, ev vebijêrk dihêle hûn bi şîfreya xwe têkevin, ne bi tiliya xwe.",
"allow-password-with-fprintd-label": "Rêdan bi têketina şîfreyê bi fprintd",
"auto-start-auth-description": "mînak, otomatîkî dest bi rastkirina tiliyê dike bêyî ku pêdivî bi pêlkeyek an klîkek hebe.",
"auto-start-auth-label": "Destpêkirina otomatîkî ya rastkirinê",
"compact-lockscreen-description": "Tenê inputa têketinê û kontrolên pergalê nîşan bide, widgetên hewayê û medyayê veşêre.",
"compact-lockscreen-label": "Kilîta ekrana berhevkirî",
"lock-on-suspend-description": "Dema ku pergal tê rawestandin, ekranê bixweber kilît bike.",
+35 -2
View File
@@ -182,7 +182,9 @@
"hide-widget-when-zero-unread-description": "Het meldingspictogram verbergen wanneer er geen ongelezen meldingen zijn.",
"hide-widget-when-zero-unread-label": "Pictogram verbergen zonder ongelezen meldingen",
"show-unread-badge-description": "Toon een badge met het aantal ongelezen meldingen.",
"show-unread-badge-label": "Ongelezen-badge tonen"
"show-unread-badge-label": "Ongelezen-badge tonen",
"unread-badge-color-description": "Selecteer de kleur voor de badge van ongelezen meldingen.",
"unread-badge-color-label": "Kleur van de ongelezen badge"
},
"section-editor": {
"placeholder": "Selecteer een widget om toe te voegen...",
@@ -254,8 +256,12 @@
"workspace": {
"character-count-description": "Aantal tekens dat wordt weergegeven van werkruimtenamen (1-10).",
"character-count-label": "Aantal tekens",
"empty-color-description": "Stel de achtergrondkleur in voor lege Workspaces.",
"empty-color-label": "Kleur van lege Workspace",
"enable-scrollwheel-description": "Schakel tussen werkruimtes met het muiswiel.",
"enable-scrollwheel-label": "Scroll om tussen werkruimtes te schakelen",
"focused-color-description": "Stel de achtergrondkleur in voor de gefocuste Workspace.",
"focused-color-label": "Kleur van de gefocuste Workspace",
"follow-focused-screen-description": "Werkruimten weergeven van het momenteel gefocuste scherm, in plaats van het scherm waar de balk zich bevindt.",
"follow-focused-screen-label": "Gefocust scherm volgen",
"grouped-border-opacity-description": "Stel het dekkingsniveau in voor de randen van de werkruimtecontainer.",
@@ -264,8 +270,14 @@
"hide-unoccupied-label": "Ongebruikte verbergen",
"label-mode-description": "Kies hoe labels van werkruimten worden weergegeven.",
"label-mode-label": "Labelmodus",
"occupied-color-description": "Stel de achtergrondkleur in voor bezette Workspaces.",
"occupied-color-label": "Kleur van bezette workspace",
"reverse-scrolling-description": "Keer de richting van het wisselen van werkruimtes om bij het scrollen.",
"reverse-scrolling-label": "Omgekeerd scrollen",
"show-applications-description": "Toon applicatiepictogrammen in elke werkruimte.",
"show-applications-label": "Toon applicaties",
"show-badge-description": "Toon de werkomgevingnummerbadge in gegroepeerde modus.",
"show-badge-label": "Werkruimtebadge weergeven",
"show-labels-only-when-occupied-description": "Toon alleen werkruimtelabels wanneer ze vensters bevatten.",
"show-labels-only-when-occupied-label": "Toon labels alleen wanneer bezet",
"unfocused-icons-opacity-description": "Stel het dekkingsniveau in voor app-pictogrammen die niet in focus zijn.",
@@ -416,6 +428,7 @@
"not-found": "Niet gevonden",
"notifications": "Meldingen",
"official": "Officieel",
"on-surface": "Op oppervlak",
"output": "Uitvoer",
"pair": "Koppelen",
"paired": "Gekoppeld",
@@ -428,6 +441,7 @@
"polling": "Polling",
"position": "Positie",
"previous": "Vorige",
"primary": "Primair",
"random": "Willekeurig",
"reboot": "Herstarten",
"refresh": "Vernieuwen",
@@ -440,6 +454,7 @@
"scanning": "Scannen...",
"screen-corners": "Schermhoeken",
"search": "Zoeken",
"secondary": "Secundair",
"security": "Beveiliging",
"select": "Selecteer",
"shortcuts": "Snelkoppelingen",
@@ -451,6 +466,7 @@
"stop": "Stop",
"suspend": "Onderbreken",
"templates": "Sjablonen",
"tertiary": "Tertiair",
"test": "Test",
"thresholds": "Drempels",
"title": "Titel",
@@ -579,7 +595,10 @@
"density-compact": "Compact",
"density-default": "Standaard",
"density-mini": "Mini",
"density-spacious": "Ruim"
"density-spacious": "Ruim",
"type-floating": "Zwevend",
"type-framed": "Omkaderd",
"type-simple": "Eenvoudig"
},
"control-center": {
"quick-settings-style-classic": "Klassiek",
@@ -707,6 +726,10 @@
"appearance-desc": "Pas de uitstraling en positie van de balk aan.",
"appearance-floating-description": "Toon de balk weer als een zwevende 'pil'.",
"appearance-floating-label": "Zwevende balk",
"appearance-frame-radius": "Binnenradius",
"appearance-frame-settings-description": "Pas de kaderdikte en binnenste hoekradius aan",
"appearance-frame-settings-label": "Kaderinstellingen",
"appearance-frame-thickness": "Dikte",
"appearance-hide-on-overview-description": "Verberg de balk en sluit panelen wanneer het compositoroverzicht actief is.",
"appearance-hide-on-overview-label": "Balk verbergen in overzicht",
"appearance-margins-description": "Pas de marges rond de zwevende balk aan.",
@@ -721,6 +744,8 @@
"appearance-show-capsule-label": "Capsules tonen",
"appearance-show-outline-description": "Toon een zichtbare rand rondom elke widget.",
"appearance-show-outline-label": "Toon widget-omtrekken",
"appearance-type-description": "Kies de stijl van de balk: Eenvoudig, Zwevend of Omkaderd",
"appearance-type-label": "Balktype",
"appearance-use-separate-opacity-description": "Maak het mogelijk om een aparte dekkingswaarde te gebruiken voor de achtergrond van de balk.",
"appearance-use-separate-opacity-label": "Gebruik afzonderlijke balkdekking",
"monitor-configure-widgets": "Widgets configureren",
@@ -1027,6 +1052,10 @@
"settings-clip-wrap-text-label": "Klembordtekst omwikkelen",
"settings-clipboard-history-description": "Toegang tot eerder gekopieerde items vanuit de launcher.",
"settings-clipboard-history-label": "Klembordgeschiedenis inschakelen",
"settings-clipboard-watch-image-description": "Volledige commandoreeks die aan wl-paste wordt doorgegeven voor afbeeldingswijzigingen. (herstart vereist)",
"settings-clipboard-watch-image-label": "Afbeelding Bewakingscommando",
"settings-clipboard-watch-text-description": "Volledige commandoreeks die aan wl-paste wordt doorgegeven voor tekstwijzigingen. (vereist herstart)",
"settings-clipboard-watch-text-label": "Tekst volgcommando",
"settings-custom-launch-prefix-description": "Voorzie commando's van een aangepaste launcher-prefix (bijv. 'runapp' voor systemd-integratie).",
"settings-custom-launch-prefix-enabled-description": "Gebruik een aangepaste prefix om applicaties te starten in plaats van de standaardmethode.",
"settings-custom-launch-prefix-enabled-label": "Aangepaste startprefix inschakelen",
@@ -1090,6 +1119,10 @@
"weather-show-in-calendar-label": "Weer in kalender weergeven"
},
"lock-screen": {
"allow-password-with-fprintd-description": "Wanneer fprintd (vingerafdrukauthenticatie) actief is, kunt u met deze optie nog steeds inloggen met uw wachtwoord in plaats van een vingerafdruk.",
"allow-password-with-fprintd-label": "Wachtwoordlogin toestaan met fprintd",
"auto-start-auth-description": "start bijvoorbeeld automatisch de vingerafdrukauthenticatie zonder dat er een toetsaanslag of klik nodig is.",
"auto-start-auth-label": "Automatische authenticatie bij opstarten",
"compact-lockscreen-description": "Toon alleen de logininvoer en systeemknoppen, verberg weer- en mediawidgets.",
"compact-lockscreen-label": "Compact vergrendelscherm",
"lock-on-suspend-description": "Vergrendel het scherm automatisch wanneer het systeem wordt onderbroken.",
+639 -14
View File
@@ -1,6 +1,6 @@
{
"actions": {
"clear-history": "Fjern historikk",
"clear-history": "Tøm historikk",
"close-app": "Steng {app}",
"connect-vpn": "Kopla til {app}",
"cycle-visualizer": "Skift visualiserar",
@@ -31,10 +31,10 @@
},
"bar": {
"active-window": {
"colorize-icons-description": "Farga ikonet til det aktive vindauga med temafargane.",
"colorize-icons-description": "Fargar ikonet til det aktive vindauga med temafargane.",
"hide-mode-description": "Styrer kva miniprogrammet gjer når det ikkje er eit aktivt vindauga.",
"scrolling-mode-description": "Styr når rullande tekst slår på for lange vindaugenamn.",
"show-app-icon-description": "Vis programikonet attmed vindaugenamnet.",
"scrolling-mode-description": "Styrer når rullande tekst slår på for lange vindaugenamn.",
"show-app-icon-description": "Viser programikonet attmed vindaugenamnet.",
"show-app-icon-label": "Vis programikon"
},
"audio-visualizer": {
@@ -48,34 +48,36 @@
"device-default": "Standard (Skjermeining)",
"device-description": "Vel kva for batterieining å visa.",
"device-label": "Batterieining",
"hide-if-idle-description": "Gøym miniprogrammet når batteriet ikkje ladar opp eller ut.",
"hide-if-idle-description": "Gøymer miniprogrammet når batteriet ikkje ladar opp eller ut.",
"hide-if-idle-label": "Gøym når i dvale",
"hide-if-not-detected-description": "Gøym miniprogrammet når inga batterieining er funnen.",
"hide-if-not-detected-description": "Gøymer miniprogrammet når inga batterieining er funnen.",
"hide-if-not-detected-label": "Gøym når ikkje funnen",
"low-battery-threshold-description": "Vis åtvaring når batterinivået fell under dette prosenttalet.",
"low-battery-threshold-description": "Viser åtvaring når batterinivået fell under dette prosenttalet.",
"low-battery-threshold-label": "Varslingsterskel for lågt batterinivå",
"show-noctalia-performance-description": "Vis ytingsmodusbrytaren for Noctalia i batterifanen.<br>Slår av skugger og animasjonar i Noctalia for å minka ressursbruk.",
"show-noctalia-performance-description": "Viser ytingsmodusbrytaren for Noctalia i batterifanen.<br>Slår av skugger og animasjonar i Noctalia for å minka ressursbruk.",
"show-noctalia-performance-label": "Vis ytingsmodusbrytaren for Noctalia",
"show-power-profile-description": "Vis veljaren for energiprofil i batterifanen.",
"show-power-profile-description": "Viser veljaren for energiprofil i batterifanen.",
"show-power-profile-label": "Vis kontrollane for energiprofil"
},
"clock": {
"clock-display-description": "Tilpass klokkevisninga ved å leggja til merke frå lista. For å bruka 12-timarsformat må du ha med 'AP'-merket.",
"clock-display-description": "Måtar klokkevisninga ved å leggja til merke frå lista. For å bruka 12-timarsformat må du ha med 'AP'-merket.",
"clock-display-label": "Klokkevisar",
"custom-font-description": "Vel eigendefinert skrifttype til klokkevisaren.",
"custom-font-label": "Eigendefinert skrifttype",
"custom-font-placeholder": "Vel eigendefinert skrifttype...",
"custom-font-search-placeholder": "Søk gjennom skrifttypar...",
"horizontal-bar-description": "Tips: Skriv \\n for å byrja ei ny linje",
"horizontal-bar-label": "Vassrett panel",
"preview": "Førevisning",
"tooltip-format-description": "Formater streng til hjelpeteksten som kjem opp når peikaren er over klokka. Haldt dette tomt for å bruka standardteksten.",
"tooltip-format-label": "Hjelpetekstformat",
"use-custom-font-description": "Overstyr standardskrifttypen med eigendefinert skrifttype til klokka.",
"use-custom-font-description": "Overstyrer standardskrifttypen med eigendefinert skrifttype til klokka.",
"use-custom-font-label": "Bruk eigendefinert skrifttype",
"use-monospaced-font-description": "Når dette er på, brukar klokka fastbreiddeskrift.",
"use-monospaced-font-label": "Bruk fastbreiddeskrift",
"use-primary-color-description": "Når dette er på, vert primærfargen brukt for å hevja ut.",
"use-primary-color-label": "Bruk primærfarge",
"vertical-bar-description": "Bruk mellomrom for å skilja kvar del på ei ny linje.",
"vertical-bar-description": "Brukar mellomrom for å skilja kvar del på ei ny linje.",
"vertical-bar-label": "Loddrett panel"
},
"control-center": {
@@ -85,7 +87,7 @@
"enable-colorization-description": "Fargar kontrollsenterikonet med temafargane.",
"icon-description": "Vel ikon frå biblioteket eller eigendefinert fil.",
"select-custom-icon": "Vel eigendefinert ikon",
"use-distro-logo-description": "Bruk kjennemerket til distribusjonen din heller enn eigendefinert ikon.",
"use-distro-logo-description": "Brukar kjennemerket til distribusjonen din heller enn eigendefinert ikon.",
"use-distro-logo-label": "Bruk distrokjennemerke heller enn ikon"
},
"custom-button": {
@@ -111,10 +113,633 @@
"left-click-label": "Venstreklikk",
"left-click-update-text": "Oppdater tekst som viser seg med venstreklikk",
"max-text-length-horizontal-description": "Øvre grense på kor mange teikn som kan visast i vassrett panel (0 for å skyla tekst).",
"max-text-length-horizontal-label": "Øvre grense på tekstlengd (vassrett)"
"max-text-length-horizontal-label": "Øvre grense på tekstlengd (vassrett)",
"max-text-length-vertical-description": "Øvre grense på kor mange teikn som kan visast i loddrett panel (0 for å skyla tekst).",
"max-text-length-vertical-label": "Øvre grense på tekstlengd (loddrett)",
"middle-click-description": "Kommando som køyrer når knappen er midtklikka.",
"middle-click-label": "Midtklikk",
"middle-click-update-text": "Oppdater tekst som viser seg med midtklikk",
"parse-json-description": "Tolkar kommandoutskrift som JSON-objekt for å setja tekst og ikon dynamisk.",
"parse-json-label": "Tolk utskrift som JSON",
"refresh-interval-description": "Tidsrom i millisekund",
"refresh-interval-label": "Tidsrom mellom oppdateringar",
"right-click-description": "Kommando som køyrer når knappen er høgreklikka.",
"right-click-label": "Høgreklikk",
"right-click-update-text": "Oppdater tekst som viser seg med høgreklikk",
"show-icon-description": "Gjer miniprogramsikonet synleg eller ikkje.",
"show-icon-label": "Vis ikon",
"text-stream-description": "Strøymde linjer frå kommandoen viser seg som tekst på knappen.",
"text-stream-label": "Strøym",
"wheel-description": "Kommando som køyrer når ein brukar rullehjulet.<br>Bruk $delta for rullehjulsdeltaen i kommandoen.",
"wheel-down-description": "Kommando som køyrer når ein rullar ned på rullehjulet.",
"wheel-down-label": "Hjul ned-kommando",
"wheel-label": "Rullehjul",
"wheel-mode-separate-description": "Slår på åtskilde kommandoar for når ein rullar opp eller ned.",
"wheel-mode-separate-label": "Skilde hjulekommandoar",
"wheel-up-description": "Kommando som køyrer når ein rullar opp på rullehjulet.",
"wheel-up-label": "Hjul opp-kommando",
"wheel-update-text": "Oppdater vist tekst på rulling"
},
"keyboard-layout": {
"show-icon-description": "Viser ikonet for tastaturoppsett."
},
"lock-keys": {
"hide-when-off-description": "Skyler indikatoren når knappen ikkje er aktiv.",
"hide-when-off-label": "Skyl når avslegen",
"show-caps-lock-description": "Viser status på Caps Lock.",
"show-caps-lock-label": "Caps Lock",
"show-num-lock-description": "Viser status på Num Lock.",
"show-num-lock-label": "Num Lock",
"show-scroll-lock-description": "Viser status på Scroll Lock.",
"show-scroll-lock-label": "Scroll Lock"
},
"media-mini": {
"compact-mode-description": "Slår på eit oppsett som sparar rom i mediaavspelarpanelet.",
"compact-mode-label": "Tettmodus",
"hide-mode-description": "Styrer kva miniprogrammet gjer når ingenting spelar av.",
"max-width-description": "Set ei øvre grense for kor breid miniprogrammet kan vera. Miniprogrammet minkar for å romma kortare innhald.",
"no-active-player": "Ingen aktiv avspelar",
"panel-section-description": "Styrer kva mediaavspelarpanelet gjer og kor det ser ut.",
"panel-section-label": "Mediaavspelarpanel",
"scrolling-mode-description": "Styrer når rullande tekst slår på med lange songtitlar.",
"scrolling-mode-label": "Rullemodus",
"show-album-art-description": "Viser albumkunst for songen som spelar av no.",
"show-album-art-label": "Vis albumkunst",
"show-artist-first-description": "Viser artist - tittel heller enn tittel - artist.",
"show-artist-first-label": "Vis artist fyrst",
"show-progress-ring-description": "Viser rund sporgangsvisar.",
"show-progress-ring-label": "Vis rund sporgangsvisar",
"show-visualizer-description": "Viser ljodvisualiserar når musikk spelar av.",
"show-visualizer-label": "Vis visualiserar",
"use-fixed-width-description": "Når dette er på, brukar miniprogrammet maksimum breidde heile tida, heller enn å måta seg til innhald dynamisk.",
"use-fixed-width-label": "Bruk fast breidde",
"visualizer-type-description": "Vel kva slags ljodvisualiserer du vil sjå.",
"visualizer-type-label": "Visualiserartype"
},
"notification-history": {
"hide-widget-when-zero-description": "Skyler varslingsikonet når det ikkje er varsel.",
"hide-widget-when-zero-label": "Skyl ikon når inga varsel",
"hide-widget-when-zero-unread-description": "Skyler varslingsikonet når det ikkje er ulesne varsel.",
"hide-widget-when-zero-unread-label": "Skyl ikon når inga ulesne varsel.",
"show-unread-badge-description": "Viser merke med kor mange ulesne varsel det er.",
"show-unread-badge-label": "Vis \"ulesne\"-merke",
"unread-badge-color-description": "Vel fargen for merket for uleste varslingar.",
"unread-badge-color-label": "Farge på ulest merke"
},
"section-editor": {
"placeholder": "Vel miniprogram å leggja til...",
"search-placeholder": "Leita i miniprogram..."
},
"spacer": {
"width-description": "Mellomrom i piksel."
},
"system-monitor": {
"compact-mode-description": "Viser statistikk som små stolpediagram heller enn tekst. Stoggar oppsettet frå å skifta.",
"compact-mode-label": "Tettmodus",
"cpu-temperature-description": "Viser temperaturmålingar frå prosessoren om mogeleg.",
"cpu-temperature-label": "Prosessortemperatur",
"cpu-usage-description": "Viser prosessorbruk no i prosenttal.",
"cpu-usage-label": "Prosessorbruk",
"disk-path-description": "Vel kva for monteringspunkt for disk å overvaka.",
"disk-path-label": "Filbane til disk",
"gpu-temperature-description": "Viser temperaturmålingar frå skjermkortet om mogeleg.",
"load-average-description": "Viser snittleg systemlast.",
"load-average-label": "Snittleg last",
"memory-percentage-description": "Viser minnebruk som prosenttal heller enn absolutte verdiar.",
"memory-percentage-label": "Minne som prosent",
"memory-usage-description": "Viser minnebruk no.",
"memory-usage-label": "Minnebruk",
"network-traffic-description": "Viser snøggleik på opplasting og nedlasting på nettverket.",
"network-traffic-label": "Nettverkstraffik",
"storage-usage-description": "Viser diskplassbruk.",
"storage-usage-label": "Diskplassbruk",
"swap-usage-description": "Viser swap-minnebruk.",
"swap-usage-label": "Swap-minnebruk",
"use-monospace-font-description": "Bruk skrift med fast breidde.",
"use-monospace-font-label": "Fastbreiddeskrift"
},
"taskbar": {
"colorize-icons-description": "Gjev ikon på oppgåvelina temafargane.",
"hide-mode-description": "Styrer kva miniprogrammet gjer når det ikkje er samsvarande vindaugo.",
"hide-mode-label": "Gøymemodus",
"icon-scale-description": "Fastset skaleringsfaktor for ikon på oppgåvelina.",
"icon-scale-label": "Ikonskalering",
"max-width-description": "Øvre grensa på kor breid oppgåvelina kan vera i høve til skjermen, i prosent.",
"max-width-label": "Øvre grense på breidde",
"only-active-workspaces-description": "Viser berre appar frå aktive arbeidsrom.",
"only-active-workspaces-label": "Berre frå aktive arbeidsrom",
"only-same-monitor-description": "Viser berre appar frå skjermen med panelet på.",
"only-same-monitor-label": "Berre frå same skjerm",
"show-pinned-apps-description": "Viser feste appar frå festepunktet i oppgåvelina.",
"show-pinned-apps-label": "Vis feste appar",
"show-title-description": "Viser vindaugenamn i oppgåvelina.",
"show-title-description-disabled": "Loddrett panel kan ikkje visa vindaugenamn.",
"show-title-label": "Vis namn",
"smart-width-description": "Måtar til breidde automatisk med talet på oppføringar.",
"smart-width-label": "Smart breidde",
"title-width-description": "Fastset breidde på vindaugenamn i oppgåvelina (i piksel).",
"title-width-label": "Namnebreidde",
"title-width-reset-tooltip": "Tilbakestill namnebreidde"
},
"tray": {
"colorize-icons-description": "Gjev ikona i systempanelet temafargane.",
"colorize-icons-label": "Farga ikon",
"drawer-enabled-description": "Når dette er på, viser ufeste systempanelobjekt seg i eit skuffepanel.<br>Når dette er avslege, viser alle systempanelobjekt seg i rad.",
"drawer-enabled-label": "Aktiver skuff",
"hide-passive-description": "Når dette er på, gøymer systempanelobjekt med \"passiv\" status seg.",
"hide-passive-label": "Gøym passive objekt"
},
"volume": {
"display-mode-description": "Vel korleis dette skal visast.",
"display-mode-label": "Visningsmodus"
},
"workspace": {
"character-count-description": "Kor mange teikn som skal visast frå arbeidsromnamn (1 - 10).",
"character-count-label": "Teikntal",
"empty-color-description": "Set bakgrunnsfargen for tomme Workspaces.",
"empty-color-label": "Farge for tomt arbeidsområde",
"enable-scrollwheel-description": "Byter mellom arbeidsrom med rullehjulet på musa.",
"enable-scrollwheel-label": "Rull for å byta arbeidsrom",
"focused-color-description": "Set bakgrunnsfargen for det fokuserte Workspace.",
"focused-color-label": "Fokusert arbeidsområde-farge",
"follow-focused-screen-description": "Viser arbeidsrom frå den aktive skjermen, heller enn skjermen der panelet finn seg.",
"follow-focused-screen-label": "Fylg aktiv skjerm",
"grouped-border-opacity-description": "Fastset kor ugjennomsynlege grensene på arbeidsromrammene er.",
"grouped-border-opacity-label": "Ugjennomsynlege grenser",
"hide-unoccupied-description": "Viser ikkje arbeidsrom utan vindaugo.",
"hide-unoccupied-label": "Gøym utan vindaugo",
"label-mode-description": "Vel korleis etikettar for arbeidsrom skal visast.",
"label-mode-label": "Etikettmodus",
"occupied-color-description": "Set bakgrunnsfargen for opptekne Workspaces.",
"occupied-color-label": "Farge for oppteke arbeidsområde",
"reverse-scrolling-description": "Reverser retninga for arbeidsområdebytte når du skrollar.",
"reverse-scrolling-label": "Omvendt rulling",
"show-applications-description": "Viser applikasjonsikon innanfor kvart arbeidsrom.",
"show-applications-label": "Vis applikasjonar",
"show-badge-description": "Vis arbeidsområdenummer-merket i gruppert modus.",
"show-badge-label": "Vis arbeidsområde-merke",
"show-labels-only-when-occupied-description": "Viser etiketter for arbeidsrom berre når dei har vindaugo.",
"show-labels-only-when-occupied-label": "Vis etikettar berre med vindaugo",
"unfocused-icons-opacity-description": "Fastset kor ugjennomsynlege ufokuserte applikasjonsikon er.",
"unfocused-icons-opacity-label": "Ugjennomsynlege ufokuserte ikon"
}
},
"battery": {
"battery-health": "Batterihelse",
"battery-level": "Batterinivå",
"capacity-level": "Kapasitet: {level}",
"charging-rate": "Ladesnøggleik: {rate} W",
"discharging-rate": "Utladingssnøggleik: {rate} W",
"health": "Helse: {percent}%",
"inhibit-idle-description": "Held systemet vake.",
"no-battery-detected": "Inga batteri funnen",
"plugged-in": "Tilkopla",
"power-profile": "Energiprofil",
"time-left": "Tid att: {tid}",
"time-until-full": "Tid til full: {tid}"
},
"bluetooth": {
"panel": {
"available-devices": "Tilgjengelege einingar",
"blocked": "Blokkert",
"connected-devices": "Kopla einingar",
"device-address": "Einingadresse",
"disabled": "Bluetooth er fråkopla",
"discoverable": "Finnande",
"enable-message": "Slå på Bluetooth for å sjå tilgjengelege einingar.",
"known-devices": "Kjende einingar",
"no-devices": "Ingen einingar funne",
"paired-devices": "Kopla einingar",
"pairing-mode": "Sjå at eininga di er i koplingsmodus.",
"scanning": "Leitar etter einingar...",
"signal-text-excellent": "Tilkopling: Framifrå",
"signal-text-fair": "Tilkopling: Grei",
"signal-text-good": "Tilkopling: God",
"signal-text-poor": "Tilkopling: Dåleg",
"signal-text-unknown": "Tilkopling: Ukjend",
"signal-text-very-poor": "Tilkopling: Svært dåleg"
}
},
"changelog": {
"error": {
"fetch-failed": "Kan ikkje lasta inn historikk. Prøv att seinare.",
"rate-limit": "Du har nått lastgrensa på GitHub. Prøv att om nokre minutt."
},
"panel": {
"buttons-discord": "Vert med på Discorden vår",
"buttons-dismiss": "Greitt",
"buttons-feedback": "Gjev attermelding",
"empty": "Versjonsnotatar er ikkje tilgjengelege enno.",
"highlight-title": "Høgdepunkt",
"released": "Gjeven ut på {date}",
"subtitle-fresh": "Takk for at du installerte Noctalia! Her er det som er med i denne utgåva.",
"subtitle-updated": "Oppdatert frå {previousVersion}",
"title": "Nytt i {version}",
"version": "Versjon {version}",
"version-new-user": "Fersk installasjon"
}
},
"colors": {
"error": "Feil",
"on-surface": "På Yta",
"primary": "Primær",
"secondary": "Sekundær",
"tertiary": "Tertiær"
},
"common": {
"actions": "Handlingar",
"add": "Legg til",
"appearance": "Utsjånad",
"apply": "Bruk",
"automation": "Automatisering",
"available": "Tilgjengeleg",
"back": "Attende",
"battery": "Batteri",
"bluetooth": "Bluetooth",
"brightness": "Ljosstyrke",
"browse": "Blad",
"calendar": "Kalender",
"calendar-panel": "Kalenderpanel",
"cancel": "Bryt av",
"cards": "Kort",
"charging": "Ladar",
"clear": "Tøm",
"clipboard": "Utklippstavle",
"close": "Steng",
"color-muted": "Dempa",
"colors": "Fargar",
"command": "Kommando",
"connect": "Kopla til",
"connected": "Kopla til",
"connecting": "Koplar til...",
"continue": "Hald fram",
"contributors": "Medverkarar",
"copied-to-clipboard": "Kopiert til utklippstavla",
"countdown": "Nedteljing",
"date": "Dato",
"default": "Standard",
"delete": "Slett",
"devices": "Einingar",
"disabled": "Avslegen",
"discharging": "Ladar ut",
"disconnect": "Kopla frå",
"disconnected": "Kopla frå",
"disconnecting": "Koplar frå...",
"download": "Last ned",
"duration": "Varing",
"edit": "Endra",
"enabled": "Påslegen",
"events": "Arrangement",
"execute": "Utfør",
"faithful": "Tru",
"focus": "Fokus",
"frequency": "Frekvens",
"gateway": "Port",
"general": "Generell",
"height": "Høgd",
"hibernate": "Set i full dvale",
"history": "Historikk",
"icon": "Ikon",
"idle": "Inaktiv",
"info": "Info",
"input": "Inndata",
"install": "Installer",
"installed": "Installert",
"interface": "Grensesnitt",
"internet": "Internett",
"language": "Mål",
"loading": "Lastar inn...",
"local": "Lokal",
"location": "Stad",
"lock": "Lås",
"logout": "Skriv ut",
"look": "Sjå",
"media": "Media",
"media-player": "Mediaavspelar",
"memory": "Minne",
"monitors": "Skjermar",
"network": "Nettverk",
"next": "Neste",
"night-light": "Natteljos",
"no": "Nei",
"no-results": "Ikkje noko utfall",
"no-summary": "Ikkje noko samandrag",
"none": "Ingen",
"not-found": "Ikkje funne",
"notifications": "Varsel",
"official": "Offisiell",
"on-surface": "På overflata",
"output": "Utdata",
"pair": "Par",
"paired": "Para",
"pairing": "Parar...",
"panels": "Panel",
"password": "Passord",
"pause": "Pause",
"pin": "Fest",
"play": "Spel",
"polling": "Avlesing",
"position": "Posisjon",
"previous": "Førre",
"primary": "Primær",
"random": "Tilfelleleg",
"reboot": "Start å nyo",
"refresh": "Lasta inn å nyo",
"required": "(krevst)",
"reset": "Attendestill",
"result-count": "{count} utfall",
"result-count-plural": "{count} utfall",
"resume": "Tak opp att",
"save": "Lagra",
"scanning": "Leitar...",
"screen-corners": "Skjermhyrne",
"search": "Søk",
"secondary": "Sekundær",
"security": "Trygging",
"select": "Vel",
"shortcuts": "Beinvegar",
"shutdown": "Slå av",
"signal": "Dekning",
"sound": "Ljod",
"sources": "Kjelder",
"start": "Start",
"stop": "Stogg",
"suspend": "Set i lett dvale",
"templates": "Malar",
"tertiary": "Tertiær",
"test": "Test",
"thresholds": "Tersklar",
"title": "Tittel",
"toast": "Skjermvarsel",
"trusted": "Lita på",
"uninstall": "Desinstaller",
"unknown": "Ukjend",
"unpair": "Par frå",
"unpin": "Fest av",
"update": "Oppdater",
"upload": "Lasta opp",
"version": "Versjon",
"vibrant": "Livleg",
"visualizer": "Visualiserar",
"volume": "Ljodstyrke",
"volumes": "Ljodstyrke",
"wallpaper": "Bakgrunnsbilete",
"weather": "Vêr",
"weather-loading": "Lastar inn vêr",
"week": "Veke",
"widgets": "Miniprogram",
"width": "Breidde",
"yes": "Ja"
},
"control-center": {
"power-profile": {
"tooltip-action": "Energiprofil",
"tooltip-disabled": "Installer power-profiles-daemon for å bruka energiprofil"
},
"wifi": {
"label-disconnected": "Kopla frå Wi-Fi",
"label-ethernet": "Ethernet"
}
},
"display-modes": {
"always-hide": "Alltid gøym",
"always-show": "Alltid vis",
"force-open": "Tving open",
"on-hover": "Ved sviv"
},
"hide-modes": {
"hidden": "Gøym når tom",
"idle": "Gøym når inaktiv",
"transparent": "Gjennomsynleg når tom",
"visible": "Alltid synleg"
},
"launcher": {
"categories": {
"all": "Alt",
"audiovideo": "Ljod & video",
"chat": "Samtale",
"development": "Utvikling",
"education": "Opplæring",
"emoji-activity": "Aktivitetar",
"emoji-animals": "Dyr",
"emoji-flags": "Flagg",
"emoji-food": "Mat og drikke",
"emoji-nature": "Natur",
"emoji-objects": "Ting",
"emoji-people": "Folk & kropp",
"emoji-recent": "Nyleg",
"emoji-symbols": "Symbol",
"emoji-travel": "Reiser & stader",
"game": "Spel",
"graphics": "Grafikk",
"misc": "Ymis",
"office": "Kontor",
"pinned": "Feste",
"system": "System",
"webbrowser": "Nettlesar"
},
"providers": {
"applications": "Applikasjonar",
"calculator": "Kalkulator",
"calculator-deprecated": "\">calc\"-kommandoen er deprekert og skal snart slettast. Skriv heller matteuttrykk beinveges i søk.",
"calculator-description": "Kalkulator — reknar ut matematiske uttrykk",
"calculator-enter-expression": "Skriv inn eit matematisk uttrykk",
"calculator-press-enter-to-copy": "Trykk Enter for å kopiera utfallet",
"clipboard": "Utklippsbok",
"clipboard-clear-description": "Tøm heile utklippsboka",
"clipboard-clear-description-full": "Slett alt frå utklippsboka",
"clipboard-clear-history": "Tøm utklippsboka",
"clipboard-delete": "Slett oppføring på utklippstavla",
"clipboard-history-disabled": "Utklippsboka avslegen",
"clipboard-history-disabled-description": "Slå på utklippsboka i innstillingane eller installer cliphist",
"clipboard-loading": "Lastar inn utklippsbok...",
"clipboard-search-description": "Leita i utklippsboka",
"command-description": "Køyr skalkommandoar",
"emoji": "Emojiveljar",
"emoji-loading": "Lastar inn emojiar...",
"emoji-loading-description": "Vent litt",
"emoji-no-recent": "Ingen nylege emojiar enno",
"emoji-search-description": "Leita i og kopiera emojiar"
}
},
"lock-screen": {
"authenticating": "Autentiserer...",
"password": "Skriv passordet ditt...",
"restart": "Start å nyo",
"shut-down": "Slå av"
},
"notifications": {
"panel": {
"click-to-expand": "Klikk for å utvida",
"description": "Varsla dine kjem opp her når dei kjem inn.",
"no-notifications": "Ingen varsel"
},
"range": {
"earlier": "Tidlegare",
"today": "I dag",
"yesterday": "I går"
},
"time": {
"diff-d": "For 1 dag sidan",
"diff-dd": "For {diff} dagar sidan",
"diff-h": "For 1 time sidan",
"diff-hh": "For {diff} timar sidan",
"diff-m": "For 1 minutt sidan",
"diff-mm": "For {diff} minutt sidan",
"now": "no"
}
},
"options": {
"bar": {
"density-comfortable": "Høveleg",
"density-compact": "Tett",
"density-default": "Standard",
"density-mini": "Ørliten",
"density-spacious": "Romsleg"
},
"control-center": {
"quick-settings-style-classic": "Klassisk",
"quick-settings-style-modern": "Moderne"
},
"frame-rates-fps": "{fps} FPS",
"scrolling-modes": {
"always": "Alltid rull",
"hover": "Rull ved sviv",
"never": "Aldri rull"
},
"session-menu-grid-layout": {
"grid": "Rutenett",
"single-row": "Einskild rad"
},
"settings-panel-mode": {
"attached": "Panel knytt til lina",
"centered": "Panel i midten",
"window": "Utskilt vindauga"
},
"visualizer-types": {
"linear": "Lineær",
"mirrored": "Spegla",
"wave": "Bylgja"
},
"visualizer-visibility": {
"with-background": "Einast med bakgrunn"
},
"workspace-labels": {
"index": "Register",
"index-and-name": "Register og namn",
"name": "Namn"
}
},
"panels": {
"about": {
"changelog": "Sjå endringar",
"contributors-desc": "Takk og ære til den {count} <b>framifrå</b> medverkaren vår!",
"contributors-description-plural": "Takk og ære til alle dei {count} <b>framifrå</b> medverkarane våre!",
"copy-info": "Kopier info",
"info-copied": "Info kopiert til utklippstavla",
"noctalia-desc": "Eit lettfram og minimalistisk skrivebordsskal skapa med omhug for Wayland, bygt med Quickshell.",
"noctalia-git-commit": "Git commit:",
"noctalia-installed-version": "Installert versjon:",
"noctalia-latest-version": "Nyaste versjon:",
"noctalia-title": "Noctaliaskal",
"privacy-policy": "Personvernfråsegn",
"support": "Støtt oss",
"system-cpu": "Prosessor:",
"system-disk": "Disk:",
"system-gpu": "Skjermkort:",
"system-host": "Vert:",
"system-install-hint": "Installer fastfetch for å sjå systemopplysningar",
"system-kernel": "Kjerne:",
"system-loading": "Lastar inn systemopplysningar...",
"system-memory": "Minne:",
"system-monitor": "Skjerm:",
"system-not-installed": "fasfetch er ikkje installert",
"system-os": "OS:",
"system-packages": "Pakkar:",
"system-product": "Vare:",
"system-title": "Systemopplysningar",
"system-uptime": "Driftstid:",
"system-wm": "WM:",
"telemetry-data-copied": "Telemetri kopiert til utklippstavla",
"telemetry-desc": "Hjelp oss gjera Noctalia betre ved å dela anonyme systemopplysningar (skjermoppløysing, kompositor, Linux-utgåve). Sende ein gong med maskinoppstart, inga sporing, data sletta automatisk etter 30 dagar.",
"telemetry-enabled": "Send anonyme systemopplysningar",
"telemetry-show-data": "Sjå data",
"telemetry-title": "Personvern",
"title": "Om",
"up-to-date": "Du er oppdatert!",
"update-available": "Oppdatering tilgjengeleg",
"view-commit": "Sjå commit på GitHub"
},
"audio": {
"devices-desc": "Set opp tilgjengeleg ljodinn- og ljoduteiningar.",
"devices-input-device-description": "Vel ljodinneininga du vil setja opp.",
"devices-input-device-label": "Inneining",
"devices-output-device-description": "Vel ljoduteininga du vil setja opp.",
"devices-output-device-label": "Uteining",
"devices-title": "Ljodeiningar",
"external-mixer-description": "Skriv inn kommandoen eller applikasjonsbana som køyrer når du slår på funksjonen for den eksterne ljodmiksaren.",
"external-mixer-label": "Kommando for ekstern ljodmiksar",
"external-mixer-placeholder": "pwvucontrol || pavucontrol",
"media-desc": "Set mediaapplikasjonane du likar best og dei du vil ignorera.",
"media-excluded-player-description": "Skriv inn lykelord for avspelarar som systemet skal ignorera. Kvart lykel ord skal vera på ei ny line.",
"media-excluded-player-label": "Borttekne avspelar",
"media-excluded-player-placeholder": "skriv substring og klikk på +",
"media-frame-rate-description": "Høgre frekvens er jamnare men krev fleire ressursar.",
"media-frame-rate-label": "Biletfrekvens",
"media-primary-player-description": "Skriv lykelord for å kjenna att avspelaren du brukar mest.",
"media-primary-player-label": "Hovudspelar",
"media-primary-player-placeholder": "t.d. spotify, vlc, mpv",
"media-scrolling-speed-description": "Tida det tek for tittelen å blada frå start til ende, i sekund.",
"media-scrolling-speed-label": "Bladefart",
"media-scrolling-title-description": "Slå på varig blading for lange mediatitlar.",
"media-scrolling-title-label": "Bladande tittel",
"media-title": "Mediaavspelarar",
"on-middle-clicked-description": "Kommando som køyr når knappen er midtklikka.",
"panel-applications-empty": "Ingen applikasjonar spelar av ljod no",
"title": "Ljod",
"visualizer-type-description": "Vel visualiseringstypen når ein spelar av media.",
"visualizer-type-label": "Visualiseringstype",
"volumes-desc": "Endrar kontrollane for ljodstyrke og ljodnivå",
"volumes-input-volume-description": "Mikrofon inn-nivå",
"volumes-mute-input-description": "Demp standard ljodinneining (mikrofon).",
"volumes-mute-input-label": "Demp ljodinndata",
"volumes-mute-output-description": "Demp hovudljoduteininga på systemet",
"volumes-mute-output-label": "Demp ljodutdata",
"volumes-output-volume-description": "Ljodstyrke på heile systemet",
"volumes-step-size-description": "Endra kor mykje ljodstyrke endrar seg for kvart steg (rullehjul, beinknappar).",
"volumes-step-size-label": "Steg for ljodstyrke",
"volumes-volume-feedback-description": "Spel av svarljod når ein endrar på ljodstyrke",
"volumes-volume-feedback-label": "Spel av svarljod med ljodstyrke",
"volumes-volume-overdrive-description": "Tillèt at ljodstyrke kan gå over 100%. Ikkje alltid støtta av maskinvare.",
"volumes-volume-overdrive-label": "Tillat overdrift på ljodstyrke"
},
"launcher": {
"clipboard-desc": "Sjå og styra utklippsboka di frå programveljaren.",
"settings-clipboard-watch-image-description": "Heil kommandostreng sendt til wl-paste for bildeendringar. (krev omstart)",
"settings-clipboard-watch-image-label": "Biletovervakingskommando",
"settings-clipboard-watch-text-description": "Fullstendig kommandostreng sendt til wl-paste for tekstendringer. (krever omstart)",
"settings-clipboard-watch-text-label": "Tekstovervakingskommando"
},
"lock-screen": {
"allow-password-with-fprintd-description": "Når fprintd (fingeravtrykkautentisering) er aktiv, lar dette alternativet deg framleis logge inn med passordet ditt i staden for eit fingeravtrykk.",
"allow-password-with-fprintd-label": "Tillat passordpålogging med fprintd",
"auto-start-auth-description": "t.d. startar automatisk fingeravtrykksautentisering utan å krevje eit tastetrykk eller eit knappetrykk.",
"auto-start-auth-label": "Autostart autentisering"
}
},
"toast": {
"donation-opened": "Donasjonsside opna i nettlesaren din"
},
"widgets": {
"datetime-tokens": {
"common-12hour-time-minutes": "Tid i 12-timar med minutt",
"common-24hour-time-minutes": "Tid i 24-timar med minutt",
"common-24hour-time-seconds": "Tid i 24-timar med sekund",
"common-european-date": "Europeisk datoformat",
"common-iso-date": "ISO datoformat",
"common-us-date": "Amerikansk datoformat",
"common-weekday-date": "Vekedag med dato",
"common-weekday-month-day": "Vekedag, månad og dag"
}
}
}
+35 -2
View File
@@ -182,7 +182,9 @@
"hide-widget-when-zero-unread-description": "Ukryj ikonę powiadomień, gdy nie ma nieprzeczytanych powiadomień.",
"hide-widget-when-zero-unread-label": "Ukryj ikonę bez nieprzeczytanych",
"show-unread-badge-description": "Wyświetl plakietkę z liczbą nieprzeczytanych powiadomień.",
"show-unread-badge-label": "Pokaż plakietkę nieprzeczytanych"
"show-unread-badge-label": "Pokaż plakietkę nieprzeczytanych",
"unread-badge-color-description": "Wybierz kolor dla plakietki nieprzeczytanych powiadomień.",
"unread-badge-color-label": "Kolor plakietki nieprzeczytanych"
},
"section-editor": {
"placeholder": "Wybierz widżet do dodania...",
@@ -254,8 +256,12 @@
"workspace": {
"character-count-description": "Liczba znaków do wyświetlenia z nazw obszarów roboczych (1-10).",
"character-count-label": "Liczba znaków",
"empty-color-description": "Ustaw kolor tła dla pustych Workspaceów.",
"empty-color-label": "Kolor pustego obszaru roboczego",
"enable-scrollwheel-description": "Przełączaj między obszarami roboczymi za pomocą kółka myszy.",
"enable-scrollwheel-label": "Przewiń, aby przełączyć obszary",
"focused-color-description": "Ustaw kolor tła dla aktywnego Workspace.",
"focused-color-label": "Kolor aktywnego obszaru roboczego",
"follow-focused-screen-description": "Wyświetl obszary robocze z aktualnie aktywnego ekranu, zamiast ekranu, na którym znajduje się pasek.",
"follow-focused-screen-label": "Podążaj za skupionym ekranem",
"grouped-border-opacity-description": "Ustaw poziom krycia dla obramowań kontenerów obszaru roboczego.",
@@ -264,8 +270,14 @@
"hide-unoccupied-label": "Ukryj nieobsadzone",
"label-mode-description": "Wybierz sposób wyświetlania etykiet obszarów roboczych.",
"label-mode-label": "Tryb etykiet",
"occupied-color-description": "Ustaw kolor tła dla zajętych Workspace'ów.",
"occupied-color-label": "Kolor zajętego obszaru roboczego",
"reverse-scrolling-description": "Odwróć kierunek przełączania obszarów roboczych podczas przewijania.",
"reverse-scrolling-label": "Odwróć przewijanie",
"show-applications-description": "Wyświetl ikony aplikacji wewnątrz każdego obszaru roboczego.",
"show-applications-label": "Pokaż aplikacje",
"show-badge-description": "Pokaż plakietkę z numerem obszaru roboczego w trybie grupowym.",
"show-badge-label": "Pokaż odznakę obszaru roboczego",
"show-labels-only-when-occupied-description": "Pokazuj etykiety obszarów roboczych tylko wtedy, gdy zawierają okna.",
"show-labels-only-when-occupied-label": "Pokaż etykiety tylko gdy obsadzone",
"unfocused-icons-opacity-description": "Ustaw poziom przezroczystości dla ikon nieaktywnych aplikacji.",
@@ -416,6 +428,7 @@
"not-found": "Nie znaleziono",
"notifications": "Powiadomienia",
"official": "Oficjalne",
"on-surface": "Na powierzchni",
"output": "Wyjście",
"pair": "Paruj",
"paired": "Sparowane",
@@ -428,6 +441,7 @@
"polling": "Odpytywanie",
"position": "Pozycja",
"previous": "Poprzedni",
"primary": "Podstawowy",
"random": "Losowy",
"reboot": "Restart",
"refresh": "Odśwież",
@@ -440,6 +454,7 @@
"scanning": "Skanowanie...",
"screen-corners": "Narożniki ekranu",
"search": "Szukaj",
"secondary": "Dodatkowy",
"security": "Bezpieczeństwo",
"select": "Wybierz",
"shortcuts": "Skróty",
@@ -451,6 +466,7 @@
"stop": "Stop",
"suspend": "Wstrzymaj",
"templates": "Szablony",
"tertiary": "Trzeciorzędowy",
"test": "Test",
"thresholds": "Progi",
"title": "Tytuł",
@@ -579,7 +595,10 @@
"density-compact": "Kompaktowy",
"density-default": "Domyślny",
"density-mini": "Mini",
"density-spacious": "Przestronny"
"density-spacious": "Przestronny",
"type-floating": "Pływający",
"type-framed": "Obramowany",
"type-simple": "Prosty"
},
"control-center": {
"quick-settings-style-classic": "Klasyczny",
@@ -707,6 +726,10 @@
"appearance-desc": "Dostosuj wygląd i pozycję paska.",
"appearance-floating-description": "Wyświetl pasek jako pływającą 'pigułkę'.",
"appearance-floating-label": "Pływający pasek",
"appearance-frame-radius": "Promień wewnętrzny",
"appearance-frame-settings-description": "Dostosuj grubość ramki i wewnętrzny promień narożników",
"appearance-frame-settings-label": "Ustawienia ramki",
"appearance-frame-thickness": "Grubość",
"appearance-hide-on-overview-description": "Ukryj pasek i zamknij panele, gdy aktywny jest przegląd kompozytora.",
"appearance-hide-on-overview-label": "Ukryj pasek w przeglądzie",
"appearance-margins-description": "Dostosuj marginesy wokół pływającego paska.",
@@ -721,6 +744,8 @@
"appearance-show-capsule-label": "Pokaż kapsułę",
"appearance-show-outline-description": "Wyświetla widoczne obramowanie wokół każdego widżetu.",
"appearance-show-outline-label": "Pokaż obramowanie widżetów",
"appearance-type-description": "Wybierz styl paska: Prosty, Pływający lub Obramowany",
"appearance-type-label": "Typ paska",
"appearance-use-separate-opacity-description": "Umożliwia użycie oddzielnej wartości krycia dla tła paska.",
"appearance-use-separate-opacity-label": "Użyj osobnej przezroczystości słupków",
"monitor-configure-widgets": "Skonfiguruj widżety",
@@ -1027,6 +1052,10 @@
"settings-clip-wrap-text-label": "Zawijaj tekst ze schowka",
"settings-clipboard-history-description": "Dostęp do wcześniej skopiowanych elementów z poziomu launchera.",
"settings-clipboard-history-label": "Włącz historię schowka",
"settings-clipboard-watch-image-description": "Pełny ciąg poleceń przekazywany do wl-paste dla zmian obrazu. (wymaga ponownego uruchomienia)",
"settings-clipboard-watch-image-label": "Polecenie Monitorowania Obrazów",
"settings-clipboard-watch-text-description": "Pełny ciąg polecenia przekazany do wl-paste dla zmian tekstu. (wymaga ponownego uruchomienia)",
"settings-clipboard-watch-text-label": "Polecenie monitorowania tekstu",
"settings-custom-launch-prefix-description": "Poprzedzaj polecenia własnym prefiksem (np. 'runapp' dla integracji z systemd).",
"settings-custom-launch-prefix-enabled-description": "Używaj własnego prefiksu do uruchamiania aplikacji zamiast metody domyślnej.",
"settings-custom-launch-prefix-enabled-label": "Włącz własny prefiks",
@@ -1090,6 +1119,10 @@
"weather-show-in-calendar-label": "Wyświetlaj pogodę w kalendarzu"
},
"lock-screen": {
"allow-password-with-fprintd-description": "Gdy fprintd (uwierzytelnianie odciskiem palca) jest aktywne, ta opcja pozwala nadal logować się za pomocą hasła zamiast odcisku palca.",
"allow-password-with-fprintd-label": "Zezwól na logowanie hasłem za pomocą fprintd",
"auto-start-auth-description": "np. automatycznie uruchamia uwierzytelnianie odciskiem palca bez konieczności naciskania klawisza lub klikania przycisku.",
"auto-start-auth-label": "Automatyczne uruchamianie uwierzytelniania",
"compact-lockscreen-description": "Pokaż tylko logowanie i kontrolki systemowe, ukrywając widżety pogody i mediów.",
"compact-lockscreen-label": "Kompaktowy ekran blokady",
"lock-on-suspend-description": "Automatycznie blokuj ekran podczas uśpienia systemu.",
+35 -2
View File
@@ -182,7 +182,9 @@
"hide-widget-when-zero-unread-description": "Ocultar o ícone de notificações quando não houver notificações não lidas.",
"hide-widget-when-zero-unread-label": "Ocultar ícone sem notificações não lidas",
"show-unread-badge-description": "Exibir um distintivo mostrando o número de notificações não lidas.",
"show-unread-badge-label": "Mostrar distintivo de não lidos"
"show-unread-badge-label": "Mostrar distintivo de não lidos",
"unread-badge-color-description": "Selecione a cor para o emblema de notificação não lida.",
"unread-badge-color-label": "Cor do distintivo de não lido"
},
"section-editor": {
"placeholder": "Selecione um widget para adicionar...",
@@ -254,8 +256,12 @@
"workspace": {
"character-count-description": "Número de caracteres a exibir dos nomes de espaços de trabalho (1-10).",
"character-count-label": "Número de caracteres",
"empty-color-description": "Definir a cor de fundo para Workspaces vazios.",
"empty-color-label": "Cor do espaço de trabalho vazio",
"enable-scrollwheel-description": "Alternar entre áreas de trabalho usando a roda do mouse.",
"enable-scrollwheel-label": "Role para alternar entre áreas de trabalho",
"focused-color-description": "Definir a cor de fundo para o Workspace focado.",
"focused-color-label": "Cor do workspace focado",
"follow-focused-screen-description": "Exibir áreas de trabalho da tela atualmente em foco, em vez da tela onde a barra está localizada.",
"follow-focused-screen-label": "Seguir tela em foco",
"grouped-border-opacity-description": "Definir o nível de opacidade para as bordas do contêiner do espaço de trabalho.",
@@ -264,8 +270,14 @@
"hide-unoccupied-label": "Ocultar desocupados",
"label-mode-description": "Escolher como os rótulos de espaço de trabalho são exibidos.",
"label-mode-label": "Modo de rótulo",
"occupied-color-description": "Definir a cor de fundo para Workspaces ocupados.",
"occupied-color-label": "Cor do espaço de trabalho ocupado",
"reverse-scrolling-description": "Inverter a direção da troca de áreas de trabalho ao rolar.",
"reverse-scrolling-label": "Rolagem invertida",
"show-applications-description": "Exibir ícones de aplicativos dentro de cada espaço de trabalho.",
"show-applications-label": "Mostrar aplicativos",
"show-badge-description": "Mostrar o selo do número da área de trabalho no modo agrupado.",
"show-badge-label": "Mostrar selo da área de trabalho",
"show-labels-only-when-occupied-description": "Mostrar rótulos de área de trabalho apenas quando contiverem janelas.",
"show-labels-only-when-occupied-label": "Mostrar rótulos apenas quando ocupado",
"unfocused-icons-opacity-description": "Definir o nível de opacidade para ícones de aplicativos não focados.",
@@ -416,6 +428,7 @@
"not-found": "Não encontrado",
"notifications": "Notificações",
"official": "Oficial",
"on-surface": "Na Superfície",
"output": "Saída",
"pair": "Emparelhar",
"paired": "Emparelhado",
@@ -428,6 +441,7 @@
"polling": "Sondagem",
"position": "Posição",
"previous": "Anterior",
"primary": "Primário",
"random": "Aleatório",
"reboot": "Reiniciar",
"refresh": "Atualizar",
@@ -440,6 +454,7 @@
"scanning": "A digitalização está em andamento...",
"screen-corners": "Cantos da tela",
"search": "Pesquisar",
"secondary": "Secundário",
"security": "Segurança",
"select": "Selecionar",
"shortcuts": "Atalhos",
@@ -451,6 +466,7 @@
"stop": "Pare",
"suspend": "Suspender",
"templates": "Modelos",
"tertiary": "Terciário",
"test": "Teste",
"thresholds": "Limiares",
"title": "Título",
@@ -579,7 +595,10 @@
"density-compact": "Compacta",
"density-default": "Padrão",
"density-mini": "Mini",
"density-spacious": "Espaçoso(a)"
"density-spacious": "Espaçoso(a)",
"type-floating": "Flutuante",
"type-framed": "Emoldurada",
"type-simple": "Simples"
},
"control-center": {
"quick-settings-style-classic": "Clássico",
@@ -707,6 +726,10 @@
"appearance-desc": "Personalize a aparência e a posição da barra.",
"appearance-floating-description": "Exibe a barra como uma 'pílula' flutuante.",
"appearance-floating-label": "Barra flutuante",
"appearance-frame-radius": "Raio Interno",
"appearance-frame-settings-description": "Ajuste a espessura da moldura e o raio interno",
"appearance-frame-settings-label": "Configurações da Moldura",
"appearance-frame-thickness": "Espessura",
"appearance-hide-on-overview-description": "Ocultar a barra e fechar os painéis quando a visão geral do compositor estiver ativa.",
"appearance-hide-on-overview-label": "Ocultar barra na visão geral",
"appearance-margins-description": "Ajuste as margens ao redor da barra flutuante.",
@@ -721,6 +744,8 @@
"appearance-show-capsule-label": "Mostrar cápsula",
"appearance-show-outline-description": "Exibe uma borda visível ao redor de cada widget.",
"appearance-show-outline-label": "Mostrar contornos do widget",
"appearance-type-description": "Escolha o estilo da barra: Simples, Flutuante ou Emoldurada",
"appearance-type-label": "Tipo de Barra",
"appearance-use-separate-opacity-description": "Permitir usar um valor de opacidade separado para o fundo da barra.",
"appearance-use-separate-opacity-label": "Usar opacidade separada para as barras",
"monitor-configure-widgets": "Configurar widgets",
@@ -1027,6 +1052,10 @@
"settings-clip-wrap-text-label": "Quebrar texto da área de transferência",
"settings-clipboard-history-description": "Acesse itens copiados anteriormente a partir do lançador.",
"settings-clipboard-history-label": "Ativar histórico da área de transferência",
"settings-clipboard-watch-image-description": "String de comando completa passada para wl-paste para alterações de imagem. (requer reinício)",
"settings-clipboard-watch-image-label": "Comando de Vigilância de Imagens",
"settings-clipboard-watch-text-description": "Cadeia de comando completa passada para wl-paste para alterações de texto. (requer reinício)",
"settings-clipboard-watch-text-label": "Comando de observação de texto",
"settings-custom-launch-prefix-description": "Prefixar comandos com um inicializador personalizado (ex. 'runapp' para integração systemd).",
"settings-custom-launch-prefix-enabled-description": "Usar um prefixo personalizado para inicializar aplicativos em vez do método padrão.",
"settings-custom-launch-prefix-enabled-label": "Habilitar prefixo de inicialização personalizado",
@@ -1090,6 +1119,10 @@
"weather-show-in-calendar-label": "Exibir clima no calendário"
},
"lock-screen": {
"allow-password-with-fprintd-description": "Quando o fprintd (autenticação por impressão digital) está ativo, esta opção permite-lhe iniciar sessão com a sua palavra-passe em vez de uma impressão digital.",
"allow-password-with-fprintd-label": "Permitir login por senha com fprintd",
"auto-start-auth-description": "p. ex., inicia automaticamente a autenticação de impressão digital sem exigir uma pressão de tecla ou um clique de botão.",
"auto-start-auth-label": "Autenticação de início automático",
"compact-lockscreen-description": "Mostrar apenas a entrada de login e os controles do sistema, ocultando os widgets de clima e mídia.",
"compact-lockscreen-label": "Tela de bloqueio compacta",
"lock-on-suspend-description": "Bloquear a tela automaticamente ao suspender o sistema.",
+46 -13
View File
@@ -182,7 +182,9 @@
"hide-widget-when-zero-unread-description": "Скрывать значок уведомлений, если нет непрочитанных.",
"hide-widget-when-zero-unread-label": "Скрывать значок без непрочитанных",
"show-unread-badge-description": "Отображать значок, показывающий количество непрочитанных уведомлений.",
"show-unread-badge-label": "Показывать значок непрочитанных"
"show-unread-badge-label": "Показывать значок непрочитанных",
"unread-badge-color-description": "Выберите цвет для значка непрочитанных уведомлений.",
"unread-badge-color-label": "Цвет значка непрочитанных"
},
"section-editor": {
"placeholder": "Выберите виджет для добавления...",
@@ -254,8 +256,12 @@
"workspace": {
"character-count-description": "Количество символов для отображения из имён рабочих пространств (1-10).",
"character-count-label": "Количество символов",
"empty-color-description": "Установить цвет фона для пустых рабочих пространств.",
"empty-color-label": "Цвет пустого рабочего пространства",
"enable-scrollwheel-description": "Переключайтесь между рабочими пространствами с помощью колеса прокрутки мыши.",
"enable-scrollwheel-label": "Прокрутите, чтобы переключить рабочие столы",
"enable-scrollwheel-label": "Прокрутите, чтобы переключить рабочие пространства",
"focused-color-description": "Установить цвет фона для активного рабочего пространства.",
"focused-color-label": "Цвет активного рабочего пространства",
"follow-focused-screen-description": "Отображать рабочие пространства с текущего активного экрана, а не с экрана, на котором расположена панель.",
"follow-focused-screen-label": "Следовать за активным экраном",
"grouped-border-opacity-description": "Установить уровень прозрачности для границ контейнера рабочей области.",
@@ -264,8 +270,14 @@
"hide-unoccupied-label": "Скрыть незанятые",
"label-mode-description": "Выберите, как отображаются метки рабочих пространств.",
"label-mode-label": "Режим метки",
"occupied-color-description": "Установить цвет фона для занятых рабочих пространств.",
"occupied-color-label": "Цвет занятого рабочего пространства",
"reverse-scrolling-description": "Изменить направление переключения рабочих пространств при прокрутке.",
"reverse-scrolling-label": "Обратная прокрутка",
"show-applications-description": "Отображать значки приложений внутри каждого рабочего пространства.",
"show-applications-label": "Показать приложения",
"show-badge-description": "Показывать значок номера рабочего пространства в сгруппированном режиме.",
"show-badge-label": "Показывать значок рабочего стола",
"show-labels-only-when-occupied-description": "Показывать метки рабочих пространств только тогда, когда они содержат окна.",
"show-labels-only-when-occupied-label": "Показывать метки только при наличии содержимого",
"unfocused-icons-opacity-description": "Установить уровень прозрачности для неактивных значков приложений.",
@@ -402,7 +414,7 @@
"lock": "Заблокировать",
"logout": "Выйти",
"look": "Внешний вид",
"media": "СМИ",
"media": "Плеер",
"media-player": "Медиаплеер",
"memory": "Память",
"monitors": "Мониторы",
@@ -416,6 +428,7 @@
"not-found": "Не найдено",
"notifications": "Уведомления",
"official": "Официальный",
"on-surface": "На поверхности",
"output": "Вывод",
"pair": "Спарить",
"paired": "Спарено",
@@ -428,6 +441,7 @@
"polling": "Опрашивание",
"position": "Позиция",
"previous": "Предыдущий",
"primary": "Основной",
"random": "Случайный",
"reboot": "Перезагрузить",
"refresh": "Обновить",
@@ -440,6 +454,7 @@
"scanning": "Сканирование...",
"screen-corners": "Углы экрана",
"search": "Поиск",
"secondary": "Вторичный",
"security": "Безопасность",
"select": "Выбрать",
"shortcuts": "Ярлыки",
@@ -451,6 +466,7 @@
"stop": "Стоп",
"suspend": "Приостановить",
"templates": "Шаблоны",
"tertiary": "Третичный",
"test": "Тест",
"thresholds": "Пороги",
"title": "Название",
@@ -579,7 +595,10 @@
"density-compact": "Компактная",
"density-default": "По умолчанию",
"density-mini": "Мини",
"density-spacious": "Просторный"
"density-spacious": "Просторный",
"type-floating": "Плавающий",
"type-framed": "В рамке",
"type-simple": "Простой"
},
"control-center": {
"quick-settings-style-classic": "Классический",
@@ -707,6 +726,10 @@
"appearance-desc": "Настройка внешнего вида и положения панели.",
"appearance-floating-description": "Отображает панель как плавающую 'таблетку'.",
"appearance-floating-label": "Плавающая панель",
"appearance-frame-radius": "Внутренний радиус",
"appearance-frame-settings-description": "Настройте толщину рамки и внутренний радиус углов",
"appearance-frame-settings-label": "Настройки рамки",
"appearance-frame-thickness": "Толщина",
"appearance-hide-on-overview-description": "Скрыть панель и закрыть панели, когда активен обзор компоновщика.",
"appearance-hide-on-overview-label": "Скрыть панель в обзоре",
"appearance-margins-description": "Настройка отступов вокруг плавающей панели.",
@@ -721,6 +744,8 @@
"appearance-show-capsule-label": "Показывать капсулу",
"appearance-show-outline-description": "Отображает видимую границу вокруг каждого виджета.",
"appearance-show-outline-label": "Показать контуры виджетов",
"appearance-type-description": "Выберите стиль панели: Простой, Плавающий или В рамке",
"appearance-type-label": "Тип панели",
"appearance-use-separate-opacity-description": "Включить использование отдельного значения прозрачности для фона полосы.",
"appearance-use-separate-opacity-label": "Использовать раздельную прозрачность столбцов",
"monitor-configure-widgets": "Настроить виджеты",
@@ -1027,6 +1052,10 @@
"settings-clip-wrap-text-label": "Обернуть текст из буфера обмена",
"settings-clipboard-history-description": "Доступ к ранее скопированным элементам из запуска.",
"settings-clipboard-history-label": "Включить историю буфера обмена",
"settings-clipboard-watch-image-description": "Полная командная строка, передаваемая в wl-paste для изменения изображений. (требуется перезапуск)",
"settings-clipboard-watch-image-label": "Команда Отслеживания Изображений",
"settings-clipboard-watch-text-description": "Полная строка команды, переданная в wl-paste для изменений текста. (требуется перезапуск)",
"settings-clipboard-watch-text-label": "Команда наблюдения за текстом",
"settings-custom-launch-prefix-description": "Добавлять префикс к командам с помощью пользовательского запуска (например, 'runapp' для интеграции с systemd).",
"settings-custom-launch-prefix-enabled-description": "Использовать пользовательский префикс для запуска приложений вместо метода по умолчанию.",
"settings-custom-launch-prefix-enabled-label": "Включить пользовательский префикс запуска",
@@ -1090,6 +1119,10 @@
"weather-show-in-calendar-label": "Отображать погоду в календаре"
},
"lock-screen": {
"allow-password-with-fprintd-description": "Когда fprintd (аутентификация по отпечатку пальца) активен, эта опция позволяет вам входить в систему с помощью пароля вместо отпечатка пальца.",
"allow-password-with-fprintd-label": "Разрешить вход по паролю с помощью fprintd",
"auto-start-auth-description": "например, автоматически запускает аутентификацию по отпечатку пальца без необходимости нажатия клавиши или щелчка кнопки.",
"auto-start-auth-label": "Автоматический запуск аутентификации",
"compact-lockscreen-description": "Показывать только поле для ввода логина и системные элементы управления, скрывая виджеты погоды и медиа.",
"compact-lockscreen-label": "Компактный экран блокировки",
"lock-on-suspend-description": "Автоматически блокировать экран при приостановке работы системы.",
@@ -1180,24 +1213,24 @@
"duration-desc": "Как долго OSD остаётся видимым перед автоматическим скрытием.",
"duration-title": "Таймаут автоскрытия",
"enabled-description": "Показывать изменения громкости и яркости в реальном времени.",
"enabled-label": "Включить экранное отображение (OSD)",
"events-desc": "Выберите, какие события запускают экранное меню.",
"general-desc": "Настроить видимость и поведение экранного меню (OSD).",
"location-description": "Где появляются экранные отображения.",
"enabled-label": "Включить уведомления (OSD)",
"events-desc": "Выберите, по каким событиям выводить уведомления (OSD).",
"general-desc": "Настроить видимость и поведение уведомлений (OSD).",
"location-description": "Расположение экранных уведолений.",
"monitors-desc": "Показывать OSD на определённых мониторах. По умолчанию на всех, если ни один не выбран.",
"title": "Экранное отображение (OSD)",
"title": "Уведомления (OSD)",
"types-brightness-description": "Показывать OSD при изменении яркости экрана.",
"types-brightness-label": "Яркость",
"types-custom-text-description": "Показывать OSD для пользовательских текстовых сообщений через IPC.",
"types-custom-text-label": "Пользовательский текст",
"types-desc": "Выберите события, которые должны запускать экранное меню (OSD).",
"types-desc": "Выберите события, которые должны запускать уведомления (OSD).",
"types-input-volume-description": "Показывать OSD при изменении громкости микрофона.",
"types-input-volume-label": "Входная громкость",
"types-lockkey-description": "Показывать OSD при переключении клавиш Caps Lock, Num Lock или Scroll Lock.",
"types-lockkey-label": "Клавиши блокировки",
"types-media-description": "Показывать OSD при изменении состояния воспроизведения медиа (воспроизведение, пауза, пропуск).",
"types-media-label": "Воспроизведение медиа",
"types-title": "События, запускающие экранное меню",
"types-title": "События, запускающие уведомления (OSD)",
"types-volume-description": "Показывать OSD при изменении громкости аудиовыхода.",
"types-volume-label": "Выходная громкость"
},
@@ -1325,7 +1358,7 @@
"box-border-radius-description": "Настраивает скругление углов основных разделов макета, таких как боковые панели, карточки и панели контента.",
"box-border-radius-label": "Радиус контейнера",
"box-border-radius-reset": "Сбросить радиус контейнера",
"control-border-radius-description": "Управляет кривизной интерактивных элементов, включая кнопки, переключатели и текстовые поля.",
"control-border-radius-description": "Управляет скруглением интерактивных элементов, включая кнопки, переключатели и текстовые поля.",
"control-border-radius-label": "Ввести радиус",
"control-border-radius-reset": "Сбросить радиус ввода",
"desc": "Настройка внешнего вида, ощущений и поведения интерфейса.",
@@ -1398,7 +1431,7 @@
"settings-view-mode-label": "Режим просмотра",
"view-mode-browse": "Просмотр каталогов",
"view-mode-cycle-tooltip": "Режим просмотра: {mode} (нажмите, чтобы изменить)",
"view-mode-recursive": "Сглаженные подкаталоги",
"view-mode-recursive": "Объединить с подкаталогами",
"view-mode-single": "Корневой каталог"
}
},
File diff suppressed because it is too large Load Diff
+35 -2
View File
@@ -182,7 +182,9 @@
"hide-widget-when-zero-unread-description": "Okunmamış bildirim yokken bildirim simgesini gizle.",
"hide-widget-when-zero-unread-label": "Okunmamış yokken simgeyi gizle",
"show-unread-badge-description": "Okunmamış bildirim sayısını gösteren bir rozet göster.",
"show-unread-badge-label": "Okunmamış bildirimleri göster"
"show-unread-badge-label": "Okunmamış bildirimleri göster",
"unread-badge-color-description": "Okunmamış bildirim rozeti için rengi seçin.",
"unread-badge-color-label": "Okunmamış rozet rengi"
},
"section-editor": {
"placeholder": "Eklenecek bir araç takımı seçin...",
@@ -254,8 +256,12 @@
"workspace": {
"character-count-description": "Çalışma alanı adlarından gösterilecek karakter sayısı (1-10).",
"character-count-label": "Karakter sayısı",
"empty-color-description": "Boş Workspace'ler için arka plan rengini ayarla.",
"empty-color-label": "Boş çalışma alanı rengi",
"enable-scrollwheel-description": "Fare tekerleği ile çalışma alanları arasında geçiş yapın.",
"enable-scrollwheel-label": "Çalışma alanları arasında geçiş yapmak için kaydırın",
"focused-color-description": "Odaklanılan Workspace için arka plan rengini ayarla.",
"focused-color-label": "Odaklanılan çalışma alanı rengi",
"follow-focused-screen-description": "Çubuğun bulunduğu ekran yerine, şu anda odaklanmış ekrandaki çalışma alanlarını göster.",
"follow-focused-screen-label": "Odaklanmış ekranı takip et",
"grouped-border-opacity-description": "Çalışma alanı kapsayıcı kenarlıklarının opaklık düzeyini ayarlayın.",
@@ -264,8 +270,14 @@
"hide-unoccupied-label": "Dolu olmayanları gizle",
"label-mode-description": "Çalışma alanı etiketlerinin nasıl gösterileceğini seçin.",
"label-mode-label": "Etiket Modu",
"occupied-color-description": "Dolu Workspaces için arka plan rengini ayarla.",
"occupied-color-label": "Dolu çalışma alanı rengi",
"reverse-scrolling-description": "Kaydırırken çalışma alanı geçiş yönünü tersine çevir.",
"reverse-scrolling-label": "Ters kaydırma",
"show-applications-description": "Her çalışma alanının içinde uygulama simgelerini görüntüle.",
"show-applications-label": "Uygulamaları göster",
"show-badge-description": "Gruplandırılmış modda çalışma alanı numarası işaretini göster.",
"show-badge-label": "Çalışma alanı rozetini göster",
"show-labels-only-when-occupied-description": "Yalnızca pencere içeren çalışma alanı etiketlerini göster.",
"show-labels-only-when-occupied-label": "Yalnızca dolu olduğunda etiketleri göster",
"unfocused-icons-opacity-description": "Odaklanılmamış uygulama simgelerinin opaklık düzeyini ayarlayın.",
@@ -416,6 +428,7 @@
"not-found": "Bulunamadı",
"notifications": "Bildirimler",
"official": "Resmi",
"on-surface": "Yüzeyde",
"output": "Çıktı",
"pair": "Eşleştir",
"paired": "Eşleştirildi",
@@ -428,6 +441,7 @@
"polling": "Yoklama",
"position": "Pozisyon",
"previous": "Önceki",
"primary": "Birincil",
"random": "Rastgele",
"reboot": "Yeniden başlat",
"refresh": "Yenile",
@@ -440,6 +454,7 @@
"scanning": "Taranıyor...",
"screen-corners": "Ekran köşeleri",
"search": "Arama",
"secondary": "İkincil",
"security": "Güvenlik",
"select": "Seç",
"shortcuts": "Kısayollar",
@@ -451,6 +466,7 @@
"stop": "Dur",
"suspend": "Askıya al",
"templates": "Şablonlar",
"tertiary": "Üçüncül",
"test": "Test",
"thresholds": "Eşikler",
"title": "Başlık",
@@ -579,7 +595,10 @@
"density-compact": "Sıkı",
"density-default": "Varsayılan",
"density-mini": "Ufak",
"density-spacious": "Geniş"
"density-spacious": "Geniş",
"type-floating": "Yüzen",
"type-framed": "Çerçeveli",
"type-simple": "Basit"
},
"control-center": {
"quick-settings-style-classic": "Klasik",
@@ -707,6 +726,10 @@
"appearance-desc": "Araç çubuğunun görünümünü ve konumunu özelleştirin.",
"appearance-floating-description": "Araç çubuğunu yüzen bir 'kapsül' olarak görüntüler. Not: Bu, ekran köşelerini kenarlara taşıyacaktır.",
"appearance-floating-label": "Yüzen araç çubuğu",
"appearance-frame-radius": "İç Yarıçap",
"appearance-frame-settings-description": "Çerçeve kalınlığını ve iç köşe yarıçapını ayarlayın",
"appearance-frame-settings-label": "Çerçeve Ayarları",
"appearance-frame-thickness": "Kalınlık",
"appearance-hide-on-overview-description": "Kompozitör önizlemesi aktif olduğunda çubuğu gizle ve panelleri kapat.",
"appearance-hide-on-overview-label": "Genel bakışta çubuğu gizle",
"appearance-margins-description": "Yüzen araç çubuğunun etrafındaki kenar boşluklarını ayarlayın.",
@@ -721,6 +744,8 @@
"appearance-show-capsule-label": "Kapsülü göster",
"appearance-show-outline-description": "Her widget'ın etrafında görünür bir kenarlık görüntüler.",
"appearance-show-outline-label": "Araç kutusu ana hatlarını göster",
"appearance-type-description": "Çubuk stilini seçin: Basit, Yüzen veya Çerçeveli",
"appearance-type-label": "Çubuk Türü",
"appearance-use-separate-opacity-description": "Çubuk arka planı için ayrı bir opaklık değeri kullanmayı etkinleştir.",
"appearance-use-separate-opacity-label": "Ayrı çubuk opaklığı kullan",
"monitor-configure-widgets": "Araçları yapılandır",
@@ -1027,6 +1052,10 @@
"settings-clip-wrap-text-label": "Panoyu metni kaydır",
"settings-clipboard-history-description": "Başlatıcıdan daha önce kopyalanan öğelere erişin.",
"settings-clipboard-history-label": "Pano geçmişini etkinleştir",
"settings-clipboard-watch-image-description": "Görüntü değişiklikleri için wl-paste'e iletilen tam komut dizesi. (yeniden başlatma gerektirir)",
"settings-clipboard-watch-image-label": "Görüntü İzleme Komutu",
"settings-clipboard-watch-text-description": "Metin değişiklikleri için wl-paste'e iletilen tam komut dizesi. (yeniden başlatma gerektirir)",
"settings-clipboard-watch-text-label": "Metin izleme komutu",
"settings-custom-launch-prefix-description": "Komutlara özel bir başlatıcı ile ön ek ekleyin (örn., systemd entegrasyonu için 'runapp').",
"settings-custom-launch-prefix-enabled-description": "Uygulamaları başlatmak için varsayılan yöntem yerine özel bir ön ek kullanın.",
"settings-custom-launch-prefix-enabled-label": "Özel başlatma ön ekini etkinleştir",
@@ -1090,6 +1119,10 @@
"weather-show-in-calendar-label": "Takvimde hava durumunu göster"
},
"lock-screen": {
"allow-password-with-fprintd-description": "fprintd (parmak izi kimlik doğrulaması) etkin olduğunda, bu seçenek parmak izi yerine parolanızı kullanarak oturum açmanızı sağlar.",
"allow-password-with-fprintd-label": "fprintd ile parola girişine izin ver",
"auto-start-auth-description": "örneğin, bir tuşa basmaya veya düğmeye tıklamaya gerek kalmadan parmak izi kimlik doğrulamasını otomatik olarak başlatır.",
"auto-start-auth-label": "Otomatik kimlik doğrulama başlatma",
"compact-lockscreen-description": "Sadece giriş girdisini ve sistem kontrollerini göster, hava durumu ve ortam araç takımlarını gizle.",
"compact-lockscreen-label": "Kompakt ekran kilidi",
"lock-on-suspend-description": "Sistemi askıya alırken otomatik olarak ekranı kilitler.",
+35 -2
View File
@@ -182,7 +182,9 @@
"hide-widget-when-zero-unread-description": "Приховувати значок сповіщень, якщо немає непрочитаних.",
"hide-widget-when-zero-unread-label": "Приховувати значок без непрочитаних",
"show-unread-badge-description": "Відображати значок з кількістю непрочитаних сповіщень.",
"show-unread-badge-label": "Показувати значок непрочитаних"
"show-unread-badge-label": "Показувати значок непрочитаних",
"unread-badge-color-description": "Виберіть колір для значка непрочитаних сповіщень.",
"unread-badge-color-label": "Колір значка непрочитаних"
},
"section-editor": {
"placeholder": "Виберіть віджет для додавання...",
@@ -254,8 +256,12 @@
"workspace": {
"character-count-description": "Кількість символів для відображення з назв робочих просторів (1-10).",
"character-count-label": "Кількість символів",
"empty-color-description": "Встановити колір фону для порожніх Workspace.",
"empty-color-label": "Колір порожнього робочого столу",
"enable-scrollwheel-description": "Перемикайтеся між робочими столами за допомогою коліщатка миші.",
"enable-scrollwheel-label": "Прокрутіть, щоб перемикати робочі простори",
"focused-color-description": "Встановити колір тла для сфокусованого Workspace.",
"focused-color-label": "Колір сфокусованого робочого столу",
"follow-focused-screen-description": "Відображати робочі простори з поточного активного екрана, а не з екрана, на якому розташована панель.",
"follow-focused-screen-label": "Слідувати за активним eкраном",
"grouped-border-opacity-description": "Встановити рівень непрозорості для меж контейнерів робочої області.",
@@ -264,8 +270,14 @@
"hide-unoccupied-label": "Приховати незайняті",
"label-mode-description": "Виберіть, як відображаються мітки робочих просторів.",
"label-mode-label": "Режим міток",
"occupied-color-description": "Встановити колір фону для зайнятих Workspace.",
"occupied-color-label": "Колір зайнятого робочого столу",
"reverse-scrolling-description": "Змінити напрямок перемикання робочих просторів під час прокручування.",
"reverse-scrolling-label": "Зворотне прокручування",
"show-applications-description": "Відображати значки програм у кожному робочому просторі.",
"show-applications-label": "Показати застосунки",
"show-badge-description": "Показувати значок номера робочого столу в згрупованому режимі.",
"show-badge-label": "Показати значок робочого столу",
"show-labels-only-when-occupied-description": "Показувати мітки робочих просторів лише тоді, коли вони містять вікна.",
"show-labels-only-when-occupied-label": "Показувати мітки лише коли поле заповнене",
"unfocused-icons-opacity-description": "Встановити рівень непрозорості для неактивних іконок застосунків.",
@@ -416,6 +428,7 @@
"not-found": "Не знайдено",
"notifications": "Сповіщення",
"official": "Офіційний",
"on-surface": "На поверхні",
"output": "Вихід",
"pair": "Спарувати",
"paired": "Спаровано",
@@ -428,6 +441,7 @@
"polling": "Опитування",
"position": "Позиція",
"previous": "Попередній",
"primary": "Основний",
"random": "Випадковий",
"reboot": "Перезапустити",
"refresh": "Оновити",
@@ -440,6 +454,7 @@
"scanning": "Сканування...",
"screen-corners": "Кути екрана",
"search": "Пошук",
"secondary": "Вторинний",
"security": "Безпека",
"select": "Вибрати",
"shortcuts": "Ярлики",
@@ -451,6 +466,7 @@
"stop": "Зупиніться",
"suspend": "Призупинити",
"templates": "Шаблони",
"tertiary": "Третинний",
"test": "Тест",
"thresholds": "Порогові значення",
"title": "Назва",
@@ -579,7 +595,10 @@
"density-compact": "Компактний",
"density-default": "Стандартний",
"density-mini": "Міні",
"density-spacious": "Просторий"
"density-spacious": "Просторий",
"type-floating": "Плаваючий",
"type-framed": "У рамці",
"type-simple": "Простий"
},
"control-center": {
"quick-settings-style-classic": "Класичний",
@@ -707,6 +726,10 @@
"appearance-desc": "Налаштуйте зовнішній вигляд та положення панелі.",
"appearance-floating-description": "Відображати панель як плаваючу 'капсулу'. Примітка: Це перемістить кути екрана до країв.",
"appearance-floating-label": "Плаваюча панель (Острівець)",
"appearance-frame-radius": "Внутрішній радіус",
"appearance-frame-settings-description": "Налаштуйте товщину рамки та внутрішній радіус кутів",
"appearance-frame-settings-label": "Налаштування рамки",
"appearance-frame-thickness": "Товщина",
"appearance-hide-on-overview-description": "Приховати панель і закрити панелі, коли активний огляд компонувальника.",
"appearance-hide-on-overview-label": "Приховати панель на огляді",
"appearance-margins-description": "Налаштуйте поля навколо плаваючої панелі.",
@@ -721,6 +744,8 @@
"appearance-show-capsule-label": "Показувати капсулу",
"appearance-show-outline-description": "Відображає видиму рамку навколо кожного віджета.",
"appearance-show-outline-label": "Показувати контури віджетів",
"appearance-type-description": "Виберіть стиль панелі: Простий, Плаваючий або У рамці",
"appearance-type-label": "Тип панелі",
"appearance-use-separate-opacity-description": "Увімкнути використання окремого значення прозорості для фону панелі.",
"appearance-use-separate-opacity-label": "Використовувати окрему прозорість стовпців",
"monitor-configure-widgets": "Налаштувати віджети",
@@ -1027,6 +1052,10 @@
"settings-clip-wrap-text-label": "Загорнути текст з буфера обміну",
"settings-clipboard-history-description": "Отримати доступ до раніше скопійованих елементів із лаунчера.",
"settings-clipboard-history-label": "Увімкнути історію буфера обміну",
"settings-clipboard-watch-image-description": "Повний рядок команди, переданий до wl-paste для зміни зображень. (потрібен перезапуск)",
"settings-clipboard-watch-image-label": "Команда Спостереження за Зображеннями",
"settings-clipboard-watch-text-description": "Повний рядок команди, переданий до wl-paste для змін тексту. (потребує перезапуску)",
"settings-clipboard-watch-text-label": "Команда спостереження за текстом",
"settings-custom-launch-prefix-description": "Додати префікс до команд запуску лаунчером (напр., 'runapp' для інтеграції з systemd).",
"settings-custom-launch-prefix-enabled-description": "Використовувати власний префікс для запуску застосунків замість стандартного методу.",
"settings-custom-launch-prefix-enabled-label": "Увімкнути користувацький префікс запуску",
@@ -1090,6 +1119,10 @@
"weather-show-in-calendar-label": "Відображати погоду в календарі"
},
"lock-screen": {
"allow-password-with-fprintd-description": "Коли fprintd (автентифікація за відбитком пальця) активний, ця опція дозволяє входити в систему за допомогою пароля замість відбитка пальця.",
"allow-password-with-fprintd-label": "Дозволити вхід за паролем за допомогою fprintd",
"auto-start-auth-description": "наприклад, автоматично запускає автентифікацію за відбитком пальця без необхідності натискання клавіші або клацання кнопки.",
"auto-start-auth-label": "Автоматичний запуск автентифікації",
"compact-lockscreen-description": "Показувати тільки поле входу та системні елементи керування, приховуючи віджети погоди та медіа.",
"compact-lockscreen-label": "Компактний екран блокування",
"lock-on-suspend-description": "Автоматично блокувати екран при призупиненні системи.",
+35 -2
View File
@@ -182,7 +182,9 @@
"hide-widget-when-zero-unread-description": "没有未读通知时隐藏通知图标。",
"hide-widget-when-zero-unread-label": "无未读时隐藏图标",
"show-unread-badge-description": "显示一个用于展示未读通知数量的徽章。",
"show-unread-badge-label": "显示未读徽章"
"show-unread-badge-label": "显示未读徽章",
"unread-badge-color-description": "选择未读通知徽章的颜色。",
"unread-badge-color-label": "未读徽章颜色"
},
"section-editor": {
"placeholder": "选择要添加的小部件...",
@@ -254,8 +256,12 @@
"workspace": {
"character-count-description": "显示工作区名称的字符数量(1-10)。",
"character-count-label": "字符数量",
"empty-color-description": "设置空工作区的背景颜色。",
"empty-color-label": "空白工作区颜色",
"enable-scrollwheel-description": "使用鼠标滚轮切换工作区。",
"enable-scrollwheel-label": "滚动切换工作区",
"focused-color-description": "设置焦点 Workspace 的背景颜色。",
"focused-color-label": "聚焦工作区颜色",
"follow-focused-screen-description": "显示当前焦点屏幕的工作区,而不是任务栏所在屏幕的工作区。",
"follow-focused-screen-label": "跟随焦点屏幕",
"grouped-border-opacity-description": "设置工作区容器边框的不透明度级别。",
@@ -264,8 +270,14 @@
"hide-unoccupied-label": "隐藏未占用",
"label-mode-description": "选择工作区标签的显示方式。",
"label-mode-label": "标签模式",
"occupied-color-description": "设置已占用Workspace的背景颜色。",
"occupied-color-label": "已占用工作区颜色",
"reverse-scrolling-description": "滚动时反转工作区切换方向。",
"reverse-scrolling-label": "反转滚动",
"show-applications-description": "在每个工作区内显示应用程序图标。",
"show-applications-label": "显示应用程序",
"show-badge-description": "在分组模式下显示工作区编号徽章。",
"show-badge-label": "显示工作区徽章",
"show-labels-only-when-occupied-description": "仅在工作区包含窗口时显示工作区标签。",
"show-labels-only-when-occupied-label": "仅在被占用时显示标签",
"unfocused-icons-opacity-description": "设置未聚焦应用图标的不透明度级别。",
@@ -416,6 +428,7 @@
"not-found": "未找到",
"notifications": "通知",
"official": "官方",
"on-surface": "在表面上",
"output": "输出",
"pair": "配对",
"paired": "已配对",
@@ -428,6 +441,7 @@
"polling": "数据轮询",
"position": "位置",
"previous": "上一个",
"primary": "主要的",
"random": "随机",
"reboot": "重启",
"refresh": "刷新",
@@ -440,6 +454,7 @@
"scanning": "扫描中...",
"screen-corners": "屏幕边角",
"search": "搜索",
"secondary": "辅助",
"security": "安全",
"select": "选择",
"shortcuts": "快捷方式",
@@ -451,6 +466,7 @@
"stop": "停止",
"suspend": "挂起",
"templates": "模板",
"tertiary": "第三",
"test": "测试",
"thresholds": "阈值",
"title": "标题",
@@ -579,7 +595,10 @@
"density-compact": "紧凑",
"density-default": "默认",
"density-mini": "迷你",
"density-spacious": "宽敞"
"density-spacious": "宽敞",
"type-floating": "悬浮",
"type-framed": "边框",
"type-simple": "简约"
},
"control-center": {
"quick-settings-style-classic": "经典",
@@ -707,6 +726,10 @@
"appearance-desc": "自定义状态栏的外观和位置。",
"appearance-floating-description": "将状态栏显示为浮动的“药丸”形状。",
"appearance-floating-label": "浮动状态栏",
"appearance-frame-radius": "内圆角",
"appearance-frame-settings-description": "调整边框粗细和内圆角半径",
"appearance-frame-settings-label": "边框设置",
"appearance-frame-thickness": "粗细",
"appearance-hide-on-overview-description": "当合成器概览处于活动状态时,隐藏栏并关闭面板。",
"appearance-hide-on-overview-label": "在概览中隐藏栏",
"appearance-margins-description": "调整浮动状态栏周围的边距。",
@@ -721,6 +744,8 @@
"appearance-show-capsule-label": "显示组件背景",
"appearance-show-outline-description": "在每个小部件周围显示可见边框。",
"appearance-show-outline-label": "显示小部件轮廓",
"appearance-type-description": "选择栏的样式:简约、悬浮或边框",
"appearance-type-label": "栏类型",
"appearance-use-separate-opacity-description": "启用后为状态栏背景使用单独的不透明度值。",
"appearance-use-separate-opacity-label": "使用单独的状态栏不透明度",
"monitor-configure-widgets": "配置小部件",
@@ -1027,6 +1052,10 @@
"settings-clip-wrap-text-label": "环绕剪贴板文本",
"settings-clipboard-history-description": "从启动器访问之前复制的项目。",
"settings-clipboard-history-label": "启用剪贴板历史记录",
"settings-clipboard-watch-image-description": "传递给 wl-paste 以进行图像更改的完整命令字符串。(需要重启)",
"settings-clipboard-watch-image-label": "图像监视命令",
"settings-clipboard-watch-text-description": "传递给 wl-paste 以进行文本更改的完整命令字符串。(需要重启)",
"settings-clipboard-watch-text-label": "文本监视命令",
"settings-custom-launch-prefix-description": "使用自定义启动器前缀命令(例如,'runapp'用于systemd集成)。",
"settings-custom-launch-prefix-enabled-description": "使用自定义前缀启动应用程序,而不是默认方法。",
"settings-custom-launch-prefix-enabled-label": "启用自定义启动前缀",
@@ -1090,6 +1119,10 @@
"weather-show-in-calendar-label": "在日历中显示天气"
},
"lock-screen": {
"allow-password-with-fprintd-description": "当 fprintd(指纹认证)处于活动状态时,此选项允许您使用密码而非指纹登录。",
"allow-password-with-fprintd-label": "允许使用 fprintd 进行密码登录",
"auto-start-auth-description": "例如,自动启动指纹验证,无需按键或单击按钮。",
"auto-start-auth-label": "自动启动身份验证",
"compact-lockscreen-description": "仅显示登录输入和系统控制,隐藏天气和媒体小部件。",
"compact-lockscreen-label": "紧凑型锁屏",
"lock-on-suspend-description": "系统挂起时自动锁定屏幕。",
+35 -2
View File
@@ -182,7 +182,9 @@
"hide-widget-when-zero-unread-description": "沒有未讀通知時隱藏通知圖示。",
"hide-widget-when-zero-unread-label": "沒有未讀通知時隱藏圖示",
"show-unread-badge-description": "顯示帶有數量的未讀標記",
"show-unread-badge-label": "顯示未讀標記"
"show-unread-badge-label": "顯示未讀標記",
"unread-badge-color-description": "選擇未讀通知徽章的顏色。",
"unread-badge-color-label": "未讀徽章顏色"
},
"section-editor": {
"placeholder": "選取一個小工具來新增...",
@@ -254,8 +256,12 @@
"workspace": {
"character-count-description": "限制工作區所設定的名字要顯示幾個字元 (1-10)",
"character-count-label": "字元上限",
"empty-color-description": "設定空白 Workspace 的背景顏色。",
"empty-color-label": "空白工作區顏色",
"enable-scrollwheel-description": "利用滑鼠滾輪在工作區間切換",
"enable-scrollwheel-label": "捲輪切換工作區",
"focused-color-description": "設定焦點 Workspace 的背景顏色。",
"focused-color-label": "聚焦的工作區顏色",
"follow-focused-screen-description": "顯示正在聚焦的螢幕所擁有的工作區, 而不是工具列所在的螢幕擁有的工作區",
"follow-focused-screen-label": "跟隨焦點所在的螢幕",
"grouped-border-opacity-description": "設定工作區邊框的不透明度",
@@ -264,8 +270,14 @@
"hide-unoccupied-label": "沒有佔用時隱藏",
"label-mode-description": "選擇工作區標籤該如何顯示",
"label-mode-label": "標籤樣式",
"occupied-color-description": "設定已佔用Workspace的背景顏色。",
"occupied-color-label": "已佔用工作區顏色",
"reverse-scrolling-description": "捲動時反轉工作區切換方向。",
"reverse-scrolling-label": "反轉捲動",
"show-applications-description": "顯示各個工作區的程式圖示",
"show-applications-label": "顯示應用程式",
"show-badge-description": "在群組模式下顯示工作區編號徽章。",
"show-badge-label": "顯示工作區標記",
"show-labels-only-when-occupied-description": "只在工作區有視窗時顯示工作區標籤",
"show-labels-only-when-occupied-label": "佔用時顯示標籤",
"unfocused-icons-opacity-description": "設定非焦點應用程式圖示的不透明度。",
@@ -416,6 +428,7 @@
"not-found": "找不到",
"notifications": "通知",
"official": "官方",
"on-surface": "在表面上",
"output": "輸出",
"pair": "配對",
"paired": "已配對",
@@ -428,6 +441,7 @@
"polling": "輪詢",
"position": "位置",
"previous": "上一首",
"primary": "主要的",
"random": "隨機",
"reboot": "重新啟動",
"refresh": "重新整理",
@@ -440,6 +454,7 @@
"scanning": "掃描中...",
"screen-corners": "畫面邊角",
"search": "搜尋",
"secondary": "輔助",
"security": "安全",
"select": "選取",
"shortcuts": "快捷鍵",
@@ -451,6 +466,7 @@
"stop": "停止",
"suspend": "暫停",
"templates": "模板",
"tertiary": "第三",
"test": "測試",
"thresholds": "門檻",
"title": "標題",
@@ -579,7 +595,10 @@
"density-compact": "緊湊",
"density-default": "預設",
"density-mini": "迷你",
"density-spacious": "寬敞"
"density-spacious": "寬敞",
"type-floating": "懸浮",
"type-framed": "邊框",
"type-simple": "簡約"
},
"control-center": {
"quick-settings-style-classic": "古典",
@@ -707,6 +726,10 @@
"appearance-desc": "自訂工具列的外觀及位置",
"appearance-floating-description": "將工具列以懸浮的圓弧長條狀顯示",
"appearance-floating-label": "懸浮工具列",
"appearance-frame-radius": "內圓角",
"appearance-frame-settings-description": "調整邊框粗細和內圓角半徑",
"appearance-frame-settings-label": "邊框設定",
"appearance-frame-thickness": "粗細",
"appearance-hide-on-overview-description": "當在合成器概覽時, 隱藏工具列並關閉面板",
"appearance-hide-on-overview-label": "概覽時隱藏工具列",
"appearance-margins-description": "調整懸浮工具列的邊距",
@@ -721,6 +744,8 @@
"appearance-show-capsule-label": "顯示小容器",
"appearance-show-outline-description": "讓每個小工具都顯示可視邊框",
"appearance-show-outline-label": "顯示小工具外框",
"appearance-type-description": "選擇欄的樣式:簡約、懸浮或邊框",
"appearance-type-label": "欄類型",
"appearance-use-separate-opacity-description": "啟用後可針對工具列背景使用分開的不透明度數值",
"appearance-use-separate-opacity-label": "分開設定工具列不透明度",
"monitor-configure-widgets": "設定小工具",
@@ -1027,6 +1052,10 @@
"settings-clip-wrap-text-label": "剪貼簿文字換行",
"settings-clipboard-history-description": "在啟動器存取先前所複製的項目",
"settings-clipboard-history-label": "啟用剪貼簿歷史",
"settings-clipboard-watch-image-description": "傳遞給 wl-paste 以進行圖像更改的完整命令字串。(需要重新啟動)",
"settings-clipboard-watch-image-label": "圖像監控命令",
"settings-clipboard-watch-text-description": "傳遞給 wl-paste 以進行文字變更的完整命令字串。(需要重新啟動)",
"settings-clipboard-watch-text-label": "文字監看指令",
"settings-custom-launch-prefix-description": "在命令前加上自訂啟動器前綴(例如:用「runapp」來整合 systemd)。",
"settings-custom-launch-prefix-enabled-description": "使用自訂前綴而不是預設的方式來啟動應用程式",
"settings-custom-launch-prefix-enabled-label": "啟用自訂的程式啟動前綴",
@@ -1090,6 +1119,10 @@
"weather-show-in-calendar-label": "在行事曆中顯示天氣"
},
"lock-screen": {
"allow-password-with-fprintd-description": "當 fprintd(指紋認證)處於活動狀態時,此選項允許您使用密碼而非指紋登入。",
"allow-password-with-fprintd-label": "允許使用 fprintd 進行密碼登入",
"auto-start-auth-description": "例如,自動啟動指紋驗證,無需按鍵或點擊按鈕。",
"auto-start-auth-label": "自動啟動驗證",
"compact-lockscreen-description": "只顯示登入欄位及電源控制, 隱藏天氣及媒體小工具",
"compact-lockscreen-label": "精簡鎖定畫面",
"lock-on-suspend-description": "在暫停系統時自動鎖定畫面",
+9 -2
View File
@@ -1,6 +1,7 @@
{
"settingsVersion": 0,
"bar": {
"barType": "simple",
"position": "top",
"monitors": [],
"density": "default",
@@ -12,6 +13,8 @@
"floating": false,
"marginVertical": 4,
"marginHorizontal": 4,
"frameThickness": 8,
"frameRadius": 12,
"outerCorners": true,
"exclusive": true,
"hideOnOverview": false,
@@ -86,7 +89,9 @@
"showChangelogOnStartup": true,
"telemetryEnabled": false,
"enableLockScreenCountdown": true,
"lockScreenCountdownDuration": 10000
"lockScreenCountdownDuration": 10000,
"autoStartAuth": false,
"allowPasswordWithFprintd": false
},
"ui": {
"fontDefault": "",
@@ -171,11 +176,13 @@
"autoPasteClipboard": false,
"enableClipPreview": true,
"clipboardWrapText": true,
"clipboardWatchTextCommand": "wl-paste --type text --watch cliphist store",
"clipboardWatchImageCommand": "wl-paste --type image --watch cliphist store",
"position": "center",
"pinnedApps": [],
"useApp2Unit": false,
"sortByMostUsed": true,
"terminalCommand": "xterm -e",
"terminalCommand": "alacritty -e",
"customLaunchPrefixEnabled": false,
"customLaunchPrefix": "",
"viewMode": "list",
+64 -3
View File
@@ -189,9 +189,9 @@
"subTabLabel": "common.appearance"
},
{
"labelKey": "panels.bar.appearance-floating-label",
"descriptionKey": "panels.bar.appearance-floating-description",
"widget": "NToggle",
"labelKey": "panels.bar.appearance-type-label",
"descriptionKey": "panels.bar.appearance-type-description",
"widget": "NComboBox",
"tab": 4,
"tabLabel": "panels.bar.title",
"subTab": 0,
@@ -206,6 +206,33 @@
"subTab": 0,
"subTabLabel": "common.appearance"
},
{
"labelKey": "panels.bar.appearance-frame-settings-label",
"descriptionKey": "panels.bar.appearance-frame-settings-description",
"widget": "NLabel",
"tab": 4,
"tabLabel": "panels.bar.title",
"subTab": 0,
"subTabLabel": "common.appearance"
},
{
"labelKey": "panels.bar.appearance-frame-thickness",
"descriptionKey": null,
"widget": "NValueSlider",
"tab": 4,
"tabLabel": "panels.bar.title",
"subTab": 0,
"subTabLabel": "common.appearance"
},
{
"labelKey": "panels.bar.appearance-frame-radius",
"descriptionKey": null,
"widget": "NValueSlider",
"tab": 4,
"tabLabel": "panels.bar.title",
"subTab": 0,
"subTabLabel": "common.appearance"
},
{
"labelKey": "panels.bar.appearance-margins-label",
"descriptionKey": "panels.bar.appearance-margins-description",
@@ -686,6 +713,24 @@
"subTab": 1,
"subTabLabel": "common.clipboard"
},
{
"labelKey": "panels.launcher.settings-clipboard-watch-text-label",
"descriptionKey": "panels.launcher.settings-clipboard-watch-text-description",
"widget": "NTextInput",
"tab": 8,
"tabLabel": "panels.launcher.title",
"subTab": 1,
"subTabLabel": "common.clipboard"
},
{
"labelKey": "panels.launcher.settings-clipboard-watch-image-label",
"descriptionKey": "panels.launcher.settings-clipboard-watch-image-description",
"widget": "NTextInput",
"tab": 8,
"tabLabel": "panels.launcher.title",
"subTab": 1,
"subTabLabel": "common.clipboard"
},
{
"labelKey": "panels.launcher.settings-use-app2unit-label",
"descriptionKey": "panels.launcher.settings-use-app2unit-description",
@@ -810,6 +855,22 @@
"tabLabel": "panels.lock-screen.title",
"subTab": null
},
{
"labelKey": "panels.lock-screen.auto-start-auth-label",
"descriptionKey": "panels.lock-screen.auto-start-auth-description",
"widget": "NToggle",
"tab": 11,
"tabLabel": "panels.lock-screen.title",
"subTab": null
},
{
"labelKey": "panels.lock-screen.allow-password-with-fprintd-label",
"descriptionKey": "panels.lock-screen.allow-password-with-fprintd-description",
"widget": "NToggle",
"tab": 11,
"tabLabel": "panels.lock-screen.title",
"subTab": null
},
{
"labelKey": "panels.lock-screen.show-session-buttons-label",
"descriptionKey": "panels.lock-screen.show-session-buttons-description",
+5 -3
View File
@@ -78,13 +78,14 @@ Singleton {
"star": "star",
"star-off": "star-off",
"battery-exclamation": "battery-exclamation",
"common.charging": "common.charging",
"battery-charging": "battery-charging",
"battery-charging-2": "battery-charging-2",
"battery-4": "battery-4",
"battery-3": "battery-3",
"battery-2": "battery-2",
"battery-1": "battery-1",
"battery": "battery",
"battery-off": "battery-off",
"wifi-0": "wifi-0",
"wifi-1": "wifi-1",
"wifi-2": "wifi-2",
@@ -177,7 +178,8 @@ Singleton {
"filepicker-eye-off": "eye-off",
"filepicker-folder-current": "checks",
"plugin": "plug-connected",
"info": "file-description"
"info": "file-description",
"official-plugin": "shield-filled"
}
// Fonts Codepoints - do not change!
@@ -747,7 +749,7 @@ Singleton {
"battery-4-filled": "\u{f721}",
"battery-automotive": "\u{ee07}",
"battery-automotive-filled": "\u{10029}",
"common.charging": "\u{ea33}",
"battery-charging": "\u{ea33}",
"battery-charging-2": "\u{ef3b}",
"battery-eco": "\u{ef3c}",
"battery-exclamation": "\u{ff1d}",
+18
View File
@@ -0,0 +1,18 @@
import QtQuick
QtObject {
/**
* Migration 45: Migrate 'floating' bar setting to 'barType'
*/
function migrate(adapter, logger, rawJson) {
logger.i("Migration45", "Migrating bar settings...");
if (adapter.bar.floating) {
adapter.bar.barType = "floating";
} else {
adapter.bar.barType = "simple";
}
return true;
}
}
+21
View File
@@ -0,0 +1,21 @@
import QtQuick
import Quickshell
QtObject {
id: root
function migrate(adapter, logger, rawJson) {
logger.i("Migration46", "Removing legacy PAM configuration file");
const shellName = "noctalia";
const configDir = Quickshell.env("NOCTALIA_CONFIG_DIR") || (Quickshell.env("XDG_CONFIG_HOME") || Quickshell.env("HOME") + "/.config") + "/" + shellName + "/";
const pamConfigDir = configDir + "pam";
// Remove the entire pam directory if it exists
const script = `rm -rf '${pamConfigDir}'`;
Quickshell.execDetached(["sh", "-c", script]);
logger.d("Migration46", "Cleaned up legacy PAM config");
return true;
}
}
+5 -1
View File
@@ -18,7 +18,9 @@ QtObject {
40: migration40Component,
42: migration42Component,
43: migration43Component,
44: migration44Component
44: migration44Component,
45: migration45Component,
46: migration46Component
})
// Migration components
@@ -34,4 +36,6 @@ QtObject {
property Component migration42Component: Migration42 {}
property Component migration43Component: Migration43 {}
property Component migration44Component: Migration44 {}
property Component migration45Component: Migration45 {}
property Component migration46Component: Migration46 {}
}
+11 -57
View File
@@ -25,7 +25,7 @@ Singleton {
- Default cache directory: ~/.cache/noctalia
*/
readonly property alias data: adapter // Used to access via Settings.data.xxx.yyy
readonly property int settingsVersion: 44
readonly property int settingsVersion: 46
readonly property bool isDebug: Quickshell.env("NOCTALIA_DEBUG") === "1"
readonly property string shellName: "noctalia"
readonly property string configDir: Quickshell.env("NOCTALIA_CONFIG_DIR") || (Quickshell.env("XDG_CONFIG_HOME") || Quickshell.env("HOME") + "/.config") + "/" + shellName + "/"
@@ -48,11 +48,6 @@ Singleton {
Quickshell.execDetached(["mkdir", "-p", configDir]);
Quickshell.execDetached(["mkdir", "-p", cacheDir]);
// Ensure PAM config file exists in configDir (create once, never override)
if (!Quickshell.env("NOCTALIA_PAM_CONFIG")) {
ensurePamConfig();
}
// Mark directories as created and trigger file loading
directoriesCreated = true;
@@ -175,6 +170,7 @@ Singleton {
// bar
property JsonObject bar: JsonObject {
property string barType: "simple" // "simple", "floating", "framed"
property string position: "top" // "top", "bottom", "left", or "right"
property list<string> monitors: [] // holds bar visibility per monitor
property string density: "default" // "compact", "default", "comfortable"
@@ -191,6 +187,10 @@ Singleton {
property int marginVertical: 4
property int marginHorizontal: 4
// Framed bar settings
property int frameThickness: 8
property int frameRadius: 12
// Bar outer corners (inverted/concave corners at bar edges when not floating)
property bool outerCorners: true
@@ -279,6 +279,8 @@ Singleton {
property bool telemetryEnabled: false
property bool enableLockScreenCountdown: true
property int lockScreenCountdownDuration: 10000
property bool autoStartAuth: false
property bool allowPasswordWithFprintd: false
}
// ui
@@ -377,11 +379,13 @@ Singleton {
property bool autoPasteClipboard: false
property bool enableClipPreview: true
property bool clipboardWrapText: true
property string clipboardWatchTextCommand: "wl-paste --type text --watch cliphist store"
property string clipboardWatchImageCommand: "wl-paste --type image --watch cliphist store"
property string position: "center" // Position: center, top_left, top_right, bottom_left, bottom_right, bottom_center, top_center
property list<string> pinnedApps: []
property bool useApp2Unit: false
property bool sortByMostUsed: true
property string terminalCommand: "xterm -e"
property string terminalCommand: "alacritty -e"
property bool customLaunchPrefixEnabled: false
property string customLaunchPrefix: ""
// View mode: "list" or "grid"
@@ -1096,56 +1100,6 @@ Singleton {
}
}
// -----------------------------------------------------
// Ensure PAM password.conf exists in configDir (create once, never override)
function ensurePamConfig() {
var pamConfigDir = configDir + "pam";
var pamConfigFile = pamConfigDir + "/password.conf";
// Check if file already exists
fileCheckPamProcess.command = ["test", "-f", pamConfigFile];
fileCheckPamProcess.running = true;
}
function doCreatePamConfig() {
var pamConfigDir = configDir + "pam";
var pamConfigFile = pamConfigDir + "/password.conf";
var pamConfigDirEsc = pamConfigDir.replace(/'/g, "'\\''");
var pamConfigFileEsc = pamConfigFile.replace(/'/g, "'\\''");
// Ensure directory exists
Quickshell.execDetached(["mkdir", "-p", pamConfigDir]);
// Generate the PAM config file content
var configContent = "auth sufficient pam_fprintd.so timeout=-1\n";
configContent += "auth sufficient /run/current-system/sw/lib/security/pam_fprintd.so timeout=-1 # for NixOS\n";
configContent += "auth required pam_unix.so\n";
// Write the config file using heredoc to avoid escaping issues
var script = `cat > '${pamConfigFileEsc}' << 'EOF'\n`;
script += configContent;
script += "EOF\n";
Quickshell.execDetached(["sh", "-c", script]);
Logger.d("Settings", "PAM config file created at:", pamConfigFile);
}
// Process for checking if PAM config file exists
Process {
id: fileCheckPamProcess
running: false
onExited: function (exitCode) {
if (exitCode === 0) {
// File exists, skip creation
Logger.d("Settings", "PAM config file already exists, skipping creation");
} else {
// File doesn't exist, create it
doCreatePamConfig();
}
}
}
// -----------------------------------------------------
// Function to clean up deprecated user/custom bar widgets settings
function upgradeWidget(widget) {
+66 -2
View File
@@ -27,6 +27,34 @@ Item {
});
}
// Hot corner: trigger click on first widget in a section
function triggerFirstWidgetInSection(sectionName: string) {
var widgets = BarService.getWidgetsBySection(sectionName, screen?.name);
for (var i = 0; i < widgets.length; i++) {
var widget = widgets[i];
if (widget && widget.visible && widget.widgetId !== "Spacer") {
if (typeof widget.clicked === "function") {
widget.clicked();
}
return;
}
}
}
// Hot corner: trigger click on last widget in a section
function triggerLastWidgetInSection(sectionName: string) {
var widgets = BarService.getWidgetsBySection(sectionName, screen?.name);
for (var i = widgets.length - 1; i >= 0; i--) {
var widget = widgets[i];
if (widget && widget.visible && widget.widgetId !== "Spacer") {
if (typeof widget.clicked === "function") {
widget.clicked();
}
return;
}
}
}
// Expose bar region for click-through mask
readonly property var barRegion: barContentLoader.item?.children[0] || null
@@ -220,11 +248,29 @@ Item {
anchors.fill: parent
clip: true
// Top edge hot corner - triggers first widget in left (top) section
MouseArea {
width: parent.width
height: Style.marginS
x: 0
y: 0
onClicked: root.triggerFirstWidgetInSection("left")
}
// Bottom edge hot corner - triggers last widget in right (bottom) section
MouseArea {
width: parent.width
height: Style.marginS
x: 0
anchors.bottom: parent.bottom
onClicked: root.triggerLastWidgetInSection("right")
}
// Top section (left widgets)
ColumnLayout {
x: Style.pixelAlignCenter(parent.width, width)
anchors.top: parent.top
anchors.topMargin: Style.marginM
anchors.topMargin: Style.marginS
spacing: Style.marginS
Repeater {
@@ -275,7 +321,7 @@ Item {
ColumnLayout {
x: Style.pixelAlignCenter(parent.width, width)
anchors.bottom: parent.bottom
anchors.bottomMargin: Style.marginM
anchors.bottomMargin: Style.marginS
spacing: Style.marginS
Repeater {
@@ -306,6 +352,24 @@ Item {
anchors.fill: parent
clip: true
// Left edge hot corner - triggers first widget in left section
MouseArea {
width: Style.marginS
height: parent.height
x: 0
y: 0
onClicked: root.triggerFirstWidgetInSection("left")
}
// Right edge hot corner - triggers last widget in right section
MouseArea {
width: Style.marginS
height: parent.height
anchors.right: parent.right
y: 0
onClicked: root.triggerLastWidgetInSection("right")
}
// Left Section
RowLayout {
id: leftSection
+9 -4
View File
@@ -34,13 +34,18 @@ Item {
signal middleClicked
signal wheel(int delta)
// Dynamic sizing based on loaded component
width: pillLoader.item ? pillLoader.item.width : 0
height: pillLoader.item ? pillLoader.item.height : 0
// Size based on content for the content dimension, fill parent for the extended dimension
// Horizontal bars: width = content, height = fill parent (for extended click area)
// Vertical bars: width = fill parent, height = content
width: isVerticalBar ? parent.width : (pillLoader.item ? pillLoader.item.implicitWidth : 0)
height: isVerticalBar ? (pillLoader.item ? pillLoader.item.implicitHeight : 0) : parent.height
implicitWidth: pillLoader.item ? pillLoader.item.implicitWidth : 0
implicitHeight: pillLoader.item ? pillLoader.item.implicitHeight : 0
// Loader to switch between vertical and horizontal pill implementations
// Loader fills BarPill so child components can extend to full bar dimension
Loader {
id: pillLoader
anchors.fill: parent
sourceComponent: isVerticalBar ? verticalPillComponent : horizontalPillComponent
Component {
+8 -2
View File
@@ -53,7 +53,8 @@ Item {
readonly property real iconSize: Style.toOdd(pillHeight * 0.48)
width: {
// Content width calculation (for implicit sizing)
readonly property real contentWidth: {
if (collapseToIcon) {
return hasIcon ? pillHeight : 0;
}
@@ -61,7 +62,12 @@ Item {
var baseWidth = hasIcon ? pillHeight : 0;
return baseWidth + Math.max(0, pill.width - overlap);
}
height: pillHeight
// Fill parent to extend click area to full bar height
// Visual content is centered vertically within
anchors.fill: parent
implicitWidth: contentWidth
implicitHeight: pillHeight
Connections {
target: root
+13 -5
View File
@@ -60,9 +60,8 @@ Item {
readonly property real iconSize: Style.toOdd(pillHeight * 0.48)
// For vertical bars: width is just icon size, height includes pill space
width: buttonSize
height: {
// Content height calculation (for implicit sizing and visual layout)
readonly property real contentHeight: {
if (collapseToIcon) {
return hasIcon ? buttonSize : 0;
}
@@ -75,6 +74,15 @@ Item {
return buttonSize;
}
// Fill parent width to extend horizontal click area
// Keep content-based height for visual layout
anchors.left: parent ? parent.left : undefined
anchors.right: parent ? parent.right : undefined
anchors.verticalCenter: parent ? parent.verticalCenter : undefined
height: contentHeight
implicitWidth: buttonSize
implicitHeight: contentHeight
Connections {
target: root
function onTooltipTextChanged() {
@@ -88,7 +96,7 @@ Item {
Rectangle {
id: pillBackground
width: buttonSize
height: root.height
height: root.contentHeight
radius: Style.radiusM
color: root.bgColor
border.color: Style.capsuleBorderColor
@@ -183,7 +191,7 @@ Item {
// Icon positioning based on direction
x: 0
y: openUpward ? (parent.height - height) : 0
y: openUpward ? (root.contentHeight - height) : 0
anchors.horizontalCenter: parent.horizontalCenter
NIcon {
+23 -3
View File
@@ -15,9 +15,16 @@ Item {
readonly property string section: widgetProps ? (widgetProps.section || "") : ""
readonly property int sectionIndex: widgetProps ? (widgetProps.sectionWidgetIndex || 0) : 0
// Don't reserve space unless the loaded widget is really visible
implicitWidth: getImplicitSize(loader.item, "implicitWidth")
implicitHeight: getImplicitSize(loader.item, "implicitHeight")
// Bar orientation and height for extended click areas
readonly property string barPosition: Settings.getBarPositionForScreen(widgetScreen?.name)
readonly property bool isVerticalBar: barPosition === "left" || barPosition === "right"
readonly property real barHeight: Style.getBarHeightForScreen(widgetScreen?.name)
// Request full bar dimension from layout to extend click areas above/below widgets
// For horizontal bars: full bar height, widget's content width
// For vertical bars: full bar width, widget's content height
implicitWidth: isVerticalBar ? barHeight : getImplicitSize(loader.item, "implicitWidth")
implicitHeight: isVerticalBar ? getImplicitSize(loader.item, "implicitHeight") : barHeight
// Remove layout space left by hidden widgets
visible: loader.item ? ((loader.item.opacity > 0.0) || (loader.item.hasOwnProperty("hideMode") && loader.item.hideMode === "transparent")) : false
@@ -66,6 +73,19 @@ Item {
Logger.d("BarWidgetLoader", "Loading widget", widgetId, "on screen:", widgetScreen.name);
// Extend widget to fill full bar dimension for extended click areas
// For horizontal bars: widget fills bar height (content width preserved)
// For vertical bars: widget fills bar width (content height preserved)
if (root.isVerticalBar) {
item.width = Qt.binding(function () {
return root.barHeight;
});
} else {
item.height = Qt.binding(function () {
return root.barHeight;
});
}
// Apply properties to loaded widget
for (var prop in widgetProps) {
if (item.hasOwnProperty(prop)) {
+61 -43
View File
@@ -47,6 +47,16 @@ PopupWindow {
// Use the content height of the Flickable for implicit height
implicitHeight: Math.min(screen?.height * 0.9, flickable.contentHeight + (Style.marginS * 2))
// When implicitHeight changes (menu content loads), force anchor recalculation
onImplicitHeightChanged: {
if (visible && anchorItem) {
Qt.callLater(() => {
anchor.updateAnchor();
});
}
}
visible: false
color: "transparent"
anchor.item: anchorItem
@@ -63,21 +73,25 @@ PopupWindow {
menuScreenX = windowXOnScreen + posInPopup.x + baseX;
} else {
const anchorGlobalPos = anchorItem.mapToItem(null, 0, 0);
menuScreenX = anchorGlobalPos.x + baseX;
const anchorScreenX = anchorGlobalPos.x;
menuScreenX = anchorScreenX + baseX;
}
const menuRight = menuScreenX + implicitWidth;
const screenRight = screen.width;
const menuLeft = menuScreenX;
// Adjust if menu would clip on the right
if (menuRight > screenRight) {
// Only adjust if menu would clip off screen boundaries
// Don't adjust if the positioning is intentional (e.g., negative offset for right bar)
if (menuRight > screenRight && menuLeft < screenRight) {
// Clipping on right edge - shift left
const overflow = menuRight - screenRight;
return baseX - overflow - Style.marginM;
} else if (menuLeft < 0 && menuRight > 0) {
// Clipping on left edge - shift right
return baseX - menuLeft + Style.marginM;
}
// Adjust if menu would clip on the left
if (menuScreenX < 0) {
return baseX - menuScreenX + Style.marginM;
}
return baseX;
}
return anchorX;
@@ -86,52 +100,56 @@ PopupWindow {
if (anchorItem && screen) {
const barPosition = Settings.getBarPositionForScreen(root.screen?.name);
// Calculate base Y offset (relative to anchor item)
let baseY = anchorY;
if (!isSubMenu && barPosition === "bottom") {
// For bottom bar, position menu above the anchor with margin (adjusted to match widget menu positioning)
// Only apply bottom bar special positioning if:
// 1. Not a submenu
// 2. Bar is at bottom
// 3. anchorY is not already negative (if negative, it's pre-calculated from drawer)
const shouldApplyBottomBarLogic = !isSubMenu && barPosition === "bottom" && anchorY >= 0;
if (shouldApplyBottomBarLogic) {
// For bottom bar from the bar itself, position menu above the anchor with margin
baseY = -(implicitHeight + Style.marginL + 2);
}
// Calculate position relative to current screen (not global coordinates)
let menuScreenY;
if (isSubMenu && anchorItem.Window && anchorItem.Window.window) {
// Submenu: anchor is inside parent PopupWindow
const posInPopup = anchorItem.mapToItem(null, 0, 0);
const parentWindow = anchorItem.Window.window;
// Convert global window Y to screen-relative Y by subtracting screen offset
const windowYOnScreen = parentWindow.y - screen.y;
menuScreenY = windowYOnScreen + posInPopup.y + baseY;
} else if (!isSubMenu && barPosition === "bottom") {
// Bottom bar main menu: subtract baseY to position above anchor
const anchorGlobalPos = anchorItem.mapToItem(null, 0, 0);
menuScreenY = anchorGlobalPos.y - baseY;
} else {
// Main menu for other positions: add baseY
const anchorGlobalPos = anchorItem.mapToItem(null, 0, 0);
menuScreenY = anchorGlobalPos.y + baseY;
// Use a robust way to get screen coordinates
const posInWindow = anchorItem.mapToItem(null, 0, 0);
const parentWindow = anchorItem.Window.window;
// Calculate screen-relative Y of the window
let windowYOnScreen = (parentWindow && screen) ? (parentWindow.y - screen.y) : 0;
// If window reported 0 but bar is at bottom, assume it's at screen bottom
if (windowYOnScreen === 0 && barPosition === "bottom" && screen) {
windowYOnScreen = screen.height - (parentWindow ? parentWindow.height : Style.getBarHeightForScreen(screen.name));
}
const menuBottom = menuScreenY + implicitHeight;
const screenBottom = screen.height;
// Calculate the screen Y of the menu top
// Use a small guess for height if implicitHeight is 0 to avoid covering the bar on the first frame
const effectiveHeight = implicitHeight > 0 ? implicitHeight : 200;
const effectiveBaseY = shouldApplyBottomBarLogic ? -(effectiveHeight + Style.marginL + 2) : baseY;
// Adjust baseY if menu would clip
if (menuBottom > screenBottom) {
// Clip at bottom - shift up by the overflow amount
const overflow = menuBottom - screenBottom;
if (!isSubMenu && barPosition === "bottom") {
return baseY + overflow + Style.marginM;
}
return baseY - overflow - Style.marginM;
const menuScreenY = windowYOnScreen + posInWindow.y + effectiveBaseY;
const menuBottom = menuScreenY + (implicitHeight > 0 ? implicitHeight : effectiveHeight);
const screenHeight = screen ? screen.height : 1080;
// Adjust the final baseY (the actual value returned to anchor.rect.y)
let finalBaseY = shouldApplyBottomBarLogic ? -(implicitHeight + Style.marginL + 2) : baseY;
// Adjust if menu would clip off the bottom
if (menuBottom > screenHeight) {
const overflow = menuBottom - screenHeight;
finalBaseY -= (overflow + Style.marginM);
}
// Adjust if menu would clip off the top
// menuScreenY < 0 means it's above the screen edge
if (menuScreenY < 0) {
// Clip at top - shift down
if (!isSubMenu && barPosition === "bottom") {
return baseY + menuScreenY - Style.marginM;
}
return baseY - menuScreenY + Style.marginM;
finalBaseY -= (menuScreenY - Style.marginM);
}
return baseY;
return finalBaseY;
}
// Fallback if no anchor/screen
+243
View File
@@ -0,0 +1,243 @@
import QtQuick
import qs.Commons
import qs.Services.Compositor
import qs.Widgets
Item {
id: pillContainer
required property var workspace
required property bool isVertical
// These must be provided by the parent Workspace widget
required property real baseDimensionRatio
required property real capsuleHeight
required property real barHeight
required property string labelMode
required property int characterCount
required property real textRatio
required property bool showLabelsOnlyWhenOccupied
required property var colorMap
required property string focusedColor
required property string occupiedColor
required property string emptyColor
required property real masterProgress
required property bool effectsActive
required property color effectColor
required property var getWorkspaceWidth
required property var getWorkspaceHeight
// Fixed dimension (cross-axis) for visual pill
readonly property real fixedDimension: Style.toOdd(capsuleHeight * baseDimensionRatio)
// Helper to safely get colors with proper reactivity
// Accesses Color singleton directly to ensure fresh values
function getColorPair(colorKey) {
switch (colorKey) {
case "primary":
return [Color.mPrimary, Color.mOnPrimary];
case "secondary":
return [Color.mSecondary, Color.mOnSecondary];
case "tertiary":
return [Color.mTertiary, Color.mOnTertiary];
case "onSurface":
return [Color.mOnSurface, Color.mSurface];
default:
return [Color.mPrimary, Color.mOnPrimary];
}
}
// Animated pill dimensions (for visual pill, not container)
property real pillWidth: isVertical ? fixedDimension : getWorkspaceWidth(workspace, false)
property real pillHeight: isVertical ? getWorkspaceHeight(workspace, false) : fixedDimension
// Container uses full barHeight on cross-axis for larger click area
width: isVertical ? barHeight : getWorkspaceWidth(workspace, false)
height: isVertical ? getWorkspaceHeight(workspace, false) : barHeight
states: [
State {
name: "active"
when: workspace.isActive
PropertyChanges {
target: pillContainer
width: isVertical ? barHeight : getWorkspaceWidth(workspace, true)
height: isVertical ? getWorkspaceHeight(workspace, true) : barHeight
pillWidth: isVertical ? fixedDimension : getWorkspaceWidth(workspace, true)
pillHeight: isVertical ? getWorkspaceHeight(workspace, true) : fixedDimension
}
}
]
transitions: [
Transition {
from: "inactive"
to: "active"
NumberAnimation {
properties: isVertical ? "height,pillHeight" : "width,pillWidth"
duration: Style.animationNormal
easing.type: Easing.OutBack
}
},
Transition {
from: "active"
to: "inactive"
NumberAnimation {
properties: isVertical ? "height,pillHeight" : "width,pillWidth"
duration: Style.animationNormal
easing.type: Easing.OutBack
}
}
]
Rectangle {
id: pill
width: pillContainer.pillWidth
height: pillContainer.pillHeight
x: Style.pixelAlignCenter(parent.width, width)
y: Style.pixelAlignCenter(parent.height, height)
radius: Style.radiusM
z: 0
color: {
if (pillMouseArea.containsMouse)
return Color.mHover;
if (workspace.isFocused)
return getColorPair(focusedColor)[0];
if (workspace.isUrgent)
return Color.mError;
if (workspace.isOccupied)
return getColorPair(occupiedColor)[0];
return Qt.alpha(getColorPair(emptyColor)[0], 0.3);
}
Loader {
active: (labelMode !== "none") && (!showLabelsOnlyWhenOccupied || workspace.isOccupied || workspace.isFocused)
anchors.centerIn: parent
sourceComponent: Component {
NText {
text: {
if (workspace.name && workspace.name.length > 0) {
if (labelMode === "name") {
return workspace.name.substring(0, characterCount);
}
if (labelMode === "index+name") {
// Vertical mode: compact format (no space, first char only)
// Horizontal mode: full format (space, more chars)
if (isVertical) {
return workspace.idx.toString() + workspace.name.substring(0, 1);
}
return workspace.idx.toString() + " " + workspace.name.substring(0, characterCount);
}
}
return workspace.idx.toString();
}
family: Settings.data.ui.fontFixed
// Size based on the fixed dimension (cross-axis) of the visual pill
pointSize: (isVertical ? pillContainer.pillWidth : pillContainer.pillHeight) * textRatio
applyUiScale: false
font.capitalization: Font.AllUppercase
font.weight: Style.fontWeightBold
wrapMode: Text.Wrap
color: {
if (pillMouseArea.containsMouse)
return Color.mOnHover;
if (workspace.isFocused)
return getColorPair(focusedColor)[1];
if (workspace.isUrgent)
return Color.mOnError;
if (workspace.isOccupied)
return getColorPair(occupiedColor)[1];
return getColorPair(emptyColor)[1];
}
Behavior on color {
enabled: !Color.isTransitioning
ColorAnimation {
duration: Style.animationFast
easing.type: Easing.InOutQuad
}
}
}
}
}
// Material 3-inspired smooth animations
Behavior on scale {
NumberAnimation {
duration: Style.animationNormal
easing.type: Easing.OutBack
}
}
Behavior on color {
enabled: !Color.isTransitioning
ColorAnimation {
duration: Style.animationFast
easing.type: Easing.InOutQuad
}
}
Behavior on opacity {
NumberAnimation {
duration: Style.animationFast
easing.type: Easing.InOutCubic
}
}
Behavior on radius {
NumberAnimation {
duration: Style.animationNormal
easing.type: Easing.OutBack
}
}
}
Behavior on width {
NumberAnimation {
duration: Style.animationNormal
easing.type: Easing.OutBack
}
}
Behavior on height {
NumberAnimation {
duration: Style.animationNormal
easing.type: Easing.OutBack
}
}
Behavior on pillWidth {
NumberAnimation {
duration: Style.animationNormal
easing.type: Easing.OutBack
}
}
Behavior on pillHeight {
NumberAnimation {
duration: Style.animationNormal
easing.type: Easing.OutBack
}
}
// Full-height click area
MouseArea {
id: pillMouseArea
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
hoverEnabled: true
onClicked: {
CompositorService.switchToWorkspace(workspace);
}
}
// Burst effect overlay for focused pill
Rectangle {
id: pillBurst
anchors.centerIn: pill
width: pillContainer.pillWidth + 18 * masterProgress * scale
height: pillContainer.pillHeight + 18 * masterProgress * scale
radius: width / 2
color: "transparent"
border.color: effectColor
border.width: Math.max(1, Math.round((2 + 6 * (1.0 - masterProgress))))
opacity: effectsActive && workspace.isFocused ? (1.0 - masterProgress) * 0.7 : 0
visible: effectsActive && workspace.isFocused
z: 1
}
}
+43 -33
View File
@@ -12,6 +12,10 @@ import qs.Widgets
Item {
id: root
Layout.preferredHeight: isVerticalBar ? -1 : Style.getBarHeightForScreen(screenName)
Layout.preferredWidth: isVerticalBar ? Style.getBarHeightForScreen(screenName) : -1
Layout.fillHeight: false
Layout.fillWidth: false
property ShellScreen screen
@@ -22,9 +26,11 @@ Item {
property int sectionWidgetsCount: 0
property var widgetMetadata: BarWidgetRegistry.widgetMetadata[widgetId] || {}
// Explicit screenName property ensures reactive binding when screen changes
readonly property string screenName: screen ? screen.name : ""
property var widgetSettings: {
if (section && sectionWidgetIndex >= 0) {
var widgets = Settings.getBarWidgetsForScreen(screen?.name)[section];
if (section && sectionWidgetIndex >= 0 && screenName) {
var widgets = Settings.getBarWidgetsForScreen(screenName)[section];
if (widgets && sectionWidgetIndex < widgets.length && widgets[sectionWidgetIndex]) {
return widgets[sectionWidgetIndex];
}
@@ -41,10 +47,10 @@ Item {
readonly property real maxWidth: (widgetSettings.maxWidth !== undefined) ? widgetSettings.maxWidth : Math.max(widgetMetadata.maxWidth || 0, screen ? screen.width * 0.06 : 0)
readonly property bool useFixedWidth: (widgetSettings.useFixedWidth !== undefined) ? widgetSettings.useFixedWidth : (widgetMetadata.useFixedWidth || false)
readonly property string barPosition: Settings.getBarPositionForScreen(screen?.name)
readonly property string barPosition: Settings.getBarPositionForScreen(screenName)
readonly property bool isVerticalBar: barPosition === "left" || barPosition === "right"
readonly property real capsuleHeight: Style.getCapsuleHeightForScreen(screen?.name)
readonly property real barFontSize: Style.getBarFontSizeForScreen(screen?.name)
readonly property real capsuleHeight: Style.getCapsuleHeightForScreen(screenName)
readonly property real barFontSize: Style.getBarFontSizeForScreen(screenName)
readonly property bool hasFocusedWindow: CompositorService.getFocusedWindow() !== null
readonly property string windowTitle: CompositorService.getFocusedWindowTitle() || "No active window"
readonly property string fallbackIcon: "user-desktop"
@@ -173,10 +179,8 @@ Item {
]
onTriggered: action => {
var popupMenuWindow = PanelService.getPopupMenuWindow(screen);
if (popupMenuWindow) {
popupMenuWindow.close();
}
contextMenu.close();
PanelService.closeContextMenu(screen);
if (action === "widget-settings") {
BarService.openWidgetSettings(screen, section, sectionWidgetIndex, widgetId, widgetSettings);
@@ -263,6 +267,7 @@ Item {
return NScrollText.ScrollMode.Hover;
return NScrollText.ScrollMode.Never;
}
forcedHover: mainMouseArea.containsMouse
NText {
text: windowTitle
pointSize: barFontSize
@@ -312,32 +317,37 @@ Item {
}
}
// Mouse area for hover detection
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
acceptedButtons: Qt.LeftButton | Qt.RightButton
onEntered: {
if ((windowTitle !== "") && isVerticalBar || (scrollingMode === "never")) {
TooltipService.show(root, windowTitle, BarService.getTooltipDirection(root.screen?.name));
}
}
onExited: {
TooltipService.hide();
}
onClicked: mouse => {
if (mouse.button === Qt.RightButton) {
var popupMenuWindow = PanelService.getPopupMenuWindow(screen);
if (popupMenuWindow) {
popupMenuWindow.showContextMenu(contextMenu);
contextMenu.openAtItem(root, screen);
}
}
}
// Mouse area moved to root
}
}
// Mouse area for hover detection
MouseArea {
id: mainMouseArea
anchors.fill: parent
// Extend click area to screen edge if widget is at the start/end
anchors.leftMargin: (!isVerticalBar && section === "left" && sectionWidgetIndex === 0) ? -Style.marginS : 0
anchors.rightMargin: (!isVerticalBar && section === "right" && sectionWidgetIndex === sectionWidgetsCount - 1) ? -Style.marginS : 0
anchors.topMargin: (isVerticalBar && section === "left" && sectionWidgetIndex === 0) ? -Style.marginM : 0
anchors.bottomMargin: (isVerticalBar && section === "right" && sectionWidgetIndex === sectionWidgetsCount - 1) ? -Style.marginM : 0
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
acceptedButtons: Qt.LeftButton | Qt.RightButton
onEntered: {
if ((windowTitle !== "") && isVerticalBar || (scrollingMode === "never")) {
TooltipService.show(root, windowTitle, BarService.getTooltipDirection(root.screen?.name));
}
}
onExited: {
TooltipService.hide();
}
onClicked: mouse => {
if (mouse.button === Qt.RightButton) {
PanelService.showContextMenu(contextMenu, root, screen);
}
}
}
Connections {
+41 -38
View File
@@ -19,14 +19,16 @@ Item {
property int sectionWidgetIndex: -1
property int sectionWidgetsCount: 0
readonly property string barPosition: Settings.getBarPositionForScreen(screen?.name)
// Explicit screenName property ensures reactive binding when screen changes
readonly property string screenName: screen ? screen.name : ""
readonly property string barPosition: Settings.getBarPositionForScreen(screenName)
readonly property bool isVerticalBar: barPosition === "left" || barPosition === "right"
readonly property real capsuleHeight: Style.getCapsuleHeightForScreen(screen?.name)
readonly property real capsuleHeight: Style.getCapsuleHeightForScreen(screenName)
property var widgetMetadata: BarWidgetRegistry.widgetMetadata[widgetId]
property var widgetSettings: {
if (section && sectionWidgetIndex >= 0) {
var widgets = Settings.getBarWidgetsForScreen(screen?.name)[section];
if (section && sectionWidgetIndex >= 0 && screenName) {
var widgets = Settings.getBarWidgetsForScreen(screenName)[section];
if (widgets && sectionWidgetIndex < widgets.length) {
return widgets[sectionWidgetIndex];
}
@@ -74,8 +76,12 @@ Item {
}
}
implicitWidth: !shouldShow ? 0 : isVerticalBar ? capsuleHeight : visualizerWidth
implicitHeight: !shouldShow ? 0 : isVerticalBar ? visualizerWidth : capsuleHeight
// Content dimensions for implicit sizing
readonly property real contentWidth: !shouldShow ? 0 : isVerticalBar ? capsuleHeight : visualizerWidth
readonly property real contentHeight: !shouldShow ? 0 : isVerticalBar ? visualizerWidth : capsuleHeight
implicitWidth: contentWidth
implicitHeight: contentHeight
visible: shouldShow
opacity: shouldShow ? 1.0 : 0.0
@@ -92,37 +98,40 @@ Item {
}
}
// Store visualizer type to force re-evaluation
readonly property string currentVisualizerType: Settings.data.audio.visualizerType
// Visual capsule centered in parent
Rectangle {
id: background
anchors.fill: parent
width: root.contentWidth
height: root.contentHeight
anchors.centerIn: parent
radius: Style.radiusS
color: Style.capsuleColor
border.color: Style.capsuleBorderColor
border.width: Style.capsuleBorderWidth
}
// Store visualizer type to force re-evaluation
readonly property string currentVisualizerType: Settings.data.audio.visualizerType
// When visualizer type or playback changes, shouldShow updates automatically
// The Loader dynamically loads the appropriate visualizer based on settings
Loader {
id: visualizerLoader
anchors.fill: parent
anchors.margins: Style.marginS
active: shouldShow
asynchronous: true
// When visualizer type or playback changes, shouldShow updates automatically
// The Loader dynamically loads the appropriate visualizer based on settings
Loader {
id: visualizerLoader
anchors.fill: parent
anchors.margins: Style.marginS
active: shouldShow
asynchronous: true
sourceComponent: {
switch (currentVisualizerType) {
case "linear":
return linearComponent;
case "mirrored":
return mirroredComponent;
case "wave":
return waveComponent;
default:
return null;
sourceComponent: {
switch (currentVisualizerType) {
case "linear":
return linearComponent;
case "mirrored":
return mirroredComponent;
case "wave":
return waveComponent;
default:
return null;
}
}
}
}
@@ -144,10 +153,8 @@ Item {
]
onTriggered: action => {
var popupMenuWindow = PanelService.getPopupMenuWindow(screen);
if (popupMenuWindow) {
popupMenuWindow.close();
}
contextMenu.close();
PanelService.closeContextMenu(screen);
if (action === "cycle-visualizer") {
const types = ["linear", "mirrored", "wave"];
@@ -170,11 +177,7 @@ Item {
onClicked: mouse => {
if (mouse.button === Qt.RightButton) {
var popupMenuWindow = PanelService.getPopupMenuWindow(screen);
if (popupMenuWindow) {
popupMenuWindow.showContextMenu(contextMenu);
contextMenu.openAtItem(root, screen);
}
PanelService.showContextMenu(contextMenu, root, screen);
} else {
const types = ["linear", "mirrored", "wave"];
const currentIndex = types.indexOf(currentVisualizerType);
+65 -119
View File
@@ -22,9 +22,11 @@ Item {
property int sectionWidgetsCount: 0
property var widgetMetadata: BarWidgetRegistry.widgetMetadata[widgetId]
// Explicit screenName property ensures reactive binding when screen changes
readonly property string screenName: screen ? screen.name : ""
property var widgetSettings: {
if (section && sectionWidgetIndex >= 0) {
var widgets = Settings.getBarWidgetsForScreen(screen?.name)[section];
if (section && sectionWidgetIndex >= 0 && screenName) {
var widgets = Settings.getBarWidgetsForScreen(screenName)[section];
if (widgets && sectionWidgetIndex < widgets.length) {
return widgets[sectionWidgetIndex];
}
@@ -32,7 +34,7 @@ Item {
return {};
}
readonly property string barPosition: Settings.getBarPositionForScreen(screen?.name)
readonly property string barPosition: Settings.getBarPositionForScreen(screenName)
readonly property bool isBarVertical: barPosition === "left" || barPosition === "right"
readonly property string displayMode: widgetSettings.displayMode !== undefined ? widgetSettings.displayMode : widgetMetadata.displayMode
readonly property real warningThreshold: widgetSettings.warningThreshold !== undefined ? widgetSettings.warningThreshold : widgetMetadata.warningThreshold
@@ -43,8 +45,6 @@ Item {
// Visibility: show if hideIfNotDetected is false, or if battery is ready (after initialization)
readonly property bool shouldShow: !hideIfNotDetected || (isReady && (hideIfIdle ? (!isCharging && !isPluggedIn) : true))
visible: shouldShow
opacity: shouldShow ? 1.0 : 0.0
// Test mode
readonly property bool testMode: false
@@ -53,84 +53,31 @@ Item {
readonly property bool testPluggedIn: false
readonly property string deviceNativePath: widgetSettings.deviceNativePath || ""
function findBatteryDevice(nativePath) {
if (!nativePath || !UPower.devices) {
return UPower.displayDevice;
}
var devices = UPower.devices.values || [];
for (var i = 0; i < devices.length; i++) {
var device = devices[i];
if (device && device.nativePath === nativePath && device.type !== UPowerDeviceType.LinePower && device.percentage !== undefined) {
return device;
}
}
return UPower.displayDevice;
readonly property var battery: BatteryService.findUPowerDevice(deviceNativePath)
readonly property var bluetoothDevice: deviceNativePath ? BatteryService.findBluetoothDevice(deviceNativePath) : null
readonly property var device: {
if (deviceNativePath)
return bluetoothDevice || battery;
return BatteryService.primaryDevice;
}
readonly property bool hasBluetoothBattery: BatteryService.isBluetoothDevice(device)
function findBluetoothDevice(nativePath) {
if (!nativePath || !BluetoothService.devices) {
return null;
}
var macMatch = nativePath.match(/([0-9a-fA-F]{2}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2})/);
if (!macMatch) {
return null;
}
var macAddress = macMatch[1].toUpperCase();
var devices = BluetoothService.devices.values || [];
for (var i = 0; i < devices.length; i++) {
var device = devices[i];
if (device && device.address && device.address.toUpperCase() === macAddress) {
return device;
}
}
return null;
}
readonly property bool isReady: testMode ? true : (BatteryService.ready && BatteryService.isDeviceReady(device))
readonly property real percent: testMode ? testPercent : (isReady ? BatteryService.getPercentage(device) : 0)
readonly property bool isCharging: testMode ? testCharging : (isReady ? BatteryService.isCharging(device) : false)
readonly property bool isPluggedIn: testMode ? testPluggedIn : (isReady ? BatteryService.isPluggedIn(device) : false)
readonly property var battery: findBatteryDevice(deviceNativePath)
readonly property var bluetoothDevice: deviceNativePath ? findBluetoothDevice(deviceNativePath) : null
readonly property bool hasBluetoothBattery: bluetoothDevice && bluetoothDevice.batteryAvailable && bluetoothDevice.battery !== undefined
readonly property bool isBluetoothConnected: bluetoothDevice && bluetoothDevice.connected === true
property bool hasNotifiedLowBattery: false
property bool initializationComplete: false
Timer {
interval: 500
running: true
onTriggered: root.initializationComplete = true
}
visible: shouldShow
opacity: shouldShow ? 1.0 : 0.0
readonly property bool isDevicePresent: {
if (testMode)
return true;
if (deviceNativePath) {
if (bluetoothDevice) {
return isBluetoothConnected;
}
if (battery && battery.nativePath === deviceNativePath) {
if (battery.type === UPowerDeviceType.Battery && battery.isPresent !== undefined) {
return battery.isPresent;
}
return battery.ready && battery.percentage !== undefined && (battery.percentage > 0 || isCharging);
}
return false;
}
if (battery) {
// For default device, check isPresent if it's a Battery type, otherwise require percentage > 0
if (battery.type === UPowerDeviceType.Battery && battery.isPresent !== undefined) {
return battery.isPresent;
}
// For non-battery types or when isPresent is undefined, require actual percentage
return battery.ready && battery.percentage !== undefined && battery.percentage > 0;
}
return false;
return BatteryService.isDevicePresent(device);
}
readonly property bool isReady: testMode ? true : (initializationComplete && battery && battery.ready && isDevicePresent && (battery.percentage !== undefined || hasBluetoothBattery))
readonly property real percent: testMode ? testPercent : (isReady ? (hasBluetoothBattery ? (bluetoothDevice.battery * 100) : (battery.percentage * 100)) : 0)
readonly property bool isCharging: testMode ? testCharging : (isReady ? battery.state === UPowerDeviceState.Charging : false)
readonly property bool isPluggedIn: testMode ? testPluggedIn : (isReady ? battery.state === UPowerDeviceState.FullyCharged || battery.state === UPowerDeviceState.PendingCharge : false)
property bool hasNotifiedLowBattery: false
implicitWidth: pill.width
implicitHeight: pill.height
@@ -139,25 +86,26 @@ Item {
hasNotifiedLowBattery = true;
ToastService.showWarning(I18n.tr("toast.battery.low"), I18n.tr("toast.battery.low-desc", {
"percent": Math.round(currentPercent)
}));
}), "battery-exclamation", "warning", 4000, "", null);
} else if (hasNotifiedLowBattery && (charging || pluggedIn || currentPercent > warningThreshold + 5)) {
hasNotifiedLowBattery = false;
}
}
function getCurrentPercent() {
return hasBluetoothBattery ? (bluetoothDevice.battery * 100) : (battery ? battery.percentage * 100 : 0);
return BatteryService.getPercentage(device);
}
Connections {
target: battery
target: device
function onPercentageChanged() {
if (battery) {
if (device) {
maybeNotify(getCurrentPercent(), isCharging, isPluggedIn, isReady);
}
}
function onStateChanged() {
if (battery) {
if (device) {
if (isCharging || isPluggedIn) {
hasNotifiedLowBattery = false;
}
@@ -167,12 +115,7 @@ Item {
}
Connections {
target: bluetoothDevice
function onBatteryChanged() {
if (bluetoothDevice && hasBluetoothBattery) {
maybeNotify(bluetoothDevice.battery * 100, battery ? isCharging : false, battery ? isPluggedIn : false, true);
}
}
target: (device && BatteryService.isBluetoothDevice(device)) ? device : null
}
NPopupContextMenu {
@@ -187,10 +130,8 @@ Item {
]
onTriggered: action => {
var popupMenuWindow = PanelService.getPopupMenuWindow(screen);
if (popupMenuWindow) {
popupMenuWindow.close();
}
contextMenu.close();
PanelService.closeContextMenu(screen);
if (action === "widget-settings") {
BarService.openWidgetSettings(screen, section, sectionWidgetIndex, widgetId, widgetSettings);
@@ -200,7 +141,6 @@ Item {
BarPill {
id: pill
screen: root.screen
oppositeDirection: BarService.getPillDirection(root)
icon: testMode ? BatteryService.getIcon(testPercent, testCharging, testPluggedIn, true) : BatteryService.getIcon(percent, isCharging, isPluggedIn, isReady)
@@ -208,52 +148,58 @@ Item {
suffix: "%"
autoHide: false
forceOpen: isReady && displayMode === "alwaysShow"
forceClose: displayMode === "alwaysHide" || (initializationComplete && !isReady)
customBackgroundColor: !initializationComplete ? "transparent" : (isCharging ? Color.mPrimary : (isLowBattery ? Color.mError : "transparent"))
customTextIconColor: !initializationComplete ? "transparent" : (isCharging ? Color.mOnPrimary : (isLowBattery ? Color.mOnError : "transparent"))
forceClose: displayMode === "alwaysHide" || (BatteryService.ready && !isReady)
customBackgroundColor: !BatteryService.ready ? "transparent" : (isCharging ? Color.mPrimary : (isLowBattery ? Color.mError : "transparent"))
customTextIconColor: !BatteryService.ready ? "transparent" : (isCharging ? Color.mOnPrimary : (isLowBattery ? Color.mOnError : "transparent"))
tooltipText: {
let lines = [];
if (testMode) {
lines.push(`Time left: ${Time.formatVagueHumanReadableDuration(12345)}.`);
lines.push("Time left: " + Time.formatVagueHumanReadableDuration(12345) + ".");
return lines.join("\n");
}
if (!isReady || !isDevicePresent) {
return I18n.tr("battery.no-battery-detected");
}
if (!isPluggedIn && battery.timeToEmpty > 0) {
lines.push(I18n.tr("battery.time-left", {
"time": Time.formatVagueHumanReadableDuration(battery.timeToEmpty)
}));
const isInternal = device === BatteryService.primaryDevice && BatteryService.isLaptopBattery;
if (isInternal) {
let timeText = BatteryService.getTimeRemainingText(device);
if (timeText && timeText !== I18n.tr("common.idle") && timeText !== I18n.tr("battery.no-battery-detected") && timeText !== I18n.tr("battery.plugged-in")) {
lines.push(timeText);
}
let rateText = BatteryService.getRateText(device);
if (rateText) {
lines.push(rateText);
}
} else if (device) {
// External / Peripheral Device (Phone, Keyboard, Mouse, Gamepad, Headphone etc.)
let name = BatteryService.getDeviceName(device);
let pct = Math.round(BatteryService.getPercentage(device));
lines.push(name + ": " + pct + suffix);
}
if (!isPluggedIn && battery.timeToFull > 0) {
lines.push(I18n.tr("battery.time-until-full", {
"time": Time.formatVagueHumanReadableDuration(battery.timeToFull)
}));
}
if (battery.changeRate !== undefined) {
const rate = Math.abs(battery.changeRate);
if (isPluggedIn) {
lines.push(I18n.tr("battery.plugged-in"));
} else if (isCharging) {
lines.push(I18n.tr("battery.charging-rate", {
"rate": rate.toFixed(2)
}));
} else {
lines.push(I18n.tr("battery.discharging-rate", {
"rate": rate.toFixed(2)
}));
// If we are showing the main laptop battery, append external devices
if (isInternal) {
var external = BatteryService.externalBatteries;
if (external.length > 0) {
if (lines.length > 0)
lines.push(""); // Separator
for (var j = 0; j < external.length; j++) {
var dev = external[j];
var dName = BatteryService.getDeviceName(dev);
var dPct = Math.round(BatteryService.getPercentage(dev));
lines.push(dName + ": " + dPct + suffix);
}
}
}
return lines.join("\n");
}
onClicked: PanelService.getPanel("batteryPanel", screen)?.toggle(this)
onRightClicked: {
var popupMenuWindow = PanelService.getPopupMenuWindow(screen);
if (popupMenuWindow) {
popupMenuWindow.showContextMenu(contextMenu);
contextMenu.openAtItem(pill, screen);
}
PanelService.showContextMenu(contextMenu, pill, screen);
}
}
}
+8 -12
View File
@@ -19,9 +19,11 @@ Item {
property int sectionWidgetsCount: 0
property var widgetMetadata: BarWidgetRegistry.widgetMetadata[widgetId]
// Explicit screenName property ensures reactive binding when screen changes
readonly property string screenName: screen ? screen.name : ""
property var widgetSettings: {
if (section && sectionWidgetIndex >= 0) {
var widgets = Settings.getBarWidgetsForScreen(screen?.name)[section];
if (section && sectionWidgetIndex >= 0 && screenName) {
var widgets = Settings.getBarWidgetsForScreen(screenName)[section];
if (widgets && sectionWidgetIndex < widgets.length) {
return widgets[sectionWidgetIndex];
}
@@ -29,7 +31,7 @@ Item {
return {};
}
readonly property string barPosition: Settings.getBarPositionForScreen(screen?.name)
readonly property string barPosition: Settings.getBarPositionForScreen(screenName)
readonly property bool isBarVertical: barPosition === "left" || barPosition === "right"
readonly property string displayMode: widgetSettings.displayMode !== undefined ? widgetSettings.displayMode : widgetMetadata.displayMode
@@ -53,10 +55,8 @@ Item {
]
onTriggered: action => {
var popupMenuWindow = PanelService.getPopupMenuWindow(screen);
if (popupMenuWindow) {
popupMenuWindow.close();
}
contextMenu.close();
PanelService.closeContextMenu(screen);
if (action === "toggle-bluetooth") {
BluetoothService.setBluetoothEnabled(!BluetoothService.enabled);
@@ -94,11 +94,7 @@ Item {
p.toggle(this);
}
onRightClicked: {
var popupMenuWindow = PanelService.getPopupMenuWindow(screen);
if (popupMenuWindow) {
popupMenuWindow.showContextMenu(contextMenu);
contextMenu.openAtItem(pill, screen);
}
PanelService.showContextMenu(contextMenu, pill, screen);
}
tooltipText: {
if (pill.text !== "") {
+8 -12
View File
@@ -20,9 +20,11 @@ Item {
property int sectionWidgetsCount: 0
property var widgetMetadata: BarWidgetRegistry.widgetMetadata[widgetId]
// Explicit screenName property ensures reactive binding when screen changes
readonly property string screenName: screen ? screen.name : ""
property var widgetSettings: {
if (section && sectionWidgetIndex >= 0) {
var widgets = Settings.getBarWidgetsForScreen(screen?.name)[section];
if (section && sectionWidgetIndex >= 0 && screenName) {
var widgets = Settings.getBarWidgetsForScreen(screenName)[section];
if (widgets && sectionWidgetIndex < widgets.length) {
return widgets[sectionWidgetIndex];
}
@@ -30,7 +32,7 @@ Item {
return {};
}
readonly property string barPosition: Settings.getBarPositionForScreen(screen?.name)
readonly property string barPosition: Settings.getBarPositionForScreen(screenName)
readonly property bool isBarVertical: barPosition === "left" || barPosition === "right"
readonly property string displayMode: (widgetSettings.displayMode !== undefined) ? widgetSettings.displayMode : widgetMetadata.displayMode
@@ -114,10 +116,8 @@ Item {
]
onTriggered: action => {
var popupMenuWindow = PanelService.getPopupMenuWindow(screen);
if (popupMenuWindow) {
popupMenuWindow.close();
}
contextMenu.close();
PanelService.closeContextMenu(screen);
if (action === "open-display-settings") {
var settingsPanel = PanelService.getPanel("settingsPanel", screen);
@@ -169,11 +169,7 @@ Item {
onClicked: PanelService.getPanel("brightnessPanel", screen)?.toggle(this)
onRightClicked: {
var popupMenuWindow = PanelService.getPopupMenuWindow(screen);
if (popupMenuWindow) {
popupMenuWindow.showContextMenu(contextMenu);
contextMenu.openAtItem(pill, screen);
}
PanelService.showContextMenu(contextMenu, pill, screen);
}
}
}
+82 -72
View File
@@ -7,7 +7,7 @@ import qs.Modules.Bar.Extras
import qs.Services.UI
import qs.Widgets
Rectangle {
Item {
id: root
property ShellScreen screen
@@ -19,9 +19,11 @@ Rectangle {
property int sectionWidgetsCount: 0
property var widgetMetadata: BarWidgetRegistry.widgetMetadata[widgetId]
// Explicit screenName property ensures reactive binding when screen changes
readonly property string screenName: screen ? screen.name : ""
property var widgetSettings: {
if (section && sectionWidgetIndex >= 0) {
var widgets = Settings.getBarWidgetsForScreen(screen?.name)[section];
if (section && sectionWidgetIndex >= 0 && screenName) {
var widgets = Settings.getBarWidgetsForScreen(screenName)[section];
if (widgets && sectionWidgetIndex < widgets.length) {
return widgets[sectionWidgetIndex];
}
@@ -29,10 +31,10 @@ Rectangle {
return {};
}
readonly property string barPosition: Settings.getBarPositionForScreen(screen?.name)
readonly property string barPosition: Settings.getBarPositionForScreen(screenName)
readonly property bool isBarVertical: barPosition === "left" || barPosition === "right"
readonly property real capsuleHeight: Style.getCapsuleHeightForScreen(screen?.name)
readonly property real barFontSize: Style.getBarFontSizeForScreen(screen?.name)
readonly property real capsuleHeight: Style.getCapsuleHeightForScreen(screenName)
readonly property real barFontSize: Style.getBarFontSizeForScreen(screenName)
readonly property var now: Time.now
// Resolve settings: try user settings or defaults from BarWidgetRegistry
@@ -43,76 +45,89 @@ Rectangle {
readonly property string formatVertical: widgetSettings.formatVertical !== undefined ? widgetSettings.formatVertical : widgetMetadata.formatVertical
readonly property string tooltipFormat: widgetSettings.tooltipFormat !== undefined ? widgetSettings.tooltipFormat : widgetMetadata.tooltipFormat
implicitWidth: isBarVertical ? capsuleHeight : Math.round((isBarVertical ? verticalLoader.implicitWidth : horizontalLoader.implicitWidth) + Style.marginXL)
// Content dimensions for implicit sizing
readonly property real contentWidth: isBarVertical ? capsuleHeight : Math.round((isBarVertical ? verticalLoader.implicitWidth : horizontalLoader.implicitWidth) + Style.marginXL)
readonly property real contentHeight: isBarVertical ? Math.round(verticalLoader.implicitHeight + Style.marginS * 2) : capsuleHeight
implicitHeight: isBarVertical ? Math.round(verticalLoader.implicitHeight + Style.marginS * 2) : capsuleHeight
// Size: use implicit width/height
// BarWidgetLoader sets explicit width/height to extend click area
implicitWidth: contentWidth
implicitHeight: contentHeight
radius: Style.radiusS
color: Style.capsuleColor
border.color: Style.capsuleBorderColor
border.width: Style.capsuleBorderWidth
Item {
id: clockContainer
// Visual clock capsule - stays at content size, centered in parent
Rectangle {
id: visualClock
width: root.contentWidth
height: root.contentHeight
anchors.centerIn: parent
// Horizontal
Loader {
id: horizontalLoader
active: !isBarVertical
radius: Style.radiusL
color: Style.capsuleColor
border.color: Style.capsuleBorderColor
border.width: Style.capsuleBorderWidth
Item {
id: clockContainer
anchors.centerIn: parent
sourceComponent: ColumnLayout {
// Horizontal
Loader {
id: horizontalLoader
active: !isBarVertical
anchors.centerIn: parent
spacing: Settings.data.bar.showCapsule ? -5 : -3
Repeater {
id: repeater
model: I18n.locale.toString(now, formatHorizontal.trim()).split("\\n")
NText {
visible: text !== ""
text: modelData
family: useCustomFont && customFont ? customFont : Settings.data.ui.fontDefault
Binding on pointSize {
value: {
if (repeater.model.length == 1) {
// Single line: Full size
return barFontSize;
} else if (repeater.model.length == 2) {
// Two lines: First line is bigger than the second
return (index == 0) ? Math.round(barFontSize * 0.9) : Math.round(barFontSize * 0.75);
} else {
// More than two lines: Make it small!
return Math.round(barFontSize * 0.75);
sourceComponent: ColumnLayout {
anchors.centerIn: parent
spacing: Settings.data.bar.showCapsule ? -5 : -3
Repeater {
id: repeater
model: I18n.locale.toString(now, formatHorizontal.trim()).split("\\n")
NText {
visible: text !== ""
text: modelData
family: useCustomFont && customFont ? customFont : Settings.data.ui.fontDefault
Binding on pointSize {
value: {
if (repeater.model.length == 1) {
// Single line: Full size
return barFontSize;
} else if (repeater.model.length == 2) {
// Two lines: First line is bigger than the second
return (index == 0) ? Math.round(barFontSize * 0.9) : Math.round(barFontSize * 0.75);
} else {
// More than two lines: Make it small!
return Math.round(barFontSize * 0.75);
}
}
}
applyUiScale: false
color: usePrimaryColor ? Color.mPrimary : Color.mOnSurface
wrapMode: Text.WordWrap
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
}
applyUiScale: false
color: usePrimaryColor ? Color.mPrimary : Color.mOnSurface
wrapMode: Text.WordWrap
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
}
}
}
}
// Vertical
Loader {
id: verticalLoader
active: isBarVertical
anchors.centerIn: parent // Now this works without layout conflicts
sourceComponent: ColumnLayout {
anchors.centerIn: parent
spacing: -2
Repeater {
model: I18n.locale.toString(now, formatVertical.trim()).split(" ")
delegate: NText {
visible: text !== ""
text: modelData
family: useCustomFont && customFont ? customFont : Settings.data.ui.fontDefault
pointSize: barFontSize
applyUiScale: false
color: usePrimaryColor ? Color.mPrimary : Color.mOnSurface
wrapMode: Text.WordWrap
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
// Vertical
Loader {
id: verticalLoader
active: isBarVertical
anchors.centerIn: parent // Now this works without layout conflicts
sourceComponent: ColumnLayout {
anchors.centerIn: parent
spacing: -2
Repeater {
model: I18n.locale.toString(now, formatVertical.trim()).split(" ")
delegate: NText {
visible: text !== ""
text: modelData
family: useCustomFont && customFont ? customFont : Settings.data.ui.fontDefault
pointSize: barFontSize
applyUiScale: false
color: usePrimaryColor ? Color.mPrimary : Color.mOnSurface
wrapMode: Text.WordWrap
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
}
}
}
}
@@ -136,10 +151,9 @@ Rectangle {
]
onTriggered: action => {
var popupMenuWindow = PanelService.getPopupMenuWindow(screen);
if (popupMenuWindow) {
popupMenuWindow.close();
}
// Close the context menu
contextMenu.close();
PanelService.closeContextMenu(screen);
if (action === "open-calendar") {
PanelService.getPanel("clockPanel", screen)?.toggle(root);
@@ -177,11 +191,7 @@ Rectangle {
onClicked: mouse => {
TooltipService.hide();
if (mouse.button === Qt.RightButton) {
var popupMenuWindow = PanelService.getPopupMenuWindow(screen);
if (popupMenuWindow) {
popupMenuWindow.showContextMenu(contextMenu);
contextMenu.openAtItem(root, screen);
}
PanelService.showContextMenu(contextMenu, root, screen);
} else {
PanelService.getPanel("clockPanel", screen)?.toggle(this);
}
+7 -11
View File
@@ -22,9 +22,11 @@ NIconButton {
property int sectionWidgetsCount: 0
property var widgetMetadata: BarWidgetRegistry.widgetMetadata[widgetId]
// Explicit screenName property ensures reactive binding when screen changes
readonly property string screenName: screen ? screen.name : ""
property var widgetSettings: {
if (section && sectionWidgetIndex >= 0) {
var widgets = Settings.getBarWidgetsForScreen(screen?.name)[section];
if (section && sectionWidgetIndex >= 0 && screenName) {
var widgets = Settings.getBarWidgetsForScreen(screenName)[section];
if (widgets && sectionWidgetIndex < widgets.length) {
return widgets[sectionWidgetIndex];
}
@@ -118,10 +120,8 @@ NIconButton {
]
onTriggered: action => {
var popupMenuWindow = PanelService.getPopupMenuWindow(screen);
if (popupMenuWindow) {
popupMenuWindow.close();
}
contextMenu.close();
PanelService.closeContextMenu(screen);
if (action === "open-launcher") {
PanelService.getPanel("launcherPanel", screen)?.toggle();
@@ -145,11 +145,7 @@ NIconButton {
}
}
onRightClicked: {
var popupMenuWindow = PanelService.getPopupMenuWindow(screen);
if (popupMenuWindow) {
popupMenuWindow.showContextMenu(contextMenu);
contextMenu.openAtItem(root, screen);
}
PanelService.showContextMenu(contextMenu, root, screen);
}
onMiddleClicked: PanelService.getPanel("launcherPanel", screen)?.toggle()
+15 -15
View File
@@ -21,9 +21,11 @@ Item {
property int sectionWidgetsCount: 0
property var widgetMetadata: BarWidgetRegistry.widgetMetadata[widgetId]
// Explicit screenName property ensures reactive binding when screen changes
readonly property string screenName: screen ? screen.name : ""
property var widgetSettings: {
if (section && sectionWidgetIndex >= 0) {
var widgets = Settings.getBarWidgetsForScreen(screen?.name)[section];
if (section && sectionWidgetIndex >= 0 && screenName) {
var widgets = Settings.getBarWidgetsForScreen(screenName)[section];
if (widgets && sectionWidgetIndex < widgets.length) {
return widgets[sectionWidgetIndex];
}
@@ -31,7 +33,7 @@ Item {
return {};
}
readonly property string barPosition: Settings.getBarPositionForScreen(screen?.name)
readonly property string barPosition: Settings.getBarPositionForScreen(screenName)
readonly property bool isVerticalBar: barPosition === "left" || barPosition === "right"
readonly property string customIcon: widgetSettings.icon || widgetMetadata.icon
@@ -231,10 +233,10 @@ Item {
}
}
onClicked: root.onClicked()
onRightClicked: root.onRightClicked()
onMiddleClicked: root.onMiddleClicked()
onWheel: delta => root.onWheel(delta)
onClicked: root.clicked()
onRightClicked: root.rightClicked()
onMiddleClicked: root.middleClicked()
onWheel: delta => root.wheeled(delta)
}
// Internal state for dynamic text
@@ -456,22 +458,20 @@ Item {
}
}
function onClicked() {
function clicked() {
if (leftClickExec) {
Quickshell.execDetached(["sh", "-lc", leftClickExec]);
Logger.i("CustomButton", `Executing command: ${leftClickExec}`);
} else if (!leftClickUpdateText) {
// No left click script was defined, open settings
var settingsPanel = PanelService.getPanel("settingsPanel", screen);
settingsPanel.requestedTab = SettingsPanel.Tab.Bar;
settingsPanel.open();
BarService.openWidgetSettings(screen, section, sectionWidgetIndex, widgetId, widgetSettings);
//SettingsPanelService.openToTab(SettingsPanel.Tab.Bar, 1, screen);
}
if (!textStream && leftClickUpdateText) {
runTextCommand();
}
}
function onRightClicked() {
function rightClicked() {
if (rightClickExec) {
Quickshell.execDetached(["sh", "-lc", rightClickExec]);
Logger.i("CustomButton", `Executing command: ${rightClickExec}`);
@@ -481,7 +481,7 @@ Item {
}
}
function onMiddleClicked() {
function middleClicked() {
if (middleClickExec) {
Quickshell.execDetached(["sh", "-lc", middleClickExec]);
Logger.i("CustomButton", `Executing command: ${middleClickExec}`);
@@ -516,7 +516,7 @@ Item {
textProc.running = true;
}
function onWheel(delta) {
function wheeled(delta) {
if (wheelMode === "unified" && wheelExec) {
let normalizedDelta = delta > 0 ? 1 : -1;
+5 -3
View File
@@ -19,9 +19,11 @@ Item {
property int sectionWidgetsCount: 0
property var widgetMetadata: BarWidgetRegistry.widgetMetadata[widgetId]
// Explicit screenName property ensures reactive binding when screen changes
readonly property string screenName: screen ? screen.name : ""
property var widgetSettings: {
if (section && sectionWidgetIndex >= 0) {
var widgets = Settings.getBarWidgetsForScreen(screen?.name)[section];
if (section && sectionWidgetIndex >= 0 && screenName) {
var widgets = Settings.getBarWidgetsForScreen(screenName)[section];
if (widgets && sectionWidgetIndex < widgets.length) {
return widgets[sectionWidgetIndex];
}
@@ -29,7 +31,7 @@ Item {
return {};
}
readonly property string barPosition: Settings.getBarPositionForScreen(screen?.name)
readonly property string barPosition: Settings.getBarPositionForScreen(screenName)
readonly property bool isBarVertical: barPosition === "left" || barPosition === "right"
implicitWidth: pill.width
+8 -12
View File
@@ -23,9 +23,11 @@ Item {
property int sectionWidgetsCount: 0
property var widgetMetadata: BarWidgetRegistry.widgetMetadata[widgetId]
// Explicit screenName property ensures reactive binding when screen changes
readonly property string screenName: screen ? screen.name : ""
property var widgetSettings: {
if (section && sectionWidgetIndex >= 0) {
var widgets = Settings.getBarWidgetsForScreen(screen?.name)[section];
if (section && sectionWidgetIndex >= 0 && screenName) {
var widgets = Settings.getBarWidgetsForScreen(screenName)[section];
if (widgets && sectionWidgetIndex < widgets.length) {
return widgets[sectionWidgetIndex];
}
@@ -33,7 +35,7 @@ Item {
return {};
}
readonly property string barPosition: Settings.getBarPositionForScreen(screen?.name)
readonly property string barPosition: Settings.getBarPositionForScreen(screenName)
readonly property bool isBarVertical: barPosition === "left" || barPosition === "right"
readonly property string displayMode: (widgetSettings.displayMode !== undefined) ? widgetSettings.displayMode : widgetMetadata.displayMode
@@ -57,10 +59,8 @@ Item {
]
onTriggered: action => {
var popupMenuWindow = PanelService.getPopupMenuWindow(screen);
if (popupMenuWindow) {
popupMenuWindow.close();
}
contextMenu.close();
PanelService.closeContextMenu(screen);
if (action === "widget-settings") {
BarService.openWidgetSettings(screen, section, sectionWidgetIndex, widgetId, widgetSettings);
@@ -82,11 +82,7 @@ Item {
forceClose: root.showIcon && root.displayMode === "alwaysHide"
onClicked: CompositorService.cycleKeyboardLayout()
onRightClicked: {
var popupMenuWindow = PanelService.getPopupMenuWindow(screen);
if (popupMenuWindow) {
popupMenuWindow.showContextMenu(contextMenu);
contextMenu.openAtItem(pill, screen);
}
PanelService.showContextMenu(contextMenu, pill, screen);
}
}
}
+9 -13
View File
@@ -17,9 +17,11 @@ NIconButton {
property int sectionWidgetsCount: 0
property var widgetMetadata: BarWidgetRegistry.widgetMetadata[widgetId]
// Explicit screenName property ensures reactive binding when screen changes
readonly property string screenName: screen ? screen.name : ""
property var widgetSettings: {
if (section && sectionWidgetIndex >= 0) {
var widgets = Settings.getBarWidgetsForScreen(screen?.name)[section];
if (section && sectionWidgetIndex >= 0 && screenName) {
var widgets = Settings.getBarWidgetsForScreen(screenName)[section];
if (widgets && sectionWidgetIndex < widgets.length) {
return widgets[sectionWidgetIndex];
}
@@ -32,8 +34,8 @@ NIconButton {
icon: iconName
tooltipText: I18n.tr("actions.open-launcher")
tooltipDirection: BarService.getTooltipDirection(screen?.name)
baseSize: Style.getCapsuleHeightForScreen(screen?.name)
tooltipDirection: BarService.getTooltipDirection(screenName)
baseSize: Style.getCapsuleHeightForScreen(screenName)
applyUiScale: false
customRadius: Style.radiusL
colorBg: Style.capsuleColor
@@ -60,10 +62,8 @@ NIconButton {
]
onTriggered: action => {
var popupMenuWindow = PanelService.getPopupMenuWindow(screen);
if (popupMenuWindow) {
popupMenuWindow.close();
}
contextMenu.close();
PanelService.closeContextMenu(screen);
if (action === "launcher-settings") {
var panel = PanelService.getPanel("settingsPanel", screen);
@@ -78,10 +78,6 @@ NIconButton {
onClicked: PanelService.getPanel("launcherPanel", screen)?.toggle()
onMiddleClicked: PanelService.getPanel("launcherPanel", screen)?.toggle()
onRightClicked: {
var popupMenuWindow = PanelService.getPopupMenuWindow(screen);
if (popupMenuWindow) {
popupMenuWindow.showContextMenu(contextMenu);
contextMenu.openAtItem(root, screen);
}
PanelService.showContextMenu(contextMenu, root, screen);
}
}
+81 -75
View File
@@ -10,7 +10,7 @@ import qs.Services.Keyboard
import qs.Services.UI
import qs.Widgets
Rectangle {
Item {
id: root
property ShellScreen screen
@@ -21,9 +21,11 @@ Rectangle {
property int sectionWidgetsCount: 0
property var widgetMetadata: BarWidgetRegistry.widgetMetadata[widgetId]
// Explicit screenName property ensures reactive binding when screen changes
readonly property string screenName: screen ? screen.name : ""
property var widgetSettings: {
if (section && sectionWidgetIndex >= 0) {
var widgets = Settings.getBarWidgetsForScreen(screen?.name)[section];
if (section && sectionWidgetIndex >= 0 && screenName) {
var widgets = Settings.getBarWidgetsForScreen(screenName)[section];
if (widgets && sectionWidgetIndex < widgets.length) {
return widgets[sectionWidgetIndex];
}
@@ -31,9 +33,9 @@ Rectangle {
return {};
}
readonly property string barPosition: Settings.getBarPositionForScreen(screen?.name)
readonly property string barPosition: Settings.getBarPositionForScreen(screenName)
readonly property bool isVertical: barPosition === "left" || barPosition === "right"
readonly property real capsuleHeight: Style.getCapsuleHeightForScreen(screen?.name)
readonly property real capsuleHeight: Style.getCapsuleHeightForScreen(screenName)
readonly property bool showCaps: (widgetSettings.showCapsLock !== undefined) ? widgetSettings.showCapsLock : widgetMetadata.showCapsLock
readonly property bool showNum: (widgetSettings.showNumLock !== undefined) ? widgetSettings.showNumLock : widgetMetadata.showNumLock
@@ -45,15 +47,12 @@ Rectangle {
readonly property bool hideWhenOff: (widgetSettings.hideWhenOff !== undefined) ? widgetSettings.hideWhenOff : (widgetMetadata.hideWhenOff !== undefined ? widgetMetadata.hideWhenOff : false)
implicitWidth: isVertical ? capsuleHeight : Math.round(layout.implicitWidth + Style.marginXL)
implicitHeight: isVertical ? Math.round(layout.implicitHeight + Style.marginXL) : capsuleHeight
// Content dimensions for implicit sizing
readonly property real contentWidth: isVertical ? capsuleHeight : Math.round(layout.implicitWidth + Style.marginXL)
readonly property real contentHeight: isVertical ? Math.round(layout.implicitHeight + Style.marginXL) : capsuleHeight
Layout.alignment: Qt.AlignVCenter
radius: Style.radiusM
color: Style.capsuleColor
border.color: Style.capsuleBorderColor
border.width: Style.capsuleBorderWidth
implicitWidth: contentWidth
implicitHeight: contentHeight
NPopupContextMenu {
id: contextMenu
@@ -67,10 +66,8 @@ Rectangle {
]
onTriggered: action => {
var popupMenuWindow = PanelService.getPopupMenuWindow(screen);
if (popupMenuWindow) {
popupMenuWindow.close();
}
contextMenu.close();
PanelService.closeContextMenu(screen);
if (action === "widget-settings") {
BarService.openWidgetSettings(screen, section, sectionWidgetIndex, widgetId, widgetSettings);
@@ -78,70 +75,79 @@ Rectangle {
}
}
// Visual capsule centered in parent
Rectangle {
id: visualCapsule
width: root.contentWidth
height: root.contentHeight
anchors.centerIn: parent
radius: Style.radiusM
color: Style.capsuleColor
border.color: Style.capsuleBorderColor
border.width: Style.capsuleBorderWidth
Item {
id: layout
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
implicitWidth: rowLayout.visible ? rowLayout.implicitWidth : colLayout.implicitWidth
implicitHeight: rowLayout.visible ? rowLayout.implicitHeight : colLayout.implicitHeight
RowLayout {
id: rowLayout
visible: !root.isVertical
spacing: 0
NIcon {
visible: root.showCaps && (!root.hideWhenOff || LockKeysService.capsLockOn)
icon: root.capsIcon
color: LockKeysService.capsLockOn ? Color.mTertiary : Qt.alpha(Color.mOnSurfaceVariant, 0.3)
}
NIcon {
visible: root.showNum && (!root.hideWhenOff || LockKeysService.numLockOn)
icon: root.numIcon
color: LockKeysService.numLockOn ? Color.mTertiary : Qt.alpha(Color.mOnSurfaceVariant, 0.3)
}
NIcon {
visible: root.showScroll && (!root.hideWhenOff || LockKeysService.scrollLockOn)
icon: root.scrollIcon
color: LockKeysService.scrollLockOn ? Color.mTertiary : Qt.alpha(Color.mOnSurfaceVariant, 0.3)
}
}
ColumnLayout {
id: colLayout
visible: root.isVertical
spacing: 0
NIcon {
visible: root.showCaps && (!root.hideWhenOff || LockKeysService.capsLockOn)
icon: root.capsIcon
color: LockKeysService.capsLockOn ? Color.mTertiary : Qt.alpha(Color.mOnSurfaceVariant, 0.3)
}
NIcon {
visible: root.showNum && (!root.hideWhenOff || LockKeysService.numLockOn)
icon: root.numIcon
color: LockKeysService.numLockOn ? Color.mTertiary : Qt.alpha(Color.mOnSurfaceVariant, 0.3)
}
NIcon {
visible: root.showScroll && (!root.hideWhenOff || LockKeysService.scrollLockOn)
icon: root.scrollIcon
color: LockKeysService.scrollLockOn ? Color.mTertiary : Qt.alpha(Color.mOnSurfaceVariant, 0.3)
}
}
}
}
// MouseArea at root level for extended click area
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.RightButton
onClicked: mouse => {
if (mouse.button === Qt.RightButton) {
var popupMenuWindow = PanelService.getPopupMenuWindow(screen);
if (popupMenuWindow) {
popupMenuWindow.showContextMenu(contextMenu);
contextMenu.openAtItem(root, screen);
}
PanelService.showContextMenu(contextMenu, root, screen);
}
}
}
Item {
id: layout
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
implicitWidth: rowLayout.visible ? rowLayout.implicitWidth : colLayout.implicitWidth
implicitHeight: rowLayout.visible ? rowLayout.implicitHeight : colLayout.implicitHeight
RowLayout {
id: rowLayout
visible: !root.isVertical
spacing: 0
NIcon {
visible: root.showCaps && (!root.hideWhenOff || LockKeysService.capsLockOn)
icon: root.capsIcon
color: LockKeysService.capsLockOn ? Color.mTertiary : Qt.alpha(Color.mOnSurfaceVariant, 0.3)
}
NIcon {
visible: root.showNum && (!root.hideWhenOff || LockKeysService.numLockOn)
icon: root.numIcon
color: LockKeysService.numLockOn ? Color.mTertiary : Qt.alpha(Color.mOnSurfaceVariant, 0.3)
}
NIcon {
visible: root.showScroll && (!root.hideWhenOff || LockKeysService.scrollLockOn)
icon: root.scrollIcon
color: LockKeysService.scrollLockOn ? Color.mTertiary : Qt.alpha(Color.mOnSurfaceVariant, 0.3)
}
}
ColumnLayout {
id: colLayout
visible: root.isVertical
spacing: 0
NIcon {
visible: root.showCaps && (!root.hideWhenOff || LockKeysService.capsLockOn)
icon: root.capsIcon
color: LockKeysService.capsLockOn ? Color.mTertiary : Qt.alpha(Color.mOnSurfaceVariant, 0.3)
}
NIcon {
visible: root.showNum && (!root.hideWhenOff || LockKeysService.numLockOn)
icon: root.numIcon
color: LockKeysService.numLockOn ? Color.mTertiary : Qt.alpha(Color.mOnSurfaceVariant, 0.3)
}
NIcon {
visible: root.showScroll && (!root.hideWhenOff || LockKeysService.scrollLockOn)
icon: root.scrollIcon
color: LockKeysService.scrollLockOn ? Color.mTertiary : Qt.alpha(Color.mOnSurfaceVariant, 0.3)
}
}
}
}
+50 -38
View File
@@ -11,6 +11,10 @@ import qs.Widgets.AudioSpectrum
Item {
id: root
Layout.preferredHeight: isVertical ? -1 : Style.getBarHeightForScreen(screenName)
Layout.preferredWidth: isVertical ? Style.getBarHeightForScreen(screenName) : -1
Layout.fillHeight: false
Layout.fillWidth: false
property ShellScreen screen
property string widgetId: ""
@@ -20,9 +24,11 @@ Item {
// Settings
property var widgetMetadata: BarWidgetRegistry.widgetMetadata[widgetId]
// Explicit screenName property ensures reactive binding when screen changes
readonly property string screenName: screen ? screen.name : ""
property var widgetSettings: {
if (section && sectionWidgetIndex >= 0) {
var widgets = Settings.getBarWidgetsForScreen(screen?.name)[section];
if (section && sectionWidgetIndex >= 0 && screenName) {
var widgets = Settings.getBarWidgetsForScreen(screenName)[section];
if (widgets && sectionWidgetIndex < widgets.length) {
return widgets[sectionWidgetIndex];
}
@@ -31,10 +37,10 @@ Item {
}
// Bar orientation (per-screen)
readonly property string barPosition: Settings.getBarPositionForScreen(screen?.name)
readonly property string barPosition: Settings.getBarPositionForScreen(screenName)
readonly property bool isVertical: barPosition === "left" || barPosition === "right"
readonly property real capsuleHeight: Style.getCapsuleHeightForScreen(screen?.name)
readonly property real barFontSize: Style.getBarFontSizeForScreen(screen?.name)
readonly property real capsuleHeight: Style.getCapsuleHeightForScreen(screenName)
readonly property real barFontSize: Style.getBarFontSizeForScreen(screenName)
// Widget settings
readonly property string hideMode: (widgetSettings.hideMode !== undefined) ? widgetSettings.hideMode : "hidden"
@@ -205,9 +211,8 @@ Item {
}
onTriggered: action => {
var popupWindow = PanelService.getPopupMenuWindow(screen);
if (popupWindow)
popupWindow.close();
contextMenu.close();
PanelService.closeContextMenu(screen);
if (action === "play-pause")
MediaService.playPause();
@@ -226,11 +231,11 @@ Item {
}
}
// Main container
// Main container - stays at content size, pixel-perfect centered in parent
Rectangle {
id: container
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
x: Style.pixelAlignCenter(parent.width, width)
y: Style.pixelAlignCenter(parent.height, height)
width: isVertical ? (isHidden ? 0 : verticalSize) : (isHidden ? 0 : contentWidth)
height: isVertical ? (isHidden ? 0 : verticalSize) : capsuleHeight
radius: Style.radiusM
@@ -329,6 +334,7 @@ Item {
}
cursorShape: hasPlayer ? Qt.PointingHandCursor : Qt.ArrowCursor
maxWidth: root.maxWidth - root.mainContentWidth
forcedHover: mainMouseArea.containsMouse
NText {
// anchors.fill: parent
color: hasPlayer ? Color.mOnSurface : Color.mOnSurfaceVariant
@@ -365,37 +371,43 @@ Item {
}
}
// Mouse interaction
MouseArea {
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton
// Mouse interaction moved to root
}
}
onClicked: mouse => {
if (mouse.button === Qt.LeftButton) {
PanelService.getPanel("mediaPlayerPanel", screen)?.toggle(container);
} else if (mouse.button === Qt.RightButton) {
TooltipService.hide();
var popupWindow = PanelService.getPopupMenuWindow(screen);
if (popupWindow) {
popupWindow.showContextMenu(contextMenu);
contextMenu.openAtItem(container, screen);
}
} else if (mouse.button === Qt.MiddleButton && hasPlayer) {
MediaService.playPause();
TooltipService.hide();
}
}
// Mouse interaction
MouseArea {
id: mainMouseArea
anchors.fill: parent
onEntered: {
if (isVertical || scrollingMode === "never") {
TooltipService.show(root, title, BarService.getTooltipDirection(root.screen?.name));
}
}
onExited: TooltipService.hide()
// Extend click area to screen edge if widget is at the start/end
anchors.leftMargin: (!isVertical && section === "left" && sectionWidgetIndex === 0) ? -Style.marginS : 0
anchors.rightMargin: (!isVertical && section === "right" && sectionWidgetIndex === sectionWidgetsCount - 1) ? -Style.marginS : 0
anchors.topMargin: (isVertical && section === "left" && sectionWidgetIndex === 0) ? -Style.marginM : 0
anchors.bottomMargin: (isVertical && section === "right" && sectionWidgetIndex === sectionWidgetsCount - 1) ? -Style.marginM : 0
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton
onClicked: mouse => {
if (mouse.button === Qt.LeftButton) {
PanelService.getPanel("mediaPlayerPanel", screen)?.toggle(container);
} else if (mouse.button === Qt.RightButton) {
TooltipService.hide();
PanelService.showContextMenu(contextMenu, container, screen);
} else if (mouse.button === Qt.MiddleButton && hasPlayer) {
MediaService.playPause();
TooltipService.hide();
}
}
onEntered: {
if (isVertical || scrollingMode === "never") {
TooltipService.show(root, title, BarService.getTooltipDirection(root.screen?.name));
}
}
onExited: TooltipService.hide()
}
// Components
+8 -12
View File
@@ -22,9 +22,11 @@ Item {
property int sectionWidgetsCount: 0
property var widgetMetadata: BarWidgetRegistry.widgetMetadata[widgetId]
// Explicit screenName property ensures reactive binding when screen changes
readonly property string screenName: screen ? screen.name : ""
property var widgetSettings: {
if (section && sectionWidgetIndex >= 0) {
var widgets = Settings.getBarWidgetsForScreen(screen?.name)[section];
if (section && sectionWidgetIndex >= 0 && screenName) {
var widgets = Settings.getBarWidgetsForScreen(screenName)[section];
if (widgets && sectionWidgetIndex < widgets.length) {
return widgets[sectionWidgetIndex];
}
@@ -32,7 +34,7 @@ Item {
return {};
}
readonly property string barPosition: Settings.getBarPositionForScreen(screen?.name)
readonly property string barPosition: Settings.getBarPositionForScreen(screenName)
readonly property bool isBarVertical: barPosition === "left" || barPosition === "right"
readonly property string displayMode: (widgetSettings.displayMode !== undefined) ? widgetSettings.displayMode : widgetMetadata.displayMode
readonly property string middleClickCommand: (widgetSettings.middleClickCommand !== undefined) ? widgetSettings.middleClickCommand : widgetMetadata.middleClickCommand
@@ -109,10 +111,8 @@ Item {
]
onTriggered: action => {
var popupMenuWindow = PanelService.getPopupMenuWindow(screen);
if (popupMenuWindow) {
popupMenuWindow.close();
}
contextMenu.close();
PanelService.closeContextMenu(screen);
if (action === "toggle-mute") {
AudioService.setInputMuted(!AudioService.inputMuted);
@@ -164,11 +164,7 @@ Item {
PanelService.getPanel("audioPanel", screen)?.toggle(this);
}
onRightClicked: {
var popupMenuWindow = PanelService.getPopupMenuWindow(screen);
if (popupMenuWindow) {
popupMenuWindow.showContextMenu(contextMenu);
contextMenu.openAtItem(pill, screen);
}
PanelService.showContextMenu(contextMenu, pill, screen);
}
onMiddleClicked: {
Quickshell.execDetached(["sh", "-lc", middleClickCommand]);
+8 -12
View File
@@ -19,9 +19,11 @@ Item {
property int sectionWidgetsCount: 0
property var widgetMetadata: BarWidgetRegistry.widgetMetadata[widgetId]
// Explicit screenName property ensures reactive binding when screen changes
readonly property string screenName: screen ? screen.name : ""
property var widgetSettings: {
if (section && sectionWidgetIndex >= 0) {
var widgets = Settings.getBarWidgetsForScreen(screen?.name)[section];
if (section && sectionWidgetIndex >= 0 && screenName) {
var widgets = Settings.getBarWidgetsForScreen(screenName)[section];
if (widgets && sectionWidgetIndex < widgets.length) {
return widgets[sectionWidgetIndex];
}
@@ -29,7 +31,7 @@ Item {
return {};
}
readonly property string barPosition: Settings.getBarPositionForScreen(screen?.name)
readonly property string barPosition: Settings.getBarPositionForScreen(screenName)
readonly property bool isBarVertical: barPosition === "left" || barPosition === "right"
readonly property string displayMode: widgetSettings.displayMode !== undefined ? widgetSettings.displayMode : widgetMetadata.displayMode
@@ -53,10 +55,8 @@ Item {
]
onTriggered: action => {
var popupMenuWindow = PanelService.getPopupMenuWindow(screen);
if (popupMenuWindow) {
popupMenuWindow.close();
}
contextMenu.close();
PanelService.closeContextMenu(screen);
if (action === "toggle-wifi") {
NetworkService.setWifiEnabled(!Settings.data.network.wifiEnabled);
@@ -115,11 +115,7 @@ Item {
panel?.toggle(this);
}
onRightClicked: {
var popupMenuWindow = PanelService.getPopupMenuWindow(screen);
if (popupMenuWindow) {
popupMenuWindow.showContextMenu(contextMenu);
contextMenu.openAtItem(pill, screen);
}
PanelService.showContextMenu(contextMenu, pill, screen);
}
tooltipText: {
try {
+21 -17
View File
@@ -21,9 +21,11 @@ NIconButton {
property int sectionWidgetsCount: 0
property var widgetMetadata: BarWidgetRegistry.widgetMetadata[widgetId]
// Explicit screenName property ensures reactive binding when screen changes
readonly property string screenName: screen ? screen.name : ""
property var widgetSettings: {
if (section && sectionWidgetIndex >= 0) {
var widgets = Settings.getBarWidgetsForScreen(screen?.name)[section];
if (section && sectionWidgetIndex >= 0 && screenName) {
var widgets = Settings.getBarWidgetsForScreen(screenName)[section];
if (widgets && sectionWidgetIndex < widgets.length) {
return widgets[sectionWidgetIndex];
}
@@ -33,6 +35,14 @@ NIconButton {
readonly property bool showUnreadBadge: (widgetSettings.showUnreadBadge !== undefined) ? widgetSettings.showUnreadBadge : widgetMetadata.showUnreadBadge
readonly property bool hideWhenZero: (widgetSettings.hideWhenZero !== undefined) ? widgetSettings.hideWhenZero : widgetMetadata.hideWhenZero
readonly property bool hideWhenZeroUnread: (widgetSettings.hideWhenZeroUnread !== undefined) ? widgetSettings.hideWhenZeroUnread : widgetMetadata.hideWhenZeroUnread
readonly property string unreadBadgeColor: (widgetSettings.unreadBadgeColor !== undefined) ? widgetSettings.unreadBadgeColor : (widgetMetadata.unreadBadgeColor || "primary")
readonly property var colorMap: {
"primary": Color.mPrimary,
"secondary": Color.mSecondary,
"tertiary": Color.mTertiary,
"onSurface": Color.mOnSurface
}
function computeUnreadCount() {
var since = NotificationService.lastSeenTs;
@@ -86,10 +96,8 @@ NIconButton {
]
onTriggered: action => {
var popupMenuWindow = PanelService.getPopupMenuWindow(screen);
if (popupMenuWindow) {
popupMenuWindow.close();
}
contextMenu.close();
PanelService.closeContextMenu(screen);
if (action === "toggle-dnd") {
NotificationService.doNotDisturb = !NotificationService.doNotDisturb;
@@ -107,26 +115,22 @@ NIconButton {
}
onRightClicked: {
var popupMenuWindow = PanelService.getPopupMenuWindow(screen);
if (popupMenuWindow) {
popupMenuWindow.showContextMenu(contextMenu);
contextMenu.openAtItem(root, screen);
}
PanelService.showContextMenu(contextMenu, root, screen);
}
Loader {
anchors.right: parent.right
anchors.top: parent.top
anchors.rightMargin: 2
anchors.topMargin: 1
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenterOffset: parent.baseSize / 4
anchors.verticalCenterOffset: -parent.baseSize / 4
z: 2
active: showUnreadBadge
sourceComponent: Rectangle {
id: badge
height: 8
height: 7
width: height
radius: Style.radiusXS
color: Color.mError
color: root.colorMap[root.unreadBadgeColor] || Color.mError
border.color: Color.mSurface
border.width: Style.borderS
visible: count > 0
+9 -13
View File
@@ -19,9 +19,11 @@ NIconButton {
property int sectionWidgetsCount: 0
property var widgetMetadata: BarWidgetRegistry.widgetMetadata[widgetId]
// Explicit screenName property ensures reactive binding when screen changes
readonly property string screenName: screen ? screen.name : ""
property var widgetSettings: {
if (section && sectionWidgetIndex >= 0) {
var widgets = Settings.getBarWidgetsForScreen(screen?.name)[section];
if (section && sectionWidgetIndex >= 0 && screenName) {
var widgets = Settings.getBarWidgetsForScreen(screenName)[section];
if (widgets && sectionWidgetIndex < widgets.length) {
return widgets[sectionWidgetIndex];
}
@@ -47,12 +49,12 @@ NIconButton {
}
}
baseSize: Style.getCapsuleHeightForScreen(screen?.name)
baseSize: Style.getCapsuleHeightForScreen(screenName)
applyUiScale: false
customRadius: Style.radiusL
icon: "power"
tooltipText: I18n.tr("tooltips.session-menu")
tooltipDirection: BarService.getTooltipDirection(screen?.name)
tooltipDirection: BarService.getTooltipDirection(screenName)
colorBg: Style.capsuleColor
colorFg: root.iconColor
colorBorder: "transparent"
@@ -72,10 +74,8 @@ NIconButton {
]
onTriggered: action => {
var popupMenuWindow = PanelService.getPopupMenuWindow(screen);
if (popupMenuWindow) {
popupMenuWindow.close();
}
contextMenu.close();
PanelService.closeContextMenu(screen);
if (action === "widget-settings") {
BarService.openWidgetSettings(screen, section, sectionWidgetIndex, widgetId, widgetSettings);
@@ -85,10 +85,6 @@ NIconButton {
onClicked: PanelService.getPanel("sessionMenuPanel", screen)?.toggle()
onRightClicked: {
var popupMenuWindow = PanelService.getPopupMenuWindow(screen);
if (popupMenuWindow) {
popupMenuWindow.showContextMenu(contextMenu);
contextMenu.openAtItem(root, screen);
}
PanelService.showContextMenu(contextMenu, root, screen);
}
}
+6 -4
View File
@@ -17,9 +17,11 @@ Item {
property int sectionWidgetsCount: 0
property var widgetMetadata: BarWidgetRegistry.widgetMetadata[widgetId]
// Explicit screenName property ensures reactive binding when screen changes
readonly property string screenName: screen ? screen.name : ""
property var widgetSettings: {
if (section && sectionWidgetIndex >= 0) {
var widgets = Settings.getBarWidgetsForScreen(screen?.name)[section];
if (section && sectionWidgetIndex >= 0 && screenName) {
var widgets = Settings.getBarWidgetsForScreen(screenName)[section];
if (widgets && sectionWidgetIndex < widgets.length) {
return widgets[sectionWidgetIndex];
}
@@ -27,9 +29,9 @@ Item {
return {};
}
readonly property string barPosition: Settings.getBarPositionForScreen(screen?.name)
readonly property string barPosition: Settings.getBarPositionForScreen(screenName)
readonly property bool isBarVertical: barPosition === "left" || barPosition === "right"
readonly property real barHeight: Style.getBarHeightForScreen(screen?.name)
readonly property real barHeight: Style.getBarHeightForScreen(screenName)
readonly property int spacerSize: widgetSettings.width !== undefined ? widgetSettings.width : widgetMetadata.width
implicitWidth: isBarVertical ? barHeight : spacerSize
File diff suppressed because it is too large Load Diff
+293 -303
View File
@@ -10,7 +10,7 @@ import qs.Services.System
import qs.Services.UI
import qs.Widgets
Rectangle {
Item {
id: root
property ShellScreen screen
@@ -21,16 +21,18 @@ Rectangle {
property int sectionWidgetIndex: -1
property int sectionWidgetsCount: 0
readonly property string barPosition: Settings.getBarPositionForScreen(screen?.name)
// Explicit screenName property ensures reactive binding when screen changes
readonly property string screenName: screen ? screen.name : ""
readonly property string barPosition: Settings.getBarPositionForScreen(screenName)
readonly property bool isVerticalBar: barPosition === "left" || barPosition === "right"
readonly property real barHeight: Style.getBarHeightForScreen(screen?.name)
readonly property real capsuleHeight: Style.getCapsuleHeightForScreen(screen?.name)
readonly property real barFontSize: Style.getBarFontSizeForScreen(screen?.name)
readonly property real barHeight: Style.getBarHeightForScreen(screenName)
readonly property real capsuleHeight: Style.getCapsuleHeightForScreen(screenName)
readonly property real barFontSize: Style.getBarFontSizeForScreen(screenName)
property var widgetMetadata: BarWidgetRegistry.widgetMetadata[widgetId]
property var widgetSettings: {
if (section && sectionWidgetIndex >= 0) {
var widgets = Settings.getBarWidgetsForScreen(screen?.name)[section];
if (section && sectionWidgetIndex >= 0 && screenName) {
var widgets = Settings.getBarWidgetsForScreen(screenName)[section];
if (widgets && sectionWidgetIndex < widgets.length) {
return widgets[sectionWidgetIndex];
}
@@ -59,19 +61,21 @@ Rectangle {
}
readonly property int titleWidth: {
if (smartWidth && showTitle && !isVerticalBar && combinedModel.length > 0) {
var entriesCount = combinedModel.length;
var baseWidth = 140;
var calculatedWidth = baseWidth / Math.sqrt(entriesCount);
// First, use user-defined title width if set
var calculatedWidth = (widgetSettings.titleWidth !== undefined) ? widgetSettings.titleWidth : widgetMetadata.titleWidth;
// Second, shrink title width if it exceeds maxTaskbarWidth when smartWidth is enabled
if (smartWidth && combinedModel.length > 0) {
if (maxTaskbarWidth > 0) {
var entriesCount = combinedModel.length;
var maxWidthPerEntry = (maxTaskbarWidth / entriesCount) - itemSize - Style.marginS - Style.marginXL;
calculatedWidth = Math.min(calculatedWidth, maxWidthPerEntry);
}
return Math.max(Math.round(calculatedWidth), 20);
calculatedWidth = Math.max(Math.round(calculatedWidth), 20);
}
return (widgetSettings.titleWidth !== undefined) ? widgetSettings.titleWidth : widgetMetadata.titleWidth;
return calculatedWidth;
}
readonly property bool showPinnedApps: (widgetSettings.showPinnedApps !== undefined) ? widgetSettings.showPinnedApps : widgetMetadata.showPinnedApps
@@ -458,10 +462,8 @@ Rectangle {
return items;
}
onTriggered: (action, item) => {
var popupMenuWindow = PanelService.getPopupMenuWindow(root.screen);
if (popupMenuWindow) {
popupMenuWindow.close();
}
contextMenu.close();
PanelService.closeContextMenu(root.screen);
// Look up the window fresh each time to avoid stale references
const selectedWindow = root.getSelectedWindow();
@@ -589,7 +591,8 @@ Rectangle {
}
}
implicitWidth: {
// Content dimensions for implicit sizing
readonly property real contentWidth: {
if (!visible)
return 0;
if (isVerticalBar)
@@ -604,311 +607,322 @@ Rectangle {
return Math.round(calculatedWidth);
}
implicitHeight: visible ? (isVerticalBar ? Math.round(taskbarLayout.implicitHeight + Style.marginXL) : capsuleHeight) : 0
radius: Style.radiusM
color: Style.capsuleColor
border.color: Style.capsuleBorderColor
border.width: Style.capsuleBorderWidth
readonly property real contentHeight: visible ? (isVerticalBar ? Math.round(taskbarLayout.implicitHeight + Style.marginXL) : capsuleHeight) : 0
GridLayout {
id: taskbarLayout
implicitWidth: contentWidth
implicitHeight: contentHeight
// Pixel-perfect centering
x: isVerticalBar ? Style.pixelAlignCenter(parent.width, width) : ((root.showTitle) ? Style.pixelAlignCenter(parent.width, width) : Style.marginM)
y: Style.pixelAlignCenter(parent.height, height)
// Visual capsule centered in parent
Rectangle {
id: visualCapsule
width: root.contentWidth
height: root.contentHeight
anchors.centerIn: parent
radius: Style.radiusM
color: Style.capsuleColor
border.color: Style.capsuleBorderColor
border.width: Style.capsuleBorderWidth
// Configure GridLayout to behave like RowLayout or ColumnLayout
rows: isVerticalBar ? -1 : 1 // -1 means unlimited
columns: isVerticalBar ? 1 : -1 // -1 means unlimited
GridLayout {
id: taskbarLayout
rowSpacing: isVerticalBar ? Style.marginXXS : 0
columnSpacing: isVerticalBar ? 0 : Style.marginXXS
// Pixel-perfect centering
x: isVerticalBar ? Style.pixelAlignCenter(parent.width, width) : ((root.showTitle) ? Style.pixelAlignCenter(parent.width, width) : Style.marginM)
y: Style.pixelAlignCenter(parent.height, height)
Repeater {
model: root.combinedModel
delegate: Item {
id: taskbarItem
required property var modelData
required property int index
property ShellScreen screen: root.screen
// Configure GridLayout to behave like RowLayout or ColumnLayout
rows: isVerticalBar ? -1 : 1 // -1 means unlimited
columns: isVerticalBar ? 1 : -1 // -1 means unlimited
readonly property bool isRunning: modelData.window !== null
readonly property bool isPinned: modelData.type === "pinned" || modelData.type === "pinned-running"
readonly property bool isFocused: isRunning && modelData.window && modelData.window.isFocused
readonly property bool isPinnedRunning: isPinned && isRunning && !isFocused
readonly property bool isHovered: root.hoveredWindowId === modelData.id
rowSpacing: isVerticalBar ? Style.marginXXS : 0
columnSpacing: isVerticalBar ? 0 : Style.marginXXS
readonly property bool shouldShowTitle: root.showTitle && modelData.type !== "pinned"
readonly property real itemSpacing: Style.marginS
readonly property real contentWidth: shouldShowTitle ? root.itemSize + itemSpacing + root.titleWidth : root.itemSize
Repeater {
model: root.combinedModel
delegate: Item {
id: taskbarItem
required property var modelData
required property int index
property ShellScreen screen: root.screen
readonly property string title: modelData.title || modelData.appId || "Unknown application"
readonly property color titleBgColor: (isHovered || isFocused) ? Color.mHover : Style.capsuleColor
readonly property color titleFgColor: (isHovered || isFocused) ? Color.mOnHover : Color.mOnSurface
readonly property bool isRunning: modelData.window !== null
readonly property bool isPinned: modelData.type === "pinned" || modelData.type === "pinned-running"
readonly property bool isFocused: isRunning && modelData.window && modelData.window.isFocused
readonly property bool isPinnedRunning: isPinned && isRunning && !isFocused
readonly property bool isHovered: root.hoveredWindowId === modelData.id
Layout.preferredWidth: root.showTitle ? Math.round(contentWidth + Style.marginXL) : Math.round(contentWidth) // Add margins for both pinned and running apps
Layout.preferredHeight: root.itemSize
Layout.alignment: Qt.AlignCenter
readonly property bool shouldShowTitle: root.showTitle && modelData.type !== "pinned"
readonly property real itemSpacing: Style.marginS
readonly property real contentWidth: shouldShowTitle ? root.itemSize + itemSpacing + root.titleWidth : root.itemSize
// Ensure dragged item is on top
z: (root.dragSourceIndex === index) ? 1000 : 1
readonly property string title: modelData.title || modelData.appId || "Unknown application"
readonly property color titleBgColor: (isHovered || isFocused) ? Color.mHover : Style.capsuleColor
readonly property color titleFgColor: (isHovered || isFocused) ? Color.mOnHover : Color.mOnSurface
property int modelIndex: index
objectName: "taskbarAppItem"
Layout.preferredWidth: root.showTitle ? Math.round(contentWidth + Style.marginXL) : Math.round(contentWidth) // Add margins for both pinned and running apps
Layout.preferredHeight: root.itemSize
Layout.alignment: Qt.AlignCenter
DropArea {
anchors.fill: parent
keys: ["taskbar-app"]
onEntered: function (drag) {
if (drag.source && drag.source.objectName === "taskbarAppItem") {
root.dragTargetIndex = taskbarItem.modelIndex;
// Ensure dragged item is on top
z: (root.dragSourceIndex === index) ? 1000 : 1
property int modelIndex: index
objectName: "taskbarAppItem"
DropArea {
anchors.fill: parent
keys: ["taskbar-app"]
onEntered: function (drag) {
if (drag.source && drag.source.objectName === "taskbarAppItem") {
root.dragTargetIndex = taskbarItem.modelIndex;
}
}
}
onExited: function () {
if (root.dragTargetIndex === taskbarItem.modelIndex) {
onExited: function () {
if (root.dragTargetIndex === taskbarItem.modelIndex) {
root.dragTargetIndex = -1;
}
}
onDropped: function (drop) {
root.dragSourceIndex = -1;
root.dragTargetIndex = -1;
}
}
onDropped: function (drop) {
root.dragSourceIndex = -1;
root.dragTargetIndex = -1;
Logger.d("Taskbar", "Dropped! Source: " + (drop.source ? drop.source.objectName : "null") + " Index: " + (drop.source ? drop.source.modelIndex : "?") + " -> Target Index: " + taskbarItem.modelIndex);
if (drop.source && drop.source.objectName === "taskbarAppItem" && drop.source !== taskbarItem) {
root.reorderApps(drop.source.modelIndex, taskbarItem.modelIndex);
} else {
Logger.d("Taskbar", "Drop ignored. Source objectName: " + (drop.source ? drop.source.objectName : "null"));
}
}
}
Item {
id: draggableContent
width: parent.width
height: parent.height
anchors.centerIn: dragging ? undefined : parent
// Visual shifting logic
readonly property bool isDragged: root.dragSourceIndex === index
property real shiftOffset: 0
// Calculate shift based on drag state
// If I am NOT the dragged item, but I am in the path of the drag
Binding on shiftOffset {
value: {
if (root.dragSourceIndex !== -1 && root.dragTargetIndex !== -1 && !draggableContent.isDragged) {
if (root.dragSourceIndex < root.dragTargetIndex) {
// Dragging Right: Items between source and target shift Left
if (index > root.dragSourceIndex && index <= root.dragTargetIndex) {
return -1 * (root.isVerticalBar ? root.itemSize : draggableContent.width); // Simple approximation, could be refined
}
} else if (root.dragSourceIndex > root.dragTargetIndex) {
// Dragging Left: Items between target and source shift Right
if (index >= root.dragTargetIndex && index < root.dragSourceIndex) {
return (root.isVerticalBar ? root.itemSize : draggableContent.width);
}
}
}
return 0;
}
}
transform: Translate {
x: !root.isVerticalBar ? draggableContent.shiftOffset : 0
y: root.isVerticalBar ? draggableContent.shiftOffset : 0
Behavior on x {
NumberAnimation {
duration: Style.animationFast
easing.type: Easing.OutQuad
}
}
Behavior on y {
NumberAnimation {
duration: Style.animationFast
easing.type: Easing.OutQuad
Logger.d("Taskbar", "Dropped! Source: " + (drop.source ? drop.source.objectName : "null") + " Index: " + (drop.source ? drop.source.modelIndex : "?") + " -> Target Index: " + taskbarItem.modelIndex);
if (drop.source && drop.source.objectName === "taskbarAppItem" && drop.source !== taskbarItem) {
root.reorderApps(drop.source.modelIndex, taskbarItem.modelIndex);
} else {
Logger.d("Taskbar", "Drop ignored. Source objectName: " + (drop.source ? drop.source.objectName : "null"));
}
}
}
property bool dragging: taskbarMouseArea.drag.active
onDraggingChanged: {
if (dragging) {
root.dragSourceIndex = index;
} else {
// Don't reset immediately on release to allow drop to handle it,
// or use a timer if needed, but drop handler usually fires.
// However, if dropped outside, we need to reset.
// Let's reset if not handled by drop area quickly?
// Actually, drag.active becomes false on release.
// We might want to clear it if no drop happened.
if (root.dragSourceIndex === index) {
// Slight delay/check? For now, let DropArea handle reset on success.
// If cancelled (dropped nowhere), we should reset.
Qt.callLater(() => {
if (!taskbarMouseArea.drag.active && root.dragSourceIndex === index) {
root.dragSourceIndex = -1;
root.dragTargetIndex = -1;
}
});
}
}
}
Drag.active: dragging
Drag.source: taskbarItem
Drag.hotSpot.x: width / 2
Drag.hotSpot.y: height / 2
Drag.keys: ["taskbar-app"]
z: dragging ? 1000 : 0
scale: dragging ? 1.05 : 1.0
Behavior on scale {
NumberAnimation {
duration: Style.animationFast
}
}
Rectangle {
id: titleBackground
visible: shouldShowTitle
anchors.centerIn: parent
Item {
id: draggableContent
width: parent.width
height: root.height
color: titleBgColor
radius: Style.radiusM
height: parent.height
anchors.centerIn: dragging ? undefined : parent
Behavior on color {
ColorAnimation {
duration: Style.animationFast
easing.type: Easing.InOutQuad
// Visual shifting logic
readonly property bool isDragged: root.dragSourceIndex === index
property real shiftOffset: 0
// Calculate shift based on drag state
// If I am NOT the dragged item, but I am in the path of the drag
Binding on shiftOffset {
value: {
if (root.dragSourceIndex !== -1 && root.dragTargetIndex !== -1 && !draggableContent.isDragged) {
if (root.dragSourceIndex < root.dragTargetIndex) {
// Dragging Right: Items between source and target shift Left
if (index > root.dragSourceIndex && index <= root.dragTargetIndex) {
return -1 * (root.isVerticalBar ? root.itemSize : draggableContent.width); // Simple approximation, could be refined
}
} else if (root.dragSourceIndex > root.dragTargetIndex) {
// Dragging Left: Items between target and source shift Right
if (index >= root.dragTargetIndex && index < root.dragSourceIndex) {
return (root.isVerticalBar ? root.itemSize : draggableContent.width);
}
}
}
return 0;
}
}
}
Rectangle {
anchors.centerIn: parent
width: taskbarItem.contentWidth
height: parent.height
color: "transparent"
transform: Translate {
x: !root.isVerticalBar ? draggableContent.shiftOffset : 0
y: root.isVerticalBar ? draggableContent.shiftOffset : 0
RowLayout {
id: itemLayout
anchors.fill: parent
spacing: taskbarItem.itemSpacing
Behavior on x {
NumberAnimation {
duration: Style.animationFast
easing.type: Easing.OutQuad
}
}
Behavior on y {
NumberAnimation {
duration: Style.animationFast
easing.type: Easing.OutQuad
}
}
}
Item {
Layout.preferredWidth: root.itemSize
Layout.preferredHeight: root.itemSize
Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
property bool dragging: taskbarMouseArea.drag.active
onDraggingChanged: {
if (dragging) {
root.dragSourceIndex = index;
} else {
// Don't reset immediately on release to allow drop to handle it,
// or use a timer if needed, but drop handler usually fires.
// However, if dropped outside, we need to reset.
// Let's reset if not handled by drop area quickly?
// Actually, drag.active becomes false on release.
// We might want to clear it if no drop happened.
if (root.dragSourceIndex === index) {
// Slight delay/check? For now, let DropArea handle reset on success.
// If cancelled (dropped nowhere), we should reset.
Qt.callLater(() => {
if (!taskbarMouseArea.drag.active && root.dragSourceIndex === index) {
root.dragSourceIndex = -1;
root.dragTargetIndex = -1;
}
});
}
}
}
IconImage {
id: appIcon
anchors.fill: parent
Drag.active: dragging
Drag.source: taskbarItem
Drag.hotSpot.x: width / 2
Drag.hotSpot.y: height / 2
Drag.keys: ["taskbar-app"]
source: ThemeIcons.iconForAppId(taskbarItem.modelData.appId)
smooth: true
asynchronous: true
z: dragging ? 1000 : 0
scale: dragging ? 1.05 : 1.0
Behavior on scale {
NumberAnimation {
duration: Style.animationFast
}
}
// Apply dock shader to all taskbar icons
layer.enabled: widgetSettings.colorizeIcons !== false
layer.effect: ShaderEffect {
property color targetColor: Settings.data.colorSchemes.darkMode ? Color.mOnSurface : Color.mSurfaceVariant
property real colorizeMode: 0.0 // Dock mode (grayscale)
Rectangle {
id: titleBackground
visible: shouldShowTitle
anchors.centerIn: parent
width: parent.width
height: root.height
color: titleBgColor
radius: Style.radiusM
fragmentShader: Qt.resolvedUrl(Quickshell.shellDir + "/Shaders/qsb/appicon_colorize.frag.qsb")
Behavior on color {
ColorAnimation {
duration: Style.animationFast
easing.type: Easing.InOutQuad
}
}
}
Rectangle {
anchors.centerIn: parent
width: taskbarItem.contentWidth
height: parent.height
color: "transparent"
RowLayout {
id: itemLayout
anchors.fill: parent
spacing: taskbarItem.itemSpacing
Item {
Layout.preferredWidth: root.itemSize
Layout.preferredHeight: root.itemSize
Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
IconImage {
id: appIcon
anchors.fill: parent
source: ThemeIcons.iconForAppId(taskbarItem.modelData.appId)
smooth: true
asynchronous: true
// Apply dock shader to all taskbar icons
layer.enabled: widgetSettings.colorizeIcons !== false
layer.effect: ShaderEffect {
property color targetColor: Settings.data.colorSchemes.darkMode ? Color.mOnSurface : Color.mSurfaceVariant
property real colorizeMode: 0.0 // Dock mode (grayscale)
fragmentShader: Qt.resolvedUrl(Quickshell.shellDir + "/Shaders/qsb/appicon_colorize.frag.qsb")
}
}
Rectangle {
id: iconBackground
visible: !shouldShowTitle
anchors.bottomMargin: -2
anchors.bottom: parent.bottom
anchors.horizontalCenter: parent.horizontalCenter
width: Style.toOdd(root.itemSize * 0.25)
height: 4
color: taskbarItem.isFocused ? Color.mPrimary : "transparent"
radius: Math.min(Style.radiusXXS, width / 2)
}
}
Rectangle {
id: iconBackground
visible: !shouldShowTitle
anchors.bottomMargin: -2
anchors.bottom: parent.bottom
anchors.horizontalCenter: parent.horizontalCenter
width: Style.toOdd(root.itemSize * 0.25)
height: 4
color: taskbarItem.isFocused ? Color.mPrimary : "transparent"
radius: Math.min(Style.radiusXXS, width / 2)
NText {
id: titleText
visible: shouldShowTitle
Layout.preferredWidth: root.titleWidth
Layout.preferredHeight: root.itemSize
Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
Layout.fillWidth: false
text: taskbarItem.title
elide: Text.ElideRight
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignLeft
pointSize: barFontSize
color: titleFgColor
opacity: Style.opacityFull
}
}
NText {
id: titleText
visible: shouldShowTitle
Layout.preferredWidth: root.titleWidth
Layout.preferredHeight: root.itemSize
Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
Layout.fillWidth: false
text: taskbarItem.title
elide: Text.ElideRight
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignLeft
pointSize: barFontSize
color: titleFgColor
opacity: Style.opacityFull
}
}
}
}
MouseArea {
id: taskbarMouseArea
objectName: "taskbarMouseArea"
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
acceptedButtons: Qt.LeftButton | Qt.RightButton
MouseArea {
id: taskbarMouseArea
objectName: "taskbarMouseArea"
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
acceptedButtons: Qt.LeftButton | Qt.RightButton
drag.target: draggableContent
drag.axis: root.isVerticalBar ? Drag.YAxis : Drag.XAxis
preventStealing: true
drag.target: draggableContent
drag.axis: root.isVerticalBar ? Drag.YAxis : Drag.XAxis
preventStealing: true
onPressed: {
// Constrain drag to roughly the taskbar area but allow some freedom
// Or just let it be free since we only care about drops
}
onReleased: {
if (draggableContent.Drag.active) {
draggableContent.Drag.drop();
onPressed: {
// Constrain drag to roughly the taskbar area but allow some freedom
// Or just let it be free since we only care about drops
}
}
onClicked: function (mouse) {
if (!modelData)
return;
if (mouse.button === Qt.LeftButton) {
if (isRunning && modelData.window) {
// Running app - focus it
try {
CompositorService.focusWindow(modelData.window);
} catch (error) {
Logger.e("Taskbar", "Failed to activate toplevel: " + error);
}
} else if (isPinned) {
// Pinned app not running - launch it
root.launchPinnedApp(modelData.appId);
onReleased: {
if (draggableContent.Drag.active) {
draggableContent.Drag.drop();
}
} else if (mouse.button === Qt.RightButton) {
}
onClicked: function (mouse) {
if (!modelData)
return;
if (mouse.button === Qt.LeftButton) {
if (isRunning && modelData.window) {
// Running app - focus it
try {
CompositorService.focusWindow(modelData.window);
} catch (error) {
Logger.e("Taskbar", "Failed to activate toplevel: " + error);
}
} else if (isPinned) {
// Pinned app not running - launch it
root.launchPinnedApp(modelData.appId);
}
} else if (mouse.button === Qt.RightButton) {
TooltipService.hide();
// Only show context menu for running apps
if (isRunning && modelData.window) {
root.selectedWindowId = modelData.id;
root.selectedAppId = modelData.appId;
root.openTaskbarContextMenu(taskbarItem);
}
}
}
onEntered: {
root.hoveredWindowId = taskbarItem.modelData.id;
TooltipService.show(taskbarItem, taskbarItem.title, BarService.getTooltipDirection(root.screen?.name));
}
onExited: {
root.hoveredWindowId = "";
TooltipService.hide();
// Only show context menu for running apps
if (isRunning && modelData.window) {
root.selectedWindowId = modelData.id;
root.selectedAppId = modelData.appId;
root.openTaskbarContextMenu(taskbarItem);
}
}
}
onEntered: {
root.hoveredWindowId = taskbarItem.modelData.id;
TooltipService.show(taskbarItem, taskbarItem.title, BarService.getTooltipDirection(root.screen?.name));
}
onExited: {
root.hoveredWindowId = "";
TooltipService.hide();
}
}
}
}
@@ -964,30 +978,6 @@ Rectangle {
// Set the model directly
contextMenu.model = items;
var popupMenuWindow = PanelService.getPopupMenuWindow(screen);
if (popupMenuWindow) {
popupMenuWindow.open();
// Calculate menu position
const globalPos = item.mapToItem(root, 0, 0);
let menuX, menuY;
if (root.barPosition === "top") {
menuX = globalPos.x + (item.width / 2) - (contextMenu.implicitWidth / 2);
menuY = barHeight + Style.marginS;
} else if (root.barPosition === "bottom") {
const menuHeight = 12 + contextMenu.model.length * contextMenu.itemHeight;
menuX = globalPos.x + (item.width / 2) - (contextMenu.implicitWidth / 2);
menuY = -menuHeight - Style.marginS;
} else if (root.barPosition === "left") {
menuX = barHeight + Style.marginS;
menuY = globalPos.y + (item.height / 2) - (contextMenu.implicitHeight / 2);
} else {
// right
menuX = -contextMenu.implicitWidth - Style.marginS;
menuY = globalPos.y + (item.height / 2) - (contextMenu.implicitHeight / 2);
}
popupMenuWindow.showContextMenu(contextMenu);
contextMenu.openAtItem(root, screen);
}
PanelService.showContextMenu(contextMenu, item, screen);
}
}
+114 -83
View File
@@ -10,7 +10,7 @@ import qs.Modules.Bar.Extras
import qs.Services.UI
import qs.Widgets
Rectangle {
Item {
id: root
property ShellScreen screen
@@ -43,9 +43,11 @@ Rectangle {
property int sectionWidgetsCount: 0
property var widgetMetadata: BarWidgetRegistry.widgetMetadata[widgetId]
// Explicit screenName property ensures reactive binding when screen changes
readonly property string screenName: screen ? screen.name : ""
property var widgetSettings: {
if (section && sectionWidgetIndex >= 0) {
var widgets = Settings.getBarWidgetsForScreen(screen?.name)[section];
if (section && sectionWidgetIndex >= 0 && screenName) {
var widgets = Settings.getBarWidgetsForScreen(screenName)[section];
if (widgets && sectionWidgetIndex < widgets.length) {
return widgets[sectionWidgetIndex];
}
@@ -53,10 +55,10 @@ Rectangle {
return {};
}
readonly property string barPosition: Settings.getBarPositionForScreen(screen?.name)
readonly property string barPosition: Settings.getBarPositionForScreen(screenName)
readonly property bool isVertical: barPosition === "left" || barPosition === "right"
readonly property real barHeight: Style.getBarHeightForScreen(screen?.name)
readonly property real capsuleHeight: Style.getCapsuleHeightForScreen(screen?.name)
readonly property real barHeight: Style.getBarHeightForScreen(screenName)
readonly property real capsuleHeight: Style.getCapsuleHeightForScreen(screenName)
readonly property bool density: Settings.data.bar.density
readonly property int iconSize: Style.toOdd(capsuleHeight * 0.65)
@@ -115,8 +117,8 @@ Rectangle {
function _performFilteredItemsUpdate() {
// Force a fresh read of settings to ensure we have the latest blacklist
var currentSettings = {};
if (section && sectionWidgetIndex >= 0) {
var w = Settings.getBarWidgetsForScreen(screen?.name)[section];
if (section && sectionWidgetIndex >= 0 && screenName) {
var w = Settings.getBarWidgetsForScreen(screenName)[section];
if (w && sectionWidgetIndex < w.length) {
currentSettings = w[sectionWidgetIndex];
}
@@ -284,35 +286,51 @@ Rectangle {
Component.onCompleted: {
root.updateFilteredItems(); // Initial update
}
implicitWidth: isVertical ? capsuleHeight : Math.round(trayFlow.implicitWidth)
implicitHeight: isVertical ? Math.round(trayFlow.implicitHeight) : capsuleHeight
radius: Style.radiusM
color: Style.capsuleColor
border.color: Style.capsuleBorderColor
border.width: Style.capsuleBorderWidth
// Content dimensions for implicit sizing
readonly property real capsuleWidth: isVertical ? capsuleHeight : Math.round(trayFlow.implicitWidth)
readonly property real capsuleContentHeight: isVertical ? Math.round(trayFlow.implicitHeight) : capsuleHeight
implicitWidth: isVertical ? barHeight : Math.round(trayFlow.implicitWidth)
implicitHeight: isVertical ? Math.round(trayFlow.implicitHeight) : barHeight
visible: filteredItems.length > 0 || dropdownItems.length > 0
opacity: (filteredItems.length > 0 || dropdownItems.length > 0) ? 1.0 : 0.0
// Visual capsule centered in parent
Rectangle {
id: visualCapsule
width: capsuleWidth
height: capsuleContentHeight
x: Style.pixelAlignCenter(parent.width, width)
y: Style.pixelAlignCenter(parent.height, height)
radius: Style.radiusM
color: Style.capsuleColor
border.color: Style.capsuleBorderColor
border.width: Style.capsuleBorderWidth
}
Flow {
id: trayFlow
spacing: Style.marginXS
flow: isVertical ? Flow.TopToBottom : Flow.LeftToRight
// Pixel-perfect centering
x: isVertical ? Style.pixelAlignCenter(parent.width, width) : 0
y: isVertical ? 0 : Style.pixelAlignCenter(parent.height, height)
// Position at edge for full click area
x: isVertical ? 0 : 0
y: isVertical ? 0 : 0
// Drawer opener (before items if opposite direction)
NIconButton {
id: chevronIconBefore
visible: root.drawerEnabled && dropdownItems.length > 0 && BarService.getPillDirection(root)
width: isVertical ? barHeight : capsuleHeight
height: isVertical ? capsuleHeight : barHeight
tooltipText: I18n.tr("tooltips.open-tray-dropdown")
tooltipDirection: BarService.getTooltipDirection(root.screen?.name)
baseSize: capsuleHeight
applyUiScale: false
customRadius: Style.radiusL
colorBg: "transparent"
colorFg: Settings.data.colorSchemes.darkMode ? Color.mOnSurface : Color.mOnPrimary
colorFg: Color.mOnSurface
colorBorder: "transparent"
colorBorderHover: "transparent"
icon: {
@@ -338,10 +356,20 @@ Rectangle {
model: root.filteredItems
delegate: Item {
width: capsuleHeight
height: capsuleHeight
id: trayDelegate
width: isVertical ? barHeight : capsuleHeight
height: isVertical ? capsuleHeight : barHeight
visible: modelData
// Tooltip anchor representing the visual area (for proper tooltip positioning)
Item {
id: tooltipAnchor
width: capsuleHeight
height: capsuleHeight
x: Style.pixelAlignCenter(parent.width, width)
y: Style.pixelAlignCenter(parent.height, height)
}
IconImage {
id: trayIcon
width: iconSize
@@ -351,8 +379,6 @@ Rectangle {
asynchronous: true
backer.fillMode: Image.PreserveAspectFit
property bool menuJustOpened: false
source: {
let icon = modelData?.icon || "";
if (!icon) {
@@ -378,82 +404,85 @@ Rectangle {
fragmentShader: Qt.resolvedUrl(Quickshell.shellDir + "/Shaders/qsb/appicon_colorize.frag.qsb")
}
}
MouseArea {
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton
onClicked: mouse => {
if (!modelData) {
MouseArea {
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton
onClicked: mouse => {
if (!modelData) {
return;
}
if (mouse.button === Qt.LeftButton) {
// Close any open menu first
if (popupMenuWindow) {
popupMenuWindow.close();
}
if (!modelData.onlyMenu) {
modelData.activate();
}
} else if (mouse.button === Qt.MiddleButton) {
// Close the menu if it was visible
if (popupMenuWindow && popupMenuWindow.visible) {
popupMenuWindow.close();
return;
}
modelData.secondaryActivate && modelData.secondaryActivate();
} else if (mouse.button === Qt.RightButton) {
TooltipService.hideImmediately();
// Close the menu if it was visible
if (popupMenuWindow && popupMenuWindow.visible) {
popupMenuWindow.close();
return;
}
if (mouse.button === Qt.LeftButton) {
// Close any open menu first
if (popupMenuWindow) {
popupMenuWindow.close();
}
// Close any opened panel
if ((PanelService.openedPanel !== null) && !PanelService.openedPanel.isClosing) {
PanelService.openedPanel.close();
}
if (!modelData.onlyMenu) {
modelData.activate();
}
} else if (mouse.button === Qt.MiddleButton) {
// Close the menu if it was visible
if (popupMenuWindow && popupMenuWindow.visible) {
popupMenuWindow.close();
return;
}
modelData.secondaryActivate && modelData.secondaryActivate();
} else if (mouse.button === Qt.RightButton) {
TooltipService.hideImmediately();
// Close the menu if it was visible
if (popupMenuWindow && popupMenuWindow.visible) {
popupMenuWindow.close();
return;
}
// Close any opened panel
if ((PanelService.openedPanel !== null) && !PanelService.openedPanel.isClosing) {
PanelService.openedPanel.close();
}
if (modelData.hasMenu && modelData.menu && popupMenuWindow && trayMenu && trayMenu.item) {
popupMenuWindow.open();
// Position menu based on bar position
if (modelData.hasMenu && modelData.menu && trayMenu && trayMenu.item) {
// Calculate menu position after ensuring menu is loaded
const calculateAndShow = () => {
// Position menu based on bar position, using tooltipAnchor for proper positioning
// Increased spacing for better alignment with other context menus
let menuX, menuY;
if (barPosition === "left") {
// For left bar: position menu to the right of the bar
menuX = width + Style.marginM;
// For left bar: position menu to the right of the visual area
menuX = tooltipAnchor.width + Style.marginL;
menuY = 0;
} else if (barPosition === "right") {
// For right bar: position menu to the left of the bar
menuX = -trayMenu.item.width - Style.marginM;
// For right bar: position menu to the left of the visual area
menuX = -trayMenu.item.implicitWidth - Style.marginL;
menuY = 0;
} else {
// For horizontal bars: center horizontally and position below
menuX = (width / 2) - (trayMenu.item.width / 2);
menuY = (barPosition === "top") ? barHeight + Style.marginS - 2 : barHeight + Style.marginS - 2;
// For horizontal bars: center horizontally and position below visual area
menuX = (tooltipAnchor.width / 2) - (trayMenu.item.implicitWidth / 2);
menuY = tooltipAnchor.height + Style.marginL;
}
trayMenu.item.trayItem = modelData;
trayMenu.item.widgetSection = root.section;
trayMenu.item.widgetIndex = root.sectionWidgetIndex;
trayMenu.item.showAt(parent, menuX, menuY);
} else {
Logger.d("Tray", "No menu available for", modelData.id, "or trayMenu not set");
}
PanelService.showTrayMenu(root.screen, modelData, trayMenu.item, tooltipAnchor, menuX, menuY, root.section, root.sectionWidgetIndex);
};
// Use Qt.callLater to ensure menu dimensions are calculated
Qt.callLater(calculateAndShow);
} else {
Logger.d("Tray", "No menu available for", modelData.id, "or trayMenu not set");
}
}
onEntered: {
if (popupMenuWindow) {
popupMenuWindow.close();
}
TooltipService.show(trayIcon, modelData.tooltipTitle || modelData.name || modelData.id || "Tray Item", BarService.getTooltipDirection(root.screen?.name));
}
onEntered: {
if (popupMenuWindow) {
popupMenuWindow.close();
}
onExited: TooltipService.hide()
TooltipService.show(tooltipAnchor, modelData.tooltipTitle || modelData.name || modelData.id || "Tray Item", BarService.getTooltipDirection(root.screen?.name));
}
onExited: TooltipService.hide()
}
}
}
@@ -462,6 +491,8 @@ Rectangle {
NIconButton {
id: chevronIconAfter
visible: root.drawerEnabled && dropdownItems.length > 0 && !BarService.getPillDirection(root)
width: isVertical ? barHeight : capsuleHeight
height: isVertical ? capsuleHeight : barHeight
tooltipText: I18n.tr("tooltips.open-tray-dropdown")
tooltipDirection: BarService.getTooltipDirection(root.screen?.name)
baseSize: capsuleHeight
@@ -487,5 +518,5 @@ Rectangle {
onClicked: toggleDrawer(this)
onRightClicked: toggleDrawer(this)
}
}
} // closes Flow
}
+9 -12
View File
@@ -18,9 +18,11 @@ Item {
property int sectionWidgetsCount: 0
property var widgetMetadata: BarWidgetRegistry.widgetMetadata[widgetId]
// Explicit screenName property ensures reactive binding when screen changes
readonly property string screenName: screen ? screen.name : ""
property var widgetSettings: {
if (section && sectionWidgetIndex >= 0) {
var widgets = Settings.getBarWidgetsForScreen(screen?.name)[section];
if (section && sectionWidgetIndex >= 0 && screenName) {
var widgets = Settings.getBarWidgetsForScreen(screenName)[section];
if (widgets && sectionWidgetIndex < widgets.length) {
return widgets[sectionWidgetIndex];
}
@@ -28,7 +30,7 @@ Item {
return {};
}
readonly property string barPosition: Settings.getBarPositionForScreen(screen?.name)
readonly property string barPosition: Settings.getBarPositionForScreen(screenName)
readonly property bool isBarVertical: barPosition === "left" || barPosition === "right"
readonly property string displayMode: widgetSettings.displayMode !== undefined ? widgetSettings.displayMode : widgetMetadata.displayMode
@@ -71,10 +73,9 @@ Item {
}
onTriggered: action => {
var popupMenuWindow = PanelService.getPopupMenuWindow(screen);
if (popupMenuWindow) {
popupMenuWindow.close();
}
contextMenu.close();
PanelService.closeContextMenu(screen);
if (!action) {
return;
}
@@ -122,11 +123,7 @@ Item {
forceOpen: !isBarVertical && root.displayMode === "alwaysShow"
forceClose: isBarVertical || root.displayMode === "alwaysHide" || !pill.text
onRightClicked: {
var popupMenuWindow = PanelService.getPopupMenuWindow(screen);
if (popupMenuWindow) {
popupMenuWindow.showContextMenu(contextMenu);
contextMenu.openAtItem(pill, screen);
}
PanelService.showContextMenu(contextMenu, pill, screen);
}
tooltipText: {
if (pill.text !== "") {
+8 -14
View File
@@ -21,9 +21,11 @@ Item {
property int sectionWidgetsCount: 0
property var widgetMetadata: BarWidgetRegistry.widgetMetadata[widgetId]
// Explicit screenName property ensures reactive binding when screen changes
readonly property string screenName: screen ? screen.name : ""
property var widgetSettings: {
if (section && sectionWidgetIndex >= 0) {
var widgets = Settings.getBarWidgetsForScreen(screen?.name)[section];
if (section && sectionWidgetIndex >= 0 && screenName) {
var widgets = Settings.getBarWidgetsForScreen(screenName)[section];
if (widgets && sectionWidgetIndex < widgets.length) {
return widgets[sectionWidgetIndex];
}
@@ -31,7 +33,7 @@ Item {
return {};
}
readonly property string barPosition: Settings.getBarPositionForScreen(screen?.name)
readonly property string barPosition: Settings.getBarPositionForScreen(screenName)
readonly property bool isBarVertical: barPosition === "left" || barPosition === "right"
readonly property string displayMode: (widgetSettings.displayMode !== undefined) ? widgetSettings.displayMode : widgetMetadata.displayMode
readonly property string middleClickCommand: (widgetSettings.middleClickCommand !== undefined) ? widgetSettings.middleClickCommand : widgetMetadata.middleClickCommand
@@ -91,11 +93,8 @@ Item {
]
onTriggered: action => {
// Close the popup menu window before handling the action
var popupMenuWindow = PanelService.getPopupMenuWindow(screen);
if (popupMenuWindow) {
popupMenuWindow.close();
}
contextMenu.close();
PanelService.closeContextMenu(screen);
if (action === "toggle-mute") {
AudioService.setOutputMuted(!AudioService.muted);
@@ -147,12 +146,7 @@ Item {
PanelService.getPanel("audioPanel", screen)?.toggle(this);
}
onRightClicked: {
// Get the shared popup menu window for this screen
var popupMenuWindow = PanelService.getPopupMenuWindow(screen);
if (popupMenuWindow) {
popupMenuWindow.showContextMenu(contextMenu);
contextMenu.openAtItem(pill, screen);
}
PanelService.showContextMenu(contextMenu, pill, screen);
}
onMiddleClicked: {
Quickshell.execDetached(["sh", "-lc", middleClickCommand]);
+3 -9
View File
@@ -38,10 +38,8 @@ NIconButton {
]
onTriggered: action => {
var popupMenuWindow = PanelService.getPopupMenuWindow(screen);
if (popupMenuWindow) {
popupMenuWindow.close();
}
contextMenu.close();
PanelService.closeContextMenu(screen);
if (action === "random-wallpaper") {
WallpaperService.setRandomWallpaper();
@@ -58,10 +56,6 @@ NIconButton {
}
}
onRightClicked: {
var popupMenuWindow = PanelService.getPopupMenuWindow(screen);
if (popupMenuWindow) {
popupMenuWindow.showContextMenu(contextMenu);
contextMenu.openAtItem(root, screen);
}
PanelService.showContextMenu(contextMenu, root, screen);
}
}
+143 -387
View File
@@ -24,9 +24,11 @@ Item {
property int sectionWidgetsCount: 0
property var widgetMetadata: BarWidgetRegistry.widgetMetadata[widgetId]
// Explicit screenName property ensures reactive binding when screen changes
readonly property string screenName: screen ? screen.name : ""
property var widgetSettings: {
if (section && sectionWidgetIndex >= 0) {
var widgets = Settings.getBarWidgetsForScreen(screen?.name)[section];
if (section && sectionWidgetIndex >= 0 && screenName) {
var widgets = Settings.getBarWidgetsForScreen(screenName)[section];
if (widgets && sectionWidgetIndex < widgets.length) {
return widgets[sectionWidgetIndex];
}
@@ -34,12 +36,12 @@ Item {
return {};
}
readonly property string barPosition: Settings.getBarPositionForScreen(screen?.name)
readonly property string barPosition: Settings.getBarPositionForScreen(screenName)
readonly property bool isVertical: barPosition === "left" || barPosition === "right"
readonly property real barHeight: Style.getBarHeightForScreen(screen?.name)
readonly property real capsuleHeight: Style.getCapsuleHeightForScreen(screen?.name)
readonly property real barFontSize: Style.getBarFontSizeForScreen(screen?.name)
readonly property real baseDimensionRatio: 0.65 * (widgetSettings.labelMode === "none" ? 0.75 : 1)
readonly property real barHeight: Style.getBarHeightForScreen(screenName)
readonly property real capsuleHeight: Style.getCapsuleHeightForScreen(screenName)
readonly property real barFontSize: Style.getBarFontSizeForScreen(screenName)
readonly property real baseDimensionRatio: 0.65
readonly property string labelMode: (widgetSettings.labelMode !== undefined) ? widgetSettings.labelMode : widgetMetadata.labelMode
readonly property bool hasLabel: (labelMode !== "none")
@@ -54,7 +56,36 @@ Item {
readonly property real unfocusedIconsOpacity: (widgetSettings.unfocusedIconsOpacity !== undefined) ? widgetSettings.unfocusedIconsOpacity : widgetMetadata.unfocusedIconsOpacity
readonly property real groupedBorderOpacity: (widgetSettings.groupedBorderOpacity !== undefined) ? widgetSettings.groupedBorderOpacity : widgetMetadata.groupedBorderOpacity
readonly property bool enableScrollWheel: (widgetSettings.enableScrollWheel !== undefined) ? widgetSettings.enableScrollWheel : widgetMetadata.enableScrollWheel
readonly property bool reverseScroll: (widgetSettings.reverseScroll !== undefined) ? widgetSettings.reverseScroll : (widgetMetadata.reverseScroll || false)
readonly property real iconScale: (widgetSettings.iconScale !== undefined) ? widgetSettings.iconScale : widgetMetadata.iconScale
readonly property string focusedColor: (widgetSettings.focusedColor !== undefined) ? widgetSettings.focusedColor : widgetMetadata.focusedColor
readonly property string occupiedColor: (widgetSettings.occupiedColor !== undefined) ? widgetSettings.occupiedColor : widgetMetadata.occupiedColor
readonly property string emptyColor: (widgetSettings.emptyColor !== undefined) ? widgetSettings.emptyColor : widgetMetadata.emptyColor
readonly property bool showBadge: (widgetSettings.showBadge !== undefined) ? widgetSettings.showBadge : widgetMetadata.showBadge
readonly property var colorMap: {
"primary": [Color.mPrimary, Color.mOnPrimary],
"secondary": [Color.mSecondary, Color.mOnSecondary],
"tertiary": [Color.mTertiary, Color.mOnTertiary],
"onSurface": [Color.mOnSurface, Color.mSurface]
}
// Helper to safely get colors with proper reactivity
// Accesses Color singleton directly to ensure fresh values
function getColorPair(colorKey) {
switch (colorKey) {
case "primary":
return [Color.mPrimary, Color.mOnPrimary];
case "secondary":
return [Color.mSecondary, Color.mOnSecondary];
case "tertiary":
return [Color.mTertiary, Color.mOnTertiary];
case "onSurface":
return [Color.mOnSurface, Color.mSurface];
default:
return [Color.mPrimary, Color.mOnPrimary];
}
}
// Only for grouped mode / show apps
readonly property int baseItemSize: Style.toOdd(capsuleHeight * 0.8)
@@ -69,16 +100,12 @@ Item {
function getSelectedWindow() {
if (!selectedWindowId)
return null;
for (var i = 0; i < localWorkspaces.count; i++) {
var ws = localWorkspaces.get(i);
if (ws && ws.windows) {
for (var j = 0; j < ws.windows.count; j++) {
var win = ws.windows.get(j);
// Using loose equality on purpose (==)
if (win && (win.id == selectedWindowId || win.address == selectedWindowId)) {
return win;
}
}
// Search directly in CompositorService to get the live window object
for (var i = 0; i < CompositorService.windows.count; i++) {
var win = CompositorService.windows.get(i);
// Using loose equality on purpose (==)
if (win && (win.id == selectedWindowId || win.address == selectedWindowId)) {
return win;
}
}
return null;
@@ -89,6 +116,8 @@ Item {
// Revision counter to force icon re-evaluation
property int iconRevision: 0
// Revision counter to force window list re-evaluation (for liveWindows binding in grouped mode)
property int windowRevision: 0
property ListModel localWorkspaces: ListModel {}
property int lastFocusedWorkspaceId: -1
@@ -115,7 +144,7 @@ Item {
// Don't calculate text width if labels are off
if (labelMode === "none") {
return Math.round(d * factor);
return Style.toOdd(d * factor);
}
var displayText = ws.idx.toString();
@@ -229,7 +258,9 @@ Item {
}
onScreenChanged: refreshWorkspaces()
onScreenNameChanged: refreshWorkspaces()
onHideUnoccupiedChanged: refreshWorkspaces()
onShowApplicationsChanged: refreshWorkspaces()
Connections {
target: CompositorService
@@ -238,11 +269,13 @@ Item {
}
function onWindowListChanged() {
if (showApplications || showLabelsOnlyWhenOccupied) {
root.windowRevision++;
refreshWorkspaces();
}
}
function onActiveWindowChanged() {
if (showApplications) {
root.windowRevision++;
refreshWorkspaces();
}
}
@@ -290,10 +323,13 @@ Item {
isOccupied: ws.isOccupied
};
if (showApplications) {
workspaceData.windows = CompositorService.getWindowsForWorkspace(ws.id);
if (ws.handle !== null && ws.handle !== undefined) {
workspaceData.handle = ws.handle;
}
// Windows are fetched live via liveWindows property in grouped mode
// to avoid Qt 6.9 ListModel nested array serialization issues
targetList.push(workspaceData);
}
}
@@ -424,10 +460,8 @@ Item {
}
onTriggered: (action, item) => {
var popupMenuWindow = PanelService.getPopupMenuWindow(screen);
if (popupMenuWindow) {
popupMenuWindow.close();
}
contextMenu.close();
PanelService.closeContextMenu(screen);
const selectedWindow = root.getSelectedWindow();
@@ -469,11 +503,7 @@ Item {
acceptedButtons: Qt.RightButton
onClicked: mouse => {
if (mouse.button === Qt.RightButton) {
var popupMenuWindow = PanelService.getPopupMenuWindow(screen);
if (popupMenuWindow) {
popupMenuWindow.showContextMenu(contextMenu);
contextMenu.openAtItem(workspaceBackground, screen);
}
PanelService.showContextMenu(contextMenu, workspaceBackground, screen);
}
}
}
@@ -509,6 +539,8 @@ Item {
var step = 120;
if (Math.abs(root.wheelAccumulatedDelta) >= step) {
var direction = root.wheelAccumulatedDelta > 0 ? -1 : 1;
if (root.reverseScroll)
direction *= -1;
// For vertical layout, natural mapping: wheel up -> previous, down -> next (already handled by sign)
// For horizontal layout, same mapping using vertical wheel
root.switchByOffset(direction);
@@ -525,173 +557,32 @@ Item {
id: pillRow
spacing: spacingBetweenPills
x: horizontalPadding
y: workspaceBackground.y + Style.pixelAlignCenter(workspaceBackground.height, height)
y: 0
visible: !isVertical && !showApplications
Repeater {
id: workspaceRepeaterHorizontal
model: localWorkspaces
Item {
id: workspacePillContainer
height: Style.toOdd(capsuleHeight * root.baseDimensionRatio)
states: [
State {
name: "active"
when: model.isActive
PropertyChanges {
target: workspacePillContainer
width: root.getWorkspaceWidth(model, true)
}
},
State {
name: "inactive"
when: !model.isActive
PropertyChanges {
target: workspacePillContainer
width: root.getWorkspaceWidth(model, false)
}
}
]
transitions: [
Transition {
from: "inactive"
to: "active"
NumberAnimation {
property: "width"
duration: Style.animationNormal
easing.type: Easing.OutBack
}
},
Transition {
from: "active"
to: "inactive"
NumberAnimation {
property: "width"
duration: Style.animationNormal
easing.type: Easing.OutBack
}
}
]
Rectangle {
id: pill
anchors.fill: parent
Loader {
active: (labelMode !== "none") && (!root.showLabelsOnlyWhenOccupied || model.isOccupied || model.isFocused)
sourceComponent: Component {
NText {
x: Style.pixelAlignCenter(pill.width, width)
y: Style.pixelAlignCenter(pill.height, height)
text: {
if (model.name && model.name.length > 0) {
if (root.labelMode === "name") {
return model.name.substring(0, characterCount);
}
if (root.labelMode === "index+name") {
return (model.idx.toString() + " " + model.name.substring(0, characterCount));
}
}
return model.idx.toString();
}
family: Settings.data.ui.fontFixed
pointSize: workspacePillContainer.height * root.textRatio
applyUiScale: false
font.capitalization: Font.AllUppercase
font.weight: Style.fontWeightBold
wrapMode: Text.Wrap
color: {
if (model.isFocused)
return Color.mOnPrimary;
if (model.isUrgent)
return Color.mOnError;
if (model.isOccupied)
return Color.mOnSecondary;
return Color.mOnSecondary;
}
}
}
}
radius: Style.radiusM
color: {
if (model.isFocused)
return Color.mPrimary;
if (model.isUrgent)
return Color.mError;
if (model.isOccupied)
return Color.mSecondary;
return Qt.alpha(Color.mSecondary, 0.3);
}
z: 0
MouseArea {
id: pillMouseArea
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: {
CompositorService.switchToWorkspace(model);
}
hoverEnabled: true
}
// Material 3-inspired smooth animation for scale, color, opacity, and radius
Behavior on scale {
NumberAnimation {
duration: Style.animationNormal
easing.type: Easing.OutBack
}
}
Behavior on color {
enabled: !Color.isTransitioning
ColorAnimation {
duration: Style.animationFast
easing.type: Easing.InOutCubic
}
}
Behavior on opacity {
NumberAnimation {
duration: Style.animationFast
easing.type: Easing.InOutCubic
}
}
Behavior on radius {
NumberAnimation {
duration: Style.animationNormal
easing.type: Easing.OutBack
}
}
}
Behavior on width {
NumberAnimation {
duration: Style.animationNormal
easing.type: Easing.OutBack
}
}
Behavior on height {
NumberAnimation {
duration: Style.animationNormal
easing.type: Easing.OutBack
}
}
// Burst effect overlay for focused pill (smaller outline)
Rectangle {
id: pillBurst
anchors.centerIn: workspacePillContainer
width: workspacePillContainer.width + 18 * root.masterProgress * scale
height: workspacePillContainer.height + 18 * root.masterProgress * scale
radius: width / 2
color: "transparent"
border.color: root.effectColor
border.width: Math.max(1, Math.round((2 + 6 * (1.0 - root.masterProgress))))
opacity: root.effectsActive && model.isFocused ? (1.0 - root.masterProgress) * 0.7 : 0
visible: root.effectsActive && model.isFocused
z: 1
}
delegate: WorkspacePill {
required property var model
workspace: model
isVertical: false
baseDimensionRatio: root.baseDimensionRatio
capsuleHeight: root.capsuleHeight
barHeight: root.barHeight
labelMode: root.labelMode
characterCount: root.characterCount
textRatio: root.textRatio
showLabelsOnlyWhenOccupied: root.showLabelsOnlyWhenOccupied
colorMap: root.colorMap
focusedColor: root.focusedColor
occupiedColor: root.occupiedColor
emptyColor: root.emptyColor
masterProgress: root.masterProgress
effectsActive: root.effectsActive
effectColor: root.effectColor
getWorkspaceWidth: root.getWorkspaceWidth
getWorkspaceHeight: root.getWorkspaceHeight
}
}
}
@@ -700,174 +591,33 @@ Item {
Column {
id: pillColumn
spacing: spacingBetweenPills
x: workspaceBackground.x + Style.pixelAlignCenter(workspaceBackground.width, width)
x: 0
y: horizontalPadding
visible: isVertical && !showApplications
Repeater {
id: workspaceRepeaterVertical
model: localWorkspaces
Item {
id: workspacePillContainerVertical
width: Style.toOdd(capsuleHeight * root.baseDimensionRatio)
states: [
State {
name: "active"
when: model.isActive
PropertyChanges {
target: workspacePillContainerVertical
height: root.getWorkspaceHeight(model, true)
}
},
State {
name: "inactive"
when: !model.isActive
PropertyChanges {
target: workspacePillContainerVertical
height: root.getWorkspaceHeight(model, false)
}
}
]
transitions: [
Transition {
from: "inactive"
to: "active"
NumberAnimation {
property: "height"
duration: Style.animationNormal
easing.type: Easing.OutBack
}
},
Transition {
from: "active"
to: "inactive"
NumberAnimation {
property: "height"
duration: Style.animationNormal
easing.type: Easing.OutBack
}
}
]
Rectangle {
id: pillVertical
anchors.fill: parent
Loader {
active: (labelMode !== "none") && (!root.showLabelsOnlyWhenOccupied || model.isOccupied || model.isFocused)
sourceComponent: Component {
NText {
x: Style.pixelAlignCenter(pillVertical.width, width)
y: Style.pixelAlignCenter(pillVertical.height, height)
text: {
if (model.name && model.name.length > 0) {
if (root.labelMode === "name") {
return model.name.substring(0, characterCount);
}
if (root.labelMode === "index+name") {
return (model.idx.toString() + model.name.substring(0, 1));
}
}
return model.idx.toString();
}
family: Settings.data.ui.fontFixed
pointSize: workspacePillContainerVertical.width * root.textRatio
applyUiScale: false
font.capitalization: Font.AllUppercase
font.weight: Style.fontWeightBold
wrapMode: Text.Wrap
color: {
if (model.isFocused)
return Color.mOnPrimary;
if (model.isUrgent)
return Color.mOnError;
if (model.isOccupied)
return Color.mOnSecondary;
return Color.mOnSecondary;
}
}
}
}
radius: Style.radiusM
color: {
if (model.isFocused)
return Color.mPrimary;
if (model.isUrgent)
return Color.mError;
if (model.isOccupied)
return Color.mSecondary;
return Qt.alpha(Color.mSecondary, 0.3);
}
z: 0
MouseArea {
id: pillMouseAreaVertical
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: {
CompositorService.switchToWorkspace(model);
}
hoverEnabled: true
}
// Material 3-inspired smooth animation for scale, color, opacity, and radius
Behavior on scale {
NumberAnimation {
duration: Style.animationNormal
easing.type: Easing.OutBack
}
}
Behavior on color {
enabled: !Color.isTransitioning
ColorAnimation {
duration: Style.animationFast
easing.type: Easing.InOutCubic
}
}
Behavior on opacity {
NumberAnimation {
duration: Style.animationFast
easing.type: Easing.InOutCubic
}
}
Behavior on radius {
NumberAnimation {
duration: Style.animationNormal
easing.type: Easing.OutBack
}
}
}
Behavior on width {
NumberAnimation {
duration: Style.animationNormal
easing.type: Easing.OutBack
}
}
Behavior on height {
NumberAnimation {
duration: Style.animationNormal
easing.type: Easing.OutBack
}
}
// Burst effect overlay for focused pill (smaller outline)
Rectangle {
id: pillBurstVertical
anchors.centerIn: workspacePillContainerVertical
width: workspacePillContainerVertical.width + 18 * root.masterProgress * scale
height: workspacePillContainerVertical.height + 18 * root.masterProgress * scale
radius: width / 2
color: "transparent"
border.color: root.effectColor
border.width: Math.max(1, Math.round((2 + 6 * (1.0 - root.masterProgress))))
opacity: root.effectsActive && model.isFocused ? (1.0 - root.masterProgress) * 0.7 : 0
visible: root.effectsActive && model.isFocused
z: 1
}
delegate: WorkspacePill {
required property var model
workspace: model
isVertical: true
baseDimensionRatio: root.baseDimensionRatio
capsuleHeight: root.capsuleHeight
barHeight: root.barHeight
labelMode: root.labelMode
characterCount: root.characterCount
textRatio: root.textRatio
showLabelsOnlyWhenOccupied: root.showLabelsOnlyWhenOccupied
colorMap: root.colorMap
focusedColor: root.focusedColor
occupiedColor: root.occupiedColor
emptyColor: root.emptyColor
masterProgress: root.masterProgress
effectsActive: root.effectsActive
effectColor: root.effectColor
getWorkspaceWidth: root.getWorkspaceWidth
getWorkspaceHeight: root.getWorkspaceHeight
}
}
}
@@ -875,7 +625,6 @@ Item {
// ========================================
// Grouped mode (showApplications = true)
// ========================================
Component {
id: groupedWorkspaceDelegate
@@ -884,7 +633,28 @@ Item {
required property var model
property var workspaceModel: model
property bool hasWindows: (workspaceModel?.windows?.length > 0 || workspaceModel?.windows?.count > 0)
// Fetch windows directly from service to avoid Qt 6.9 ListModel nested array issues
property var liveWindows: []
property bool hasWindows: liveWindows.length > 0
function updateWindows() {
var wsId = workspaceModel?.id;
if (wsId !== undefined && wsId !== null) {
liveWindows = CompositorService.getWindowsForWorkspace(wsId);
} else {
liveWindows = [];
}
}
Component.onCompleted: updateWindows()
onWorkspaceModelChanged: updateWindows()
Connections {
target: root
function onWindowRevisionChanged() {
groupedContainer.updateWindows();
}
}
width: Style.toOdd((hasWindows ? groupedIconsFlow.implicitWidth : root.iconSize) + (root.isVertical ? (root.baseItemSize - root.iconSize + Style.marginXS) : Style.marginXL))
height: Style.toOdd((hasWindows ? groupedIconsFlow.implicitHeight : root.iconSize) + (root.isVertical ? Style.marginL : (root.baseItemSize - root.iconSize + Style.marginXS)))
@@ -938,7 +708,7 @@ Item {
flow: root.isVertical ? Flow.TopToBottom : Flow.LeftToRight
Repeater {
model: groupedContainer.workspaceModel.windows
model: groupedContainer.liveWindows
delegate: Item {
id: groupedTaskbarItem
@@ -955,17 +725,16 @@ Item {
height: parent.height
source: {
root.iconRevision; // Force re-evaluation when revision changes
const win = (typeof modelData !== "undefined") ? modelData : model;
return ThemeIcons.iconForAppId(win.appId?.toLowerCase());
return ThemeIcons.iconForAppId(modelData.appId?.toLowerCase());
}
smooth: true
asynchronous: true
opacity: (typeof modelData !== "undefined" ? modelData.isFocused : model.isFocused) ? Style.opacityFull : unfocusedIconsOpacity
layer.enabled: root.colorizeIcons && !(typeof modelData !== "undefined" ? modelData.isFocused : model.isFocused)
opacity: modelData.isFocused ? Style.opacityFull : unfocusedIconsOpacity
layer.enabled: root.colorizeIcons && !modelData.isFocused
Rectangle {
id: groupedFocusIndicator
visible: (typeof modelData !== "undefined" ? modelData.isFocused : model.isFocused)
visible: modelData.isFocused
anchors.bottomMargin: -2
anchors.bottom: parent.bottom
anchors.horizontalCenter: parent.horizontalCenter
@@ -990,30 +759,23 @@ Item {
preventStealing: true
onPressed: mouse => {
const win = (typeof modelData !== "undefined") ? modelData : model;
if (!win)
return;
if (mouse.button === Qt.LeftButton) {
CompositorService.focusWindow(win);
CompositorService.focusWindow(modelData);
}
}
onReleased: mouse => {
const win = (typeof modelData !== "undefined") ? modelData : model;
if (!win)
return;
if (mouse.button === Qt.RightButton) {
mouse.accepted = true;
TooltipService.hide();
root.selectedWindowId = win.id || win.address || "";
root.selectedAppId = win.appId;
root.selectedWindowId = modelData.id || modelData.address || "";
root.selectedAppId = modelData.appId;
openGroupedContextMenu(groupedTaskbarItem);
}
}
onEntered: {
const win = (typeof modelData !== "undefined") ? modelData : model;
groupedTaskbarItem.itemHovered = true;
TooltipService.show(groupedTaskbarItem, win.title || win.appId || "Unknown app.", BarService.getTooltipDirection(root.screen?.name));
TooltipService.show(groupedTaskbarItem, modelData.title || modelData.appId || "Unknown app.", BarService.getTooltipDirection(root.screenName));
}
onExited: {
groupedTaskbarItem.itemHovered = false;
@@ -1027,7 +789,7 @@ Item {
Item {
id: groupedWorkspaceNumberContainer
visible: root.labelMode !== "none" && (!root.showLabelsOnlyWhenOccupied || groupedContainer.hasWindows || groupedContainer.workspaceModel.isFocused)
visible: root.labelMode !== "none" && root.showBadge && (!root.showLabelsOnlyWhenOccupied || groupedContainer.hasWindows || groupedContainer.workspaceModel.isFocused)
anchors {
left: parent.left
@@ -1047,17 +809,13 @@ Item {
color: {
if (groupedContainer.workspaceModel.isFocused)
return Color.mPrimary;
return root.getColorPair(root.focusedColor)[0];
if (groupedContainer.workspaceModel.isUrgent)
return Color.mError;
if (groupedContainer.hasWindows)
return Color.mSecondary;
return root.getColorPair(root.occupiedColor)[0];
if (Settings.data.colorSchemes.darkMode) {
return Qt.darker(Color.mSecondary, 1.5);
} else {
return Qt.lighter(Color.mSecondary, 1.5);
}
return root.getColorPair(root.emptyColor)[0];
}
scale: groupedContainer.workspaceModel.isActive ? 1.0 : 0.8
@@ -1120,11 +878,13 @@ Item {
color: {
if (groupedContainer.workspaceModel.isFocused)
return Color.mOnPrimary;
return root.getColorPair(root.focusedColor)[1];
if (groupedContainer.workspaceModel.isUrgent)
return Color.mOnError;
if (groupedContainer.hasWindows)
return root.getColorPair(root.occupiedColor)[1];
return Color.mOnSecondary;
return root.getColorPair(root.emptyColor)[1];
}
Behavior on opacity {
@@ -1162,10 +922,6 @@ Item {
}
function openGroupedContextMenu(item) {
var popupMenuWindow = PanelService.getPopupMenuWindow(screen);
if (popupMenuWindow) {
popupMenuWindow.showContextMenu(contextMenu);
contextMenu.openAtItem(item, screen);
}
PanelService.showContextMenu(contextMenu, item, screen);
}
}
+85 -25
View File
@@ -110,6 +110,15 @@ Loader {
property int dragSourceIndex: -1
property int dragTargetIndex: -1
// when dragging ended but the cursor is outside the dock area, restart the timer
onDragSourceIndexChanged: {
if (dragSourceIndex === -1) {
if (autoHide && !dockHovered && !anyAppHovered && !peekHovered && !menuHovered) {
hideTimer.restart();
}
}
}
// Revision counter to force icon re-evaluation
property int iconRevision: 0
@@ -123,10 +132,17 @@ Loader {
function getAppKey(appData) {
if (!appData)
return null;
// prefer toplevel object identity for running apps to distinguish instances
// Use stable appId for pinned apps to maintain their slot regardless of running state
if (appData.type === "pinned" || appData.type === "pinned-running") {
return appData.appId;
}
// prefer toplevel object identity for unpinned running apps to distinguish instances
if (appData.toplevel)
return appData.toplevel;
// fallback to appId for pinned-only apps
// fallback to appId
return appData.appId;
}
@@ -138,17 +154,23 @@ Loader {
const sorted = [];
const remaining = [...apps];
// 1. Pick apps that are in the session order
// Pick apps that are in the session order
for (let i = 0; i < sessionAppOrder.length; i++) {
const key = sessionAppOrder[i];
const idx = remaining.findIndex(app => getAppKey(app) === key);
if (idx !== -1) {
sorted.push(remaining[idx]);
remaining.splice(idx, 1);
// Pick ALL matching apps (e.g. all instances of a pinned app)
while (true) {
const idx = remaining.findIndex(app => getAppKey(app) === key);
if (idx !== -1) {
sorted.push(remaining[idx]);
remaining.splice(idx, 1);
} else {
break;
}
}
}
// 2. Append any new/remaining apps
// Append any new/remaining apps
remaining.forEach(app => sorted.push(app));
return sorted;
@@ -201,7 +223,10 @@ Loader {
function normalizeAppId(appId) {
if (!appId || typeof appId !== 'string')
return "";
return appId.toLowerCase().trim();
let id = appId.toLowerCase().trim();
if (id.endsWith(".desktop"))
id = id.substring(0, id.length - 8);
return id;
}
// Helper function to check if an app ID matches a pinned app (case-insensitive)
@@ -249,6 +274,9 @@ Loader {
//push an app onto combined with the given appType
function pushApp(appType, toplevel, appId, title) {
// Use canonical ID for pinned apps to ensure key stability
const canonicalId = isAppIdPinned(appId, pinnedApps) ? (pinnedApps.find(p => normalizeAppId(p) === normalizeAppId(appId)) || appId) : appId;
// For running apps, track by toplevel object to allow multiple instances
if (toplevel) {
if (processedToplevels.has(toplevel)) {
@@ -260,30 +288,30 @@ Loader {
combined.push({
"type": appType,
"toplevel": toplevel,
"appId": appId,
"appId": canonicalId,
"title": title
});
processedToplevels.add(toplevel);
} else {
// For pinned apps that aren't running, track by appId to avoid duplicates
if (processedPinnedAppIds.has(appId)) {
if (processedPinnedAppIds.has(canonicalId)) {
return; // Already processed this pinned app
}
combined.push({
"type": appType,
"toplevel": toplevel,
"appId": appId,
"appId": canonicalId,
"title": title
});
processedPinnedAppIds.add(appId);
processedPinnedAppIds.add(canonicalId);
}
}
function pushRunning(first) {
runningApps.forEach(toplevel => {
if (toplevel) {
// Skip pinned apps if they were already processed (when pinnedStatic is true)
const isPinned = pinnedApps.includes(toplevel.appId);
// Use robust matching to check if pinned
const isPinned = isAppIdPinned(toplevel.appId, pinnedApps);
if (!first && isPinned && processedToplevels.has(toplevel)) {
return; // Already added by pushPinned()
}
@@ -294,8 +322,8 @@ Loader {
function pushPinned() {
pinnedApps.forEach(pinnedAppId => {
// Find all running instances of this pinned app
const matchingToplevels = runningApps.filter(app => app && app.appId === pinnedAppId);
// Find all running instances of this pinned app using robust matching
const matchingToplevels = runningApps.filter(app => app && normalizeAppId(app.appId) === normalizeAppId(pinnedAppId));
if (matchingToplevels.length > 0) {
// Add all running instances as pinned-running
@@ -321,9 +349,36 @@ Loader {
}
dockApps = sortDockApps(combined);
// Sync session order if needed (e.g. first run or new apps added)
if (!sessionAppOrder || sessionAppOrder.length === 0 || sessionAppOrder.length !== dockApps.length) {
// Sync session order if needed
// Instead of resetting everything when length changes, we reconcile the keys
if (!sessionAppOrder || sessionAppOrder.length === 0) {
sessionAppOrder = dockApps.map(getAppKey);
} else {
const currentKeys = new Set(dockApps.map(getAppKey));
const existingKeys = new Set();
const newOrder = [];
// Keep existing keys that are still present
sessionAppOrder.forEach(key => {
if (currentKeys.has(key)) {
newOrder.push(key);
existingKeys.add(key);
}
});
// Add new keys at the end
dockApps.forEach(app => {
const key = getAppKey(app);
if (!existingKeys.has(key)) {
newOrder.push(key);
existingKeys.add(key);
}
});
if (JSON.stringify(newOrder) !== JSON.stringify(sessionAppOrder)) {
sessionAppOrder = newOrder;
}
}
}
@@ -343,6 +398,11 @@ Loader {
id: hideTimer
interval: hideDelay
onTriggered: {
// do not hide if dragging
if (root.dragSourceIndex !== -1) {
return;
}
// Force menuHovered to false if no menu is current or visible
if (!root.currentContextMenu || !root.currentContextMenu.visible) {
menuHovered = false;
@@ -471,8 +531,8 @@ Loader {
WlrLayershell.namespace: "noctalia-dock-" + (screen?.name || "unknown")
WlrLayershell.exclusionMode: exclusive ? ExclusionMode.Auto : ExclusionMode.Ignore
implicitWidth: dockContainerWrapper.width
implicitHeight: dockContainerWrapper.height
implicitWidth: Math.round(dockContainerWrapper.width + (root.isVertical ? 0 : Style.marginXL * 6))
implicitHeight: Math.round(dockContainerWrapper.height)
// Position based on dock setting
anchors.top: dockPosition === "top"
@@ -566,12 +626,13 @@ Loader {
showTimer.stop();
hideTimer.stop();
unloadTimer.stop(); // Cancel unload if hovering
hidden = false; // Make sure dock is visible
}
}
onExited: {
dockHovered = false;
if (autoHide && !anyAppHovered && !peekHovered && !menuHovered) {
if (autoHide && !anyAppHovered && !peekHovered && !menuHovered && root.dragSourceIndex === -1) {
hideTimer.restart();
}
}
@@ -825,12 +886,10 @@ Loader {
onVisibleChanged: {
if (visible) {
root.currentContextMenu = contextMenu;
anyAppHovered = false;
} else if (root.currentContextMenu === contextMenu) {
root.currentContextMenu = null;
hideTimer.stop();
menuHovered = false;
anyAppHovered = false;
// Restart hide timer after menu closes
if (autoHide && !dockHovered && !anyAppHovered && !peekHovered && !menuHovered) {
hideTimer.restart();
@@ -878,6 +937,7 @@ Loader {
showTimer.stop();
hideTimer.stop();
unloadTimer.stop(); // Cancel unload if hovering app
hidden = false; // Make sure dock is visible
}
}
@@ -888,7 +948,7 @@ Loader {
if (!root.currentContextMenu || !root.currentContextMenu.visible) {
menuHovered = false;
}
if (autoHide && !dockHovered && !peekHovered && !menuHovered) {
if (autoHide && !dockHovered && !peekHovered && !menuHovered && root.dragSourceIndex === -1) {
hideTimer.restart();
}
}
+4 -9
View File
@@ -27,7 +27,7 @@ PopupWindow {
property real menuContentWidth: 160
implicitWidth: menuContentWidth + (Style.marginXL)
implicitHeight: contextMenuColumn.implicitHeight + (Style.marginXL)
implicitHeight: (root.items.length * 32) + (Style.marginXL)
color: "transparent"
visible: false
@@ -256,14 +256,9 @@ PopupWindow {
toplevel = toplevelData;
initItems();
// Force a complete re-evaluation by waiting for the next frame
Qt.callLater(() => {
Qt.callLater(() => {
visible = true;
canAutoClose = false;
gracePeriodTimer.restart();
});
});
visible = true;
canAutoClose = false;
gracePeriodTimer.restart();
}
function hide() {
+48 -18
View File
@@ -17,18 +17,49 @@ Scope {
property bool showInfo: false
property string errorMessage: ""
property string infoMessage: ""
property bool fprintdAvailable: false
readonly property string pamConfigDirectory: Quickshell.env("NOCTALIA_PAM_CONFIG") ? "/etc/pam.d" : Settings.configDir + "pam"
readonly property string pamConfig: Quickshell.env("NOCTALIA_PAM_CONFIG") || "password.conf"
readonly property string pamConfigDirectory: "/etc/pam.d"
property string pamConfig: Quickshell.env("NOCTALIA_PAM_SERVICE") || "login"
property bool pamReady: false
Component.onCompleted: {
checkFprintdProc.running = true;
if (Quickshell.env("NOCTALIA_PAM_CONFIG")) {
Logger.i("LockContext", "NOCTALIA_PAM_CONFIG is set, using system PAM config: /etc/pam.d/" + pamConfig);
if (Quickshell.env("NOCTALIA_PAM_SERVICE")) {
Logger.i("LockContext", "NOCTALIA_PAM_SERVICE is set, using system PAM config: /etc/pam.d/" + pamConfig);
pamReady = true;
} else {
Logger.i("LockContext", "Using generated PAM config:", pamConfigDirectory + "/" + pamConfig);
Logger.i("LockContext", "Probing for best PAM service...");
detectPamServiceProc.running = true;
}
}
Process {
id: detectPamServiceProc
command: ["sh", "-c", "
if [ -f /etc/pam.d/login ]; then echo 'login'; exit 0; fi;
if [ -f /etc/pam.d/system-auth ]; then echo 'system-auth'; exit 0; fi;
if [ -f /etc/pam.d/common-auth ]; then echo 'common-auth'; exit 0; fi;
echo 'login';
"]
stdout: StdioCollector {
onStreamFinished: {
const service = String(text || "").trim();
if (service.length > 0) {
root.pamConfig = service;
Logger.i("LockContext", "Detected PAM service: " + service);
} else {
Logger.w("LockContext", "Failed to detect PAM service, defaulting to login");
}
root.pamReady = true;
}
}
stderr: StdioCollector {}
}
onPamReadyChanged: {
if (pamReady) {
if (Settings.data.general.autoStartAuth && currentText === "") {
pam.start();
}
}
}
@@ -51,16 +82,23 @@ Scope {
if (!waitingForPassword) {
pam.abort();
}
if (fprintdAvailable) {
if (Settings.data.general.allowPasswordWithFprintd) {
occupyFingerprintSensorProc.running = true;
}
} else {
occupyFingerprintSensorProc.running = false;
pam.start();
if (pamReady && Settings.data.general.autoStartAuth) {
pam.start();
}
}
}
function tryUnlock() {
if (!pamReady) {
Logger.w("LockContext", "PAM not ready yet, ignoring unlock attempt");
return;
}
if (waitingForPassword) {
pam.respond(currentText);
unlockInProgress = true;
@@ -73,14 +111,6 @@ Scope {
pam.start();
}
Process {
id: checkFprintdProc
command: ["sh", "-c", "command -v fprintd-verify"]
onExited: function (exitCode) {
fprintdAvailable = (exitCode === 0);
}
}
Process {
id: occupyFingerprintSensorProc
command: ["fprintd-verify"]
+2 -8
View File
@@ -77,18 +77,12 @@ Loader {
Item {
id: batteryIndicator
property bool initializationComplete: false
Timer {
interval: 500
running: true
onTriggered: batteryIndicator.initializationComplete = true
}
property bool isReady: initializationComplete && BatteryService.batteryReady
property bool isReady: BatteryService.ready && BatteryService.batteryReady
property real percent: BatteryService.batteryPercentage
property bool charging: BatteryService.batteryCharging
property bool pluggedIn: BatteryService.batteryPluggedIn
property bool batteryVisible: isReady && percent > 0 && BatteryService.hasAnyBattery()
property bool batteryVisible: isReady && percent >= 0 && BatteryService.hasAnyBattery()
}
Item {
+3 -1
View File
@@ -21,7 +21,9 @@ Item {
required property TextInput passwordInput
Component.onCompleted: {
doUnlock();
if (Settings.data.general.autoStartAuth) {
doUnlock();
}
}
function doUnlock() {
+19 -14
View File
@@ -13,6 +13,7 @@ import qs.Services.UI
Variants {
model: Quickshell.screens
delegate: Item {
id: windowItem
required property ShellScreen modelData
property bool shouldBeActive: {
@@ -76,23 +77,27 @@ Variants {
// BarExclusionZone - created after MainScreen has fully loaded
// Disabled when bar is hidden or not configured for this screen
Loader {
active: {
if (!parent.windowLoaded || !parent.shouldBeActive || !BarService.isVisible)
return false;
Repeater {
model: Settings.data.bar.barType === "framed" ? ["top", "bottom", "left", "right"] : [Settings.getBarPositionForScreen(windowItem.modelData?.name)]
delegate: Loader {
active: {
if (!windowItem.windowLoaded || !windowItem.shouldBeActive || !BarService.effectivelyVisible)
return false;
// Check if bar is configured for this screen
var monitors = Settings.data.bar.monitors || [];
return monitors.length === 0 || monitors.includes(modelData?.name);
}
asynchronous: false
// Check if bar is configured for this screen
var monitors = Settings.data.bar.monitors || [];
return monitors.length === 0 || monitors.includes(windowItem.modelData?.name);
}
asynchronous: false
sourceComponent: BarExclusionZone {
screen: modelData
}
sourceComponent: BarExclusionZone {
screen: windowItem.modelData
edge: modelData
}
onLoaded: {
Logger.d("AllScreens", "BarExclusionZone created for", modelData?.name);
onLoaded: {
Logger.d("AllScreens", "BarExclusionZone (" + modelData + ") created for", windowItem.modelData?.name);
}
}
}
+141 -38
View File
@@ -48,6 +48,11 @@ ShapePath {
// Corner radius (from Style)
readonly property real radius: Style.radiusL
// Framed bar properties
readonly property bool isFramed: Settings.data.bar.barType === "framed"
readonly property real frameThickness: Settings.data.bar.frameThickness ?? 12
readonly property real frameRadius: Settings.data.bar.frameRadius ?? 20
// Bar position - since bar's parent fills the screen and Shape also fills the screen,
// we can use bar.x and bar.y directly (they're already in screen coordinates)
readonly property point barMappedPos: bar ? Qt.point(bar.x, bar.y) : Qt.point(0, 0)
@@ -56,6 +61,18 @@ ShapePath {
readonly property real barWidth: (bar && shouldShow) ? bar.width : 0
readonly property real barHeight: (bar && shouldShow) ? bar.height : 0
// Screen dimensions for frame
readonly property real screenWidth: windowRoot?.screen?.width || 0
readonly property real screenHeight: windowRoot?.screen?.height || 0
// Inner hole dimensions for framed mode - always relative to screen
readonly property string barPosition: Settings.getBarPositionForScreen(windowRoot?.screen?.name)
readonly property bool barIsVertical: barPosition === "left" || barPosition === "right"
readonly property real holeX: (barPosition === "left") ? barWidth : frameThickness
readonly property real holeY: (barPosition === "top") ? barHeight : frameThickness
readonly property real holeWidth: screenWidth - (barPosition === "left" || barPosition === "right" ? (barWidth + frameThickness) : (frameThickness * 2))
readonly property real holeHeight: screenHeight - (barPosition === "top" || barPosition === "bottom" ? (barHeight + frameThickness) : (frameThickness * 2))
// Flatten corners if bar is too small (handle null bar)
readonly property bool shouldFlatten: bar ? ShapeCornerHelper.shouldFlatten(barWidth, barHeight, radius) : false
readonly property real effectiveRadius: shouldFlatten ? (bar ? ShapeCornerHelper.getFlattenedRadius(Math.min(barWidth, barHeight), radius) : 0) : radius
@@ -89,73 +106,159 @@ ShapePath {
// ShapePath configuration
strokeWidth: -1 // No stroke, fill only
fillColor: backgroundColor
fillRule: isFramed ? ShapePath.OddEvenFill : ShapePath.WindingFill
// Starting position (top-left corner, after the arc)
// Use mapped coordinates relative to the Shape container
startX: barMappedPos.x + tlRadius * tlMultX
startY: barMappedPos.y
// Starting position
// In framed mode, we start at (0,0) to draw the screen rectangle first
startX: isFramed ? 0 : (barMappedPos.x + tlRadius * tlMultX)
startY: isFramed ? 0 : barMappedPos.y
// ========== PATH DEFINITION ==========
// Draws a rectangle with potentially inverted corners
// All coordinates are relative to startX/startY
// Top edge (moving right)
// 1. Main Bar / Outer Screen Rectangle
PathLine {
relativeX: root.barWidth - root.tlRadius * root.tlMultX - root.trRadius * root.trMultX
relativeY: 0
x: {
if (!root.shouldShow)
return 0;
if (root.isFramed)
return root.screenWidth;
return root.barMappedPos.x + root.barWidth - root.trRadius * root.trMultX;
}
y: root.isFramed ? 0 : root.barMappedPos.y
}
// Top-right corner arc
// Bar top-right corner (only if not framed)
PathArc {
relativeX: root.trRadius * root.trMultX
relativeY: root.trRadius * root.trMultY
radiusX: root.trRadius
radiusY: root.trRadius
x: root.isFramed ? (root.shouldShow ? root.screenWidth : 0) : (root.barMappedPos.x + root.barWidth)
y: root.isFramed ? 0 : (root.barMappedPos.y + root.trRadius * root.trMultY)
radiusX: root.isFramed ? 0 : root.trRadius
radiusY: root.isFramed ? 0 : root.trRadius
direction: ShapeCornerHelper.getArcDirection(root.trMultX, root.trMultY)
}
// Right edge (moving down)
PathLine {
relativeX: 0
relativeY: root.barHeight - root.trRadius * root.trMultY - root.brRadius * root.brMultY
x: root.isFramed ? (root.shouldShow ? root.screenWidth : 0) : (root.barMappedPos.x + root.barWidth)
y: {
if (!root.shouldShow)
return 0;
if (root.isFramed)
return root.screenHeight;
return root.barMappedPos.y + root.barHeight - root.brRadius * root.brMultY;
}
}
// Bottom-right corner arc
// Bar bottom-right corner (only if not framed)
PathArc {
relativeX: -root.brRadius * root.brMultX
relativeY: root.brRadius * root.brMultY
radiusX: root.brRadius
radiusY: root.brRadius
x: root.isFramed ? (root.shouldShow ? root.screenWidth : 0) : (root.barMappedPos.x + root.barWidth - root.brRadius * root.brMultX)
y: root.isFramed ? (root.shouldShow ? root.screenHeight : 0) : (root.barMappedPos.y + root.barHeight)
radiusX: root.isFramed ? 0 : root.brRadius
radiusY: root.isFramed ? 0 : root.brRadius
direction: ShapeCornerHelper.getArcDirection(root.brMultX, root.brMultY)
}
// Bottom edge (moving left)
PathLine {
relativeX: -(root.barWidth - root.brRadius * root.brMultX - root.blRadius * root.blMultX)
relativeY: 0
x: {
if (!root.shouldShow)
return 0;
if (root.isFramed)
return 0;
return root.barMappedPos.x + root.blRadius * root.blMultX;
}
y: root.isFramed ? (root.shouldShow ? root.screenHeight : 0) : (root.barMappedPos.y + root.barHeight)
}
// Bottom-left corner arc
// Bar bottom-left corner (only if not framed)
PathArc {
relativeX: -root.blRadius * root.blMultX
relativeY: -root.blRadius * root.blMultY
radiusX: root.blRadius
radiusY: root.blRadius
x: root.isFramed ? 0 : root.barMappedPos.x
y: root.isFramed ? (root.shouldShow ? root.screenHeight : 0) : (root.barMappedPos.y + root.barHeight - root.blRadius * root.blMultY)
radiusX: root.isFramed ? 0 : root.blRadius
radiusY: root.isFramed ? 0 : root.blRadius
direction: ShapeCornerHelper.getArcDirection(root.blMultX, root.blMultY)
}
// Left edge (moving up) - closes the path back to start
PathLine {
relativeX: 0
relativeY: -(root.barHeight - root.blRadius * root.blMultY - root.tlRadius * root.tlMultY)
x: root.isFramed ? 0 : root.barMappedPos.x
y: {
if (!root.shouldShow)
return 0;
if (root.isFramed)
return 0;
return root.barMappedPos.y + root.tlRadius * root.tlMultY;
}
}
// Top-left corner arc (back to start)
// Bar top-left corner (only if not framed, back to start)
PathArc {
relativeX: root.tlRadius * root.tlMultX
relativeY: -root.tlRadius * root.tlMultY
radiusX: root.tlRadius
radiusY: root.tlRadius
x: root.isFramed ? 0 : (root.barMappedPos.x + root.tlRadius * root.tlMultX)
y: root.isFramed ? 0 : root.barMappedPos.y
radiusX: root.isFramed ? 0 : root.tlRadius
radiusY: root.isFramed ? 0 : root.tlRadius
direction: ShapeCornerHelper.getArcDirection(root.tlMultX, root.tlMultY)
}
// 2. Inner Hole for Framed Mode (Clockwise)
PathMove {
x: (root.isFramed && root.shouldShow) ? (root.holeX + root.frameRadius) : root.startX
y: (root.isFramed && root.shouldShow) ? root.holeY : root.startY
}
// Top edge
PathLine {
x: (root.isFramed && root.shouldShow) ? (root.holeX + root.holeWidth - root.frameRadius) : ((root.isFramed && root.shouldShow) ? (root.holeX + root.frameRadius) : root.startX)
y: (root.isFramed && root.shouldShow) ? root.holeY : ((root.isFramed && root.shouldShow) ? root.holeY : root.startY)
}
// Top-right corner
PathArc {
x: (root.isFramed && root.shouldShow) ? (root.holeX + root.holeWidth) : ((root.isFramed && root.shouldShow) ? (root.holeX + root.holeWidth - root.frameRadius) : root.startX)
y: (root.isFramed && root.shouldShow) ? (root.holeY + root.frameRadius) : ((root.isFramed && root.shouldShow) ? root.holeY : root.startY)
radiusX: (root.isFramed && root.shouldShow) ? root.frameRadius : 0
radiusY: (root.isFramed && root.shouldShow) ? root.frameRadius : 0
direction: PathArc.Clockwise
}
// Right edge
PathLine {
x: (root.isFramed && root.shouldShow) ? (root.holeX + root.holeWidth) : root.startX
y: (root.isFramed && root.shouldShow) ? (root.holeY + root.holeHeight - root.frameRadius) : root.startY
}
// Bottom-right corner
PathArc {
x: (root.isFramed && root.shouldShow) ? (root.holeX + root.holeWidth - root.frameRadius) : root.startX
y: (root.isFramed && root.shouldShow) ? (root.holeY + root.holeHeight) : root.startY
radiusX: (root.isFramed && root.shouldShow) ? root.frameRadius : 0
radiusY: (root.isFramed && root.shouldShow) ? root.frameRadius : 0
direction: PathArc.Clockwise
}
// Bottom edge
PathLine {
x: (root.isFramed && root.shouldShow) ? (root.holeX + root.frameRadius) : root.startX
y: (root.isFramed && root.shouldShow) ? (root.holeY + root.holeHeight) : root.startY
}
// Bottom-left corner
PathArc {
x: (root.isFramed && root.shouldShow) ? root.holeX : root.startX
y: (root.isFramed && root.shouldShow) ? (root.holeY + root.holeHeight - root.frameRadius) : root.startY
radiusX: (root.isFramed && root.shouldShow) ? root.frameRadius : 0
radiusY: (root.isFramed && root.shouldShow) ? root.frameRadius : 0
direction: PathArc.Clockwise
}
// Left edge
PathLine {
x: (root.isFramed && root.shouldShow) ? root.holeX : root.startX
y: (root.isFramed && root.shouldShow) ? (root.holeY + root.frameRadius) : root.startY
}
// Top-left corner (back to start)
PathArc {
x: (root.isFramed && root.shouldShow) ? (root.holeX + root.frameRadius) : root.startX
y: (root.isFramed && root.shouldShow) ? root.holeY : root.startY
radiusX: (root.isFramed && root.shouldShow) ? root.frameRadius : 0
radiusY: (root.isFramed && root.shouldShow) ? root.frameRadius : 0
direction: PathArc.Clockwise
}
}
+7 -5
View File
@@ -32,6 +32,8 @@ PanelWindow {
// Position and size to match bar location (per-screen)
readonly property string barPosition: Settings.getBarPositionForScreen(barWindow.screen?.name)
readonly property bool barIsVertical: barPosition === "left" || barPosition === "right"
readonly property bool isFramed: Settings.data.bar.barType === "framed"
readonly property real frameThickness: Settings.data.bar.frameThickness ?? 12
readonly property bool barFloating: Settings.data.bar.floating || false
readonly property real barMarginH: Math.ceil(barFloating ? Settings.data.bar.marginHorizontal : 0)
readonly property real barMarginV: Math.ceil(barFloating ? Settings.data.bar.marginVertical : 0)
@@ -45,12 +47,12 @@ PanelWindow {
right: barPosition === "right" || !barIsVertical
}
// Handle floating margins
// Handle floating margins and framed mode offsets
margins {
top: barPosition === "top" || barIsVertical ? barMarginV : 0
bottom: barPosition === "bottom" || barIsVertical ? barMarginV : 0
left: barPosition === "left" || !barIsVertical ? barMarginH : 0
right: barPosition === "right" || !barIsVertical ? barMarginH : 0
top: (barPosition === "top") ? barMarginV : (isFramed ? frameThickness : barMarginV)
bottom: (barPosition === "bottom") ? barMarginV : (isFramed ? frameThickness : barMarginV)
left: (barPosition === "left") ? barMarginH : (isFramed ? frameThickness : barMarginH)
right: (barPosition === "right") ? barMarginH : (isFramed ? frameThickness : barMarginH)
}
// Set a tight window size
+17 -18
View File
@@ -13,13 +13,14 @@ import qs.Services.Compositor
PanelWindow {
id: root
// Edge to anchor to and thickness to reserve
property string edge: Settings.getBarPositionForScreen(screen?.name)
property real thickness: (edge === Settings.getBarPositionForScreen(screen?.name)) ? Style.getBarHeightForScreen(screen?.name) : (Settings.data.bar.frameThickness ?? 12)
readonly property bool exclusive: Settings.data.bar.exclusive
readonly property string barPosition: Settings.getBarPositionForScreen(screen?.name)
readonly property bool barIsVertical: barPosition === "left" || barPosition === "right"
readonly property real barHeight: Style.getBarHeightForScreen(screen?.name)
readonly property bool barFloating: Settings.data.bar.floating || false
readonly property real barMarginH: barFloating ? Math.ceil(Settings.data.bar.marginHorizontal) : 0
readonly property real barMarginV: barFloating ? Math.ceil(Settings.data.bar.marginVertical) : 0
readonly property real barMarginH: (barFloating && edge === Settings.getBarPositionForScreen(screen?.name)) ? Math.ceil(Settings.data.bar.marginHorizontal) : 0
readonly property real barMarginV: (barFloating && edge === Settings.getBarPositionForScreen(screen?.name)) ? Math.ceil(Settings.data.bar.marginVertical) : 0
readonly property real fractOffset: CompositorService.getDisplayScale(screen?.name) % 1.0
// Invisible - just reserves space
@@ -29,30 +30,28 @@ PanelWindow {
// Wayland layer shell configuration
WlrLayershell.layer: WlrLayer.Top
WlrLayershell.namespace: "noctalia-bar-exclusion-" + (screen?.name || "unknown")
WlrLayershell.namespace: "noctalia-bar-exclusion-" + edge + "-" + (screen?.name || "unknown")
WlrLayershell.exclusionMode: exclusive ? ExclusionMode.Auto : ExclusionMode.Ignore
// Anchor based on bar position
// Anchor based on specified edge
anchors {
top: barPosition === "top"
bottom: barPosition === "bottom"
left: barPosition === "left" || barPosition === "top" || barPosition === "bottom"
right: barPosition === "right" || barPosition === "top" || barPosition === "bottom"
top: edge === "top"
bottom: edge === "bottom"
left: edge === "left" || edge === "top" || edge === "bottom"
right: edge === "right" || edge === "top" || edge === "bottom"
}
// Size based on bar orientation
// Size based on orientation
implicitWidth: {
if (barIsVertical) {
// Vertical bar: reserve bar height + margin on the anchored edge only
return barHeight + barMarginH - fractOffset;
if (edge === "left" || edge === "right") {
return thickness + barMarginH - fractOffset;
}
return 0; // Auto-width when left/right anchors are true
}
implicitHeight: {
if (!barIsVertical) {
// Horizontal bar: reserve bar height + margin on the anchored edge only
return barHeight + barMarginV - fractOffset;
if (edge === "top" || edge === "bottom") {
return thickness + barMarginV - fractOffset;
}
return 0; // Auto-height when top/bottom anchors are true
}
+64 -9
View File
@@ -134,13 +134,58 @@ PanelWindow {
Region {
id: barMaskRegion
x: barPlaceholder.x
y: barPlaceholder.y
readonly property bool isFramed: Settings.data.bar.barType === "framed"
readonly property real barThickness: Style.barHeight
readonly property real frameThickness: Settings.data.bar.frameThickness ?? 12
readonly property string barPos: Settings.data.bar.position || "top"
// Set width/height to 0 if bar shouldn't show on this screen (makes region empty)
width: root.barShouldShow ? barPlaceholder.width : 0
height: root.barShouldShow ? barPlaceholder.height : 0
intersection: Intersection.Subtract
// Bar / Frame Mask
Region {
// Mode: Simple or Floating
x: barPlaceholder.x
y: barPlaceholder.y
width: (!barMaskRegion.isFramed && root.barShouldShow) ? barPlaceholder.width : 0
height: (!barMaskRegion.isFramed && root.barShouldShow) ? barPlaceholder.height : 0
intersection: Intersection.Subtract
}
// Mode: Framed - 4 sides
Region {
// Top side
Region {
x: 0
y: 0
width: (barMaskRegion.isFramed && root.barShouldShow) ? root.width : 0
height: (barMaskRegion.isFramed && root.barShouldShow) ? (barMaskRegion.barPos === "top" ? barMaskRegion.barThickness : barMaskRegion.frameThickness) : 0
intersection: Intersection.Subtract
}
// Bottom side
Region {
x: 0
y: (barMaskRegion.isFramed && root.barShouldShow) ? (root.height - (barMaskRegion.barPos === "bottom" ? barMaskRegion.barThickness : barMaskRegion.frameThickness)) : 0
width: (barMaskRegion.isFramed && root.barShouldShow) ? root.width : 0
height: (barMaskRegion.isFramed && root.barShouldShow) ? (barMaskRegion.barPos === "bottom" ? barMaskRegion.barThickness : barMaskRegion.frameThickness) : 0
intersection: Intersection.Subtract
}
// Left side
Region {
x: 0
y: 0
width: (barMaskRegion.isFramed && root.barShouldShow) ? (barMaskRegion.barPos === "left" ? barMaskRegion.barThickness : barMaskRegion.frameThickness) : 0
height: (barMaskRegion.isFramed && root.barShouldShow) ? root.height : 0
intersection: Intersection.Subtract
}
// Right side
Region {
x: (barMaskRegion.isFramed && root.barShouldShow) ? (root.width - (barMaskRegion.barPos === "right" ? barMaskRegion.barThickness : barMaskRegion.frameThickness)) : 0
width: (barMaskRegion.isFramed && root.barShouldShow) ? (barMaskRegion.barPos === "right" ? barMaskRegion.barThickness : barMaskRegion.frameThickness) : 0
height: (barMaskRegion.isFramed && root.barShouldShow) ? root.height : 0
intersection: Intersection.Subtract
}
}
}
// Background region for click-to-close - reactive sizing
@@ -323,6 +368,8 @@ PanelWindow {
// Bar background positioning properties (per-screen)
readonly property string barPosition: Settings.getBarPositionForScreen(screen?.name)
readonly property bool barIsVertical: barPosition === "left" || barPosition === "right"
readonly property bool isFramed: Settings.data.bar.barType === "framed"
readonly property real frameThickness: Settings.data.bar.frameThickness ?? 12
readonly property bool barFloating: Settings.data.bar.floating || false
readonly property real barMarginH: barFloating ? Math.floor(Settings.data.bar.marginHorizontal) : 0
readonly property real barMarginV: barFloating ? Math.floor(Settings.data.bar.marginVertical) : 0
@@ -333,24 +380,32 @@ PanelWindow {
x: {
if (barPosition === "right")
return screen.width - barHeight - barMarginH;
if (isFramed && !barIsVertical)
return frameThickness;
return barMarginH;
}
y: {
if (barPosition === "bottom")
return screen.height - barHeight - barMarginV;
if (isFramed && barIsVertical)
return frameThickness;
return barMarginV;
}
width: {
if (barIsVertical) {
return barHeight;
}
if (isFramed)
return screen.width - frameThickness * 2;
return screen.width - barMarginH * 2;
}
height: {
if (barIsVertical) {
return screen.height - barMarginV * 2;
if (!barIsVertical) {
return barHeight;
}
return barHeight;
if (isFramed)
return screen.height - frameThickness * 2;
return screen.height - barMarginV * 2;
}
// Corner states (same as Bar.qml)
+5 -2
View File
@@ -2,6 +2,7 @@ import QtQuick
import Quickshell
import Quickshell.Wayland
import qs.Commons
import qs.Services.Compositor
import qs.Services.UI
import qs.Widgets
@@ -31,8 +32,10 @@ PanelWindow {
visible: false
color: "transparent"
// Use Top layer (same as MainScreen) for proper event handling
WlrLayershell.layer: WlrLayer.Top
// Use Top layer for proper event handling, but on labwc use Bottom
// to avoid stealing input from popups while still catching outside clicks.
// However, when a dialog is open, always use Top so dialogs appear above apps.
WlrLayershell.layer: (CompositorService.isLabwc && !hasDialog) ? WlrLayer.Bottom : WlrLayer.Top
WlrLayershell.keyboardFocus: hasDialog ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None
WlrLayershell.namespace: "noctalia-" + windowType + "-" + (screen?.name || "unknown")
WlrLayershell.exclusionMode: ExclusionMode.Ignore
+87 -64
View File
@@ -91,6 +91,8 @@ Item {
readonly property string barPosition: Settings.getBarPositionForScreen(screen?.name)
readonly property bool barIsVertical: barPosition === "left" || barPosition === "right"
readonly property real barHeight: Style.getBarHeightForScreen(screen?.name)
readonly property bool isFramed: Settings.data.bar.barType === "framed"
readonly property real frameThickness: Settings.data.bar.frameThickness ?? 12
readonly property bool barFloating: Settings.data.bar.floating
readonly property real barMarginH: barFloating ? Math.ceil(Settings.data.bar.marginHorizontal) : 0
readonly property real barMarginV: barFloating ? Math.ceil(Settings.data.bar.marginVertical) : 0
@@ -172,15 +174,19 @@ Item {
barWindowX = screenWidth - root.barMarginH - root.barHeight;
} else if (root.barPosition === "left") {
barWindowX = root.barMarginH;
} else if (root.isFramed) {
barWindowX = root.frameThickness;
}
// For top/bottom bars, barWindowX stays 0 (full width window)
// For top/bottom bars, barWindowX stays 0 (full width window) unless framed
if (root.barPosition === "bottom") {
barWindowY = screenHeight - root.barMarginV - root.barHeight;
} else if (root.barPosition === "top") {
barWindowY = root.barMarginV;
} else if (root.isFramed) {
barWindowY = root.frameThickness;
}
// For left/right bars, barWindowY stays 0 (full height window)
// For left/right bars, barWindowY stays 0 (full height window) unless framed
root.buttonPosition = Qt.point(barWindowX + buttonLocal.x, barWindowY + buttonLocal.y);
root.buttonWidth = buttonItem.width;
@@ -298,6 +304,12 @@ Item {
return;
}
// Effective screen margins (account for frame thickness)
var effMarginL = Style.marginL + (root.isFramed ? root.frameThickness : 0);
var effMarginR = Style.marginL + (root.isFramed ? root.frameThickness : 0);
var effMarginT = Style.marginL + (root.isFramed ? root.frameThickness : 0);
var effMarginB = Style.marginL + (root.isFramed ? root.frameThickness : 0);
// Calculate panel dimensions first (needed for positioning)
var w;
// Priority 1: Content-driven size (dynamic)
@@ -310,7 +322,7 @@ Item {
else {
w = root.preferredWidth;
}
var panelWidth = Math.min(w, root.width - Style.marginL * 2);
var panelWidth = Math.min(w, root.width - effMarginL - effMarginR);
var h;
// Priority 1: Content-driven size (dynamic)
@@ -323,7 +335,7 @@ Item {
else {
h = root.preferredHeight;
}
var panelHeight = Math.min(h, root.height - root.barHeight - Style.marginL * 2);
var panelHeight = Math.min(h, root.height - root.barHeight - effMarginT - effMarginB);
// Update panelBackground target size (will be animated)
panelBackground.targetWidth = panelWidth;
@@ -337,6 +349,17 @@ Item {
var topBarEdgeWithOverlap = root.barMarginV + root.barHeight - overlap;
var bottomBarEdgeWithOverlap = root.height - root.barMarginV - root.barHeight + overlap;
if (root.isFramed) {
if (root.barPosition === "left")
leftBarEdgeWithOverlap = root.barHeight - overlap;
if (root.barPosition === "right")
rightBarEdgeWithOverlap = root.width - root.barHeight + overlap;
if (root.barPosition === "top")
topBarEdgeWithOverlap = root.barHeight - overlap;
if (root.barPosition === "bottom")
bottomBarEdgeWithOverlap = root.height - root.barHeight + overlap;
}
// Calculate position
var calculatedX;
var calculatedY;
@@ -355,14 +378,14 @@ Item {
} else {
// Detached panels: center on button X position
var panelX = root.buttonPosition.x + root.buttonWidth / 2 - panelWidth / 2;
var minX = Style.marginL;
var maxX = root.width - panelWidth - Style.marginL;
var minX = effMarginL;
var maxX = root.width - panelWidth - effMarginR;
// Account for vertical bar taking up space
if (root.barPosition === "left") {
minX = root.barMarginH + root.barHeight + Style.marginL;
minX = (root.isFramed ? 0 : root.barMarginH) + root.barHeight + Style.marginL;
} else if (root.barPosition === "right") {
maxX = root.width - root.barMarginH - root.barHeight - panelWidth - Style.marginL;
maxX = root.width - (root.isFramed ? 0 : root.barMarginH) - root.barHeight - panelWidth - Style.marginL;
}
panelX = Math.max(minX, Math.min(panelX, maxX));
@@ -373,11 +396,11 @@ Item {
var panelX = root.buttonPosition.x + root.buttonWidth / 2 - panelWidth / 2;
if (panelContent.allowAttach) {
var cornerInset = root.barFloating ? Style.radiusL * 2 : 0;
var barLeftEdge = root.barMarginH + cornerInset;
var barRightEdge = root.width - root.barMarginH - cornerInset;
var barLeftEdge = (root.isFramed ? root.frameThickness : root.barMarginH) + cornerInset;
var barRightEdge = root.width - (root.isFramed ? root.frameThickness : root.barMarginH) - cornerInset;
panelX = Math.max(barLeftEdge, Math.min(panelX, barRightEdge - panelWidth));
} else {
panelX = Math.max(Style.marginL, Math.min(panelX, root.width - panelWidth - Style.marginL));
panelX = Math.max(effMarginL, Math.min(panelX, root.width - panelWidth - effMarginR));
}
calculatedX = panelX;
}
@@ -386,12 +409,12 @@ Item {
if (root.panelAnchorHorizontalCenter) {
if (root.barIsVertical) {
if (root.barPosition === "left") {
var availableStart = root.barMarginH + root.barHeight;
var availableWidth = root.width - availableStart;
var availableStart = (root.isFramed ? 0 : root.barMarginH) + root.barHeight;
var availableWidth = root.width - availableStart - (root.isFramed ? root.frameThickness : 0);
calculatedX = availableStart + (availableWidth - panelWidth) / 2;
} else if (root.barPosition === "right") {
var availableWidth = root.width - root.barMarginH - root.barHeight;
calculatedX = (availableWidth - panelWidth) / 2;
var availableWidth = root.width - (root.isFramed ? 0 : root.barMarginH) - root.barHeight - (root.isFramed ? root.frameThickness : 0);
calculatedX = (root.isFramed ? root.frameThickness : 0) + (availableWidth - panelWidth) / 2;
} else {
calculatedX = (root.width - panelWidth) / 2;
}
@@ -410,12 +433,12 @@ Item {
var rightCornerInset = Style.radiusL * 2;
calculatedX = root.width - root.barMarginH - rightCornerInset - panelWidth;
} else {
calculatedX = root.width - panelWidth;
calculatedX = root.width - panelWidth - (root.isFramed ? root.frameThickness : 0);
}
}
} else {
// Not attached: position at right with margin
calculatedX = root.width - panelWidth - Style.marginL;
calculatedX = root.width - panelWidth - effMarginR;
}
} else if (root.panelAnchorLeft) {
// Use raw panelAnchorLeft for positioning decision
@@ -429,12 +452,12 @@ Item {
var leftCornerInset = Style.radiusL * 2;
calculatedX = root.barMarginH + leftCornerInset;
} else {
calculatedX = 0;
calculatedX = (root.isFramed ? root.frameThickness : 0);
}
}
} else {
// Not attached: position at left with margin
calculatedX = Style.marginL;
calculatedX = effMarginL;
}
} else {
// No explicit anchor: attach to bar if allowAttach, otherwise center
@@ -449,19 +472,19 @@ Item {
} else {
// Not attached: center in available space
if (root.barPosition === "left") {
var availableStart = root.barMarginH + root.barHeight;
var availableWidth = root.width - availableStart - Style.marginL;
var availableStart = (root.isFramed ? 0 : root.barMarginH) + root.barHeight;
var availableWidth = root.width - availableStart - effMarginR;
calculatedX = availableStart + (availableWidth - panelWidth) / 2;
} else {
var availableWidth = root.width - root.barMarginH - root.barHeight - Style.marginL;
calculatedX = Style.marginL + (availableWidth - panelWidth) / 2;
var availableWidth = root.width - (root.isFramed ? 0 : root.barMarginH) - root.barHeight - effMarginL;
calculatedX = effMarginL + (availableWidth - panelWidth) / 2;
}
}
} else {
if (panelContent.allowAttach) {
var cornerInset = Style.radiusL + (root.barFloating ? Style.radiusL : 0);
var barLeftEdge = root.barMarginH + cornerInset;
var barRightEdge = root.width - root.barMarginH - cornerInset;
var barLeftEdge = (root.isFramed ? root.frameThickness : root.barMarginH) + cornerInset;
var barRightEdge = root.width - (root.isFramed ? root.frameThickness : root.barMarginH) - cornerInset;
var centeredX = (root.width - panelWidth) / 2;
calculatedX = Math.max(barLeftEdge, Math.min(centeredX, barRightEdge - panelWidth));
} else {
@@ -473,8 +496,8 @@ Item {
// Edge snapping for X
if (panelContent.allowAttach && !root.barFloating && root.width > 0 && panelWidth > 0) {
var leftEdgePos = root.barPosition === "left" ? leftBarEdgeWithOverlap : root.barMarginH;
var rightEdgePos = root.barPosition === "right" ? rightBarEdgeWithOverlap - panelWidth : root.width - root.barMarginH - panelWidth;
var leftEdgePos = root.barPosition === "left" ? leftBarEdgeWithOverlap : (root.isFramed ? root.frameThickness : root.barMarginH);
var rightEdgePos = root.barPosition === "right" ? rightBarEdgeWithOverlap - panelWidth : root.width - (root.isFramed ? root.frameThickness : root.barMarginH) - panelWidth;
// Only snap to left edge if panel is actually meant to be at left (or no explicit anchor)
var shouldSnapToLeft = root.effectivePanelAnchorLeft || (!root.hasExplicitHorizontalAnchor && root.barPosition === "left");
@@ -494,30 +517,30 @@ Item {
if (panelContent.allowAttach) {
calculatedY = topBarEdgeWithOverlap;
} else {
calculatedY = root.barMarginV + root.barHeight + Style.marginM;
calculatedY = (root.isFramed ? 0 : root.barMarginV) + root.barHeight + Style.marginM;
}
} else if (root.barPosition === "bottom") {
if (panelContent.allowAttach) {
calculatedY = bottomBarEdgeWithOverlap - panelHeight;
} else {
calculatedY = root.height - root.barMarginV - root.barHeight - panelHeight - Style.marginM;
calculatedY = root.height - (root.isFramed ? 0 : root.barMarginV) - root.barHeight - panelHeight - Style.marginM;
}
} else if (root.barIsVertical) {
var panelY = root.buttonPosition.y + root.buttonHeight / 2 - panelHeight / 2;
var extraPadding = (panelContent.allowAttach && root.barFloating) ? Style.radiusL : 0;
if (panelContent.allowAttach) {
var cornerInset = extraPadding + (root.barFloating ? Style.radiusL : 0);
var barTopEdge = root.barMarginV + cornerInset;
var barBottomEdge = root.height - root.barMarginV - cornerInset;
var barTopEdge = (root.isFramed ? root.frameThickness : root.barMarginV) + cornerInset;
var barBottomEdge = root.height - (root.isFramed ? root.frameThickness : root.barMarginV) - cornerInset;
panelY = Math.max(barTopEdge, Math.min(panelY, barBottomEdge - panelHeight));
} else {
panelY = Math.max(Style.marginL + extraPadding, Math.min(panelY, root.height - panelHeight - Style.marginL - extraPadding));
panelY = Math.max(effMarginT + extraPadding, Math.min(panelY, root.height - panelHeight - effMarginB - extraPadding));
}
calculatedY = panelY;
}
} else {
// Standard anchor positioning
var barOffset = !panelContent.allowAttach && (root.barPosition === "top" || root.barPosition === "bottom") ? root.barMarginV + root.barHeight + Style.marginM : 0;
var barOffset = !panelContent.allowAttach && (root.barPosition === "top" || root.barPosition === "bottom") ? (root.isFramed ? 0 : root.barMarginV) + root.barHeight + Style.marginM : 0;
if (panelContent.allowAttach && !root.barIsVertical) {
// Attached to horizontal bar: position with overlap
@@ -532,12 +555,12 @@ Item {
if (root.panelAnchorVerticalCenter) {
if (!root.barIsVertical) {
if (root.barPosition === "top") {
var availableStart = root.barMarginV + root.barHeight;
var availableHeight = root.height - availableStart;
var availableStart = (root.isFramed ? 0 : root.barMarginV) + root.barHeight;
var availableHeight = root.height - availableStart - (root.isFramed ? root.frameThickness : 0);
calculatedY = availableStart + (availableHeight - panelHeight) / 2;
} else if (root.barPosition === "bottom") {
var availableHeight = root.height - root.barMarginV - root.barHeight;
calculatedY = (availableHeight - panelHeight) / 2;
var availableHeight = root.height - (root.isFramed ? 0 : root.barMarginV) - root.barHeight - (root.isFramed ? root.frameThickness : 0);
calculatedY = (root.isFramed ? root.frameThickness : 0) + (availableHeight - panelHeight) / 2;
} else {
calculatedY = (root.height - panelHeight) / 2;
}
@@ -546,25 +569,25 @@ Item {
}
} else if (root.panelAnchorTop) {
if (root.effectivePanelAnchorTop) {
calculatedY = root.barPosition === "top" ? topBarEdgeWithOverlap : 0;
calculatedY = root.barPosition === "top" ? topBarEdgeWithOverlap : (root.isFramed ? root.frameThickness : 0);
} else {
var topBarOffset = (root.barPosition === "top") ? barOffset : 0;
calculatedY = topBarOffset + Style.marginL;
calculatedY = topBarOffset + effMarginT;
}
} else if (root.panelAnchorBottom) {
if (root.effectivePanelAnchorBottom) {
calculatedY = root.barPosition === "bottom" ? bottomBarEdgeWithOverlap - panelHeight : root.height - panelHeight;
calculatedY = root.barPosition === "bottom" ? bottomBarEdgeWithOverlap - panelHeight : root.height - panelHeight - (root.isFramed ? root.frameThickness : 0);
} else {
var bottomBarOffset = (root.barPosition === "bottom") ? barOffset : 0;
calculatedY = root.height - panelHeight - bottomBarOffset - Style.marginL;
calculatedY = root.height - panelHeight - bottomBarOffset - effMarginB;
}
} else {
// No explicit vertical anchor
if (root.barIsVertical) {
if (panelContent.allowAttach) {
var cornerInset = root.barFloating ? Style.radiusL * 2 : 0;
var barTopEdge = root.barMarginV + cornerInset;
var barBottomEdge = root.height - root.barMarginV - cornerInset;
var barTopEdge = (root.isFramed ? root.frameThickness : root.barMarginV) + cornerInset;
var barBottomEdge = root.height - (root.isFramed ? root.frameThickness : root.barMarginV) - cornerInset;
var centeredY = (root.height - panelHeight) / 2;
calculatedY = Math.max(barTopEdge, Math.min(centeredY, barBottomEdge - panelHeight));
} else {
@@ -573,11 +596,11 @@ Item {
} else {
// Horizontal bar, not attached
if (root.barPosition === "top") {
calculatedY = barOffset + Style.marginL;
calculatedY = barOffset + effMarginT;
} else if (root.barPosition === "bottom") {
calculatedY = Style.marginL;
calculatedY = effMarginT;
} else {
calculatedY = Style.marginL;
calculatedY = effMarginT;
}
}
}
@@ -586,8 +609,8 @@ Item {
// Edge snapping for Y
if (panelContent.allowAttach && !root.barFloating && root.height > 0 && panelHeight > 0) {
var topEdgePos = root.barPosition === "top" ? topBarEdgeWithOverlap : root.barMarginV;
var bottomEdgePos = root.barPosition === "bottom" ? bottomBarEdgeWithOverlap - panelHeight : root.height - root.barMarginV - panelHeight;
var topEdgePos = root.barPosition === "top" ? topBarEdgeWithOverlap : (root.isFramed ? root.frameThickness : root.barMarginV);
var bottomEdgePos = root.barPosition === "bottom" ? bottomBarEdgeWithOverlap - panelHeight : root.height - (root.isFramed ? root.frameThickness : root.barMarginV) - panelHeight;
// Only snap to top edge if panel is actually meant to be at top (or no explicit anchor)
var shouldSnapToTop = root.effectivePanelAnchorTop || (!root.hasExplicitVerticalAnchor && root.barPosition === "top");
@@ -755,16 +778,16 @@ Item {
}
// Edge detection - detect if panel is touching screen edges
readonly property bool touchingLeftEdge: allowAttach && panelBackground.x <= 1
readonly property bool touchingRightEdge: allowAttach && (panelBackground.x + panelBackground.width) >= (root.width - 1)
readonly property bool touchingTopEdge: allowAttach && panelBackground.y <= 1
readonly property bool touchingBottomEdge: allowAttach && (panelBackground.y + panelBackground.height) >= (root.height - 1)
readonly property bool touchingLeftEdge: allowAttach && panelBackground.x <= (isFramed ? frameThickness + 1 : 1)
readonly property bool touchingRightEdge: allowAttach && (panelBackground.x + panelBackground.width) >= (root.width - (isFramed ? frameThickness + 1 : 1))
readonly property bool touchingTopEdge: allowAttach && panelBackground.y <= (isFramed ? frameThickness + 1 : 1)
readonly property bool touchingBottomEdge: allowAttach && (panelBackground.y + panelBackground.height) >= (root.height - (isFramed ? frameThickness + 1 : 1))
// Bar edge detection - detect if panel is touching bar edges (for cases where centered panels snap to bar due to height constraints)
readonly property bool touchingTopBar: allowAttachToBar && root.barPosition === "top" && !root.barIsVertical && Math.abs(panelBackground.y - (root.barMarginV + root.barHeight)) <= 1
readonly property bool touchingBottomBar: allowAttachToBar && root.barPosition === "bottom" && !root.barIsVertical && Math.abs((panelBackground.y + panelBackground.height) - (root.height - root.barMarginV - root.barHeight)) <= 1
readonly property bool touchingLeftBar: allowAttachToBar && root.barPosition === "left" && root.barIsVertical && Math.abs(panelBackground.x - (root.barMarginH + root.barHeight)) <= 1
readonly property bool touchingRightBar: allowAttachToBar && root.barPosition === "right" && root.barIsVertical && Math.abs((panelBackground.x + panelBackground.width) - (root.width - root.barMarginH - root.barHeight)) <= 1
readonly property bool touchingTopBar: allowAttachToBar && root.barPosition === "top" && !root.barIsVertical && Math.abs(panelBackground.y - ((isFramed ? 0 : root.barMarginV) + root.barHeight)) <= 1
readonly property bool touchingBottomBar: allowAttachToBar && root.barPosition === "bottom" && !root.barIsVertical && Math.abs((panelBackground.y + panelBackground.height) - (root.height - (isFramed ? 0 : root.barMarginV) - root.barHeight)) <= 1
readonly property bool touchingLeftBar: allowAttachToBar && root.barPosition === "left" && root.barIsVertical && Math.abs(panelBackground.x - ((isFramed ? 0 : root.barMarginH) + root.barHeight)) <= 1
readonly property bool touchingRightBar: allowAttachToBar && root.barPosition === "right" && root.barIsVertical && Math.abs((panelBackground.x + panelBackground.width) - (root.width - (isFramed ? 0 : root.barMarginH) - root.barHeight)) <= 1
// Expose panelBackground for geometry placeholder
property alias geometryPlaceholder: panelBackground
@@ -792,31 +815,31 @@ Item {
readonly property bool willTouchTopBar: {
if (!panelContent.allowAttachToBar || root.barPosition !== "top" || root.barIsVertical)
return false;
var targetTopBarY = root.barMarginV + root.barHeight;
var targetTopBarY = (isFramed ? 0 : root.barMarginV) + root.barHeight;
return Math.abs(panelBackground.targetY - targetTopBarY) <= 1;
}
readonly property bool willTouchBottomBar: {
if (!panelContent.allowAttachToBar || root.barPosition !== "bottom" || root.barIsVertical)
return false;
var targetBottomBarY = root.height - root.barMarginV - root.barHeight - panelBackground.targetHeight;
var targetBottomBarY = root.height - (isFramed ? 0 : root.barMarginV) - root.barHeight - panelBackground.targetHeight;
return Math.abs(panelBackground.targetY - targetBottomBarY) <= 1;
}
readonly property bool willTouchLeftBar: {
if (!panelContent.allowAttachToBar || root.barPosition !== "left" || !root.barIsVertical)
return false;
var targetLeftBarX = root.barMarginH + root.barHeight;
var targetLeftBarX = (isFramed ? 0 : root.barMarginH) + root.barHeight;
return Math.abs(panelBackground.targetX - targetLeftBarX) <= 1;
}
readonly property bool willTouchRightBar: {
if (!panelContent.allowAttachToBar || root.barPosition !== "right" || !root.barIsVertical)
return false;
var targetRightBarX = root.width - root.barMarginH - root.barHeight - panelBackground.targetWidth;
var targetRightBarX = root.width - (isFramed ? 0 : root.barMarginH) - root.barHeight - panelBackground.targetWidth;
return Math.abs(panelBackground.targetX - targetRightBarX) <= 1;
}
readonly property bool willTouchTopEdge: panelContent.allowAttach && panelBackground.targetY <= 1
readonly property bool willTouchBottomEdge: panelContent.allowAttach && (panelBackground.targetY + panelBackground.targetHeight) >= (root.height - 1)
readonly property bool willTouchLeftEdge: panelContent.allowAttach && panelBackground.targetX <= 1
readonly property bool willTouchRightEdge: panelContent.allowAttach && (panelBackground.targetX + panelBackground.targetWidth) >= (root.width - 1)
readonly property bool willTouchTopEdge: panelContent.allowAttach && panelBackground.targetY <= (isFramed ? frameThickness + 1 : 1)
readonly property bool willTouchBottomEdge: panelContent.allowAttach && (panelBackground.targetY + panelBackground.targetHeight) >= (root.height - (isFramed ? frameThickness + 1 : 1))
readonly property bool willTouchLeftEdge: panelContent.allowAttach && panelBackground.targetX <= (isFramed ? frameThickness + 1 : 1)
readonly property bool willTouchRightEdge: panelContent.allowAttach && (panelBackground.targetX + panelBackground.targetWidth) >= (root.width - (isFramed ? frameThickness + 1 : 1))
readonly property bool isActuallyAttachedToAnyEdge: {
return willTouchTopBar || willTouchBottomBar || willTouchLeftBar || willTouchRightBar || willTouchTopEdge || willTouchBottomEdge || willTouchLeftEdge || willTouchRightEdge;
+12 -10
View File
@@ -80,34 +80,37 @@ Variants {
readonly property bool isFloating: Settings.data.bar.floating
readonly property real barHeight: Style.getBarHeightForScreen(notifWindow.screen?.name)
readonly property bool isFramed: Settings.data.bar.barType === "framed"
readonly property real frameThickness: Settings.data.bar.frameThickness ?? 8
readonly property int notifWidth: Math.round(440 * Style.uiScaleRatio)
readonly property int shadowPadding: Style.shadowBlurMax + Style.marginL
// Calculate bar offsets for each edge separately
// Calculate bar and frame offsets for each edge separately
readonly property int barOffsetTop: {
if (barPos !== "top")
return 0;
return isFramed ? frameThickness : 0;
const floatMarginV = isFloating ? Math.ceil(Settings.data.bar.marginVertical) : 0;
return barHeight + floatMarginV;
}
readonly property int barOffsetBottom: {
if (barPos !== "bottom")
return 0;
return isFramed ? frameThickness : 0;
const floatMarginV = isFloating ? Math.ceil(Settings.data.bar.marginVertical) : 0;
return barHeight + floatMarginV;
}
readonly property int barOffsetLeft: {
if (barPos !== "left")
return 0;
return isFramed ? frameThickness : 0;
const floatMarginH = isFloating ? Math.ceil(Settings.data.bar.marginHorizontal) : 0;
return barHeight + floatMarginH;
}
readonly property int barOffsetRight: {
if (barPos !== "right")
return 0;
return isFramed ? frameThickness : 0;
const floatMarginH = isFloating ? Math.ceil(Settings.data.bar.marginHorizontal) : 0;
return barHeight + floatMarginH;
}
@@ -120,7 +123,7 @@ Variants {
// Margins for PanelWindow - only apply bar offset for the specific edge where the bar is
margins.top: isTop ? barOffsetTop - shadowPadding + Style.marginM : 0
margins.bottom: isBottom ? barOffsetBottom - shadowPadding + Style.marginM : 0
margins.bottom: isBottom ? barOffsetBottom - shadowPadding : 0
margins.left: isLeft ? barOffsetLeft - shadowPadding + Style.marginM : 0
margins.right: isRight ? barOffsetRight - shadowPadding + Style.marginM : 0
@@ -233,13 +236,12 @@ Variants {
height: 2
color: "transparent"
readonly property real availableWidth: parent.width - (2 * parent.radius)
Rectangle {
id: progressBar
readonly property real progressWidth: cardBackground.width - (2 * cardBackground.radius)
height: parent.height
x: parent.parent.radius + (parent.availableWidth * (1 - model.progress)) / 2
width: parent.availableWidth * model.progress
x: cardBackground.radius + (progressWidth * (1 - model.progress)) / 2
width: progressWidth * model.progress
color: {
var baseColor = model.urgency === 2 ? Color.mError : model.urgency === 0 ? Color.mOnSurface : Color.mPrimary;
+7
View File
@@ -422,6 +422,8 @@ Variants {
readonly property string screenBarPosition: Settings.getBarPositionForScreen(root.modelData?.name)
readonly property real barHeight: Style.getBarHeightForScreen(root.modelData?.name)
readonly property bool isFramed: Settings.data.bar.barType === "framed"
readonly property real frameThickness: Settings.data.bar.frameThickness ?? 8
function calculateMargin(isAnchored, position) {
if (!isAnchored)
@@ -433,6 +435,11 @@ Variants {
const floatExtra = Math.ceil(Settings.data.bar.floating ? (isVertical ? Settings.data.bar.marginVertical : Settings.data.bar.marginHorizontal) : 0);
return barHeight + base + floatExtra;
}
if (isFramed) {
return base + frameThickness;
}
return base;
}
+8 -6
View File
@@ -339,8 +339,6 @@ SmartPanel {
}
}
// Tab Bar
// Content Stack
StackLayout {
Layout.fillWidth: true
@@ -349,14 +347,16 @@ SmartPanel {
// Applications Tab (Volume)
NScrollView {
id: volumeScrollView
horizontalPolicy: ScrollBar.AlwaysOff
verticalPolicy: ScrollBar.AsNeeded
clip: true
contentWidth: availableWidth
reserveScrollbarSpace: false
gradientColor: Color.mSurface
ColumnLayout {
spacing: Style.marginM
width: parent.width
width: volumeScrollView.availableWidth
// Output Volume
NBox {
@@ -801,15 +801,17 @@ SmartPanel {
// Devices Tab
NScrollView {
id: devicesScrollView
horizontalPolicy: ScrollBar.AlwaysOff
verticalPolicy: ScrollBar.AsNeeded
clip: true
contentWidth: availableWidth
reserveScrollbarSpace: false
gradientColor: Color.mSurface
// AudioService Devices
ColumnLayout {
spacing: Style.marginM
width: parent.width
width: devicesScrollView.availableWidth
// -------------------------------
// Output Devices
+179 -172
View File
@@ -34,128 +34,34 @@ SmartPanel {
return "";
}
// Helper function to find battery device by nativePath
function findBatteryDevice(nativePath) {
if (!nativePath || nativePath === "") {
return UPower.displayDevice;
}
if (!UPower.devices) {
return UPower.displayDevice;
}
var deviceArray = UPower.devices.values || [];
for (var i = 0; i < deviceArray.length; i++) {
var device = deviceArray[i];
if (device && device.nativePath === nativePath) {
if (device.type === UPowerDeviceType.LinePower) {
continue;
}
if (device.percentage !== undefined) {
return device;
}
}
}
return UPower.displayDevice;
}
// Helper function to find Bluetooth device by MAC address from nativePath
function findBluetoothDevice(nativePath) {
if (!nativePath || !BluetoothService.devices) {
return null;
}
var macMatch = nativePath.match(/([0-9a-fA-F]{2}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2})/);
if (!macMatch) {
return null;
}
var macAddress = macMatch[1].toUpperCase();
var deviceArray = BluetoothService.devices.values || [];
for (var i = 0; i < deviceArray.length; i++) {
var device = deviceArray[i];
if (device && device.address && device.address.toUpperCase() === macAddress) {
return device;
}
}
return null;
}
readonly property string deviceNativePath: getBatteryDevicePath()
readonly property var battery: findBatteryDevice(deviceNativePath)
readonly property var bluetoothDevice: deviceNativePath ? findBluetoothDevice(deviceNativePath) : null
readonly property bool hasBluetoothBattery: bluetoothDevice && bluetoothDevice.batteryAvailable && bluetoothDevice.battery !== undefined
readonly property bool isBluetoothConnected: bluetoothDevice && bluetoothDevice.connected !== undefined ? bluetoothDevice.connected : false
// Check if device is actually present/connected
readonly property bool isDevicePresent: {
if (deviceNativePath && deviceNativePath !== "") {
if (bluetoothDevice) {
return isBluetoothConnected;
}
if (battery && battery.nativePath === deviceNativePath) {
if (battery.type === UPowerDeviceType.Battery && battery.isPresent !== undefined) {
return battery.isPresent;
}
return battery.ready && battery.percentage !== undefined && (battery.percentage > 0 || battery.state === UPowerDeviceState.Charging);
}
return false;
}
if (battery) {
if (battery.type === UPowerDeviceType.Battery && battery.isPresent !== undefined) {
return battery.isPresent;
}
return battery.ready && battery.percentage !== undefined;
}
return false;
}
// Use the centralized helper to find the specific device or fallback to primary
readonly property var selectedDevice: BatteryService.resolveDevice(deviceNativePath)
readonly property bool isReady: battery && battery.ready && isDevicePresent && (battery.percentage !== undefined || hasBluetoothBattery)
readonly property int percent: isReady ? Math.round(hasBluetoothBattery ? (bluetoothDevice.battery * 100) : (battery.percentage * 100)) : -1
readonly property bool isCharging: isReady ? battery.state === UPowerDeviceState.Charging : false
readonly property bool isPluggedIn: isReady ? (battery.state === UPowerDeviceState.FullyCharged || battery.state === UPowerDeviceState.PendingCharge) : false
readonly property bool healthAvailable: (isReady && battery.healthSupported) || BatteryService.healthAvailable
readonly property int healthPercent: (isReady && battery.healthSupported) ? Math.round(battery.healthPercentage) : BatteryService.healthPercent
// Check if selected device is actually present/connected
readonly property bool isDevicePresent: BatteryService.isDevicePresent(selectedDevice)
readonly property bool isReady: BatteryService.isDeviceReady(selectedDevice)
function getDeviceName() {
if (!isReady) {
return "";
}
// Don't show name for laptop batteries
if (battery && battery.isLaptopBattery) {
return "";
}
if (bluetoothDevice && bluetoothDevice.name) {
return bluetoothDevice.name;
}
if (battery && battery.model) {
return battery.model;
}
return "";
}
readonly property int percent: isReady ? Math.round(BatteryService.getPercentage(selectedDevice)) : -1
readonly property bool isCharging: BatteryService.isCharging(selectedDevice)
readonly property bool isPluggedIn: BatteryService.isPluggedIn(selectedDevice)
readonly property string deviceName: getDeviceName()
readonly property bool isLaptopBattery: selectedDevice && !BatteryService.isBluetoothDevice(selectedDevice)
readonly property bool healthAvailable: (isReady && isLaptopBattery && selectedDevice.healthSupported) || (isLaptopBattery && BatteryService.healthAvailable)
readonly property int healthPercent: (isReady && isLaptopBattery && selectedDevice.healthSupported) ? Math.round(selectedDevice.healthPercentage) : BatteryService.healthPercent
readonly property string deviceName: BatteryService.getDeviceName(selectedDevice)
readonly property string panelTitle: deviceName ? `${I18n.tr("common.battery")} - ${deviceName}` : I18n.tr("common.battery")
readonly property string timeText: {
if (!isReady || !isDevicePresent)
return I18n.tr("battery.no-battery-detected");
if (isPluggedIn) {
return I18n.tr("battery.plugged-in");
}
if (battery.timeToFull > 0) {
return I18n.tr("battery.time-until-full", {
"time": Time.formatVagueHumanReadableDuration(battery.timeToFull)
});
}
if (battery.timeToEmpty > 0) {
return I18n.tr("battery.time-left", {
"time": Time.formatVagueHumanReadableDuration(battery.timeToEmpty)
});
}
return I18n.tr("common.idle");
}
// Use the centralized list of all devices
readonly property var allDevices: BatteryService.devices
readonly property var laptopBatteries: BatteryService.laptopBatteries
readonly property var otherDevices: BatteryService.externalBatteries
readonly property string timeText: BatteryService.getTimeRemainingText(selectedDevice)
readonly property string iconName: BatteryService.getIcon(percent, isCharging, isPluggedIn, isReady)
property var batteryWidgetInstance: BarService.lookupWidget("Battery", screen ? screen.name : null)
@@ -262,95 +168,196 @@ SmartPanel {
NBox {
Layout.fillWidth: true
implicitHeight: chargeLayout.implicitHeight + Style.marginL * 2
visible: isReady
visible: allDevices.length > 0
ColumnLayout {
id: chargeLayout
anchors.fill: parent
anchors.margins: Style.marginL
spacing: Style.marginS
spacing: Style.marginL
RowLayout {
Layout.fillWidth: true
spacing: Style.marginS
// Laptop batteries section
Repeater {
model: laptopBatteries
delegate: ColumnLayout {
Layout.fillWidth: true
spacing: Style.marginS
ColumnLayout {
NText {
text: I18n.tr("battery.battery-level")
color: Color.mOnSurface
pointSize: Style.fontSizeS
RowLayout {
Layout.fillWidth: true
spacing: Style.marginS
ColumnLayout {
Layout.fillWidth: true
spacing: Style.marginS
RowLayout {
NIcon {
color: (isCharging || isPluggedIn) ? Color.mPrimary : Color.mOnSurface
icon: iconName
}
NText {
readonly property string dName: BatteryService.getDeviceName(modelData)
text: dName ? dName : I18n.tr("common.battery")
color: Color.mOnSurface
pointSize: Style.fontSizeS
}
}
RowLayout {
Layout.fillWidth: true
spacing: Style.marginS
Rectangle {
Layout.fillWidth: true
height: Math.round(8 * Style.uiScaleRatio)
radius: Math.min(Style.radiusL, height / 2)
color: Color.mSurface
Rectangle {
anchors.verticalCenter: parent.verticalCenter
height: parent.height
radius: parent.radius
width: {
var p = BatteryService.getPercentage(modelData);
var ratio = Math.max(0, Math.min(1, p / 100));
return parent.width * ratio;
}
color: Color.mPrimary
}
}
NText {
Layout.preferredWidth: 40 * Style.uiScaleRatio
horizontalAlignment: Text.AlignRight
text: `${Math.round(BatteryService.getPercentage(modelData))}%`
color: Color.mOnSurface
pointSize: Style.fontSizeS
font.weight: Style.fontWeightBold
}
}
}
}
Rectangle {
// Health for this specific laptop battery
ColumnLayout {
Layout.fillWidth: true
height: Math.round(8 * Style.uiScaleRatio)
radius: Math.min(Style.radiusL, height / 2)
color: Color.mSurfaceVariant
spacing: Style.marginS
visible: modelData.healthSupported || (modelData === BatteryService.primaryDevice && BatteryService.healthAvailable)
RowLayout {
Layout.fillWidth: true
spacing: Style.marginS
Rectangle {
anchors.verticalCenter: parent.verticalCenter
height: parent.height
radius: parent.radius
width: {
var ratio = Math.max(0, Math.min(1, percent / 100));
return parent.width * ratio;
NIcon {
icon: "heart"
}
NText {
text: I18n.tr("battery.battery-health")
color: Color.mOnSurface
pointSize: Style.fontSizeS
}
}
RowLayout {
Layout.fillWidth: true
spacing: Style.marginS
Rectangle {
Layout.fillWidth: true
height: Math.round(8 * Style.uiScaleRatio)
radius: height / 2
color: Color.mSurface
Rectangle {
anchors.verticalCenter: parent.verticalCenter
height: parent.height
radius: parent.radius
width: {
var h = modelData.healthSupported ? modelData.healthPercentage : (modelData === BatteryService.primaryDevice ? BatteryService.healthPercent : 0);
if (h <= 0)
return 0;
var ratio = Math.max(0, Math.min(1, h / 100));
return parent.width * ratio;
}
color: {
var h = modelData.healthSupported ? modelData.healthPercentage : (modelData === BatteryService.primaryDevice ? BatteryService.healthPercent : 0);
return h >= 80 ? Color.mPrimary : (h >= 50 ? Color.mTertiary : Color.mError);
}
}
}
NText {
Layout.preferredWidth: 40 * Style.uiScaleRatio
horizontalAlignment: Text.AlignRight
readonly property int h: modelData.healthSupported ? Math.round(modelData.healthPercentage) : (modelData === BatteryService.primaryDevice ? BatteryService.healthPercent : -1)
text: h >= 0 ? `${h}%` : "--"
color: Color.mOnSurface
pointSize: Style.fontSizeS
font.weight: Style.fontWeightBold
}
color: Color.mPrimary
}
}
}
NText {
text: percent >= 0 ? `${percent}%` : "--"
color: Color.mOnSurface
pointSize: Style.fontSizeS
font.weight: Style.fontWeightBold
}
}
RowLayout {
NDivider {
Layout.fillWidth: true
spacing: Style.marginS
visible: healthAvailable
visible: laptopBatteries.length > 0 && otherDevices.length > 0
}
ColumnLayout {
// Other devices (Bluetooth) section
Repeater {
model: otherDevices
delegate: ColumnLayout {
Layout.fillWidth: true
spacing: Style.marginS
RowLayout {
spacing: Style.marginXS
Layout.fillWidth: true
spacing: Style.marginS
NIcon {
icon: BluetoothService.getDeviceIcon(modelData)
}
NText {
text: I18n.tr("battery.battery-health")
readonly property string dName: BatteryService.getDeviceName(modelData)
text: dName ? dName : I18n.tr("common.bluetooth")
color: Color.mOnSurface
pointSize: Style.fontSizeS
}
}
Rectangle {
RowLayout {
Layout.fillWidth: true
height: Math.round(8 * Style.uiScaleRatio)
radius: Math.min(Style.radiusL, height / 2)
color: Color.mSurfaceVariant
spacing: Style.marginS
Rectangle {
anchors.verticalCenter: parent.verticalCenter
height: parent.height
radius: parent.radius
width: {
if (!healthAvailable || healthPercent <= 0)
return 0;
var ratio = Math.max(0, Math.min(1, healthPercent / 100));
return parent.width * ratio;
Layout.fillWidth: true
height: Math.round(8 * Style.uiScaleRatio)
radius: Math.min(Style.radiusL, height / 2)
color: Color.mSurface
Rectangle {
anchors.verticalCenter: parent.verticalCenter
height: parent.height
radius: parent.radius
width: {
var p = BatteryService.getPercentage(modelData);
var ratio = Math.max(0, Math.min(1, p / 100));
return parent.width * ratio;
}
color: Color.mPrimary
}
color: healthPercent >= 80 ? Color.mPrimary : (healthPercent >= 50 ? Color.mTertiary : Color.mError)
}
NText {
Layout.preferredWidth: 40 * Style.uiScaleRatio
horizontalAlignment: Text.AlignRight
text: `${Math.round(BatteryService.getPercentage(modelData))}%`
color: Color.mOnSurface
pointSize: Style.fontSizeS
font.weight: Style.fontWeightBold
}
}
}
NText {
text: healthPercent >= 0 ? `${healthPercent}%` : "--"
color: Color.mOnSurface
pointSize: Style.fontSizeS
font.weight: Style.fontWeightBold
}
}
}
}
@@ -429,7 +436,7 @@ SmartPanel {
NDivider {
Layout.fillWidth: true
visible: showPowerProfiles && showNoctaliaPerformance
visible: showPowerProfiles && PowerProfileService.available && showNoctaliaPerformance
}
RowLayout {
@@ -218,12 +218,6 @@ NBox {
enabled: (canConnect || canDisconnect || canPair) && !isBusy
outlined: !button.hovered
fontSize: Style.fontSizeS
backgroundColor: {
if (device.canDisconnect && !isBusy) {
return Color.mError;
}
return Color.mPrimary;
}
tooltipText: root.tooltipText
text: {
if (modelData.pairing) {
+4 -2
View File
@@ -90,15 +90,17 @@ SmartPanel {
}
NScrollView {
id: bluetoothScrollView
Layout.fillWidth: true
Layout.fillHeight: true
horizontalPolicy: ScrollBar.AlwaysOff
verticalPolicy: ScrollBar.AsNeeded
clip: true
reserveScrollbarSpace: false
gradientColor: Color.mSurface
ColumnLayout {
id: devicesList
width: parent.width
width: bluetoothScrollView.availableWidth
spacing: Style.marginM
// Adapter not available of disabled
@@ -75,17 +75,19 @@ SmartPanel {
}
NScrollView {
id: brightnessScrollView
Layout.fillWidth: true
Layout.fillHeight: true
horizontalPolicy: ScrollBar.AlwaysOff
verticalPolicy: ScrollBar.AsNeeded
clip: true
contentWidth: availableWidth
reserveScrollbarSpace: false
gradientColor: Color.mSurface
// AudioService Devices
ColumnLayout {
spacing: Style.marginM
width: parent.width
width: brightnessScrollView.availableWidth
Repeater {
model: Quickshell.screens || []
+2 -1
View File
@@ -126,6 +126,7 @@ SmartPanel {
}
NScrollView {
id: changelogScrollView
Layout.fillWidth: true
Layout.fillHeight: true
horizontalPolicy: ScrollBar.AlwaysOff
@@ -133,7 +134,7 @@ SmartPanel {
padding: 0
ColumnLayout {
width: parent.width
width: changelogScrollView.availableWidth
spacing: Style.marginM
NText {
+2 -2
View File
@@ -157,14 +157,14 @@ Item {
visible: !isImageContent && !loadingFullContent
NScrollView {
id: clipboardScrollView
Layout.fillWidth: true
Layout.fillHeight: true
clip: true
horizontalPolicy: Settings.data.appLauncher.clipboardWrapText ? ScrollBar.AlwaysOff : ScrollBar.AsNeeded
NText {
text: fullContent
width: Settings.data.appLauncher.clipboardWrapText ? parent.width : implicitWidth
width: Settings.data.appLauncher.clipboardWrapText ? clipboardScrollView.availableWidth : implicitWidth
wrapMode: Settings.data.appLauncher.clipboardWrapText ? Text.Wrap : Text.NoWrap
textFormat: Text.PlainText
font.pointSize: Style.fontSizeM
+28 -8
View File
@@ -755,6 +755,14 @@ SmartPanel {
}
}
SessionProvider {
id: sessionProvider
Component.onCompleted: {
registerProvider(this);
Logger.d("Launcher", "Registered: SessionProvider");
}
}
// ---------------------------------------------------
panelContent: Rectangle {
id: ui
@@ -877,8 +885,8 @@ SmartPanel {
RowLayout {
anchors.fill: parent
anchors.margins: Style.marginL // Apply overall margins here
spacing: Style.marginM // Apply spacing between elements here
anchors.margins: Style.marginL
spacing: Style.marginM
// Left Pane
ColumnLayout {
@@ -992,6 +1000,8 @@ SmartPanel {
}
}
// --------------------------
// LIST VIEW
Component {
id: listViewComponent
NListView {
@@ -999,6 +1009,9 @@ SmartPanel {
horizontalPolicy: ScrollBar.AlwaysOff
verticalPolicy: ScrollBar.AlwaysOff
reserveScrollbarSpace: false
gradientColor: Color.mSurface
wheelScrollMultiplier: 4.0
width: parent.width
height: parent.height
@@ -1031,7 +1044,7 @@ SmartPanel {
}
}
width: resultsList.width
width: resultsList.availableWidth
implicitHeight: entryHeight
clip: true
color: entry.isSelected ? Color.mHover : Color.mSurface
@@ -1271,6 +1284,8 @@ SmartPanel {
}
}
// --------------------------
// SINGLE ITEM VIEW, ex: kaggi
Component {
id: singleViewComponent
@@ -1300,17 +1315,17 @@ SmartPanel {
}
}
ScrollView {
NScrollView {
id: descriptionScrollView
Layout.alignment: Qt.AlignTop | Qt.AlignLeft
Layout.topMargin: Style.fontSizeL + Style.marginXL
Layout.fillWidth: true
Layout.fillHeight: true
clip: true
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
contentWidth: availableWidth
horizontalPolicy: ScrollBar.AlwaysOff
reserveScrollbarSpace: false
NText {
width: parent.width
width: descriptionScrollView.availableWidth
text: root.results.length > 0 ? root.results[0].description : ""
pointSize: Style.fontSizeM
font.weight: Font.Bold
@@ -1326,6 +1341,8 @@ SmartPanel {
}
}
// --------------------------
// GRID VIEW
Component {
id: gridViewComponent
NGridView {
@@ -1333,6 +1350,9 @@ SmartPanel {
horizontalPolicy: ScrollBar.AlwaysOff
verticalPolicy: ScrollBar.AlwaysOff
reserveScrollbarSpace: false
gradientColor: "transparent" //Color.mSurface
wheelScrollMultiplier: 4.0
width: parent.width
height: parent.height
@@ -443,11 +443,12 @@ Item {
return [];
// Set category mode based on whether there's a query
showsCategories = !query || query.trim() === "";
const isSearching = !!(query && query.trim() !== "");
showsCategories = !isSearching;
// Filter by category first
// Filter by category only when NOT searching
let filteredEntries = entries;
if (selectedCategory && selectedCategory !== "all") {
if (!isSearching && selectedCategory && selectedCategory !== "all") {
filteredEntries = entries.filter(app => appMatchesCategory(app, selectedCategory));
}
@@ -543,7 +544,7 @@ Item {
"description": app.genericName || app.comment || "",
"icon": app.icon || "application-x-executable",
"isImage": false,
"_score": (score !== undefined ? score : 0) + 1,
"_score": (score !== undefined ? score : 0),
"provider": root,
"onActivate": function () {
// Close the launcher/SmartPanel immediately without any animations.
@@ -0,0 +1,185 @@
import QtQuick
import Quickshell
import qs.Commons
import qs.Services.Compositor
import qs.Services.UI
Item {
id: root
// Provider metadata
property string name: I18n.tr("tooltips.session-menu")
property var launcher: null
property bool handleSearch: true
property string supportedLayouts: "list"
// Session actions with search keywords
readonly property var sessionActions: [
{
"action": "lock",
"labelKey": "common.lock",
"icon": "lock",
"keywords": ["lock", "screen", "secure"]
},
{
"action": "suspend",
"labelKey": "common.suspend",
"icon": "suspend",
"keywords": ["suspend", "sleep", "standby"]
},
{
"action": "hibernate",
"labelKey": "common.hibernate",
"icon": "hibernate",
"keywords": ["hibernate", "disk"]
},
{
"action": "reboot",
"labelKey": "common.reboot",
"icon": "reboot",
"keywords": ["reboot", "restart", "reload"]
},
{
"action": "logout",
"labelKey": "common.logout",
"icon": "logout",
"keywords": ["logout", "sign out", "exit", "leave"]
},
{
"action": "shutdown",
"labelKey": "common.shutdown",
"icon": "shutdown",
"keywords": ["shutdown", "power off", "turn off", "poweroff"]
}
]
function init() {
Logger.d("SessionProvider", "Initialized");
}
function getEnabledActions() {
var powerOptions = Settings.data.sessionMenu.powerOptions || [];
var enabledSet = {};
for (var i = 0; i < powerOptions.length; i++) {
if (powerOptions[i].enabled) {
enabledSet[powerOptions[i].action] = powerOptions[i];
}
}
var enabled = [];
for (var j = 0; j < sessionActions.length; j++) {
var action = sessionActions[j];
if (enabledSet[action.action]) {
enabled.push({
"action": action.action,
"labelKey": action.labelKey,
"icon": action.icon,
"keywords": action.keywords,
"command": enabledSet[action.action].command || ""
});
}
}
return enabled;
}
function getResults(query) {
if (!query)
return [];
var trimmed = query.trim();
if (!trimmed || trimmed.length < 2)
return [];
var enabledActions = getEnabledActions();
if (enabledActions.length === 0)
return [];
// Build searchable items with resolved translations
var items = [];
for (var i = 0; i < enabledActions.length; i++) {
var action = enabledActions[i];
var label = I18n.tr(action.labelKey);
items.push({
"action": action.action,
"icon": action.icon,
"label": label,
"command": action.command,
"searchText": [label.toLowerCase()].concat(action.keywords).join(" ")
});
}
var results = FuzzySort.go(trimmed, items, {
"keys": ["label", "searchText"],
"limit": 6
});
var launcherItems = [];
for (var j = 0; j < results.length; j++) {
var entry = results[j].obj;
var score = results[j].score;
launcherItems.push({
"name": entry.label,
"description": I18n.tr("tooltips.session-menu"),
"icon": entry.icon,
"isTablerIcon": true,
"isImage": false,
"_score": score - 1,
"provider": root,
"onActivate": createActivateHandler(entry.action, entry.command)
});
}
return launcherItems;
}
function createActivateHandler(action, command) {
return function () {
if (launcher)
launcher.close();
Qt.callLater(() => {
executeAction(action, command);
});
};
}
function executeAction(action, command) {
// If custom command is defined, execute it
if (command && command.trim() !== "") {
Logger.i("SessionProvider", "Executing custom command for action:", action, "Command:", command);
Quickshell.execDetached(["sh", "-lc", command]);
return;
}
// Otherwise, use default behavior
switch (action) {
case "lock":
if (PanelService.lockScreen && !PanelService.lockScreen.active) {
PanelService.lockScreen.active = true;
}
break;
case "suspend":
if (Settings.data.general.lockOnSuspend) {
CompositorService.lockAndSuspend();
} else {
CompositorService.suspend();
}
break;
case "hibernate":
CompositorService.hibernate();
break;
case "reboot":
CompositorService.reboot();
break;
case "logout":
CompositorService.logout();
break;
case "shutdown":
CompositorService.shutdown();
break;
}
}
}
@@ -85,7 +85,7 @@ Item {
"icon": "settings",
"isTablerIcon": true,
"isImage": false,
"_score": score,
"_score": score - 2,
"provider": root,
"onActivate": createActivateHandler(entry)
});
+3 -2
View File
@@ -284,11 +284,12 @@ SmartPanel {
Layout.fillHeight: true
horizontalPolicy: ScrollBar.AlwaysOff
verticalPolicy: ScrollBar.AsNeeded
clip: true
reserveScrollbarSpace: false
gradientColor: Color.mSurface
ColumnLayout {
id: contentColumn
width: parent.width
width: contentScroll.availableWidth
spacing: Style.marginM
// WiFi disabled state
@@ -255,15 +255,16 @@ SmartPanel {
NScrollView {
id: scrollView
anchors.fill: parent
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
ScrollBar.vertical.policy: ScrollBar.AsNeeded
clip: true
horizontalPolicy: ScrollBar.AlwaysOff
verticalPolicy: ScrollBar.AsNeeded
reserveScrollbarSpace: false
gradientColor: Color.mSurface
// Track which notification is expanded
property string expandedId: ""
ColumnLayout {
width: parent.width
width: scrollView.availableWidth
spacing: Style.marginM
// Empty state when no notifications
@@ -541,39 +542,6 @@ SmartPanel {
}
}
}
// Overlay gradient to smooth the hard cut due to scrolling at the bottom (only visible when scrollable)
Rectangle {
anchors.fill: parent
color: "transparent"
visible: scrollView.ScrollBar.vertical && scrollView.ScrollBar.vertical.size < 1.0
opacity: {
const scrollBar = scrollView.ScrollBar.vertical;
return (scrollBar.position + scrollBar.size >= 0.99) ? 0 : 1;
}
Behavior on opacity {
NumberAnimation {
duration: Style.animationFast
easing.type: Easing.InOutQuad
}
}
gradient: Gradient {
GradientStop {
position: 0.0
color: "transparent"
}
GradientStop {
position: 0.85
color: "transparent"
}
GradientStop {
position: 1.0
color: Qt.alpha(Color.mSurface, 0.95)
}
}
}
}
}
}
+12 -6
View File
@@ -553,8 +553,8 @@ SmartPanel {
Repeater {
model: powerOptions
delegate: LargeButton {
Layout.preferredWidth: 200 * Style.uiScaleRatio
Layout.preferredHeight: 200 * Style.uiScaleRatio
Layout.preferredWidth: Math.round(200 * Style.uiScaleRatio)
Layout.preferredHeight: Math.round(200 * Style.uiScaleRatio)
icon: modelData.icon
title: modelData.title
isShutdown: modelData.isShutdown || false
@@ -909,6 +909,9 @@ SmartPanel {
border.width: Style.borderS
border.color: Color.mOutline
layer.enabled: hoverScale !== 1.0
layer.smooth: true
// Scale transform for hover effect
transform: Scale {
origin.x: largeButtonRoot.width / 2
@@ -958,13 +961,16 @@ SmartPanel {
return Color.mOnPrimary;
return Color.mOnSurface;
}
pointSize: Style.fontSizeXXXL * 2
width: 80 * Style.uiScaleRatio
height: 80 * Style.uiScaleRatio
pointSize: Style.fontSizeXXXL * 2.25
width: 90 * Style.uiScaleRatio
height: 90 * Style.uiScaleRatio
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
property real iconScale: (largeButtonRoot.isSelected || largeButtonRoot.effectiveHover) ? 1.1 : 1.0
property real iconScale: (largeButtonRoot.isSelected || largeButtonRoot.effectiveHover) ? 1.15 : 1.0
layer.enabled: iconScale !== 1.0
layer.smooth: true
transform: Scale {
origin.x: iconElement.width / 2
@@ -20,7 +20,7 @@ Popup {
readonly property real maxHeight: screen ? screen.height * 0.9 : 800
width: Math.max(content.implicitWidth + padding * 2, 500)
width: Math.max(content.implicitWidth + padding * 2, 640)
height: Math.min(content.implicitHeight + padding * 2, maxHeight)
padding: Style.marginXL
modal: true
@@ -73,7 +73,7 @@ Popup {
NIconButton {
icon: "close"
tooltipText: I18n.tr("common.close")
onClicked: root.close()
onClicked: saveAndClose()
}
}
@@ -91,9 +91,10 @@ Popup {
Layout.fillWidth: true
Layout.fillHeight: true
Layout.minimumHeight: 100
gradientColor: Color.mSurface
ColumnLayout {
width: scrollView.width
width: scrollView.availableWidth
spacing: Style.marginM
// Settings based on widget type
@@ -151,26 +152,34 @@ Popup {
}
NButton {
text: I18n.tr("common.cancel")
text: I18n.tr("common.close")
outlined: true
onClicked: root.close()
}
NButton {
text: I18n.tr("common.apply")
icon: "check"
onClicked: {
if (settingsLoader.item && settingsLoader.item.saveSettings) {
var newSettings = settingsLoader.item.saveSettings();
root.updateWidgetSettings(root.sectionId, root.widgetIndex, newSettings);
root.close();
}
}
onClicked: saveAndClose()
}
}
}
}
Connections {
target: settingsLoader.item
function onSettingsChanged(newSettings) {
if (newSettings) {
root.updateWidgetSettings(root.sectionId, root.widgetIndex, newSettings);
}
}
ignoreUnknownSignals: true
}
function saveAndClose() {
if (settingsLoader.item && typeof settingsLoader.item.saveSettings === 'function') {
var newSettings = settingsLoader.item.saveSettings();
if (newSettings) {
root.updateWidgetSettings(root.sectionId, root.widgetIndex, newSettings);
}
}
root.close();
}
function loadWidgetSettings() {
const source = BarWidgetRegistry.widgetSettingsMap[widgetId];
if (source) {
@@ -12,6 +12,8 @@ ColumnLayout {
property var widgetData: null
property var widgetMetadata: null
signal settingsChanged(var settings)
// Local state
property bool valueShowIcon: widgetData.showIcon !== undefined ? widgetData.showIcon : widgetMetadata.showIcon
property string valueHideMode: "hidden" // Default to 'Hide When Empty'
@@ -56,7 +58,10 @@ ColumnLayout {
}
]
currentKey: root.valueHideMode
onSelected: key => root.valueHideMode = key
onSelected: key => {
root.valueHideMode = key;
settingsChanged(saveSettings());
}
}
NToggle {
@@ -64,7 +69,10 @@ ColumnLayout {
label: I18n.tr("bar.active-window.show-app-icon-label")
description: I18n.tr("bar.active-window.show-app-icon-description")
checked: root.valueShowIcon
onToggled: checked => root.valueShowIcon = checked
onToggled: checked => {
root.valueShowIcon = checked;
settingsChanged(saveSettings());
}
}
NToggle {
@@ -72,7 +80,10 @@ ColumnLayout {
label: I18n.tr("bar.tray.colorize-icons-label")
description: I18n.tr("bar.active-window.colorize-icons-description")
checked: root.valueColorizeIcons
onToggled: checked => root.valueColorizeIcons = checked
onToggled: checked => {
root.valueColorizeIcons = checked;
settingsChanged(saveSettings());
}
}
NTextInput {
@@ -82,6 +93,7 @@ ColumnLayout {
description: I18n.tr("bar.media-mini.max-width-description")
placeholderText: widgetMetadata.maxWidth
text: valueMaxWidth
onEditingFinished: settingsChanged(saveSettings())
}
NToggle {
@@ -89,7 +101,10 @@ ColumnLayout {
label: I18n.tr("bar.media-mini.use-fixed-width-label")
description: I18n.tr("bar.media-mini.use-fixed-width-description")
checked: valueUseFixedWidth
onToggled: checked => valueUseFixedWidth = checked
onToggled: checked => {
valueUseFixedWidth = checked;
settingsChanged(saveSettings());
}
}
NComboBox {
@@ -110,7 +125,10 @@ ColumnLayout {
}
]
currentKey: valueScrollingMode
onSelected: key => valueScrollingMode = key
onSelected: key => {
valueScrollingMode = key;
settingsChanged(saveSettings());
}
minimumWidth: 200
}
}
@@ -12,6 +12,8 @@ ColumnLayout {
property var widgetData: null
property var widgetMetadata: null
signal settingsChanged(var settings)
// Local state
property bool valueHideWhenIdle: widgetData.hideWhenIdle !== undefined ? widgetData.hideWhenIdle : widgetMetadata.hideWhenIdle
property string valueColorName: widgetData.colorName !== undefined ? widgetData.colorName : widgetMetadata.colorName
@@ -31,6 +33,7 @@ ColumnLayout {
description: I18n.tr("bar.audio-visualizer.width-description")
text: widgetData.width || widgetMetadata.width
placeholderText: I18n.tr("placeholders.enter-width-pixels")
onEditingFinished: settingsChanged(saveSettings())
}
NComboBox {
@@ -60,13 +63,19 @@ ColumnLayout {
}
]
currentKey: root.valueColorName
onSelected: key => root.valueColorName = key
onSelected: key => {
root.valueColorName = key;
settingsChanged(saveSettings());
}
}
NToggle {
label: I18n.tr("bar.audio-visualizer.hide-when-idle-label")
description: I18n.tr("bar.audio-visualizer.hide-when-idle-description")
checked: valueHideWhenIdle
onToggled: checked => valueHideWhenIdle = checked
onToggled: checked => {
valueHideWhenIdle = checked;
settingsChanged(saveSettings());
}
}
}
@@ -1,9 +1,10 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Quickshell.Services.UPower
import qs.Commons
import qs.Services.Hardware
import qs.Widgets
import qs.Services.Hardware
ColumnLayout {
id: root
@@ -13,6 +14,8 @@ ColumnLayout {
property var widgetData: null
property var widgetMetadata: null
signal settingsChanged(var settings)
// Local state
property string valueDisplayMode: widgetData.displayMode !== undefined ? widgetData.displayMode : widgetMetadata.displayMode
property int valueWarningThreshold: widgetData.warningThreshold !== undefined ? widgetData.warningThreshold : widgetMetadata.warningThreshold
@@ -22,60 +25,7 @@ ColumnLayout {
property bool valueHideIfNotDetected: widgetData.hideIfNotDetected !== undefined ? widgetData.hideIfNotDetected : widgetMetadata.hideIfNotDetected
property bool valueHideIfIdle: widgetData.hideIfIdle !== undefined ? widgetData.hideIfIdle : widgetMetadata.hideIfIdle
// Build model of available battery devices
function buildDeviceModel() {
var model = [
{
"key": "",
"name": I18n.tr("bar.battery.device-default")
}
];
if (!UPower.devices) {
return model;
}
var deviceArray = UPower.devices.values || [];
for (var i = 0; i < deviceArray.length; i++) {
var device = deviceArray[i];
if (!device || device.type === UPowerDeviceType.LinePower) {
continue;
}
var displayName = device.model || device.nativePath || "Unknown";
model.push({
"key": device.nativePath || "",
"name": displayName
});
}
return model;
}
readonly property int _deviceCount: (UPower.devices && UPower.devices.values) ? UPower.devices.values.length : 0
property var deviceModel: buildDeviceModel()
on_DeviceCountChanged: {
deviceModel = buildDeviceModel();
}
Connections {
target: UPower.devices
function onValuesChanged() {
deviceModel = buildDeviceModel();
}
}
Timer {
id: refreshTimer
interval: 2000
running: true
repeat: true
onTriggered: {
var currentCount = (UPower.devices && UPower.devices.values) ? UPower.devices.values.length : 0;
if (currentCount !== root._deviceCount) {
deviceModel = buildDeviceModel();
}
}
}
property var deviceModel: BatteryService.devicesModel
function saveSettings() {
var settings = Object.assign({}, widgetData || {});
@@ -105,10 +55,13 @@ ColumnLayout {
Layout.fillWidth: true
label: I18n.tr("bar.battery.device-label")
description: I18n.tr("bar.battery.device-description")
minimumWidth: 134
minimumWidth: 200
model: root.deviceModel
currentKey: root.valueDeviceNativePath
onSelected: key => root.valueDeviceNativePath = key
onSelected: key => {
root.valueDeviceNativePath = key;
settingsChanged(saveSettings());
}
}
// Update currentKey when model changes to ensure selection is preserved
@@ -122,15 +75,17 @@ ColumnLayout {
NIconButton {
icon: "refresh"
// TODO i18n
tooltipText: "Refresh device list"
onClicked: deviceModel = buildDeviceModel()
onClicked: BatteryService.devicesModel = BatteryService.buildDeviceModel()
}
}
NComboBox {
Layout.fillWidth: true
label: I18n.tr("bar.volume.display-mode-label")
description: I18n.tr("bar.volume.display-mode-description")
minimumWidth: 134
minimumWidth: 240
model: [
{
"key": "onhover",
@@ -146,7 +101,10 @@ ColumnLayout {
}
]
currentKey: root.valueDisplayMode
onSelected: key => root.valueDisplayMode = key
onSelected: key => {
root.valueDisplayMode = key;
settingsChanged(saveSettings());
}
}
NSpinBox {
@@ -156,34 +114,49 @@ ColumnLayout {
suffix: "%"
minimum: 5
maximum: 50
onValueChanged: valueWarningThreshold = value
onValueChanged: {
valueWarningThreshold = value;
settingsChanged(saveSettings());
}
}
NToggle {
label: I18n.tr("bar.battery.show-power-profile-label")
description: I18n.tr("bar.battery.show-power-profile-description")
checked: valueShowPowerProfiles
onToggled: checked => valueShowPowerProfiles = checked
onToggled: checked => {
valueShowPowerProfiles = checked;
settingsChanged(saveSettings());
}
}
NToggle {
label: I18n.tr("bar.battery.show-noctalia-performance-label")
description: I18n.tr("bar.battery.show-noctalia-performance-description")
checked: valueShowNoctaliaPerformance
onToggled: checked => valueShowNoctaliaPerformance = checked
onToggled: checked => {
valueShowNoctaliaPerformance = checked;
settingsChanged(saveSettings());
}
}
NToggle {
label: I18n.tr("bar.battery.hide-if-not-detected-label")
description: I18n.tr("bar.battery.hide-if-not-detected-description")
checked: valueHideIfNotDetected
onToggled: checked => valueHideIfNotDetected = checked
onToggled: checked => {
valueHideIfNotDetected = checked;
settingsChanged(saveSettings());
}
}
NToggle {
label: I18n.tr("bar.battery.hide-if-idle-label")
description: I18n.tr("bar.battery.hide-if-idle-description")
checked: valueHideIfIdle
onToggled: checked => valueHideIfIdle = checked
onToggled: checked => {
valueHideIfIdle = checked;
settingsChanged(saveSettings());
}
}
}
@@ -11,6 +11,8 @@ ColumnLayout {
property var widgetData: null
property var widgetMetadata: null
signal settingsChanged(var settings)
// Local state
property string valueDisplayMode: widgetData.displayMode !== undefined ? widgetData.displayMode : widgetMetadata.displayMode
@@ -39,6 +41,9 @@ ColumnLayout {
}
]
currentKey: root.valueDisplayMode
onSelected: key => root.valueDisplayMode = key
onSelected: key => {
root.valueDisplayMode = key;
settingsChanged(saveSettings());
}
}
}
@@ -12,6 +12,8 @@ ColumnLayout {
property var widgetData: null
property var widgetMetadata: null
signal settingsChanged(var settings)
// Local state
property string valueDisplayMode: widgetData.displayMode !== undefined ? widgetData.displayMode : widgetMetadata.displayMode
@@ -40,6 +42,9 @@ ColumnLayout {
}
]
currentKey: valueDisplayMode
onSelected: key => valueDisplayMode = key
onSelected: key => {
valueDisplayMode = key;
settingsChanged(saveSettings());
}
}
}
@@ -14,6 +14,8 @@ ColumnLayout {
property var widgetData: null
property var widgetMetadata: null
signal settingsChanged(var settings)
// Local state
property bool valueUsePrimaryColor: widgetData.usePrimaryColor !== undefined ? widgetData.usePrimaryColor : widgetMetadata.usePrimaryColor
property bool valueUseCustomFont: widgetData.useCustomFont !== undefined ? widgetData.useCustomFont : widgetMetadata.useCustomFont
@@ -71,7 +73,10 @@ ColumnLayout {
label: I18n.tr("bar.clock.use-primary-color-label")
description: I18n.tr("bar.clock.use-primary-color-description")
checked: valueUsePrimaryColor
onToggled: checked => valueUsePrimaryColor = checked
onToggled: checked => {
valueUsePrimaryColor = checked;
settingsChanged(saveSettings());
}
}
NToggle {
@@ -79,7 +84,10 @@ ColumnLayout {
label: I18n.tr("bar.clock.use-custom-font-label")
description: I18n.tr("bar.clock.use-custom-font-description")
checked: valueUseCustomFont
onToggled: checked => valueUseCustomFont = checked
onToggled: checked => {
valueUseCustomFont = checked;
settingsChanged(saveSettings());
}
}
NSearchableComboBox {
@@ -95,6 +103,7 @@ ColumnLayout {
minimumWidth: 300
onSelected: function (key) {
valueCustomFont = key;
settingsChanged(saveSettings());
}
}
@@ -129,6 +138,7 @@ ColumnLayout {
placeholderText: "HH:mm ddd, MMM dd"
text: valueFormatHorizontal
onTextChanged: valueFormatHorizontal = text
onEditingFinished: settingsChanged(saveSettings())
Component.onCompleted: {
if (inputItem) {
inputItem.onActiveFocusChanged.connect(function () {
@@ -153,6 +163,7 @@ ColumnLayout {
placeholderText: "HH mm dd MM"
text: valueFormatVertical
onTextChanged: valueFormatVertical = text
onEditingFinished: settingsChanged(saveSettings())
Component.onCompleted: {
if (inputItem) {
inputItem.onActiveFocusChanged.connect(function () {
@@ -172,6 +183,7 @@ ColumnLayout {
placeholderText: "HH:mm, ddd MMM dd"
text: valueTooltipFormat
onTextChanged: valueTooltipFormat = text
onEditingFinished: settingsChanged(saveSettings())
Component.onCompleted: {
if (inputItem) {
inputItem.onActiveFocusChanged.connect(function () {
@@ -283,7 +295,7 @@ ColumnLayout {
NDateTimeTokens {
Layout.fillWidth: true
height: 200
Layout.preferredHeight: 300
// Connect to token clicked signal if NDateTimeTokens provides it
onTokenClicked: token => root.insertToken(token)
@@ -14,6 +14,8 @@ ColumnLayout {
property var widgetData: null
property var widgetMetadata: null
signal settingsChanged(var settings)
// Local state
property string valueIcon: widgetData.icon !== undefined ? widgetData.icon : widgetMetadata.icon
property bool valueUseDistroLogo: widgetData.useDistroLogo !== undefined ? widgetData.useDistroLogo : widgetMetadata.useDistroLogo
@@ -35,14 +37,20 @@ ColumnLayout {
label: I18n.tr("bar.control-center.use-distro-logo-label")
description: I18n.tr("bar.control-center.use-distro-logo-description")
checked: valueUseDistroLogo
onToggled: checked => valueUseDistroLogo = checked
onToggled: checked => {
valueUseDistroLogo = checked;
settingsChanged(saveSettings());
}
}
NToggle {
label: I18n.tr("bar.custom-button.enable-colorization-label")
description: I18n.tr("bar.control-center.enable-colorization-description")
checked: valueEnableColorization
onToggled: checked => valueEnableColorization = checked
onToggled: checked => {
valueEnableColorization = checked;
settingsChanged(saveSettings());
}
}
NComboBox {
@@ -74,6 +82,7 @@ ColumnLayout {
currentKey: valueColorizeSystemIcon
onSelected: function (key) {
valueColorizeSystemIcon = key;
settingsChanged(saveSettings());
}
}
@@ -123,6 +132,7 @@ ColumnLayout {
onIconSelected: iconName => {
valueIcon = iconName;
valueCustomIconPath = "";
settingsChanged(saveSettings());
}
}
@@ -135,6 +145,7 @@ ColumnLayout {
onAccepted: paths => {
if (paths.length > 0) {
valueCustomIconPath = paths[0]; // Use first selected file
settingsChanged(saveSettings());
}
}
}
@@ -13,6 +13,8 @@ ColumnLayout {
property var widgetData: null
property var widgetMetadata: null
signal settingsChanged(var settings)
property string valueIcon: widgetData.icon !== undefined ? widgetData.icon : widgetMetadata.icon
property bool valueTextStream: widgetData.textStream !== undefined ? widgetData.textStream : widgetMetadata.textStream
property bool valueParseJson: widgetData.parseJson !== undefined ? widgetData.parseJson : widgetMetadata.parseJson
@@ -57,365 +59,404 @@ ColumnLayout {
return settings;
}
NScrollView {
Layout.preferredWidth: Math.round(600 * Style.uiScaleRatio)
Layout.preferredHeight: Math.round(700 * Style.uiScaleRatio)
horizontalPolicy: ScrollBar.AlwaysOff
verticalPolicy: ScrollBar.AsNeeded
padding: Style.marginL
focus: true
RowLayout {
spacing: Style.marginM
ColumnLayout {
width: parent.width
NLabel {
label: I18n.tr("common.icon")
description: I18n.tr("bar.custom-button.icon-description")
}
NIcon {
Layout.alignment: Qt.AlignVCenter
icon: valueIcon
pointSize: Style.fontSizeXL
visible: valueIcon !== ""
}
NButton {
text: I18n.tr("common.browse")
onClicked: iconPicker.open()
}
}
NIconPicker {
id: iconPicker
initialIcon: valueIcon
onIconSelected: function (iconName) {
valueIcon = iconName;
settingsChanged(saveSettings());
}
}
NToggle {
id: showIconToggle
label: I18n.tr("bar.custom-button.show-icon-label", "Show icon")
description: I18n.tr("bar.custom-button.show-icon-description", "Toggles the visibility of the widget's icon.")
checked: valueShowIcon
onToggled: checked => {
valueShowIcon = checked;
settingsChanged(saveSettings());
}
visible: textCommandInput.text !== ""
}
NToggle {
label: I18n.tr("bar.custom-button.enable-colorization-label")
description: I18n.tr("bar.custom-button.enable-colorization-description")
checked: valueEnableColorization
onToggled: checked => {
valueEnableColorization = checked;
settingsChanged(saveSettings());
}
}
NComboBox {
visible: valueEnableColorization
label: I18n.tr("bar.custom-button.color-selection-label")
description: I18n.tr("bar.custom-button.color-selection-description")
model: [
{
"name": I18n.tr("common.none"),
"key": "none"
},
{
"name": I18n.tr("colors.primary"),
"key": "primary"
},
{
"name": I18n.tr("colors.secondary"),
"key": "secondary"
},
{
"name": I18n.tr("colors.tertiary"),
"key": "tertiary"
},
{
"name": I18n.tr("colors.error"),
"key": "error"
}
]
currentKey: valueColorizeSystemIcon
onSelected: key => {
valueColorizeSystemIcon = key;
settingsChanged(saveSettings());
}
}
NTextInput {
Layout.fillWidth: true
label: I18n.tr("bar.custom-button.ipc-identifier-label")
description: I18n.tr("bar.custom-button.ipc-identifier-description")
placeholderText: I18n.tr("placeholders.enter-ipc-identifier")
text: valueIpcIdentifier
onTextChanged: valueIpcIdentifier = text
onEditingFinished: settingsChanged(saveSettings())
}
RowLayout {
spacing: Style.marginM
NTextInput {
id: leftClickExecInput
Layout.fillWidth: true
label: I18n.tr("bar.custom-button.left-click-label")
description: I18n.tr("bar.custom-button.left-click-description")
placeholderText: I18n.tr("placeholders.enter-command")
text: widgetData?.leftClickExec || widgetMetadata.leftClickExec
onEditingFinished: settingsChanged(saveSettings())
}
NToggle {
id: leftClickUpdateText
enabled: !valueTextStream
Layout.alignment: Qt.AlignRight | Qt.AlignBottom
Layout.bottomMargin: Style.marginS
onEntered: TooltipService.show(leftClickUpdateText, I18n.tr("bar.custom-button.left-click-update-text"), "auto")
onExited: TooltipService.hide()
checked: widgetData?.leftClickUpdateText ?? widgetMetadata.leftClickUpdateText
onToggled: isChecked => {
checked = isChecked;
settingsChanged(saveSettings());
}
}
}
RowLayout {
spacing: Style.marginM
NTextInput {
id: rightClickExecInput
Layout.fillWidth: true
label: I18n.tr("bar.custom-button.right-click-label")
description: I18n.tr("bar.custom-button.right-click-description")
placeholderText: I18n.tr("placeholders.enter-command")
text: widgetData?.rightClickExec || widgetMetadata.rightClickExec
onEditingFinished: settingsChanged(saveSettings())
}
NToggle {
id: rightClickUpdateText
enabled: !valueTextStream
Layout.alignment: Qt.AlignRight | Qt.AlignBottom
Layout.bottomMargin: Style.marginS
onEntered: TooltipService.show(rightClickUpdateText, I18n.tr("bar.custom-button.right-click-update-text"), "auto")
onExited: TooltipService.hide()
checked: widgetData?.rightClickUpdateText ?? widgetMetadata.rightClickUpdateText
onToggled: isChecked => {
checked = isChecked;
settingsChanged(saveSettings());
}
}
}
RowLayout {
spacing: Style.marginM
NTextInput {
id: middleClickExecInput
Layout.fillWidth: true
label: I18n.tr("bar.custom-button.middle-click-label")
description: I18n.tr("bar.custom-button.middle-click-description")
placeholderText: I18n.tr("placeholders.enter-command")
text: widgetData.middleClickExec || widgetMetadata.middleClickExec
onEditingFinished: settingsChanged(saveSettings())
}
NToggle {
id: middleClickUpdateText
enabled: !valueTextStream
Layout.alignment: Qt.AlignRight | Qt.AlignBottom
Layout.bottomMargin: Style.marginS
onEntered: TooltipService.show(middleClickUpdateText, I18n.tr("bar.custom-button.middle-click-update-text"), "auto")
onExited: TooltipService.hide()
checked: widgetData?.middleClickUpdateText ?? widgetMetadata.middleClickUpdateText
onToggled: isChecked => {
checked = isChecked;
settingsChanged(saveSettings());
}
}
}
// Wheel command settings
NToggle {
id: separateWheelToggle
Layout.fillWidth: true
label: I18n.tr("bar.custom-button.wheel-mode-separate-label", "Separate wheel commands")
description: I18n.tr("bar.custom-button.wheel-mode-separate-description", "Enable separate commands for wheel up and down")
property bool internalChecked: (widgetData?.wheelMode || widgetMetadata?.wheelMode) === "separate"
checked: internalChecked
onToggled: checked => {
internalChecked = checked;
settingsChanged(saveSettings());
}
}
ColumnLayout {
Layout.fillWidth: true
RowLayout {
id: unifiedWheelLayout
visible: !separateWheelToggle.checked
spacing: Style.marginM
RowLayout {
spacing: Style.marginM
NLabel {
label: I18n.tr("common.icon")
description: I18n.tr("bar.custom-button.icon-description")
}
NIcon {
Layout.alignment: Qt.AlignVCenter
icon: valueIcon
pointSize: Style.fontSizeXL
visible: valueIcon !== ""
}
NButton {
text: I18n.tr("common.browse")
onClicked: iconPicker.open()
}
}
NIconPicker {
id: iconPicker
initialIcon: valueIcon
onIconSelected: function (iconName) {
valueIcon = iconName;
}
}
NToggle {
id: showIconToggle
label: I18n.tr("bar.custom-button.show-icon-label", "Show icon")
description: I18n.tr("bar.custom-button.show-icon-description", "Toggles the visibility of the widget's icon.")
checked: valueShowIcon
onToggled: checked => valueShowIcon = checked
visible: textCommandInput.text !== ""
}
NToggle {
label: I18n.tr("bar.custom-button.enable-colorization-label")
description: I18n.tr("bar.custom-button.enable-colorization-description")
checked: valueEnableColorization
onToggled: checked => valueEnableColorization = checked
}
NComboBox {
visible: valueEnableColorization
label: I18n.tr("bar.custom-button.color-selection-label")
description: I18n.tr("bar.custom-button.color-selection-description")
model: [
{
"name": I18n.tr("common.none"),
"key": "none"
},
{
"name": I18n.tr("colors.primary"),
"key": "primary"
},
{
"name": I18n.tr("colors.secondary"),
"key": "secondary"
},
{
"name": I18n.tr("colors.tertiary"),
"key": "tertiary"
},
{
"name": I18n.tr("colors.error"),
"key": "error"
}
]
currentKey: valueColorizeSystemIcon
onSelected: key => valueColorizeSystemIcon = key
}
NTextInput {
id: wheelExecInput
Layout.fillWidth: true
label: I18n.tr("bar.custom-button.ipc-identifier-label")
description: I18n.tr("bar.custom-button.ipc-identifier-description")
placeholderText: I18n.tr("placeholders.enter-ipc-identifier")
text: valueIpcIdentifier
onTextChanged: valueIpcIdentifier = text
label: I18n.tr("bar.custom-button.wheel-label")
description: I18n.tr("bar.custom-button.wheel-description")
placeholderText: I18n.tr("placeholders.enter-command")
text: widgetData?.wheelExec || widgetMetadata?.wheelExec
onEditingFinished: settingsChanged(saveSettings())
}
RowLayout {
spacing: Style.marginM
NTextInput {
id: leftClickExecInput
Layout.fillWidth: true
label: I18n.tr("bar.custom-button.left-click-label")
description: I18n.tr("bar.custom-button.left-click-description")
placeholderText: I18n.tr("placeholders.enter-command")
text: widgetData?.leftClickExec || widgetMetadata.leftClickExec
}
NToggle {
id: leftClickUpdateText
enabled: !valueTextStream
Layout.alignment: Qt.AlignRight | Qt.AlignBottom
Layout.bottomMargin: Style.marginS
onEntered: TooltipService.show(leftClickUpdateText, I18n.tr("bar.custom-button.left-click-update-text"), "auto")
onExited: TooltipService.hide()
checked: widgetData?.leftClickUpdateText ?? widgetMetadata.leftClickUpdateText
onToggled: isChecked => checked = isChecked
}
}
RowLayout {
spacing: Style.marginM
NTextInput {
id: rightClickExecInput
Layout.fillWidth: true
label: I18n.tr("bar.custom-button.right-click-label")
description: I18n.tr("bar.custom-button.right-click-description")
placeholderText: I18n.tr("placeholders.enter-command")
text: widgetData?.rightClickExec || widgetMetadata.rightClickExec
}
NToggle {
id: rightClickUpdateText
enabled: !valueTextStream
Layout.alignment: Qt.AlignRight | Qt.AlignBottom
Layout.bottomMargin: Style.marginS
onEntered: TooltipService.show(rightClickUpdateText, I18n.tr("bar.custom-button.right-click-update-text"), "auto")
onExited: TooltipService.hide()
checked: widgetData?.rightClickUpdateText ?? widgetMetadata.rightClickUpdateText
onToggled: isChecked => checked = isChecked
}
}
RowLayout {
spacing: Style.marginM
NTextInput {
id: middleClickExecInput
Layout.fillWidth: true
label: I18n.tr("bar.custom-button.middle-click-label")
description: I18n.tr("bar.custom-button.middle-click-description")
placeholderText: I18n.tr("placeholders.enter-command")
text: widgetData.middleClickExec || widgetMetadata.middleClickExec
}
NToggle {
id: middleClickUpdateText
enabled: !valueTextStream
Layout.alignment: Qt.AlignRight | Qt.AlignBottom
Layout.bottomMargin: Style.marginS
onEntered: TooltipService.show(middleClickUpdateText, I18n.tr("bar.custom-button.middle-click-update-text"), "auto")
onExited: TooltipService.hide()
checked: widgetData?.middleClickUpdateText ?? widgetMetadata.middleClickUpdateText
onToggled: isChecked => checked = isChecked
}
}
// Wheel command settings
NToggle {
id: separateWheelToggle
Layout.fillWidth: true
label: I18n.tr("bar.custom-button.wheel-mode-separate-label", "Separate wheel commands")
description: I18n.tr("bar.custom-button.wheel-mode-separate-description", "Enable separate commands for wheel up and down")
property bool internalChecked: (widgetData?.wheelMode || widgetMetadata?.wheelMode) === "separate"
checked: internalChecked
onToggled: checked => {
internalChecked = checked;
id: wheelUpdateText
enabled: !valueTextStream
Layout.alignment: Qt.AlignRight | Qt.AlignBottom
Layout.bottomMargin: Style.marginS
onEntered: TooltipService.show(wheelUpdateText, I18n.tr("bar.custom-button.wheel-update-text"), "auto")
onExited: TooltipService.hide()
checked: widgetData?.wheelUpdateText ?? widgetMetadata?.wheelUpdateText
onToggled: isChecked => {
checked = isChecked;
settingsChanged(saveSettings());
}
}
}
ColumnLayout {
Layout.fillWidth: true
Layout.preferredWidth: parent.width
ColumnLayout {
id: separatedWheelLayout
Layout.fillWidth: true
visible: separateWheelToggle.checked
RowLayout {
id: unifiedWheelLayout
visible: !separateWheelToggle.checked
spacing: Style.marginM
RowLayout {
spacing: Style.marginM
NTextInput {
id: wheelExecInput
Layout.fillWidth: true
label: I18n.tr("bar.custom-button.wheel-label")
description: I18n.tr("bar.custom-button.wheel-description")
placeholderText: I18n.tr("placeholders.enter-command")
text: widgetData?.wheelExec || widgetMetadata?.wheelExec
}
NToggle {
id: wheelUpdateText
enabled: !valueTextStream
Layout.alignment: Qt.AlignRight | Qt.AlignBottom
Layout.bottomMargin: Style.marginS
onEntered: TooltipService.show(wheelUpdateText, I18n.tr("bar.custom-button.wheel-update-text"), "auto")
onExited: TooltipService.hide()
checked: widgetData?.wheelUpdateText ?? widgetMetadata?.wheelUpdateText
onToggled: isChecked => checked = isChecked
}
}
ColumnLayout {
id: separatedWheelLayout
NTextInput {
id: wheelUpExecInput
Layout.fillWidth: true
visible: separateWheelToggle.checked
label: I18n.tr("bar.custom-button.wheel-up-label")
description: I18n.tr("bar.custom-button.wheel-up-description")
placeholderText: I18n.tr("placeholders.enter-command")
text: widgetData?.wheelUpExec || widgetMetadata?.wheelUpExec
onEditingFinished: settingsChanged(saveSettings())
}
RowLayout {
spacing: Style.marginM
NTextInput {
id: wheelUpExecInput
Layout.fillWidth: true
label: I18n.tr("bar.custom-button.wheel-up-label")
description: I18n.tr("bar.custom-button.wheel-up-description")
placeholderText: I18n.tr("placeholders.enter-command")
text: widgetData?.wheelUpExec || widgetMetadata?.wheelUpExec
}
NToggle {
id: wheelUpUpdateText
enabled: !valueTextStream
Layout.alignment: Qt.AlignRight | Qt.AlignBottom
Layout.bottomMargin: Style.marginS
onEntered: TooltipService.show(wheelUpUpdateText, I18n.tr("bar.custom-button.wheel-update-text"), "auto")
onExited: TooltipService.hide()
checked: (widgetData?.wheelUpUpdateText !== undefined) ? widgetData.wheelUpUpdateText : widgetMetadata?.wheelUpUpdateText
onToggled: isChecked => checked = isChecked
}
}
RowLayout {
spacing: Style.marginM
NTextInput {
id: wheelDownExecInput
Layout.fillWidth: true
label: I18n.tr("bar.custom-button.wheel-down-label")
description: I18n.tr("bar.custom-button.wheel-down-description")
placeholderText: I18n.tr("placeholders.enter-command")
text: widgetData?.wheelDownExec || widgetMetadata?.wheelDownExec
}
NToggle {
id: wheelDownUpdateText
enabled: !valueTextStream
Layout.alignment: Qt.AlignRight | Qt.AlignBottom
Layout.bottomMargin: Style.marginS
onEntered: TooltipService.show(wheelDownUpdateText, I18n.tr("bar.custom-button.wheel-update-text"), "auto")
onExited: TooltipService.hide()
checked: (widgetData?.wheelDownUpdateText !== undefined) ? widgetData.wheelDownUpdateText : widgetMetadata?.wheelDownUpdateText
onToggled: isChecked => checked = isChecked
}
}
NToggle {
id: wheelUpUpdateText
enabled: !valueTextStream
Layout.alignment: Qt.AlignRight | Qt.AlignBottom
Layout.bottomMargin: Style.marginS
onEntered: TooltipService.show(wheelUpUpdateText, I18n.tr("bar.custom-button.wheel-update-text"), "auto")
onExited: TooltipService.hide()
checked: (widgetData?.wheelUpUpdateText !== undefined) ? widgetData.wheelUpUpdateText : widgetMetadata?.wheelUpUpdateText
onToggled: isChecked => {
checked = isChecked;
settingsChanged(saveSettings());
}
}
}
NDivider {
Layout.fillWidth: true
}
RowLayout {
spacing: Style.marginM
NHeader {
label: I18n.tr("bar.custom-button.dynamic-text")
}
NTextInput {
id: wheelDownExecInput
Layout.fillWidth: true
label: I18n.tr("bar.custom-button.wheel-down-label")
description: I18n.tr("bar.custom-button.wheel-down-description")
placeholderText: I18n.tr("placeholders.enter-command")
text: widgetData?.wheelDownExec || widgetMetadata?.wheelDownExec
onEditingFinished: settingsChanged(saveSettings())
}
NSpinBox {
label: I18n.tr("bar.custom-button.max-text-length-horizontal-label", "Max text length (horizontal)")
description: I18n.tr("bar.custom-button.max-text-length-horizontal-description", "Maximum number of characters to show in horizontal bar (0 to hide text)")
from: 0
to: 100
value: valueMaxTextLengthHorizontal
onValueChanged: valueMaxTextLengthHorizontal = value
}
NSpinBox {
label: I18n.tr("bar.custom-button.max-text-length-vertical-label", "Max text length (vertical)")
description: I18n.tr("bar.custom-button.max-text-length-vertical-description", "Maximum number of characters to show in vertical bar (0 to hide text)")
from: 0
to: 100
value: valueMaxTextLengthVertical
onValueChanged: valueMaxTextLengthVertical = value
}
NToggle {
id: textStreamInput
label: I18n.tr("bar.custom-button.text-stream-label")
description: I18n.tr("bar.custom-button.text-stream-description")
checked: valueTextStream
onToggled: checked => valueTextStream = checked
}
NToggle {
id: parseJsonInput
label: I18n.tr("bar.custom-button.parse-json-label", "Parse output as JSON")
description: I18n.tr("bar.custom-button.parse-json-description", "Parse the command output as a JSON object to dynamically set text and icon.")
checked: valueParseJson
onToggled: checked => valueParseJson = checked
}
NTextInput {
id: textCommandInput
Layout.fillWidth: true
label: I18n.tr("bar.custom-button.display-command-output-label")
description: valueTextStream ? I18n.tr("bar.custom-button.display-command-output-stream-description") : I18n.tr("bar.custom-button.display-command-output-description")
placeholderText: I18n.tr("placeholders.command-example")
text: widgetData?.textCommand || widgetMetadata.textCommand
}
NTextInput {
id: textCollapseInput
Layout.fillWidth: true
visible: valueTextStream
label: I18n.tr("bar.custom-button.collapse-condition-label")
description: I18n.tr("bar.custom-button.collapse-condition-description")
placeholderText: I18n.tr("placeholders.enter-text-to-collapse")
text: widgetData?.textCollapse || widgetMetadata.textCollapse
}
NTextInput {
id: textIntervalInput
Layout.fillWidth: true
visible: !valueTextStream
label: I18n.tr("bar.custom-button.refresh-interval-label")
description: I18n.tr("bar.custom-button.refresh-interval-description")
placeholderText: String(widgetMetadata.textIntervalMs)
text: widgetData && widgetData.textIntervalMs !== undefined ? String(widgetData.textIntervalMs) : ""
}
NComboBox {
id: hideModeComboBox
label: I18n.tr("bar.custom-button.hide-mode-label", "Hide mode")
description: I18n.tr("bar.custom-button.hide-mode-description", "Controls widget visibility when the command has no output.")
model: [
{
name: I18n.tr("bar.custom-button.hide-mode-always-expanded", "Always expanded"),
key: "alwaysExpanded"
},
{
name: I18n.tr("bar.custom-button.hide-mode-expand-with-output", "Expand when has output"),
key: "expandWithOutput"
},
{
name: I18n.tr("bar.custom-button.hide-mode-max-transparent", "Max expanded but transparent"),
key: "maxTransparent"
}
]
currentKey: valueHideMode
onSelected: key => valueHideMode = key
visible: textCommandInput.text !== "" && valueTextStream == true
NToggle {
id: wheelDownUpdateText
enabled: !valueTextStream
Layout.alignment: Qt.AlignRight | Qt.AlignBottom
Layout.bottomMargin: Style.marginS
onEntered: TooltipService.show(wheelDownUpdateText, I18n.tr("bar.custom-button.wheel-update-text"), "auto")
onExited: TooltipService.hide()
checked: (widgetData?.wheelDownUpdateText !== undefined) ? widgetData.wheelDownUpdateText : widgetMetadata?.wheelDownUpdateText
onToggled: isChecked => {
checked = isChecked;
settingsChanged(saveSettings());
}
}
}
}
}
NDivider {
Layout.fillWidth: true
}
NHeader {
label: I18n.tr("bar.custom-button.dynamic-text")
}
NSpinBox {
label: I18n.tr("bar.custom-button.max-text-length-horizontal-label", "Max text length (horizontal)")
description: I18n.tr("bar.custom-button.max-text-length-horizontal-description", "Maximum number of characters to show in horizontal bar (0 to hide text)")
from: 0
to: 100
value: valueMaxTextLengthHorizontal
onValueChanged: {
valueMaxTextLengthHorizontal = value;
settingsChanged(saveSettings());
}
}
NSpinBox {
label: I18n.tr("bar.custom-button.max-text-length-vertical-label", "Max text length (vertical)")
description: I18n.tr("bar.custom-button.max-text-length-vertical-description", "Maximum number of characters to show in vertical bar (0 to hide text)")
from: 0
to: 100
value: valueMaxTextLengthVertical
onValueChanged: {
valueMaxTextLengthVertical = value;
settingsChanged(saveSettings());
}
}
NToggle {
id: textStreamInput
label: I18n.tr("bar.custom-button.text-stream-label")
description: I18n.tr("bar.custom-button.text-stream-description")
checked: valueTextStream
onToggled: checked => {
valueTextStream = checked;
settingsChanged(saveSettings());
}
}
NToggle {
id: parseJsonInput
label: I18n.tr("bar.custom-button.parse-json-label", "Parse output as JSON")
description: I18n.tr("bar.custom-button.parse-json-description", "Parse the command output as a JSON object to dynamically set text and icon.")
checked: valueParseJson
onToggled: checked => {
valueParseJson = checked;
settingsChanged(saveSettings());
}
}
NTextInput {
id: textCommandInput
Layout.fillWidth: true
label: I18n.tr("bar.custom-button.display-command-output-label")
description: valueTextStream ? I18n.tr("bar.custom-button.display-command-output-stream-description") : I18n.tr("bar.custom-button.display-command-output-description")
placeholderText: I18n.tr("placeholders.command-example")
text: widgetData?.textCommand || widgetMetadata.textCommand
onEditingFinished: settingsChanged(saveSettings())
}
NTextInput {
id: textCollapseInput
Layout.fillWidth: true
visible: valueTextStream
label: I18n.tr("bar.custom-button.collapse-condition-label")
description: I18n.tr("bar.custom-button.collapse-condition-description")
placeholderText: I18n.tr("placeholders.enter-text-to-collapse")
text: widgetData?.textCollapse || widgetMetadata.textCollapse
onEditingFinished: settingsChanged(saveSettings())
}
NTextInput {
id: textIntervalInput
Layout.fillWidth: true
visible: !valueTextStream
label: I18n.tr("bar.custom-button.refresh-interval-label")
description: I18n.tr("bar.custom-button.refresh-interval-description")
placeholderText: String(widgetMetadata.textIntervalMs)
text: widgetData && widgetData.textIntervalMs !== undefined ? String(widgetData.textIntervalMs) : ""
onEditingFinished: settingsChanged(saveSettings())
}
NComboBox {
id: hideModeComboBox
label: I18n.tr("bar.custom-button.hide-mode-label", "Hide mode")
description: I18n.tr("bar.custom-button.hide-mode-description", "Controls widget visibility when the command has no output.")
model: [
{
name: I18n.tr("bar.custom-button.hide-mode-always-expanded", "Always expanded"),
key: "alwaysExpanded"
},
{
name: I18n.tr("bar.custom-button.hide-mode-expand-with-output", "Expand when has output"),
key: "expandWithOutput"
},
{
name: I18n.tr("bar.custom-button.hide-mode-max-transparent", "Max expanded but transparent"),
key: "maxTransparent"
}
]
currentKey: valueHideMode
onSelected: key => {
valueHideMode = key;
settingsChanged(saveSettings());
}
visible: textCommandInput.text !== "" && valueTextStream == true
}
}
@@ -12,6 +12,8 @@ ColumnLayout {
property var widgetData: null
property var widgetMetadata: null
signal settingsChanged(var settings)
// Local state
property string valueDisplayMode: widgetData.displayMode !== undefined ? widgetData.displayMode : widgetMetadata.displayMode
property bool valueShowIcon: widgetData.showIcon !== undefined ? widgetData.showIcon : widgetMetadata.showIcon
@@ -43,13 +45,19 @@ ColumnLayout {
}
]
currentKey: valueDisplayMode
onSelected: key => valueDisplayMode = key
onSelected: key => {
valueDisplayMode = key;
settingsChanged(saveSettings());
}
}
NToggle {
label: I18n.tr("bar.custom-button.show-icon-label")
description: I18n.tr("bar.keyboard-layout.show-icon-description")
checked: valueShowIcon
onToggled: checked => valueShowIcon = checked
onToggled: checked => {
valueShowIcon = checked;
settingsChanged(saveSettings());
}
}
}
@@ -13,6 +13,8 @@ ColumnLayout {
property var widgetData: null
property var widgetMetadata: null
signal settingsChanged(var settings)
// Local state
property bool valueUsePrimaryColor: widgetData.usePrimaryColor !== undefined ? widgetData.usePrimaryColor : (widgetMetadata ? widgetMetadata.usePrimaryColor : false)
@@ -26,6 +28,9 @@ ColumnLayout {
label: I18n.tr("bar.clock.use-primary-color-label")
description: I18n.tr("bar.clock.use-primary-color-description")
checked: valueUsePrimaryColor
onToggled: checked => valueUsePrimaryColor = checked
onToggled: checked => {
valueUsePrimaryColor = checked;
settingsChanged(saveSettings());
}
}
}
@@ -12,6 +12,8 @@ ColumnLayout {
property var widgetData: null
property var widgetMetadata: null
signal settingsChanged(var settings)
// Local state
property bool valueShowCapsLock: widgetData.showCapsLock !== undefined ? widgetData.showCapsLock : widgetMetadata.showCapsLock
property bool valueShowNumLock: widgetData.showNumLock !== undefined ? widgetData.showNumLock : widgetMetadata.showNumLock
@@ -42,7 +44,10 @@ ColumnLayout {
label: I18n.tr("bar.lock-keys.show-caps-lock-label")
description: I18n.tr("bar.lock-keys.show-caps-lock-description")
checked: valueShowCapsLock
onToggled: checked => valueShowCapsLock = checked
onToggled: checked => {
valueShowCapsLock = checked;
settingsChanged(saveSettings());
}
}
NIcon {
@@ -65,6 +70,7 @@ ColumnLayout {
query: "letter-c"
onIconSelected: function (iconName) {
capsIcon = iconName;
settingsChanged(saveSettings());
}
}
@@ -75,7 +81,10 @@ ColumnLayout {
label: I18n.tr("bar.lock-keys.show-num-lock-label")
description: I18n.tr("bar.lock-keys.show-num-lock-description")
checked: valueShowNumLock
onToggled: checked => valueShowNumLock = checked
onToggled: checked => {
valueShowNumLock = checked;
settingsChanged(saveSettings());
}
}
NIcon {
@@ -98,6 +107,7 @@ ColumnLayout {
query: "letter-n"
onIconSelected: function (iconName) {
numIcon = iconName;
settingsChanged(saveSettings());
}
}
@@ -108,7 +118,10 @@ ColumnLayout {
label: I18n.tr("bar.lock-keys.show-scroll-lock-label")
description: I18n.tr("bar.lock-keys.show-scroll-lock-description")
checked: valueShowScrollLock
onToggled: checked => valueShowScrollLock = checked
onToggled: checked => {
valueShowScrollLock = checked;
settingsChanged(saveSettings());
}
}
NIcon {
@@ -131,6 +144,7 @@ ColumnLayout {
query: "letter-s"
onIconSelected: function (iconName) {
scrollIcon = iconName;
settingsChanged(saveSettings());
}
}
@@ -143,6 +157,9 @@ ColumnLayout {
label: I18n.tr("bar.lock-keys.hide-when-off-label")
description: I18n.tr("bar.lock-keys.hide-when-off-description")
checked: valueHideWhenOff
onToggled: checked => valueHideWhenOff = checked
onToggled: checked => {
valueHideWhenOff = checked;
settingsChanged(saveSettings());
}
}
}

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