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/mainwindow.h | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) (limited to 'src/mainwindow.h') diff --git a/src/mainwindow.h b/src/mainwindow.h index adb973c..8043727 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -27,6 +27,7 @@ #include #include "config.h" +#include "htmlbuilder.h" #include "keymap.h" // Included rather than forward-declared: SyncPhaseTracker is held by value, so // its size must be known here. MailSync itself stays a forward declaration. @@ -184,6 +185,13 @@ private slots: void onTagsApplied(const TagChange &change); void onAllTagsReady(const QStringList &tags); + /// Thread counts for the placeholder's helper lines, in the order + /// requestPlaceholderCounts() asked for them. + void onCountsReady(const QVector &counts, quint64 generation); + + /// Runs a query the user clicked on the placeholder pane. + void onPlaceholderQueryRequested(const QString &query); + private: void buildUi(); @@ -199,6 +207,20 @@ private: /// Asks the worker to re-enumerate the database tags for the completer. void requestAllTags(); + /// Shows the placeholder pane and asks the worker to refresh its counts. + /// + /// **The single route to a blank pane.** Every site that used to call + /// MessageView::clear() goes through here, so the pane is never left empty + /// by accident and the counts are refreshed exactly when they are about to + /// be looked at. A count goes stale the moment a tag is edited, and one + /// nobody is looking at is not worth keeping fresh. + void showPlaceholderPane(); + + /// The helper lines, built from the last counts received. Rendered with + /// whatever the previous answer was until the new one lands, so the pane + /// never flashes empty while the worker replies. + QList placeholderHelpers() const; + void showWarnings(); void showShortcutReference(); void showAbout(); @@ -385,6 +407,21 @@ private: /// Indeterminate, shown only while a sync runs. See setSyncBusy(). QProgressBar *m_syncProgress = nullptr; + /// The last counts the worker answered, one per kPlaceholderQueries entry. + /// Empty until the first reply, which renders the pane without its helper + /// lines rather than with three zeroes that would be a lie. + QVector m_placeholderCounts; + + /// Discriminates a counts reply from a superseded request, the same way the + /// query generation does. A reply for an older request is dropped rather + /// than repainting the pane with counts taken before the last edit. + quint64 m_countsGeneration = 0; + + /// Set when a sync ends in failure, cleared when one succeeds. Drives the + /// placeholder's sync line, which appears only when something needs + /// attention, so it must survive until the next successful run. + bool m_lastSyncFailed = false; + /// Holds the sync log and its close button, so the pane can be dismissed. QWidget *m_syncLogPane = nullptr; QPlainTextEdit *m_syncLog = nullptr; -- cgit v1.2.3