From 7b595dc5da973440ffac51ac53a613e2b3d62728 Mon Sep 17 00:00:00 2001 From: Ricardo Guilherme Schmidt <3esmit@gmail.com> Date: Fri, 17 Jul 2026 20:54:51 -0300 Subject: [PATCH] fix(amm): keep trade controls reachable --- apps/amm/qml/pages/SwapPage.qml | 79 +++++++++++++++++++---------- apps/amm/tests/qml/tst_SwapPage.qml | 38 ++++++++++++++ 2 files changed, 90 insertions(+), 27 deletions(-) diff --git a/apps/amm/qml/pages/SwapPage.qml b/apps/amm/qml/pages/SwapPage.qml index df872f4..27b86cc 100644 --- a/apps/amm/qml/pages/SwapPage.qml +++ b/apps/amm/qml/pages/SwapPage.qml @@ -1,6 +1,7 @@ pragma ComponentBehavior: Bound import QtQuick 2.15 +import QtQuick.Controls 2.15 import QtQuick.Layouts 1.15 import Logos.Wallet import "../components/shared" @@ -87,37 +88,61 @@ Item { } } - ColumnLayout { - anchors.centerIn: parent - spacing: 28 + Flickable { + id: swapScroll - SwapCard { - id: swapCard - objectName: "swapCard" - Layout.alignment: Qt.AlignHCenter - theme: pageTheme - tokens: root.tokens - width: Math.min(480, root.width - 32) + objectName: "swapScroll" + anchors.fill: parent + clip: true + contentWidth: width + contentHeight: Math.max(height, + swapContent.y + swapContent.implicitHeight + 16) + flickableDirection: Flickable.VerticalFlick + boundsBehavior: Flickable.StopAtBounds - onRequestTokenSelect: function(side) { - tokenModal.targetSide = side - tokenModal.open() - } - - onPreviewRequested: function(snapshot) { - swapConfirmationDialog.openWithSnapshot(snapshot) - } + ScrollBar.vertical: ScrollBar { + policy: ScrollBar.AsNeeded } - Text { - objectName: "swapPreviewNotice" - Layout.alignment: Qt.AlignHCenter - Layout.maximumWidth: Math.max(0, Math.min(480, root.width - 32)) - text: qsTr("Preview only — sample data. No swap will be submitted.") - color: pageTheme.colors.textSecondary - font.pixelSize: 15 - horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap + ColumnLayout { + id: swapContent + + objectName: "swapContent" + x: 16 + y: Math.max(56, Math.round((swapScroll.height - implicitHeight) / 2)) + width: Math.max(0, swapScroll.width - 32) + spacing: 28 + + SwapCard { + id: swapCard + objectName: "swapCard" + Layout.fillWidth: true + Layout.maximumWidth: 480 + Layout.alignment: Qt.AlignHCenter + theme: pageTheme + tokens: root.tokens + + onRequestTokenSelect: function(side) { + tokenModal.targetSide = side + tokenModal.open() + } + + onPreviewRequested: function(snapshot) { + swapConfirmationDialog.openWithSnapshot(snapshot) + } + } + + Text { + objectName: "swapPreviewNotice" + Layout.fillWidth: true + Layout.maximumWidth: 480 + Layout.alignment: Qt.AlignHCenter + text: qsTr("Preview only — sample data. No swap will be submitted.") + color: pageTheme.colors.textSecondary + font.pixelSize: 15 + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + } } } diff --git a/apps/amm/tests/qml/tst_SwapPage.qml b/apps/amm/tests/qml/tst_SwapPage.qml index 101072d..f1ef080 100644 --- a/apps/amm/tests/qml/tst_SwapPage.qml +++ b/apps/amm/tests/qml/tst_SwapPage.qml @@ -138,5 +138,43 @@ Item { verify(!card.canSubmit) compare(card.submitButtonText, "Select different tokens") } + + function test_tradeContentStaysReachable_data() { + return [ + { "tag": "short", "width": 360, "height": 184 }, + { "tag": "expanded", "width": 400, "height": 544 } + ] + } + + function test_tradeContentStaysReachable(data) { + const page = createTemporaryObject(pageComponent, root, { + "width": data.width, + "height": data.height + }) + verify(page) + + const scroll = findChild(page, "swapScroll") + const card = findChild(page, "swapCard") + const notice = findChild(page, "swapPreviewNotice") + verify(scroll) + verify(card) + verify(notice) + card.setToken("sell", page.tokens[0]) + card.setToken("buy", page.tokens[1]) + card.sellInput = "1" + card.editingSide = "sell" + wait(0) + + const cardPosition = card.mapToItem(page, 0, 0) + verify(cardPosition.x >= 16) + verify(cardPosition.x + card.width <= page.width - 16) + + if (scroll.contentHeight > scroll.height) + scroll.contentY = scroll.contentHeight - scroll.height + wait(0) + const noticePosition = notice.mapToItem(scroll, 0, 0) + verify(noticePosition.y >= 0) + verify(noticePosition.y + notice.height <= scroll.height) + } } }