Skip to content

Commit 55e399d

Browse files
feat(webview): add back/forward nav and fix dock width
Replace setMinimumWidth with sizeHint + resizeDocks so the AI dock gets its preferred 600px on first attach without permanently clamping the minimum, letting users resize freely afterward. - Back/forward buttons added to WebViewWidget toolbar on all platforms (Mac WKWebView, Windows WebView2); virtual interface extended with goBack/goForward - NewWindowRequestedHandler intercepts window.open and target=_blank, navigating in-place rather than spawning a detached window that WebView2 cannot manage
1 parent ff4a84b commit 55e399d

7 files changed

Lines changed: 73 additions & 1 deletion

File tree

src/dialogs/MainWindow.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3739,6 +3739,8 @@ void MainWindow::attachAiAgentDock(AiAgentDock *dock)
37393739
DockMiddleClickCloser::install(dock);
37403740
if (existing) {
37413741
tabifyDockWidget(existing, dock);
3742+
} else {
3743+
resizeDocks({dock}, {600}, Qt::Horizontal);
37423744
}
37433745
dock->setVisible(true);
37443746
dock->raise();

src/docks/AiAgentDock.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,6 @@ AiAgentDock::AiAgentDock(QString sessionId,
5353
{
5454
setAttribute(Qt::WA_DeleteOnClose, true);
5555
setObjectName(QStringLiteral("AiAgentDock_%1").arg(m_sessionId));
56-
setMinimumWidth(600);
5756
// Spec ("Default dock area"): dock is unrestricted — user may move it to
5857
// any side. defaultArea() is only consulted on first attach.
5958
setAllowedAreas(Qt::AllDockWidgetAreas);
@@ -201,6 +200,11 @@ AiAgentDock::~AiAgentDock()
201200
}
202201
}
203202

