1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-06-23 11:35:25 -04:00

greeter: redesign the switch-user/auto-login flow

- Fix visual glitches
- Make auto-login and switch user texts not always visible
- Make from-zero memory state not confusing
This commit is contained in:
bbedward
2026-06-22 13:50:50 -04:00
parent 556c0819c4
commit b5e2e68a22
2 changed files with 222 additions and 225 deletions
+67 -155
View File
@@ -73,7 +73,7 @@ Item {
readonly property bool greeterPamHasExternalAuth: greeterPamHasFprint || greeterPamHasU2f
readonly property bool multipleUsersAvailable: GreeterUsersService.loaded && GreeterUsersService.users.length > 1
readonly property bool showUserPicker: multipleUsersAvailable && !GreeterState.showPasswordInput && !manualUsernameEntry
readonly property bool showAccountSwitchLink: multipleUsersAvailable && !GreeterState.showPasswordInput && !GreeterState.unlocking
readonly property bool showAccountSwitchLink: multipleUsersAvailable && manualUsernameEntry && !GreeterState.showPasswordInput && !GreeterState.unlocking
readonly property int userPickerMaxHeight: Math.min(400, Math.max(120, height * 0.35))
property bool userListOpen: false
property bool manualUsernameEntry: false
@@ -533,7 +533,6 @@ Item {
passwordFailureCount = 0;
clearAuthFeedback();
externalAuthAutoStartedForUser = "";
root.autoLoginOnSuccess = false;
}
root.pickerThemeUsername = user;
GreeterState.username = user;
@@ -870,6 +869,13 @@ Item {
anchors.fill: parent
color: "transparent"
MouseArea {
anchors.fill: parent
enabled: root.userListOpen
visible: root.userListOpen
onClicked: root.userListOpen = false
}
Column {
id: greeterMainColumn
@@ -1006,17 +1012,6 @@ Item {
opacity: 0.9
}
StyledText {
id: userPickerHint
anchors.horizontalCenter: parent.horizontalCenter
visible: root.showUserPicker && !GreeterState.showPasswordInput && !GreeterState.username && !root.userListOpen
text: I18n.tr("Select user...", "greeter user picker placeholder")
font.pixelSize: Theme.fontSizeMedium
color: "white"
opacity: 0.85
}
ColumnLayout {
id: authColumn
@@ -1055,7 +1050,7 @@ Item {
radius: width / 2
color: "transparent"
border.color: Theme.primary
border.width: avatarPickerArea.containsMouse || root.userListOpen ? 2 : 0
border.width: (avatarPickerArea.containsMouse || root.userListOpen) && !GreeterState.showPasswordInput ? 2 : 0
visible: root.multipleUsersAvailable
Behavior on border.width {
NumberAnimation {
@@ -1065,6 +1060,29 @@ Item {
}
}
// Switch-user affordance: hover scrim over the selected user's avatar.
Rectangle {
anchors.fill: parent
radius: width / 2
color: Qt.rgba(0, 0, 0, 0.55)
opacity: (root.multipleUsersAvailable && GreeterState.showPasswordInput && avatarPickerArea.containsMouse) ? 1 : 0
visible: opacity > 0
Behavior on opacity {
NumberAnimation {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
}
DankIcon {
anchors.centerIn: parent
name: "switch_account"
size: 24
color: "white"
}
}
MouseArea {
id: avatarPickerArea
@@ -1089,6 +1107,7 @@ Item {
Layout.fillWidth: true
Layout.preferredHeight: root.showUserPicker && root.userListOpen ? Math.max(60, userPicker.implicitHeight + Theme.spacingM * 2) : 60
clip: true
radius: Theme.cornerRadius
color: Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, 0.9)
border.color: inputField.activeFocus ? Theme.primary : Qt.rgba(1, 1, 1, 0.3)
@@ -1105,8 +1124,13 @@ Item {
maxExpandedHeight: root.userPickerMaxHeight
visible: root.showUserPicker && !GreeterState.showPasswordInput
expanded: root.userListOpen
autoLoginVisible: GreetdSettings.rememberLastUser && GreetdSettings.rememberLastSession
autoLoginChecked: root.autoLoginOnSuccess
manualEntryVisible: true
onUserSelected: username => root.selectUser(username, false)
onToggleRequested: root.userListOpen = !root.userListOpen
onAutoLoginToggled: root.autoLoginOnSuccess = !root.autoLoginOnSuccess
onManualEntryRequested: root.enterManualUsernameEntry()
}
DankIcon {
@@ -1341,6 +1365,13 @@ Item {
easing.type: Theme.standardEasing
}
}
Behavior on Layout.preferredHeight {
NumberAnimation {
duration: Theme.mediumDuration
easing.type: Theme.standardEasing
}
}
}
}
@@ -1353,7 +1384,7 @@ Item {
id: accountSwitchLabel
anchors.horizontalCenter: parent.horizontalCenter
text: root.manualUsernameEntry ? I18n.tr("Back to user list", "greeter link to return from manual username entry to user picker") : I18n.tr("Not listed?", "greeter link to switch to manual username entry")
text: I18n.tr("Back to user list", "greeter link to return from manual username entry to user picker")
color: Theme.primary
font.pixelSize: Theme.fontSizeSmall
font.underline: accountSwitchMouse.containsMouse
@@ -1365,12 +1396,7 @@ Item {
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
if (root.manualUsernameEntry)
root.returnToUserListFromManualEntry();
else
root.enterManualUsernameEntry();
}
onClicked: root.returnToUserListFromManualEntry()
}
}
@@ -1395,150 +1421,36 @@ Item {
}
}
// Password-screen actions: Switch User + Auto-login toggle as one compact chip row
// Single-user auto-login toggle: its only home, since there is no picker to host it.
// Multi-user switching lives on the avatar hover; multi-user auto-login lives in the picker.
// Height stays reserved during unlocking (fade only) so the centered column doesn't jump.
Item {
id: passwordActions
readonly property bool autoLoginAvailable: GreetdSettings.rememberLastUser && GreetdSettings.rememberLastSession
readonly property bool showAutoLoginToggle: !root.multipleUsersAvailable && autoLoginAvailable
Layout.fillWidth: true
Layout.topMargin: Theme.spacingXS
Layout.preferredHeight: visible ? 32 : 0
visible: GreeterState.showPasswordInput && !GreeterState.unlocking && (root.multipleUsersAvailable || autoLoginAvailable)
visible: GreeterState.showPasswordInput && showAutoLoginToggle
opacity: GreeterState.unlocking ? 0 : 1
Row {
Behavior on opacity {
NumberAnimation {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
}
DankActionButton {
anchors.centerIn: parent
spacing: Theme.spacingS
Rectangle {
id: switchUserChip
visible: root.multipleUsersAvailable
height: 32
width: switchUserContent.implicitWidth + Theme.spacingM * 2
radius: height / 2
color: Theme.withAlpha(Theme.surfaceVariant, 0.65)
Rectangle {
anchors.fill: parent
radius: parent.radius
color: (switchUserMouse.containsMouse || switchUserMouse.pressed) ? Theme.surfaceTextHover : "transparent"
Behavior on color {
ColorAnimation {
duration: Theme.shorterDuration
easing.type: Theme.standardEasing
}
}
}
DankRipple {
id: switchUserRipple
cornerRadius: switchUserChip.radius
rippleColor: Theme.surfaceVariantText
}
Row {
id: switchUserContent
anchors.centerIn: parent
spacing: Theme.spacingXS
DankIcon {
name: "people"
size: 16
color: Theme.surfaceVariantText
anchors.verticalCenter: parent.verticalCenter
}
StyledText {
text: I18n.tr("Switch User")
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
anchors.verticalCenter: parent.verticalCenter
}
}
MouseArea {
id: switchUserMouse
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onPressed: mouse => switchUserRipple.trigger(mouse.x, mouse.y)
onClicked: root.returnToUserPicker()
}
}
Rectangle {
id: autoLoginChip
visible: passwordActions.autoLoginAvailable
height: 32
width: autoLoginContent.implicitWidth + Theme.spacingM * 2
radius: height / 2
color: root.autoLoginOnSuccess ? Theme.withAlpha(Theme.primary, 0.85) : Theme.withAlpha(Theme.surfaceVariant, 0.65)
Behavior on color {
ColorAnimation {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
}
Rectangle {
anchors.fill: parent
radius: parent.radius
color: {
if (autoLoginMouse.pressed)
return root.autoLoginOnSuccess ? Theme.primaryPressed : Theme.surfaceTextHover;
if (autoLoginMouse.containsMouse)
return root.autoLoginOnSuccess ? Theme.primaryHover : Theme.surfaceTextHover;
return "transparent";
}
Behavior on color {
ColorAnimation {
duration: Theme.shorterDuration
easing.type: Theme.standardEasing
}
}
}
DankRipple {
id: autoLoginRipple
cornerRadius: autoLoginChip.radius
rippleColor: root.autoLoginOnSuccess ? Theme.primaryText : Theme.surfaceVariantText
}
Row {
id: autoLoginContent
anchors.centerIn: parent
spacing: Theme.spacingXS
DankIcon {
name: root.autoLoginOnSuccess ? "check" : "login"
size: 16
color: root.autoLoginOnSuccess ? Theme.primaryText : Theme.surfaceVariantText
anchors.verticalCenter: parent.verticalCenter
}
StyledText {
text: I18n.tr("Auto-login")
font.pixelSize: Theme.fontSizeSmall
font.weight: root.autoLoginOnSuccess ? Font.Medium : Font.Normal
color: root.autoLoginOnSuccess ? Theme.primaryText : Theme.surfaceVariantText
anchors.verticalCenter: parent.verticalCenter
}
}
MouseArea {
id: autoLoginMouse
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: root.autoLoginOnSuccess = !root.autoLoginOnSuccess
onPressed: mouse => autoLoginRipple.trigger(mouse.x, mouse.y)
}
}
iconName: root.autoLoginOnSuccess ? "check_box" : "check_box_outline_blank"
iconSize: 18
buttonSize: 32
iconColor: root.autoLoginOnSuccess ? Theme.primary : Qt.rgba(1, 1, 1, 0.55)
tooltipText: I18n.tr("Auto-login")
onClicked: root.autoLoginOnSuccess = !root.autoLoginOnSuccess
}
}
}
+155 -70
View File
@@ -7,22 +7,38 @@ import qs.Widgets
Item {
id: root
LayoutMirroring.enabled: I18n.isRtl
LayoutMirroring.childrenInherit: true
property bool expanded: false
property int maxExpandedHeight: 400
property bool autoLoginVisible: false
property bool autoLoginChecked: false
property bool manualEntryVisible: false
signal userSelected(string username)
signal toggleRequested()
signal toggleRequested
signal autoLoginToggled
signal manualEntryRequested
readonly property int rowHeight: 52
readonly property int collapsedBarHeight: 36
readonly property int expandedListHeight: {
if (!expanded)
return 0;
readonly property int actionRowHeight: 44
readonly property int userListFullHeight: {
const count = GreeterUsersService.users.length;
if (count === 0)
return 0;
const fullHeight = count * rowHeight + Math.max(0, count - 1) * Theme.spacingXS;
return Math.min(maxExpandedHeight, fullHeight);
return count * rowHeight + Math.max(0, count - 1) * Theme.spacingXS;
}
readonly property int manualEntryBlockHeight: manualEntryVisible ? actionRowHeight + Theme.spacingXS : 0
readonly property int autoLoginBlockHeight: autoLoginVisible ? actionRowHeight + Theme.spacingXS : 0
readonly property int expandedContentHeight: {
if (!expanded)
return 0;
if (GreeterUsersService.users.length === 0 && !autoLoginVisible && !manualEntryVisible)
return 0;
return Math.min(maxExpandedHeight, userListFullHeight + manualEntryBlockHeight + autoLoginBlockHeight);
}
function encodeFileUrl(path) {
@@ -38,49 +54,34 @@ Item {
return "";
}
implicitHeight: expanded ? expandedListHeight : collapsedBarHeight
implicitHeight: expanded ? expandedContentHeight : collapsedBarHeight
implicitWidth: parent ? parent.width : 320
RowLayout {
anchors.left: parent.left
anchors.right: parent.right
anchors.verticalCenter: expanded ? undefined : parent.verticalCenter
height: collapsedBarHeight
visible: !expanded && !!GreeterState.username
spacing: Theme.spacingM
StyledText {
Layout.fillWidth: true
text: GreeterUsersService.optionLabel(GreeterState.username)
color: Theme.surfaceText
font.pixelSize: Theme.fontSizeMedium
elide: Text.ElideRight
}
DankIcon {
name: "expand_more"
size: 20
color: Theme.surfaceVariantText
}
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: root.toggleRequested()
}
}
Item {
anchors.left: parent.left
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
height: collapsedBarHeight
visible: !expanded && !GreeterState.username
visible: !expanded
DankIcon {
anchors.centerIn: parent
name: "expand_more"
size: 20
color: Theme.surfaceVariantText
RowLayout {
anchors.fill: parent
spacing: Theme.spacingM
StyledText {
Layout.fillWidth: true
text: GreeterState.username ? GreeterUsersService.optionLabel(GreeterState.username) : I18n.tr("Select user...", "greeter user picker placeholder")
color: GreeterState.username ? Theme.surfaceText : Theme.surfaceVariantText
font.pixelSize: Theme.fontSizeMedium
elide: Text.ElideRight
}
DankIcon {
Layout.alignment: Qt.AlignVCenter
name: "expand_more"
size: 20
color: Theme.surfaceVariantText
}
}
MouseArea {
@@ -90,31 +91,80 @@ Item {
}
}
DankListView {
id: userListView
Column {
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
height: expandedListHeight
height: root.expandedContentHeight
visible: expanded
clip: true
interactive: contentHeight > height
spacing: Theme.spacingXS
model: GreeterUsersService.users
delegate: Rectangle {
id: userRow
DankListView {
id: userListView
required property var modelData
required property int index
width: parent.width
height: parent.height - root.manualEntryBlockHeight - root.autoLoginBlockHeight
clip: true
interactive: contentHeight > height
spacing: Theme.spacingXS
model: GreeterUsersService.users
width: userListView.width
height: root.rowHeight
delegate: Rectangle {
id: userRow
required property var modelData
required property int index
width: userListView.width
height: root.rowHeight
radius: Theme.cornerRadius
color: userRowMouse.containsMouse ? Theme.surfacePressed : "transparent"
border.color: GreeterState.username === userRow.modelData.username ? Theme.primary : "transparent"
border.width: GreeterState.username === userRow.modelData.username ? 1 : 0
RowLayout {
anchors.fill: parent
anchors.leftMargin: Theme.spacingS
anchors.rightMargin: Theme.spacingS
spacing: Theme.spacingM
Item {
Layout.preferredWidth: 36
Layout.preferredHeight: 36
DankCircularImage {
anchors.fill: parent
imageSource: root.profileImageSource(userRow.modelData.username)
fallbackIcon: "person"
}
}
StyledText {
Layout.fillWidth: true
text: GreeterUsersService.optionLabel(userRow.modelData.username)
color: Theme.surfaceText
font.pixelSize: Theme.fontSizeMedium
elide: Text.ElideRight
}
}
MouseArea {
id: userRowMouse
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: root.userSelected(userRow.modelData.username)
}
}
}
Rectangle {
width: parent.width
height: root.actionRowHeight
visible: root.manualEntryVisible
radius: Theme.cornerRadius
color: userRowMouse.containsMouse ? Theme.surfacePressed : "transparent"
border.color: GreeterState.username === userRow.modelData.username ? Theme.primary : "transparent"
border.width: GreeterState.username === userRow.modelData.username ? 1 : 0
color: manualEntryRowMouse.containsMouse ? Theme.surfacePressed : "transparent"
RowLayout {
anchors.fill: parent
@@ -122,20 +172,16 @@ Item {
anchors.rightMargin: Theme.spacingS
spacing: Theme.spacingM
Item {
Layout.preferredWidth: 36
Layout.preferredHeight: 36
DankCircularImage {
anchors.fill: parent
imageSource: root.profileImageSource(userRow.modelData.username)
fallbackIcon: "person"
}
DankIcon {
Layout.alignment: Qt.AlignVCenter
name: "person_add"
size: 20
color: Theme.surfaceVariantText
}
StyledText {
Layout.fillWidth: true
text: GreeterUsersService.optionLabel(userRow.modelData.username)
text: I18n.tr("Not listed?", "greeter link to switch to manual username entry")
color: Theme.surfaceText
font.pixelSize: Theme.fontSizeMedium
elide: Text.ElideRight
@@ -143,12 +189,51 @@ Item {
}
MouseArea {
id: userRowMouse
id: manualEntryRowMouse
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: root.userSelected(userRow.modelData.username)
onClicked: root.manualEntryRequested()
}
}
Rectangle {
width: parent.width
height: root.actionRowHeight
visible: root.autoLoginVisible
radius: Theme.cornerRadius
color: autoLoginRowMouse.containsMouse ? Theme.surfacePressed : "transparent"
RowLayout {
anchors.fill: parent
anchors.leftMargin: Theme.spacingS
anchors.rightMargin: Theme.spacingS
spacing: Theme.spacingM
DankIcon {
Layout.alignment: Qt.AlignVCenter
name: root.autoLoginChecked ? "check_box" : "check_box_outline_blank"
size: 20
color: root.autoLoginChecked ? Theme.primary : Theme.surfaceVariantText
}
StyledText {
Layout.fillWidth: true
text: I18n.tr("Auto-login")
color: Theme.surfaceText
font.pixelSize: Theme.fontSizeMedium
elide: Text.ElideRight
}
}
MouseArea {
id: autoLoginRowMouse
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: root.autoLoginToggled()
}
}
}