Compare commits

...

14 Commits

Author SHA1 Message Date
ec217df778 Fix toasts 2025-11-23 14:28:49 +01:00
aa4ee099e8 Add inner bar implementation for notifications 2025-11-23 13:59:47 +01:00
7e4b78d77b Use a BarItem for the notification pill 2025-11-23 13:59:47 +01:00
Lysec
01a26fd910 Merge pull request #827 from notiant/patch-1
LockScreen: make 'hibernate' optional
2025-11-22 13:14:53 +01:00
notiant
0293b8c8dd LockScreen: make 'hibernate' optional 2025-11-22 13:04:44 +01:00
Lysec
3914c32c96 Merge pull request #823 from acdcbyl/main
Matugen: Add Telegram's Theme
2025-11-22 13:04:29 +01:00
Lysec
4652691c4c Merge pull request #825 from lonerOrz/fix/tray
Fix inconsistent tray drawer behavior for different mouse buttons
2025-11-22 12:54:30 +01:00
Lysec
679fd5c40e Merge pull request #826 from art0rz/fix/recording-button
Add screen recording loading feedback
2025-11-22 12:49:32 +01:00
Ly-sec
48c5435cef SetupWizard: ensure setuoCompleted is always being saved 2025-11-22 12:46:22 +01:00
loner
880ae9c7b9 fix: Fix inconsistent tray drawer behavior for different mouse buttons 2025-11-22 18:03:33 +08:00
Aiser
0f650b36f7 Matugen: Add Telegram's Theme 2025-11-22 14:52:38 +08:00
ItsLemmy
823042b245 Panels: properly animate height with vertical bar + Bluetooth sizing refinement. 2025-11-22 00:33:42 -05:00
ItsLemmy
9c550af64e UpdateService: fix wrong changelog when updating from 3.2.0-dev to 3.2.0-git 2025-11-21 23:25:22 -05:00
art0rz
f181bdf21c Add screen recording loading feedback 2025-11-21 15:52:29 +01:00
29 changed files with 459 additions and 76 deletions

View File

