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).
This commit is contained in:
WHC
2026-10-11 21:00:11 +08:00
parent f8030ec201
commit 8e5bdd0c9d
2 changed files with 53 additions and 9 deletions
+16 -9
View File
@@ -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
+37
View File
@@ -20,6 +20,7 @@
#include <QPainter>
#include <QPointingDevice>
#include <QQuickItem>
#include <QQuickStyle>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QQuickWindow>
@@ -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<QMargins>();
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. */