rotated view + wheel inverter

This commit is contained in:
2026-08-26 17:10:34 +02:00
parent 78988700be
commit c33dbc147f
7 changed files with 341 additions and 135 deletions
@@ -1,7 +1,8 @@
import ZShell.Config
import ZShell.Llm
import QtQuick
import QtQuick.Layouts
import ZShell.Components
import ZShell.Config
import ZShell.Llm
import qs.Modules.Notifications.Sidebar.Chat.Content
import qs.Components
import qs.Services
@@ -15,7 +16,7 @@ Item {
signal requestClose
function scrollToBottom(): void {
Qt.callLater(list.positionViewAtBeginning);
Qt.callLater(() => listLoader.item?.positionViewAtBeginning());
}
function send(text: string): void {
@@ -26,8 +27,6 @@ Item {
input.text = "";
}
Component.onCompleted: input.focus = true
RowLayout {
id: header
@@ -54,10 +53,8 @@ Item {
}
}
VerticalFadeListView {
id: list
property bool userScrolledUp: false
Loader {
id: listLoader
anchors.bottom: input.top
anchors.bottomMargin: Tokens.spacing.medium
@@ -65,58 +62,83 @@ Item {
anchors.right: parent.right
anchors.top: header.bottom
anchors.topMargin: Tokens.spacing.medium
cacheBuffer: height * 20
clip: true
fadeAmount: 0.05
fadeThreshold: Tokens.padding.medium
model: root.chatData.messagesModel
spacing: 0
verticalLayoutDirection: VerticalFadeListView.BottomToTop
active: root.chatData && root.chatData.loaded
add: Transition {
Anim {
from: list.width
property: "x"
to: 0
sourceComponent: ListView {
id: list
property bool userScrolledUp: false
function scrollToBottom(): void {
Qt.callLater(() => positionViewAtBeginning());
}
}
delegate: MessageDelegate {}
displaced: Transition {
Anim {
property: "y"
cacheBuffer: height * 20
clip: true
// fadeAmount: 0.05
// fadeThreshold: Tokens.padding.medium
model: root.chatData.messagesModel
spacing: 0
rotation: 180
// add: Transition {
// Anim {
// from: 10
// property: "y"
// }
// }
delegate: MessageDelegate {
rotation: 180
}
}
move: Transition {
// displaced: Transition {
// Anim {
// property: "y"
// }
// }
// move: Transition {
// Anim {
// property: "y"
// }
// }
Component.onCompleted: {
positionViewAtBeginning();
forceActiveFocus();
}
onAtYEndChanged: {
if (atYEnd)
userScrolledUp = false;
}
onContentHeightChanged: {
if (!userScrolledUp && atYEnd)
scrollToBottom();
}
onCountChanged: {
if (!userScrolledUp)
scrollToBottom();
}
onMovingChanged: {
if (moving)
userScrolledUp = !atYEnd;
}
Anim {
property: "y"
id: scrollAnim
property: "contentY"
target: list
type: Anim.DefaultEffects
}
WheelInverter {
target: list
}
}
Component.onCompleted: positionViewAtBeginning()
onAtYEndChanged: {
if (atYEnd)
userScrolledUp = false;
}
onContentHeightChanged: {
if (!userScrolledUp && atYEnd)
root.scrollToBottom();
}
onCountChanged: {
if (!userScrolledUp)
root.scrollToBottom();
}
onMovingChanged: {
if (moving)
userScrolledUp = !atYEnd;
}
Anim {
id: scrollAnim
property: "contentY"
target: list
type: Anim.DefaultEffects
}
// Trigger the load even before we're ready to show the list.
Component.onCompleted: if (root.chatData)
root.chatData.messagesModel
}
EmptyBackground {
@@ -133,7 +155,7 @@ Item {
anchors.horizontalCenter: parent.horizontalCenter
implicitHeight: scrollToBottom.implicitHeight
implicitWidth: scrollToBottom.implicitWidth
scale: list.visibleArea.yPosition + list.visibleArea.heightRatio < 1 && list.contentHeight > list.height ? 1 : 0
scale: !listLoader.item.atYBeginning && listLoader.item.contentHeight > listLoader.item.height ? 1 : 0
visible: scale > 0
Behavior on scale {
@@ -157,7 +179,7 @@ Item {
type: IconButton.Tonal
onClicked: {
list.positionViewAtBeginning();
listLoader.item.positionViewAtBeginning();
}
}
}