@@ -0,0 +1,139 @@
// Material You theme for Telegram Desktop
// Generated by matugen
COLOR_GRAY: {{colors.outline.default.hex}};
COLOR_DARK: {{colors.surface_variant.default.hex}};
windowBg: {{colors.background.default.hex}}; // Main background
windowFg: {{colors.on_background.default.hex}}; // Main text
windowBgOver: {{colors.surface_variant.default.hex}}; // Generic background on hover
windowBgRipple: {{colors.surface_variant.default.hex}}; // Ripple effect
windowFgOver: {{colors.on_surface_variant.default.hex}}; // Text on hover
windowSubTextFg: {{colors.outline.default.hex}}; // Minor text
windowSubTextFgOver: {{colors.outline.default.hex}}; // Minor text on hover
windowBoldFg: {{colors.on_background.default.hex}}; // Bold text
windowBoldFgOver: {{colors.on_surface_variant.default.hex}}; // Bold text on hover
windowBgActive: {{colors.primary.default.hex}}; // Active items background
windowFgActive: {{colors.on_primary.default.hex}}; // Active items text
windowActiveTextFg: {{colors.primary.default.hex}}; // Active items text
windowShadowFg: {{colors.shadow.default.hex}}; // Window shadow
windowShadowFgFallback: {{colors.shadow.default.hex}}; // Fallback for shadow
historyOutIconFg: {{colors.primary.default.hex}};
historyIconFgInverted: {{colors.on_surface.default.hex}};
msgServiceBg: {{colors.primary_container.default.hex}};
msgServiceFg: {{colors.on_surface.default.hex}};
msgOutBg: {{colors.primary_container.default.hex}};
msgOutBgSelected : {{colors.tertiary_container.default.hex}};
msgOutServiceFg: {{colors.on_surface.default.hex}};
msgOutDateFg: {{colors.on_surface.default.hex}};
historySentIconFg: {{colors.on_surface.default.hex}};
msgOutDateFgSelected: {{colors.on_surface.default.hex}};
msgDateImgFg: {{colors.on_surface.default.hex}};
dialogsSentIconFg: {{colors.primary.default.hex}};
dialogsSentIconFgOver: {{colors.primary.default.hex}};
dialogsOnlineBadgeFg: {{colors.primary.default.hex}};
shadowFg: {{colors.shadow.default.hex}}; // General shadow
slideFadeOutBg: {{colors.background.default.hex}};
slideFadeOutShadowFg: {{colors.shadow.default.hex}};
imageBg: {{colors.surface.default.hex}};
imageBgTransparent: {{colors.surface.default.hex}};
activeButtonBg: {{colors.primary.default.hex}}; // Active button background
activeButtonBgOver: {{colors.primary_container.default.hex}}; // Active button hover background
activeButtonBgRipple: {{colors.on_primary_container.default.hex}}; // Active button ripple
activeButtonFg: {{colors.on_primary.default.hex}}; // Active button text
activeButtonFgOver: {{colors.on_primary_container.default.hex}}; // Active button hover text
activeButtonSecondaryFg: {{colors.on_primary.default.hex}}; // Active button secondary text
activeButtonSecondaryFgOver: {{colors.on_primary_container.default.hex}}; // Active button secondary hover text
activeLineFg: {{colors.on_surface.default.hex}};
dialogsBgActive: {{colors.primary.default.hex}};
lightButtonBg: {{colors.surface.default.hex}}; // Light button background
lightButtonBgOver: {{colors.surface_variant.default.hex}}; // Light button hover background
lightButtonBgRipple: {{colors.primary.default.hex}}; // Light button ripple
lightButtonFg: {{colors.on_surface.default.hex}}; // Light button text
lightButtonFgOver: {{colors.on_surface_variant.default.hex}}; // Light button hover text
attentionButtonFg: {{colors.error.default.hex}};
attentionButtonFgOver: {{colors.error.default.hex}};
attentionButtonBgOver: {{colors.error_container.default.hex}};
attentionButtonBgRipple: {{colors.on_error_container.default.hex}};
outlineButtonBg: {{colors.surface.default.hex}}; // Outline button background
outlineButtonBgOver: {{colors.surface_variant.default.hex}}; // Outline button hover background
outlineButtonOutlineFg: {{colors.primary.default.hex}}; // Outline button color
outlineButtonBgRipple: {{colors.primary.default.hex}}; // Outline button ripple
menuBg: {{colors.surface.default.hex}};
menuBgOver: {{colors.surface_variant.default.hex}};
menuBgRipple: {{colors.primary.default.hex}};
menuIconFg: {{colors.on_surface.default.hex}};
menuIconFgOver: {{colors.on_surface_variant.default.hex}};
menuSubmenuArrowFg: {{colors.outline.default.hex}};
menuFgDisabled: {{colors.outline.default.hex}};
menuSeparatorFg: {{colors.outline.default.hex}};
scrollBarBg: {{colors.primary.default.hex}}40; // Scroll bar background (40% opacity)
scrollBarBgOver: {{colors.primary.default.hex}}60; // Scroll bar hover background (60% opacity)
scrollBg: {{colors.surface_variant.default.hex}}40; // Scroll bar track (40% opacity)
scrollBgOver: {{colors.surface_variant.default.hex}}60; // Scroll bar track on hover (60% opacity)
smallCloseIconFg: {{colors.outline.default.hex}};
smallCloseIconFgOver: {{colors.on_surface_variant.default.hex}};
radialFg: {{colors.primary.default.hex}};
radialBg: {{colors.surface.default.hex}};
placeholderFg: {{colors.outline.default.hex}}; // Placeholder text
placeholderFgActive: {{colors.primary.default.hex}}; // Active placeholder text
inputBorderFg: {{colors.outline.default.hex}}; // Input border
filterInputBorderFg: {{colors.outline.default.hex}}; // Search input border
filterInputInactiveBg: {{colors.surface.default.hex}}; // Inactive search input background
checkboxFg: {{colors.primary.default.hex}}; // Checkbox color
titleBg: {{colors.surface.default.hex}}; // Window title background
titleShadow: {{colors.shadow.default.hex}};
titleButtonFg: {{colors.on_surface.default.hex}}; // Title button color
titleButtonBgOver: {{colors.surface_variant.default.hex}}; // Title button hover background
titleButtonFgOver: {{colors.on_surface_variant.default.hex}}; // Title button hover color
titleButtonCloseBgOver: {{colors.error.default.hex}};
titleButtonCloseFgOver: {{colors.on_error.default.hex}};
titleFgActive: {{colors.on_surface.default.hex}}; // Active title text
titleFg: {{colors.on_surface.default.hex}}; // Inactive title text
trayCounterBg: {{colors.error.default.hex}}; // Tray counter background
trayCounterBgMute: {{colors.outline.default.hex}}; // Muted tray counter background
trayCounterFg: {{colors.on_error.default.hex}}; // Tray counter text
trayCounterBgMacInvert: {{colors.error.default.hex}}; // Mac tray counter
trayCounterFgMacInvert: {{colors.on_error.default.hex}}; // Mac tray counter text
layerBg: {{colors.surface.default.hex}}99; // Layer background (60% opacity)
cancelIconFg: {{colors.error.default.hex}}; // Cancel icon
cancelIconFgOver: {{colors.error.default.hex}}; // Cancel icon on hover
boxBg: {{colors.surface.default.hex}}; // Box background
boxTextFg: {{colors.on_surface.default.hex}}; // Box text
boxTextFgGood: {{colors.primary.default.hex}}; // Box good text
boxTextFgError: {{colors.error.default.hex}}; // Box error text
boxTitleFg: {{colors.on_surface.default.hex}}; // Box title text
boxSearchBg: {{colors.surface.default.hex}}; // Box search field background
boxSearchCancelIconFg: {{colors.error.default.hex}}; // Box search cancel icon
boxSearchCancelIconFgOver: {{colors.error.default.hex}}; // Box search cancel icon on hover
contactsBg: {{colors.surface.default.hex}}; // Contacts background
contactsBgOver: {{colors.surface_variant.default.hex}}; // Contacts background on hover
contactsNameFg: {{colors.on_surface.default.hex}}; // Contact name
contactsStatusFg: {{colors.outline.default.hex}}; // Contact status
contactsStatusFgOver: {{colors.on_surface_variant.default.hex}}; // Contact status on hover
contactsStatusFgOnline: {{colors.primary.default.hex}}; // Online contact status
photoCropFadeBg: {{colors.surface.default.hex}}cc; // Photo crop fade background
photoCropPointFg: {{colors.primary.default.hex}}; // Photo crop points
chat_inBubbleSelected: #313244; // inbox selected chat background
chat_outBubbleSelected: #313244; // outbox selected chat background

