WallpaperPanel: add keyboard controls thanks to pC

LockScreen: add cursor to password field, clear password after login
attempt
i18n: small cleanup
autoformat
This commit is contained in:
Ly-sec
2025-10-05 10:25:47 +02:00
parent 4c001b12b9
commit c15faa3727
8 changed files with 148 additions and 60 deletions
+95 -48
View File
@@ -47,6 +47,9 @@ Loader {
lockScreen.scheduleUnloadAfterUnlock()
lockContext.currentText = ""
}
onFailed: {
lockContext.currentText = ""
}
}
WlSessionLock {
@@ -228,6 +231,47 @@ Loader {
}
}
// Error notification
Rectangle {
width: 400 * scaling
height: 50 * scaling
anchors.horizontalCenter: parent.horizontalCenter
anchors.bottom: parent.bottom
anchors.bottomMargin: 300 * scaling
radius: 25 * scaling
color: Color.mError
border.color: Color.mError
border.width: 1
visible: lockContext.showFailure && lockContext.errorMessage
opacity: visible ? 1.0 : 0.0
RowLayout {
anchors.centerIn: parent
spacing: 8 * scaling
NIcon {
icon: "alert-circle"
pointSize: Style.fontSizeM * scaling
color: Color.mOnError
}
NText {
text: lockContext.errorMessage || "Authentication failed"
color: Color.mOnError
pointSize: Style.fontSizeM * scaling
font.weight: Font.Medium
horizontalAlignment: Text.AlignHCenter
}
}
Behavior on opacity {
NumberAnimation {
duration: 300
easing.type: Easing.OutCubic
}
}
}
// Bottom container with weather, password input and controls
Rectangle {
width: 700 * scaling
@@ -554,12 +598,57 @@ Loader {
Component.onCompleted: forceActiveFocus()
}
NText {
text: passwordInput.text.length > 0 ? "•".repeat(passwordInput.text.length) : I18n.tr("lock-screen.password")
color: passwordInput.text.length > 0 ? Color.mOnSurface : Color.mOnSurfaceVariant
pointSize: Style.fontSizeL * scaling
width: 560 * scaling
opacity: passwordInput.text.length > 0 ? 1.0 : 0.6
Row {
spacing: 0
Rectangle {
width: 2 * scaling
height: 20 * scaling
color: Color.mPrimary
visible: passwordInput.activeFocus && passwordInput.text.length === 0
anchors.verticalCenter: parent.verticalCenter
SequentialAnimation on opacity {
loops: Animation.Infinite
running: passwordInput.activeFocus && passwordInput.text.length === 0
NumberAnimation {
to: 0
duration: 530
}
NumberAnimation {
to: 1
duration: 530
}
}
}
NText {
text: passwordInput.text.length > 0 ? "•".repeat(passwordInput.text.length) : ""
color: passwordInput.text.length > 0 ? Color.mOnSurface : Color.mOnSurfaceVariant
pointSize: Style.fontSizeL * scaling
opacity: passwordInput.text.length > 0 ? 1.0 : 0.6
}
Rectangle {
width: 2 * scaling
height: 20 * scaling
color: Color.mPrimary
visible: passwordInput.activeFocus && passwordInput.text.length > 0
anchors.verticalCenter: parent.verticalCenter
SequentialAnimation on opacity {
loops: Animation.Infinite
running: passwordInput.activeFocus && passwordInput.text.length > 0
NumberAnimation {
to: 0
duration: 530
}
NumberAnimation {
to: 1
duration: 530
}
}
}
}
}
@@ -745,48 +834,6 @@ Loader {
}
}
}
// Status message
NText {
anchors.horizontalCenter: parent.horizontalCenter
anchors.bottom: parent.bottom
anchors.bottomMargin: 150 * scaling
text: {
if (lockContext.unlockInProgress)
return lockContext.infoMessage || "Authenticating..."
if (lockContext.showFailure && lockContext.errorMessage)
return lockContext.errorMessage
if (lockContext.showFailure)
return "Authentication failed. Please try again."
return ""
}
color: lockContext.unlockInProgress ? Color.mPrimary : (lockContext.showFailure ? Color.mError : Color.transparent)
pointSize: Style.fontSizeS * scaling
horizontalAlignment: Text.AlignHCenter
opacity: text.length > 0 ? 1.0 : 0.0
SequentialAnimation on opacity {
running: lockContext.unlockInProgress
loops: Animation.Infinite
NumberAnimation {
to: 0.6
duration: 1000
easing.type: Easing.InOutQuad
}
NumberAnimation {
to: 1.0
duration: 1000
easing.type: Easing.InOutQuad
}
}
Behavior on opacity {
NumberAnimation {
duration: 300
easing.type: Easing.OutCubic
}
}
}
}
}
}
+53
View File
@@ -218,6 +218,13 @@ NPanel {
searchDebounceTimer.restart()
}
Keys.onDownPressed: {
let currentView = screenRepeater.itemAt(currentScreenIndex)
if (currentView && currentView.gridView) {
currentView.gridView.forceActiveFocus()
}
}
Component.onCompleted: {
if (searchInput.inputItem && searchInput.inputItem.visible) {
searchInput.inputItem.forceActiveFocus()
@@ -231,6 +238,7 @@ NPanel {
// Component for each screen's wallpaper view
component WallpaperScreenView: Item {
property var targetScreen
property alias gridView: wallpaperGridView
// Local reactive state for this screen
property list<string> wallpapersList: []
@@ -310,6 +318,9 @@ NPanel {
visible: !WallpaperService.scanning
interactive: true // Enable delegate recycling and native scrolling
clip: true
focus: true
keyNavigationEnabled: true
keyNavigationWraps: false
model: filteredWallpapers
@@ -324,6 +335,47 @@ NPanel {
topMargin: Style.marginS * scaling
bottomMargin: Style.marginS * scaling
highlightFollowsCurrentItem: true
highlightMoveDuration: 150
highlight: Rectangle {
color: Color.mSecondary
opacity: 0.3
radius: Style.radiusS * scaling
z: 0
}
onCurrentIndexChanged: {
// Synchronize scroll with current item position
if (currentIndex >= 0 && scrollView.contentItem) {
let row = Math.floor(currentIndex / columns)
let itemY = row * cellHeight
let viewportTop = scrollView.contentItem.contentY
let viewportBottom = viewportTop + scrollView.height
// If item is out of view, scroll
if (itemY < viewportTop) {
scrollView.contentItem.contentY = Math.max(0, itemY - cellHeight)
} else if (itemY + cellHeight > viewportBottom) {
scrollView.contentItem.contentY = itemY + cellHeight - scrollView.height + cellHeight
}
}
}
Keys.onPressed: event => {
if (event.key === Qt.Key_Return || event.key === Qt.Key_Space) {
if (currentIndex >= 0 && currentIndex < filteredWallpapers.length) {
let path = filteredWallpapers[currentIndex]
if (Settings.data.wallpaper.setWallpaperOnAllMonitors) {
WallpaperService.changeWallpaper(path, undefined)
} else {
WallpaperService.changeWallpaper(path, targetScreen.name)
}
}
event.accepted = true
}
}
ScrollBar.vertical: ScrollBar {
policy: ScrollBar.AsNeeded
}
@@ -399,6 +451,7 @@ NPanel {
TapHandler {
onTapped: {
wallpaperGridView.currentIndex = index
if (Settings.data.wallpaper.setWallpaperOnAllMonitors) {
WallpaperService.changeWallpaper(wallpaperPath, undefined)
} else {