From 334b510e2673a6ab3875ffa7a4c5b3b2dd09a369 Mon Sep 17 00:00:00 2001 From: "Danilo M." Date: Fri, 7 Aug 2026 17:51:52 +0200 Subject: feat(ui): fill the blank message pane with a branded placeholder An empty right pane said nothing, and multi-select made it a routine sight. It now carries the wordmark, thread counts that run their query when clicked, and a sync line that appears only when something needs attention. Rendered into the existing web view as a third document shape, so there is one document path and one set of security rules. The brand palette is a deliberate exception to deriving colours from the desktop theme, since a logo is brand rather than chrome; the theme still picks which of the two sets is used. Counts refresh when the pane is about to show rather than in the background: one goes stale the moment a tag is edited, and refreshing one nobody is looking at is work for nothing. A generation counter discards a superseded reply, and a late answer cannot repaint over an opened thread. The helper lines are real links because JavaScript is off in this profile. The handler is gated on the placeholder actually being displayed, so the same URL inside a message body is dropped: a stranger's mail must not drive the thread list, even to run a harmless query. Three defects found while building, all silent: - Every CSS percentage was invalid. QString::arg does not collapse "%%" into "%", so the document carried "50%%" and the browser dropped each declaration holding one, disabling the mask, the glow and both radial gradients while still rendering something plausible. Substitution is by named token now, which cannot collide with a percent sign. - A geometry probe endorsed the layout while that was live, because it measured only properties without percentages. - The font test passed against a build with one face missing, since the other satisfied both of its checks on its own. The mockup's light values needed correcting against a real pane: the grid vanished at a 2% luminance step on white, and the glow subtracts light there rather than adding it, washing the pane. Strength only, not hue. --- src/htmlbuilder.h | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) (limited to 'src/htmlbuilder.h') diff --git a/src/htmlbuilder.h b/src/htmlbuilder.h index 75fd1f8..3ade530 100644 --- a/src/htmlbuilder.h +++ b/src/htmlbuilder.h @@ -81,6 +81,58 @@ public: QColor quote; ///< Quoted lines in plain text. }; + /// The brand colours of the placeholder pane. + /// + /// **A deliberate exception to the Palette above**, which derives from the + /// desktop theme. A logo is brand rather than chrome, so these are the + /// values from the user's mockup and are not blended toward anything. The + /// desktop theme still decides WHICH set is used, so the pane never renders + /// a light lockup on a dark desktop. + struct BrandPalette { + QColor background; ///< The pane, behind the radial wash. + QColor backgroundIn; ///< The lighter centre of that wash. + QColor grid; ///< Grid rules, and the icon tile's border. + QColor tile; ///< The icon tile's fill. + QColor tileBorder; ///< The icon tile's edge. Separate from `grid`, + ///< which needs a different strength on light. + QColor accent; ///< "Mail" in the wordmark, and the envelope. + QColor accentEdge; ///< The envelope's stroke. + QColor title; ///< The wordmark, apart from the accent span. + QColor subtitle; ///< The tagline, the helpers and the footer. + + /// Percent alpha of the accent glow. Deliberately different between the + /// two sets: on dark the glow adds light and can be generous, on light + /// it subtracts and the same value washes the whole pane. + int glowAlpha = 0; + + /// Percent opacity of the grid, for the same reason. + int gridOpacity = 35; + }; + + /// Picks the dark or the light brand set from the desktop palette. + /// + /// Decided on the window's Base lightness, the same surface the document + /// Palette reads, so the two agree about which way round the theme is. + static BrandPalette brandPaletteFrom(const QPalette &palette); + + /// One helper line under the wordmark: a count, and the query it runs. + /// + /// An empty query renders as text rather than as a link, which is what the + /// sync line uses: it reports a state rather than naming a search. + struct PlaceholderHelper { + QString label; ///< Already-translated, e.g. "12 unread". + QString query; ///< notmuch query, or empty for a non-link line. + }; + + /// The pane shown when no thread is displayed. + /// + /// Rendered into the same web view as a message rather than into a second + /// widget stacked behind it, so there is one document path and one set of + /// security rules. + static QString buildPlaceholder(const QList &helpers, + const QString &version, + const BrandPalette &brand); + /// Derives the document palette from a widget palette. /// /// The dim and border colours are blends rather than fixed greys, which is -- cgit v1.2.3