View File

@@ -1041,7 +1041,11 @@
"description-missing": "Erfordert die Installation von {app}"
},
"spicetify": {
"description": "Schreibe {Dateipfad}. Das Comfy-Theme muss manuell installiert und aktiviert werden",
"description": "Schreibe {filepath}. Das Comfy-Theme muss manuell installiert und aktiviert werden",
"description-missing": "Benötigt die Installation von {app}"
},
"telegram": {
"description": "Schreibe {filepath}.",
"description-missing": "Benötigt die Installation von {app}"
},
"vicinae": {
@@ -1506,6 +1510,10 @@
"description": "Den Bildschirm beim Suspendieren des Systems automatisch sperren.",
"label": "Sperren beim Ruhezustand"
},
"show-hibernate": {
"description": "Die Option 'Ruhezustand' in den Energieaktionen anzeigen.",
"label": "Ruhezustand anzeigen"
},
"title": "Sperrbildschirm"
},
"network": {

View File

@@ -570,7 +570,8 @@
"follow_bar": "Follow bar (default)",
"top_center": "Top center",
"top_left": "Top left",
"top_right": "Top right"
"top_right": "Top right",
"bar": "Bar (inline)"
}
},
"osd": {
@@ -1044,6 +1045,10 @@
"description": "Write {filepath}. Comfy theme needs to be installed and activated manually.",
"description-missing": "Requires {app} to be installed"
},
"telegram": {
"description": "Write {filepath}.",
"description-missing": "Requires {app} to be installed"
},
"vicinae": {
"description": "Write {filepath} and reload",
"description-missing": "Requires {app} to be installed"
@@ -1506,6 +1511,10 @@
"description": "Automatically lock the screen when suspending the system.",
"label": "Lock on suspend"
},
"show-hibernate": {
"description": "Show the option 'hibernate' in the energy actions.",
"label": "Show hibernate"
},
"title": "Lock screen"
},
"network": {

View File

@@ -1044,6 +1044,10 @@
"description": "Escribe {filepath}. El tema Comfy debe ser instalado y activado manualmente.",
"description-missing": "Requiere que {app} esté instalado/a."
},
"telegram": {
"description": "Escribe {filepath}.",
"description-missing": "Requiere que {app} esté instalado/a."
},
"vicinae": {
"description": "Escribir {filepath} y recargar",
"description-missing": "Requiere que {app} esté instalado"
@@ -1506,6 +1510,10 @@
"description": "Bloquear la pantalla automáticamente al suspender el sistema.",
"label": "Bloquear al suspender"
},
"show-hibernate": {
"description": "Mostrar la opción 'hibernar' en las acciones de energía.",
"label": "Mostrar hibernar"
},
"title": "Pantalla de bloqueo"
},
"network": {

View File

@@ -1044,6 +1044,10 @@
"description": "Écrire {filepath}. Le thème Comfy doit être installé et activé manuellement.",
"description-missing": "Nécessite l'installation de {app}"
},
"telegram": {
"description": "Écrire {filepath}.",
"description-missing": "Nécessite l'installation de {app}"
},
"vicinae": {
"description": "Écrire {filepath} et recharger",
"description-missing": "Nécessite que le lanceur {app} soit installé"
@@ -1506,6 +1510,10 @@
"description": "Verrouiller automatiquement l'écran lors de la mise en veille du système.",
"label": "Verrouiller à la suspension"
},
"show-hibernate": {
"description": "Afficher loption 'hiberner' dans les actions dénergie.",
"label": "Afficher lhibernation"
},
"title": "Écran de verrouillage"
},
"network": {

View File

@@ -1044,6 +1044,10 @@
"description": "Schrijf {filepath}. Het Comfy-thema moet handmatig worden geïnstalleerd en geactiveerd.",
"description-missing": "Vereist dat {app} is geïnstalleerd."
},
"telegram": {
"description": "Schrijf {filepath}.",
"description-missing": "Vereist dat {app} is geïnstalleerd."
},
"vicinae": {
"description": "Schrijf {filepath} en herlaad.",
"description-missing": "Vereist dat {app} is geïnstalleerd."
@@ -1506,6 +1510,10 @@
"description": "Vergrendel het scherm automatisch wanneer het systeem wordt onderbroken.",
"label": "Vergrendelen bij onderbreken"
},
"show-hibernate": {
"description": "De optie 'sluimerstand' tonen in de energieacties.",
"label": "Sluimerstand tonen"
},
"title": "Vergrendelscherm"
},
"network": {

View File

@@ -1044,6 +1044,10 @@
"description": "Escreva em {filepath}. O tema Comfy precisa ser instalado e ativado manualmente.",
"description-missing": "Requer que o {app} esteja instalado."
},
"telegram": {
"description": "Escreva em {filepath}.",
"description-missing": "Requer que o {app} esteja instalado."
},
"vicinae": {
"description": "Escrever {filepath} e recarregar",
"description-missing": "Requer que o {app} esteja instalado"
@@ -1506,6 +1510,10 @@
"description": "Bloquear a tela automaticamente ao suspender o sistema.",
"label": "Bloquear ao suspender"
},
"show-hibernate": {
"description": "Mostrar a opção 'hibernar' nas ações de energia.",
"label": "Mostrar hibernar"
},
"title": "Tela de bloqueio"
},
"network": {

View File

@@ -1044,6 +1044,10 @@
"description": "Записать {filepath}. Тему Comfy нужно установить и активировать вручную.",
"description-missing": "Требуется установка {app}"
},
"telegram": {
"description": "Записать {filepath}.",
"description-missing": "Требуется установка {app}"
},
"vicinae": {
"description": "Записать {filepath} и перезагрузить",
"description-missing": "Требуется установка {app}"
@@ -1506,6 +1510,10 @@
"description": "Автоматически блокировать экран при приостановке работы системы.",
"label": "Блокировать при приостановке"
},
"show-hibernate": {
"description": "Показывать опцию 'спящий режим' в действиях питания.",
"label": "Показывать спящий режим"
},
"title": "Экран блокировки"
},
"network": {

View File

@@ -1044,6 +1044,10 @@
"description": "{filepath} dosyasına yaz. Comfy temasının kurulu ve manuel olarak etkinleştirilmiş olması gerekir.",
"description-missing": "Kurulum için {app} gereklidir"
},
"telegram": {
"description": "{filepath} dosyasına yaz.",
"description-missing": "Kurulum için {app} gereklidir"
},
"vicinae": {
"description": "{filepath} dosyasına yaz ve yeniden yükle",
"description-missing": "Kurulum için {app} gereklidir"
@@ -1506,6 +1510,10 @@
"description": "Sistemi askıya alırken otomatik olarak ekranı kilitler.",
"label": "Askıya alırken kilitle"
},
"show-hibernate": {
"description": "Güç işlemlerinde 'hazırda beklet' seçeneğini göster.",
"label": "Hazırda beklet seçeneğini göster"
},
"title": "Ekran Kilit"
},
"network": {

View File

@@ -1044,6 +1044,10 @@
"description": "Записати {filepath}. Тему Comfy потрібно встановити та активувати вручну.",
"description-missing": "Потрібна установка {app}"
},
"telegram": {
"description": "Записати {filepath}.",
"description-missing": "Потрібна установка {app}"
},
"vicinae": {
"description": "Записати {filepath} та перезавантажити",
"description-missing": "Потрібна установка {app}"
@@ -1506,6 +1510,10 @@
"description": "Автоматично блокувати екран при призупиненні системи.",
"label": "Блокувати при призупиненні"
},
"show-hibernate": {
"description": "Показувати опцію 'сплячий режим' у діях живлення.",
"label": "Показувати сплячий режим"
},
"title": "Екран блокування"
},
"network": {

View File

@@ -1044,6 +1044,10 @@
"description": "写入 {filepath}。Comfy 主题需要手动安装和激活。",
"description-missing": "需要安装 {app}"
},
"telegram": {
"description": "写入 {filepath}。",
"description-missing": "需要安装 {app}"
},
"vicinae": {
"description": "写入 {filepath} 并重新加载",
"description-missing": "需要安装 {app}"
@@ -1506,6 +1510,10 @@
"description": "系统挂起时自动锁定屏幕。",
"label": "挂起时锁定"
},
"show-hibernate": {
"description": "在电源操作中显示'休眠'选项。",
"label": "显示休眠"
},
"title": "锁屏"
},
"network": {

View File

@@ -70,6 +70,7 @@
"animationDisabled": false,
"compactLockScreen": false,
"lockOnSuspend": true,
"showHibernateOnLockScreen": true,
"enableShadows": true,
"shadowDirection": "bottom_right",
"shadowOffsetX": 2,

View File

@@ -215,6 +215,7 @@ Singleton {
property bool animationDisabled: false
property bool compactLockScreen: false
property bool lockOnSuspend: true
property bool showHibernateOnLockScreen: true
property bool enableShadows: true
property string shadowDirection: "bottom_right"
property int shadowOffsetX: 2
@@ -506,6 +507,7 @@ Singleton {
property bool walker: false
property bool code: false
property bool spicetify: false
property bool telegram: false
property bool enableUserTemplates: false
}

View File

@@ -9,7 +9,7 @@ import qs.Services.System
import qs.Services.UI
import qs.Widgets
NIconButton {
Item {
id: root
property ShellScreen screen
@@ -33,6 +33,9 @@ NIconButton {
readonly property bool showUnreadBadge: (widgetSettings.showUnreadBadge !== undefined) ? widgetSettings.showUnreadBadge : widgetMetadata.showUnreadBadge
readonly property bool hideWhenZero: (widgetSettings.hideWhenZero !== undefined) ? widgetSettings.hideWhenZero : widgetMetadata.hideWhenZero
implicitWidth: pill.width
implicitHeight: pill.height
function computeUnreadCount() {
var since = NotificationService.lastSeenTs;
var count = 0;
@@ -46,17 +49,6 @@ NIconButton {
return count;
}
baseSize: Style.capsuleHeight
applyUiScale: false
density: Settings.data.bar.density
icon: NotificationService.doNotDisturb ? "bell-off" : "bell"
tooltipText: NotificationService.doNotDisturb ? I18n.tr("tooltips.open-notification-history-disable-dnd") : I18n.tr("tooltips.open-notification-history-enable-dnd")
tooltipDirection: BarService.getTooltipDirection()
colorBg: Style.capsuleColor
colorFg: Color.mOnSurface
colorBorder: Color.transparent
colorBorderHover: Color.transparent
NPopupContextMenu {
id: contextMenu
@@ -94,37 +86,77 @@ NIconButton {
}
}
onClicked: {
var panel = PanelService.getPanel("notificationHistoryPanel", screen);
panel?.toggle(this);
}
BarPill {
id: pill
onRightClicked: {
var popupMenuWindow = PanelService.getPopupMenuWindow(screen);
if (popupMenuWindow) {
const pos = BarService.getContextMenuPosition(root, contextMenu.implicitWidth, contextMenu.implicitHeight);
contextMenu.openAtItem(root, pos.x, pos.y);
popupMenuWindow.showContextMenu(contextMenu);
property string currentNotif
Connections {
target: NotificationService.activeList
function onCountChanged() {
// keep current text a bit longer for the animation
if (NotificationService.activeList.count > 0) {
var notif = NotificationService.activeList.get(0)
var summary = notif.summary.trim()
var body = notif.body.trim()
pill.currentNotif = `${summary}: ${body}`.replace(/\n/g, " ")
}
}
}
}
Loader {
anchors.right: parent.right
anchors.top: parent.top
anchors.rightMargin: 2
anchors.topMargin: 1
z: 2
active: showUnreadBadge && (!hideWhenZero || computeUnreadCount() > 0)
sourceComponent: Rectangle {
id: badge
readonly property int count: computeUnreadCount()
height: 8
width: height
radius: height / 2
color: Color.mError
border.color: Color.mSurface
border.width: Style.borderS
visible: count > 0 || !hideWhenZero
Component.onCompleted: {
function dismiss(notificationId) {
if (Settings.data.notifications?.location == "bar") {
NotificationService.dismissActiveNotification(notificationId)
}
}
NotificationService.animateAndRemove.connect(dismiss);
}
screen: root.screen
density: Settings.data.bar.density
oppositeDirection: BarService.getPillDirection(root)
icon: NotificationService.doNotDisturb ? "bell-off" : "bell"
tooltipText: NotificationService.doNotDisturb ? I18n.tr("tooltips.open-notification-history-disable-dnd") : I18n.tr("tooltips.open-notification-history-enable-dnd")
text: currentNotif
forceOpen: Settings.data.notifications?.location == "bar" && NotificationService.activeList.count > 0
// prevent open via mouse over
forceClose: NotificationService.activeList.count == 0
onClicked: {
var panel = PanelService.getPanel("notificationHistoryPanel", screen);
panel?.toggle(this);
}
onRightClicked: {
var popupMenuWindow = PanelService.getPopupMenuWindow(screen);
if (popupMenuWindow) {
const pos = BarService.getContextMenuPosition(root, contextMenu.implicitWidth, contextMenu.implicitHeight);
contextMenu.openAtItem(root, pos.x, pos.y);
popupMenuWindow.showContextMenu(contextMenu);
}
}
Loader {
anchors.right: parent.right
anchors.top: parent.top
anchors.rightMargin: 2
anchors.topMargin: 1
z: 2
active: showUnreadBadge && (!hideWhenZero || computeUnreadCount() > 0)
sourceComponent: Rectangle {
id: badge
readonly property int count: computeUnreadCount()
height: 8
width: height
radius: height / 2
color: Color.mError
border.color: Color.mSurface
border.width: Style.borderS
visible: count > 0 || !hideWhenZero
}
}
}
}

View File

@@ -1,3 +1,4 @@
import QtQuick
import Quickshell
import qs.Commons
import qs.Services.Media
@@ -11,7 +12,7 @@ NIconButton {
property ShellScreen screen
icon: "camera-video"
icon: ScreenRecorderService.isPending ? "" : "camera-video"
tooltipText: ScreenRecorderService.isRecording ? I18n.tr("tooltips.click-to-stop-recording") : I18n.tr("tooltips.click-to-start-recording")
tooltipDirection: BarService.getTooltipDirection()
density: Settings.data.bar.density
@@ -31,4 +32,32 @@ NIconButton {
}
onClicked: handleClick()
// Custom spinner shown only during pending start
NIcon {
id: pendingSpinner
icon: "loader-2"
visible: ScreenRecorderService.isPending
pointSize: {
switch (root.density) {
case "compact":
return Math.max(1, root.width * 0.65);
default:
return Math.max(1, root.width * 0.48);
}
}
applyUiScale: root.applyUiScale
color: root.enabled && root.hovering ? colorFgHover : colorFg
anchors.centerIn: parent
transformOrigin: Item.Center
RotationAnimation on rotation {
running: ScreenRecorderService.isPending
from: 0
to: 360
duration: Style.animationSlow
loops: Animation.Infinite
onStopped: pendingSpinner.rotation = 0
}
}
}

View File

@@ -343,15 +343,15 @@ Loader {
text: {
var lang = I18n.locale.name.split("_")[0];
var formats = {
"en": "dddd, MMMM d",
"de": "dddd, d. MMMM",
"es": "dddd, d 'de' MMMM",
"fr": "dddd d MMMM",
"es": "dddd, d 'de' MMMM",
"pt": "dddd, d 'de' MMMM",
"zh": "yyyy年M月d日 dddd",
"uk": "dddd, d MMMM",
"tr": "dddd, d MMMM"
"nl": "dddd d MMMM"
};
return I18n.locale.toString(Time.now, formats[lang] || "dddd, MMMM d");
return I18n.locale.toString(Time.now, formats[lang] || "dddd, d MMMM");
}
pointSize: Style.fontSizeXL
font.weight: Font.Medium
@@ -524,7 +524,7 @@ Loader {
}
Text {
id: hibernateText
text: I18n.tr("session-menu.hibernate")
text: Settings.data.general.showHibernateOnLockScreen ? I18n.tr("session-menu.hibernate") : ""
font.pointSize: buttonRowTextMeasurer.fontSize
font.weight: Font.Medium
}
@@ -550,7 +550,7 @@ Loader {
// Button row needs: margins + 5 buttons + 4 spacings + margins
// Plus ColumnLayout margins (14 on each side = 28 total)
// Add extra buffer to ensure password input has proper padding
property real minButtonRowWidth: buttonRowTextMeasurer.minButtonWidth > 0 ? (5 * buttonRowTextMeasurer.minButtonWidth) + 40 + (2 * Style.marginM) + 28 + (2 * Style.marginM) : 750
property real minButtonRowWidth: buttonRowTextMeasurer.minButtonWidth > 0 ? ((Settings.data.general.showHibernateOnLockScreen ? 5 : 4) * buttonRowTextMeasurer.minButtonWidth) + 40 + (2 * Style.marginM) + 28 + (2 * Style.marginM) : 750
width: Math.max(750, minButtonRowWidth)
ColumnLayout {
@@ -749,7 +749,7 @@ Loader {
}
}
// 3-day forecast
// Forecast
RowLayout {
visible: Settings.data.location.weatherEnabled && LocationService.data.weather !== null
Layout.preferredWidth: 260
@@ -757,7 +757,7 @@ Loader {
spacing: 4
Repeater {
model: 3
model: MediaService.currentPlayer && MediaService.canPlay ? 3 : 4
delegate: ColumnLayout {
Layout.fillWidth: true
spacing: 3
@@ -804,8 +804,6 @@ Loader {
Item {
Layout.fillWidth: true
visible: !(Settings.data.location.weatherEnabled && LocationService.data.weather !== null)
Layout.preferredWidth: visible ? 1 : 0
}
// Battery and Keyboard Layout (full mode only)
@@ -1183,6 +1181,7 @@ Loader {
}
Rectangle {
visible: Settings.data.general.showHibernateOnLockScreen
Layout.fillWidth: true
Layout.minimumWidth: buttonRowTextMeasurer.minButtonWidth
Layout.preferredHeight: Settings.data.general.compactLockScreen ? 36 : 48

View File

@@ -615,11 +615,7 @@ Item {
Behavior on height {
NumberAnimation {
duration: {
if (!panelBackground.shouldAnimateHeight)
return 0;
return root.isClosing ? Style.animationFast : Style.animationNormal;
}
duration: root.isClosing ? Style.animationFast : Style.animationNormal
easing.type: Easing.BezierSpline
easing.bezierCurve: panelBackground.bezierCurve
}

View File

@@ -22,7 +22,7 @@ Variants {
property ListModel notificationModel: NotificationService.activeList
// Always create window (but with 0x0 dimensions when no notifications)
active: true
active: Settings.data.notifications?.location != "bar"
// Keep loader active briefly after last notification to allow animations to complete
Timer {

View File

@@ -18,7 +18,11 @@ SmartPanel {
panelContent: Rectangle {
color: Color.transparent
property real contentPreferredHeight: !(BluetoothService.adapter && BluetoothService.adapter.enabled) ? Math.min(preferredHeight, Math.max(280 * Style.uiScaleRatio, mainColumn.implicitHeight + Style.marginL * 2)) : (mainColumn.implicitHeight + Style.marginL * 2)
// Calculate content height based on header + devices list (or minimum for empty states)
property real headerHeight: headerRow.implicitHeight + Style.marginM * 2
property real devicesHeight: devicesList.implicitHeight
property real calculatedHeight: headerHeight + devicesHeight + Style.marginL * 2 + Style.marginM
property real contentPreferredHeight: (BluetoothService.adapter && BluetoothService.adapter.enabled) ? Math.min(root.preferredHeight, Math.max(280 * Style.uiScaleRatio, calculatedHeight)) : Math.min(root.preferredHeight, 280 * Style.uiScaleRatio)
ColumnLayout {
id: mainColumn
@@ -83,6 +87,7 @@ SmartPanel {
// Adapter not available of disabled
NBox {
id: disabledBox
visible: !(BluetoothService.adapter && BluetoothService.adapter.enabled)
Layout.fillWidth: true
Layout.fillHeight: true
@@ -135,6 +140,7 @@ SmartPanel {
contentWidth: availableWidth
ColumnLayout {
id: devicesList
width: parent.width
spacing: Style.marginM

View File

@@ -835,6 +835,24 @@ ColumnLayout {
}
}
}
NCheckbox {
label: "Telegram"
description: ProgramCheckerService.telegramAvailable ? I18n.tr("settings.color-scheme.templates.programs.telegram.description", {
"filepath": "~/.config/telegram-desktop/themes/noctalia.tdesktop-theme"
}) : I18n.tr("settings.color-scheme.templates.programs.telegram.description-missing", {
"app": "telegram"
})
checked: Settings.data.templates.telegram
enabled: ProgramCheckerService.telegramAvailable
opacity: ProgramCheckerService.telegramAvailable ? 1.0 : 0.6
onToggled: checked => {
if (ProgramCheckerService.telegramAvailable) {
Settings.data.templates.telegram = checked;
AppThemeService.generate();
}
}
}
}
// Miscellaneous
NCollapsible {

View File

@@ -21,6 +21,13 @@ ColumnLayout {
checked: Settings.data.general.compactLockScreen
onToggled: checked => Settings.data.general.compactLockScreen = checked
}
NToggle {
label: I18n.tr("settings.lock-screen.show-hibernate.label")
description: I18n.tr("settings.lock-screen.show-hibernate.description")
checked: Settings.data.general.showHibernateOnLockScreen
onToggled: checked => Settings.data.general.showHibernateOnLockScreen = checked
}
NDivider {
Layout.fillWidth: true

View File

@@ -74,6 +74,10 @@ ColumnLayout {
{
"key": "bottom_right",
"name": I18n.tr("options.launcher.position.bottom_right")
},
{
"key": "bar",
"name": I18n.tr("options.launcher.position.bar")
}
]
currentKey: Settings.data.notifications.location || "top_right"

View File

@@ -24,11 +24,24 @@ SmartPanel {
property int currentStep: 0
property int totalSteps: 5
property bool isCompleting: false
onOpened: function () {
selectedScaleRatio = Settings.data.general.scaleRatio;
selectedBarPosition = Settings.data.bar.position;
selectedWallpaperDirectory = Settings.data.wallpaper.directory || Settings.defaultWallpapersDirectory;
isCompleting = false;
}
Connections {
target: Settings
function onSettingsSaved() {
if (isCompleting) {
Logger.i("SetupWizard", "Settings saved, closing panel");
isCompleting = false;
root.close();
}
}
}
// Setup wizard data
@@ -366,24 +379,50 @@ SmartPanel {
}
function completeSetup() {
Logger.i("SetupWizard", "Completing setup with selected options");
if (selectedWallpaperDirectory !== Settings.data.wallpaper.directory) {
Settings.data.wallpaper.directory = selectedWallpaperDirectory;
WallpaperService.refreshWallpapersList();
if (isCompleting) {
Logger.w("SetupWizard", "completeSetup() called while already completing, ignoring");
return;
}
if (selectedWallpaper !== "") {
WallpaperService.changeWallpaper(selectedWallpaper, undefined);
try {
Logger.i("SetupWizard", "Completing setup with selected options");
isCompleting = true;
if (selectedWallpaperDirectory !== Settings.data.wallpaper.directory) {
Settings.data.wallpaper.directory = selectedWallpaperDirectory;
WallpaperService.refreshWallpapersList();
}
if (selectedWallpaper !== "") {
WallpaperService.changeWallpaper(selectedWallpaper, undefined);
}
Settings.data.general.scaleRatio = selectedScaleRatio;
Settings.data.bar.position = selectedBarPosition;
Settings.data.setupCompleted = true;
// Save settings immediately and wait for settingsSaved signal before closing
Settings.saveImmediate();
Logger.i("SetupWizard", "Setup completed successfully, waiting for settings save confirmation");
// Fallback: if settingsSaved signal doesn't fire within 2 seconds, close anyway
closeTimer.start();
} catch (error) {
Logger.e("SetupWizard", "Error completing setup:", error);
isCompleting = false;
}
}
Settings.data.general.scaleRatio = selectedScaleRatio;
Settings.data.bar.position = selectedBarPosition;
Settings.data.setupCompleted = true;
Settings.saveImmediate();
Logger.i("SetupWizard", "Setup completed successfully");
root.close();
Timer {
id: closeTimer
interval: 2000
onTriggered: {
if (isCompleting) {
Logger.w("SetupWizard", "Settings save timeout, closing panel anyway");
isCompleting = false;
root.close();
}
}
}
function applyWallpaperSettings() {

View File

@@ -184,9 +184,15 @@ SmartPanel {
if (!modelData.onlyMenu) {
modelData.activate();
}
if ((PanelService.openedPanel !== null) && !PanelService.openedPanel.isClosing) {
PanelService.openedPanel.close();
}
} else if (mouse.button === Qt.MiddleButton) {
// Middle click: activate with middle button
modelData.secondaryActivate && modelData.secondaryActivate();
if ((PanelService.openedPanel !== null) && !PanelService.openedPanel.isClosing) {
PanelService.openedPanel.close();
}
} else if (mouse.button === Qt.RightButton) {
// Right click: open context menu
TooltipService.hideImmediately();

View File

@@ -134,7 +134,11 @@ Item {
screen: root.screen
// Parse location setting
readonly property string location: Settings.data.notifications?.location || "top_right"
readonly property string location: {
if (Settings.data.notifications?.location == "bar")
return "top_right"
return Settings.data.notifications?.location || "top_right"
}
readonly property bool isTop: location.startsWith("top")
readonly property bool isBottom: location.startsWith("bottom")
readonly property bool isLeft: location.endsWith("_left")

View File

@@ -124,6 +124,12 @@ Singleton {
from = from.replace(root.developmentSuffix, "");
to = to.replace(root.developmentSuffix, "");
// 'from' always need to be before 'to'
// handle edge case that will show up as we changed -dev to -git
if (from === to) {
from = "v3.0.0";
}
Logger.d("UpdateService", "Fetching upgrade log", "from:", from, "to:", to);
const url = `${upgradeLogBaseUrl}/${from}/${to}`;

View File

@@ -27,6 +27,7 @@ Singleton {
property bool codeAvailable: false
property bool gnomeCalendarAvailable: false
property bool spicetifyAvailable: false
property bool telegramAvailable: false
// Discord client auto-detection
property var availableDiscordClients: []
@@ -181,7 +182,8 @@ Singleton {
"wlsunsetAvailable": ["which", "wlsunset"],
"codeAvailable": ["which", "code"],
"gnomeCalendarAvailable": ["which", "gnome-calendar"],
"spicetifyAvailable": ["which", "spicetify"]
"spicetifyAvailable": ["which", "spicetify"],
"telegramAvailable": ["which", "telegram-desktop"]
})
// Internal tracking

View File

@@ -208,6 +208,17 @@ Singleton {
}
],
"postProcess": () => `spicetify -q apply --no-restart`
},
{
"id": "telegram",
"name": "Telegram",
"category": "applications",
"input": "telegram.tdesktop-theme",
"outputs": [
{
"path": "~/.config/telegram-desktop/themes/noctalia.tdesktop-theme"
}
]
}
]

View File

@@ -134,6 +134,7 @@ ShellRoot {
setupWizardTimer.start();
} else {
Settings.data.setupCompleted = true;
Settings.saveImmediate();
}
}