From 8e5bdd0c9d421b17b7a0abf1ce2801f39436dcdf Mon Sep 17 00:00:00 2001 From: WHC Date: Sun, 11 Oct 2026 21:00:11 +0800 Subject: [PATCH] the header is a Rectangle, and the phone puts it where it is A Controls ToolBar hands its children to a style-owned contentItem, and on the NX789J that item sat 35 px below the bar itself: the header was at y 0 h 42 while its own gear was at y 56 -- drawn on top of the sidebar's first lines, and clicked there too, so the settings dialog could not be opened from the gear at all (eleven checks that follow it failed with it). A desktop never showed it: same style (Basic), same numbers, gear at y 21. A plain Rectangle has no such indirection -- its children are where it is. Nothing else changed; the background and the border line are the same. --uitest prints what it sees at that point now (style, window, header, gear, safe area), which is how this was found: note style Basic, window 1280x853, header y 0 h 42, gear centre y 21 Before: gear centre y 56, `FAIL the gear opens the settings dialog`, 17 failures on the device. After: 95 ok, 4 FAIL (Open/Share and the two clipboard-image checks, which Android has no answer for). --- app/qml/Main.qml | 25 ++++++++++++++++--------- app/src/ui/UiTest.cpp | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 9 deletions(-) diff --git a/app/qml/Main.qml b/app/qml/Main.qml index 0fe8f60..2f8dc9a 100644 --- a/app/qml/Main.qml +++ b/app/qml/Main.qml @@ -201,18 +201,25 @@ ApplicationWindow { } // ---- the shell ------------------------------------------------------- - header: ToolBar { + /* A plain Rectangle, not a ToolBar. A Controls ToolBar hands its children + * to a style-owned contentItem, and on this phone that item sat 35 px below + * the bar itself -- header y 0 h 42 with its gear at y 56 -- so the gear + * was drawn, and clicked, outside the header, over the sidebar. A + * Rectangle puts its children where it is (checked with --uitest on the + * device and on both styles at home). */ + header: Rectangle { id: appBar + objectName: "appBar" implicitHeight: Theme.headerHeight - background: Rectangle { - color: Theme.surface - Rectangle { - anchors.bottom: parent.bottom - width: parent.width - height: 1 - color: Theme.border - } + color: Theme.surface + + Rectangle { + anchors.bottom: parent.bottom + width: parent.width + height: 1 + color: Theme.border } + RowLayout { anchors.fill: parent anchors.leftMargin: Theme.pad diff --git a/app/src/ui/UiTest.cpp b/app/src/ui/UiTest.cpp index cd78d64..1b37ca1 100644 --- a/app/src/ui/UiTest.cpp +++ b/app/src/ui/UiTest.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -86,6 +87,19 @@ void check(bool ok, const QString &what) { g_fail++; } +/* A line in the run's log that is not a check: what the window looked like at + * a step, for the cases where "it did nothing" is all the checks can say. */ +void note(const QString &what) { + std::printf("note %s\n", qPrintable(what)); + std::fflush(stdout); + if (g_runLog.isOpen()) { + g_runLog.write("note "); + g_runLog.write(what.toUtf8()); + g_runLog.write("\n"); + g_runLog.flush(); + } +} + /* Every item with this objectName in the visual tree. A view that has just * been replayed can still hold the delegates it is about to drop, so a name * can match more than one item for a moment. */ @@ -665,6 +679,29 @@ int runUiTest(AgentCore &core, const QString &size) { clickMouse(win, QStringLiteral("settingsButton")); QQuickItem *panel = item(win, QStringLiteral("settingsPanel")); check(panel && panel->isVisible(), QStringLiteral("the gear opens the settings dialog")); + /* Where the pieces actually are, whether or not that worked: a click goes to + * whatever is on top at that point, and a style lays a header out its own + * way -- the phone runs a different style from a desktop (Universal against + * Basic), which is where the header's content ended up 35 px below the + * header itself. */ + { + QQuickItem *bar = item(win, QStringLiteral("appBar")); + const QMargins safe = win->property("safeAreaMargins").value(); + note(QStringLiteral("style %1, window %2x%3, header y %4 h %5, " + "gear centre y %6, safe area %7/%8/%9/%10") + .arg(QQuickStyle::name()) + .arg(win->width()) + .arg(win->height()) + .arg(bar ? bar->y() : -1) + .arg(bar ? bar->height() : -1) + .arg(item(win, QStringLiteral("settingsButton")) + ? centre(item(win, QStringLiteral("settingsButton"))).y() + : -1) + .arg(safe.left()) + .arg(safe.top()) + .arg(safe.right()) + .arg(safe.bottom())); + } /* item() answers with an item that exists even when nothing is visible (the * QObject-tree fallback), so "the dialog is not open" is the invisible case * too. */