203+
QSize AiAgentDock::sizeHint() const
204+
{
205+
return QSize(600, 400);
206+
}
207+
204208
void AiAgentDock::closeEvent(QCloseEvent *event)
205209
{
206210
if (m_model && m_model->isProcessing()) {

src/docks/AiAgentDock.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ class AiAgentDock : public QDockWidget
9292
void goalDebugLogAppended(const QString &entry);
9393

9494
protected:
95+
QSize sizeHint() const override;
9596
void closeEvent(QCloseEvent *event) override;
9697

9798
// Test seam — override in tests to bypass the modal QMessageBox.

src/widgets/WebViewWidget.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,22 @@ void WebViewWidget::setupToolbar()
3333
m_toolbarLayout->setContentsMargins(4, 0, 4, 0);
3434
m_toolbarLayout->setSpacing(4);
3535

36+
m_backBtn = new QToolButton(toolbarWidget);
37+
m_backBtn->setAutoRaise(true);
38+
m_backBtn->setIcon(style()->standardIcon(QStyle::SP_ArrowBack));
39+
m_backBtn->setToolTip(tr("Back"));
40+
m_backBtn->setIconSize(QSize(14, 14));
41+
connect(m_backBtn, &QToolButton::clicked, this, &WebViewWidget::goBack);
42+
m_toolbarLayout->addWidget(m_backBtn);
43+
44+
m_forwardBtn = new QToolButton(toolbarWidget);
45+
m_forwardBtn->setAutoRaise(true);
46+
m_forwardBtn->setIcon(style()->standardIcon(QStyle::SP_ArrowForward));
47+
m_forwardBtn->setToolTip(tr("Forward"));
48+
m_forwardBtn->setIconSize(QSize(14, 14));
49+
connect(m_forwardBtn, &QToolButton::clicked, this, &WebViewWidget::goForward);
50+
m_toolbarLayout->addWidget(m_forwardBtn);
51+
3652
m_reloadBtn = new QToolButton(toolbarWidget);
3753
m_reloadBtn->setAutoRaise(true);
3854
m_reloadBtn->setIcon(style()->standardIcon(QStyle::SP_BrowserReload));

src/widgets/WebViewWidget.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ class WebViewWidget : public QWidget
2929
virtual void navigate(const QUrl &url) = 0;
3030
virtual void reload() = 0;
3131
virtual void stop() = 0;
32+
virtual void goBack() = 0;
33+
virtual void goForward() = 0;
3234
virtual void destroy() = 0;
3335
virtual QString debugInfo() const { return QString(); }
3436

@@ -68,6 +70,8 @@ class WebViewWidget : public QWidget
6870
QUrl m_url;
6971
QVBoxLayout *m_mainLayout = nullptr;
7072
QHBoxLayout *m_toolbarLayout = nullptr;
73+
QToolButton *m_backBtn = nullptr;
74+
QToolButton *m_forwardBtn = nullptr;
7175
QToolButton *m_reloadBtn = nullptr;
7276
QLabel *m_urlLabel = nullptr;
7377
QToolButton *m_stopBtn = nullptr;

src/widgets/WebViewWidget_mac.mm

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@ void navigate(const QUrl &url) override
7575

7676
void reload() override { setLoading(true); [m_webView reload]; }
7777
void stop() override { [m_webView stopLoading]; setLoading(false); }
78+
void goBack() override { if (m_webView) [m_webView goBack]; }
79+
void goForward() override { if (m_webView) [m_webView goForward]; }
7880

7981
void destroy() override
8082
{

src/widgets/WebViewWidget_win.cpp

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,18 @@ class WebViewWidgetWin : public WebViewWidget
150150
setLoading(false);
151151
}
152152

153+
void goBack() override
154+
{
155+
if (!m_webView) return;
156+
m_webView->GoBack();
157+
}
158+
159+
void goForward() override
160+
{
161+
if (!m_webView) return;
162+
m_webView->GoForward();
163+
}
164+
153165
void destroy() override
154166
{
155167
m_alive->store(false, std::memory_order_release);
@@ -344,6 +356,11 @@ class WebViewWidgetWin : public WebViewWidget
344356
m_webView->add_DocumentTitleChanged(
345357
new DocumentTitleChangedHandler(this), &titleToken);
346358

359+
// Block new windows — navigate in-place instead
360+
EventRegistrationToken newWinToken;
361+
m_webView->add_NewWindowRequested(
362+
new NewWindowRequestedHandler(this), &newWinToken);
363+
347364
// Navigate to initial URL
348365
setLoading(true);
349366
m_dbgNavigateCalled = true;
@@ -479,6 +496,32 @@ class WebViewWidgetWin : public WebViewWidget
479496
}
480497
};
481498

499+
// NewWindowRequestedHandler: intercepts window.open / target="_blank" and navigates in-place.
500+
struct NewWindowRequestedHandler : ICoreWebView2NewWindowRequestedEventHandler {
501+
WebViewWidgetWin *owner;
502+
std::shared_ptr<std::atomic<bool>> alive;
503+
ULONG refCount = 1;
504+
NewWindowRequestedHandler(WebViewWidgetWin *o) : owner(o), alive(o->m_alive) {}
505+
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppv) override {
506+
if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_ICoreWebView2NewWindowRequestedEventHandler)) {
507+
*ppv = this; AddRef(); return S_OK;
508+
}
509+
*ppv = nullptr; return E_NOINTERFACE;
510+
}
511+
ULONG STDMETHODCALLTYPE AddRef() override { return ++refCount; }
512+
ULONG STDMETHODCALLTYPE Release() override { if (--refCount == 0) { delete this; return 0; } return refCount; }
513+
HRESULT STDMETHODCALLTYPE Invoke(ICoreWebView2 *, ICoreWebView2NewWindowRequestedEventArgs *args) override {
514+
if (!alive->load(std::memory_order_acquire)) return S_OK;
515+
LPWSTR uri = nullptr;
516+
if (args && SUCCEEDED(args->get_Uri(&uri)) && uri) {
517+
owner->m_webView->Navigate(uri);
518+
CoTaskMemFree(uri);
519+
}
520+
if (args) args->put_Handled(TRUE);
521+
return S_OK;
522+
}
523+
};
524+
482525
// --- CDP Discovery ---
483526

484527
void startCdpDiscovery()

0 commit comments

Comments
 (0)