fix(ui): stabilize new tab title and icon (#1896)

Fixes #1640.
This commit is contained in:
Oleg Shparber
2026-05-31 00:11:19 +03:00
committed by GitHub
parent f03c9df7f8
commit f7d732e0c4
8 changed files with 94 additions and 4 deletions
+13
View File
@@ -0,0 +1,13 @@
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M12 3a9 9 0 1 0 9 9" />
</svg>

After

Width:  |  Height:  |  Size: 243 B

+1 -1
View File
@@ -3,7 +3,7 @@
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Welcome</title>
<title>New tab</title>
<link rel="stylesheet" type="text/css" href="assets/css/oat.min.css">
<link rel="stylesheet" type="text/css" href="assets/css/welcome.css">
</head>
+1
View File
@@ -16,6 +16,7 @@
<file alias="dots-vertical.svg">../../assets/tabler/dots-vertical.svg</file>
<file alias="info-circle.svg">../../assets/tabler/info-circle.svg</file>
<file alias="library.svg">../../assets/tabler/library.svg</file>
<file alias="loader-2.svg">../../assets/tabler/loader-2.svg</file>
<file alias="logout.svg">../../assets/tabler/logout.svg</file>
<file alias="minus.svg">../../assets/tabler/minus.svg</file>
<file alias="plus.svg">../../assets/tabler/plus.svg</file>
+63
View File
@@ -11,6 +11,10 @@
#include <QDataStream>
#include <QKeyEvent>
#include <QLoggingCategory>
#include <QMetaEnum>
#include <QTimer>
#include <QUrl>
#include <QVBoxLayout>
#include <QWebChannel>
#include <QWebEngineHistory>
@@ -19,6 +23,13 @@
namespace Zeal::Browser {
namespace {
Q_LOGGING_CATEGORY(log, "zeal.browser.webcontrol")
// Bound recovery attempts so a deterministically crashing page does not thrash forever.
constexpr int MaxRenderProcessReloadAttempts = 3;
} // namespace
WebControl::WebControl(QWidget *parent)
: QWidget(parent)
{
@@ -40,6 +51,57 @@ WebControl::WebControl(QWidget *parent)
connect(m_webView, &QWebEngineView::urlChanged, this, &WebControl::urlChanged);
connect(m_webView, &WebView::zoomLevelChanged, this, &WebControl::zoomLevelChanged);
// On a failed load that set no title, relabel the tab to signal the failure.
connect(m_webView, &QWebEngineView::loadFinished, this, [this](bool ok) {
if (ok) {
m_renderProcessReloadAttempts = 0;
return;
}
qCWarning(log, "Failed to load '%s'.", qPrintable(m_webView->url().toString()));
if (m_webView->title().isEmpty()) {
emit titleChanged(tr("Couldn't load page"));
}
});
// A crashed render process leaves a blank page and never updates the title. Reload a
// bounded number of times to recover, then relabel the tab if it keeps dying.
connect(m_webView,
&QWebEngineView::renderProcessTerminated,
this,
[this](QWebEnginePage::RenderProcessTerminationStatus status, int exitCode) {
if (status == QWebEnginePage::NormalTerminationStatus) {
return;
}
const char *statusName = QMetaEnum::fromType<QWebEnginePage::RenderProcessTerminationStatus>().valueToKey(
status);
qCWarning(log,
"Render process for '%s' terminated (%s, exit code %d).",
qPrintable(m_webView->url().toString()),
statusName,
exitCode);
if (m_renderProcessReloadAttempts < MaxRenderProcessReloadAttempts) {
++m_renderProcessReloadAttempts;
// Reloading directly from the termination handler is not supported; defer it.
// Skip if the user has navigated away in the meantime.
const QUrl crashedUrl = m_webView->url();
QTimer::singleShot(0, m_webView, [this, crashedUrl]() {
if (m_webView->url() == crashedUrl) {
m_webView->reload();
}
});
return;
}
qCWarning(log,
"Giving up reloading '%s' after %d attempts.",
qPrintable(m_webView->url().toString()),
MaxRenderProcessReloadAttempts);
emit titleChanged(tr("Couldn't load page"));
});
layout->addWidget(m_webView);
setLayout(layout);
@@ -97,6 +159,7 @@ void WebControl::setWebBridgeObject(const QString &name, QObject *object)
void WebControl::load(const QUrl &url)
{
m_renderProcessReloadAttempts = 0;
m_webView->load(url);
}
+1
View File
@@ -63,6 +63,7 @@ private:
WebView *m_webView = nullptr;
SearchToolBar *m_searchToolBar = nullptr;
int m_renderProcessReloadAttempts = 0;
};
} // namespace Zeal::Browser
+3 -1
View File
@@ -230,7 +230,9 @@ void MainWindow::addTab(BrowserTab *tab, int index, bool activate)
}
m_webViewStack->insertWidget(index, tab);
m_tabBar->insertTab(index, tr("Loading…"));
// Seed with "New tab" to match browsers and avoid a stranded label.
// The placeholder icon reserves the slot so the label does not jump when the favicon loads.
m_tabBar->insertTab(index, IconHelper::fromResource(QStringLiteral(":/icons/tabler/loader-2.svg")), tr("New tab"));
m_tabBar->setTabData(index, QVariant::fromValue(tab));
if (activate) {
+7 -2
View File
@@ -125,12 +125,17 @@ bool useThemeIcons()
#endif
}
QIcon fromResource(const QString &svgResource)
{
return QIcon(new TintedSvgIconEngine(svgResource));
}
QIcon fromTheme(const QString &themeName, const QString &fallbackSvgResource)
{
if (useThemeIcons()) {
return QIcon::fromTheme(themeName, QIcon(new TintedSvgIconEngine(fallbackSvgResource)));
return QIcon::fromTheme(themeName, fromResource(fallbackSvgResource));
}
return QIcon(new TintedSvgIconEngine(fallbackSvgResource));
return fromResource(fallbackSvgResource);
}
} // namespace Zeal::WidgetUi::IconHelper
+5
View File
@@ -19,6 +19,11 @@ bool useThemeIcons();
// color schemes. Use freedesktop icon naming for themeName (e.g. "go-previous").
QIcon fromTheme(const QString &themeName, const QString &fallbackSvgResource);
// Returns a monochrome Tabler SVG loaded from svgResource and tinted to the active
// palette, without any desktop icon theme lookup. Use this for glyphs that have no
// suitable freedesktop equivalent (e.g. the tab loading placeholder).
QIcon fromResource(const QString &svgResource);
} // namespace Zeal::WidgetUi::IconHelper
#endif // ZEAL_WIDGETUI_ICONHELPER_H