diff --git a/Common/Prefs.qml b/Common/Prefs.qml index 94d1ce1a..1c8ef839 100644 --- a/Common/Prefs.qml +++ b/Common/Prefs.qml @@ -17,6 +17,7 @@ Singleton { property bool use24HourClock: true property bool useFahrenheit: false property bool nightModeEnabled: false + property string profileImage: "" Component.onCompleted: loadSettings() @@ -57,6 +58,7 @@ Singleton { use24HourClock = settings.use24HourClock !== undefined ? settings.use24HourClock : true useFahrenheit = settings.useFahrenheit !== undefined ? settings.useFahrenheit : false nightModeEnabled = settings.nightModeEnabled !== undefined ? settings.nightModeEnabled : false + profileImage = settings.profileImage !== undefined ? settings.profileImage : "" console.log("Loaded settings - themeIndex:", themeIndex, "isDynamic:", themeIsDynamic, "lightMode:", isLightMode, "transparency:", topBarTransparency, "recentApps:", recentlyUsedApps.length) applyStoredTheme() @@ -79,7 +81,8 @@ Singleton { recentlyUsedApps, use24HourClock, useFahrenheit, - nightModeEnabled + nightModeEnabled, + profileImage }, null, 2)) console.log("Saving settings - themeIndex:", themeIndex, "isDynamic:", themeIsDynamic, "lightMode:", isLightMode, "transparency:", topBarTransparency, "recentApps:", recentlyUsedApps.length) } @@ -191,4 +194,10 @@ Singleton { nightModeEnabled = enabled saveSettings() } + + function setProfileImage(imageUrl) { + console.log("Prefs setProfileImage called - profileImage:", imageUrl) + profileImage = imageUrl + saveSettings() + } } \ No newline at end of file diff --git a/Widgets/ControlCenter/ControlCenterPopup.qml b/Widgets/ControlCenter/ControlCenterPopup.qml index 04340e56..01dd916e 100644 --- a/Widgets/ControlCenter/ControlCenterPopup.qml +++ b/Widgets/ControlCenter/ControlCenterPopup.qml @@ -99,43 +99,79 @@ PanelWindow { anchors.rightMargin: Theme.spacingL spacing: Theme.spacingL - // Profile Picture - Rectangle { - width: 54 - height: 54 - radius: 27 - color: Theme.primary - border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12) - border.width: 2 + // Profile Picture Container + Item { + width: 64 + height: 64 - Image { + // Background circle for fallback icon + Rectangle { anchors.fill: parent - anchors.margins: 2 - source: UserInfoService.profilePicture - fillMode: Image.PreserveAspectCrop - visible: UserInfoService.profileAvailable - smooth: true - - layer.enabled: true - layer.effect: MultiEffect { - maskEnabled: true - maskSource: Rectangle { - width: parent.width - height: parent.height - radius: width / 2 - visible: false - } - } + radius: 32 + color: Theme.primary + visible: Prefs.profileImage === "" || profileImage.status === Image.Error } - // Fallback icon when no profile picture + // Profile image (working version) + Image { + id: profileImage + anchors.fill: parent + source: { + if (Prefs.profileImage === "") return "" + // Add file:// prefix if it's a local path (starts with /) + if (Prefs.profileImage.startsWith("/")) { + return "file://" + Prefs.profileImage + } + // Return as-is if it already has a protocol or is a web URL + return Prefs.profileImage + } + fillMode: Image.PreserveAspectCrop + visible: Prefs.profileImage !== "" && status !== Image.Error + smooth: true + asynchronous: true + mipmap: true + cache: false + } + + // Subtle circular outline for images + Rectangle { + anchors.fill: parent + radius: 32 + color: "transparent" + border.color: Qt.rgba(0, 0, 0, 0.15) // Subtle dark outline + border.width: 1 + visible: Prefs.profileImage !== "" && profileImage.status !== Image.Error + } + + // Highlight ring to make it pop + Rectangle { + anchors.fill: parent + anchors.margins: -1 + radius: 33 + color: "transparent" + border.color: Qt.rgba(255, 255, 255, 0.1) // Subtle light ring + border.width: 1 + visible: Prefs.profileImage !== "" && profileImage.status !== Image.Error + } + + // Fallback icon for no profile picture (generic person) Text { anchors.centerIn: parent text: "person" font.family: Theme.iconFont - font.pixelSize: Theme.iconSize + 4 + font.pixelSize: Theme.iconSize + 8 color: Theme.onPrimary - visible: !UserInfoService.profileAvailable + visible: Prefs.profileImage === "" + } + + // Error icon when image fails to load + Text { + anchors.centerIn: parent + text: "warning" + font.family: Theme.iconFont + font.pixelSize: Theme.iconSize + 8 + color: Theme.onPrimary + visible: Prefs.profileImage !== "" && profileImage.status === Image.Error } } @@ -144,6 +180,7 @@ PanelWindow { anchors.verticalCenter: parent.verticalCenter spacing: Theme.spacingXS + Text { text: UserInfoService.fullName || UserInfoService.username || "User" font.pixelSize: Theme.fontSizeLarge diff --git a/Widgets/SettingsPopup.qml b/Widgets/SettingsPopup.qml index 235c2799..ba1ae1e4 100644 --- a/Widgets/SettingsPopup.qml +++ b/Widgets/SettingsPopup.qml @@ -139,6 +139,71 @@ PanelWindow { width: parent.width spacing: Theme.spacingL + // Profile Settings + SettingsSection { + title: "Profile" + iconName: "person" + + content: Column { + width: parent.width + spacing: Theme.spacingM + + // Profile Image URL Input + Column { + width: parent.width + spacing: Theme.spacingS + + Text { + text: "Profile Image" + font.pixelSize: Theme.fontSizeMedium + color: Theme.surfaceText + font.weight: Font.Medium + } + + Rectangle { + width: parent.width + height: 48 + radius: Theme.cornerRadius + color: Theme.surfaceVariant + border.color: profileImageInput.activeFocus ? Theme.primary : Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.3) + border.width: profileImageInput.activeFocus ? 2 : 1 + + TextInput { + id: profileImageInput + anchors.fill: parent + anchors.margins: Theme.spacingM + verticalAlignment: TextInput.AlignVCenter + color: Theme.surfaceText + font.pixelSize: Theme.fontSizeMedium + text: Prefs.profileImage + selectByMouse: true + + onEditingFinished: { + Prefs.setProfileImage(text) + } + + // Placeholder text + Text { + anchors.verticalCenter: parent.verticalCenter + text: "Enter image path (e.g., /home/user/picture.png)" + color: Theme.surfaceVariantText + font.pixelSize: Theme.fontSizeMedium + visible: profileImageInput.text.length === 0 && !profileImageInput.activeFocus + } + } + } + + Text { + text: "Enter a file path or web URL for your profile picture" + font.pixelSize: Theme.fontSizeSmall + color: Theme.surfaceVariantText + wrapMode: Text.WordWrap + width: parent.width + } + } + } + } + // Clock Settings SettingsSection { title: "Clock & Time"