diff --git a/HelpSource/editor.js b/HelpSource/editor.js index 608cb5d8ffa..7c71e858cd1 100644 --- a/HelpSource/editor.js +++ b/HelpSource/editor.js @@ -38,7 +38,10 @@ const init = () => { lineWrapping: true, viewportMargin: Infinity, extraKeys: { - 'Shift-Enter': evalLine + // noop: prevent both codemirror and the browser to handle Shift-Enter + 'Shift-Enter': ()=>{}, + // prevent only codemirror to handle Ctrl+D + 'Ctrl-D': false } }) @@ -59,15 +62,6 @@ const init = () => { } -const evalLine = () => { - // If we are not running in the SC IDE, do nothing. - if (!window.IDE) { - return; - } - // Ask IDE to eval line. Calls back to `selectLine()` - window.IDE.evaluateLine(); -} - /* returns the code selection, line or region */ const selectRegion = (options = { flash: true }) => { let range = window.getSelection().getRangeAt(0) diff --git a/QtCollider/factories.cpp b/QtCollider/factories.cpp index 85bb89bec8b..afe9304f5da 100644 --- a/QtCollider/factories.cpp +++ b/QtCollider/factories.cpp @@ -26,6 +26,11 @@ QC_DECLARE_QWIDGET_FACTORY(QLabel); +#ifdef SC_USE_QTWEBENGINE +# include "widgets/QcWebView.h" +QC_DECLARE_QWIDGET_FACTORY(WebView); +#endif + static void doLoadFactories() { QC_ADD_FACTORY(QcDefaultWidget); QC_ADD_FACTORY(QcHLayoutWidget); diff --git a/QtCollider/widgets/QcWebView.cpp b/QtCollider/widgets/QcWebView.cpp index 2c9e72e1ae4..fe4b1b68ae9 100644 --- a/QtCollider/widgets/QcWebView.cpp +++ b/QtCollider/widgets/QcWebView.cpp @@ -23,7 +23,6 @@ # include "QcWebView.h" # include "../widgets/web_page.hpp" -# include "../QcWidgetFactory.h" # include # include # include @@ -35,13 +34,10 @@ # include # include -QC_DECLARE_QWIDGET_FACTORY(WebView); - namespace QtCollider { -WebView::WebView(QWidget* parent): QWebEngineView(parent), _interpretSelection(false), _editable(false) { +WebView::WebView(QWidget* parent): QWebEngineView(parent), _editable(false) { QtCollider::WebPage* page = new WebPage(this); - page->setDelegateReload(true); setPage(page); connectPage(page); @@ -55,9 +51,7 @@ WebView::WebView(QWidget* parent): QWebEngineView(parent), _interpretSelection(f page->action(QWebEnginePage::Paste)->setShortcut(QKeySequence::Paste); page->action(QWebEnginePage::Reload)->setShortcut(QKeySequence::Refresh); - connect(this, SIGNAL(interpret(QString)), qApp, SLOT(interpret(QString)), Qt::QueuedConnection); - - connect(this, SIGNAL(loadFinished(bool)), this, SLOT(updateEditable(bool))); + connect(this, SIGNAL(loadFinished(bool)), this, SLOT(pageLoaded(bool))); } void WebView::connectPage(QtCollider::WebPage* page) { @@ -159,7 +153,8 @@ void WebView::toPlainText(QcCallback* cb) const { void WebView::runJavaScript(const QString& script, QcCallback* cb) { if (page()) { if (cb) { - page()->runJavaScript(script, cb->asFunctor()); + // convert QVariant to string until we deal with QVariants + page()->runJavaScript(script, [cb](const QVariant& t) { cb->call(t.toString()); }); } else { page()->runJavaScript(script, [](const QVariant&) {}); } @@ -228,29 +223,34 @@ void WebView::contextMenuEvent(QContextMenuEvent* event) { menu.exec(event->globalPos()); } -void WebView::keyPressEvent(QKeyEvent* e) { - int key = e->key(); - int mods = e->modifiers(); - - if (_interpretSelection - && (key == Qt::Key_Enter || (key == Qt::Key_Return && mods & (Qt::ControlModifier | Qt::ShiftModifier)))) { - QString selection = selectedText(); - if (!selection.isEmpty()) { - Q_EMIT(interpret(selection)); - return; - } +// webView's renderer keypresses don't arrive to webView +// duplicate them +bool WebView::eventFilter(QObject* obj, QEvent* event) { + if (event->type() == QEvent::KeyPress) { + // takes ownership of newEvent + QApplication::postEvent(this, new QKeyEvent(*static_cast(event))); } - QWebEngineView::keyPressEvent(e); + event->ignore(); + return false; } -void WebView::updateEditable(bool ok) { - if (ok) { - if (_editable) { - page()->runJavaScript("document.documentElement.contentEditable = true"); - } else { - page()->runJavaScript("document.documentElement.contentEditable = false"); - } +// stop keypresses here to avoid duplicates in parents +bool WebView::event(QEvent* ev) { + if (ev->type() == QEvent::KeyPress) + return true; + + return QWebEngineView::event(ev); +} + +void WebView::pageLoaded(bool ok) { this->focusProxy()->installEventFilter(this); } + +void WebView::setEditable(bool b) { + _editable = b; + if (_editable) { + page()->runJavaScript("document.documentElement.contentEditable = true"); + } else { + page()->runJavaScript("document.documentElement.contentEditable = false"); } } diff --git a/QtCollider/widgets/QcWebView.h b/QtCollider/widgets/QcWebView.h index 530f1319d3d..01895dcd07e 100644 --- a/QtCollider/widgets/QcWebView.h +++ b/QtCollider/widgets/QcWebView.h @@ -42,7 +42,7 @@ class WebView : public QWebEngineView { public: Q_INVOKABLE void setFontFamily(int genericFontFamily, const QString& fontFamily); - Q_INVOKABLE void triggerPageAction(int action, bool checked); + Q_INVOKABLE void triggerPageAction(int action, bool checked = false); Q_INVOKABLE QAction* pageAction(QWebEnginePage::WebAction) const; // QWebEnginePage forwards @@ -58,13 +58,12 @@ class WebView : public QWebEngineView { Q_INVOKABLE void navigate(const QString& url); public Q_SLOTS: - void findText(const QString& searchText, bool reversed, QtCollider::QcCallback* cb); + void findText(const QString& searchText, bool reversed, QtCollider::QcCallback* cb = nullptr); Q_SIGNALS: void linkActivated(const QString&, int, bool); void jsConsoleMsg(const QString&, int, const QString&); void reloadTriggered(const QString&); - void interpret(const QString& code); // QWebEnginePage forwards void linkHovered(const QString& url); @@ -99,16 +98,9 @@ public Q_SLOTS: bool delegateReload() const; void setDelegateReload(bool); - Q_PROPERTY(bool enterInterpretsSelection READ interpretSelection WRITE setInterpretSelection); - bool interpretSelection() const { return _interpretSelection; } - void setInterpretSelection(bool b) { _interpretSelection = b; } - Q_PROPERTY(bool editable READ editable WRITE setEditable); bool editable() const { return _editable; } - void setEditable(bool b) { - _editable = b; - updateEditable(true); - } + void setEditable(bool b); // QWebEnginePage properties Q_PROPERTY(QString requestedUrl READ requestedUrl) @@ -140,19 +132,19 @@ public Q_SLOTS: inline static QUrl urlFromString(const QString& str) { return QUrl::fromUserInput(str); } protected: - virtual void keyPressEvent(QKeyEvent*); - virtual void contextMenuEvent(QContextMenuEvent*); + void contextMenuEvent(QContextMenuEvent*) override; + bool event(QEvent* ev) override; + bool eventFilter(QObject* obj, QEvent* event) override; public Q_SLOTS: void onPageReload(); void onRenderProcessTerminated(QWebEnginePage::RenderProcessTerminationStatus, int); void onLinkClicked(const QUrl&, QWebEnginePage::NavigationType, bool); - void updateEditable(bool); + void pageLoaded(bool); private: void connectPage(QtCollider::WebPage* page); - bool _interpretSelection; bool _editable; }; diff --git a/SCClassLibrary/Common/GUI/Base/QWebView.sc b/SCClassLibrary/Common/GUI/Base/QWebView.sc index 3148a4b33ef..997b82dd908 100644 --- a/SCClassLibrary/Common/GUI/Base/QWebView.sc +++ b/SCClassLibrary/Common/GUI/Base/QWebView.sc @@ -3,7 +3,7 @@ WebView : View { var desktop()->availableGeometry(this); mSizeHint = QSize(availableScreenRect.width() * 0.4, availableScreenRect.height() * 0.7); - HelpWebPage* webPage = new HelpWebPage(this); - webPage->setDelegateReload(true); - - mWebView = new QWebEngineView; // setPage does not take ownership of webPage; it must be deleted manually later (see below) - mWebView->setPage(webPage); + mWebView = new QtCollider::WebView(this); mWebView->settings()->setAttribute(QWebEngineSettings::LocalStorageEnabled, true); mWebView->setContextMenuPolicy(Qt::CustomContextMenu); @@ -90,11 +80,15 @@ HelpBrowser::HelpBrowser(QWidget* parent): QWidget(parent) { setLayout(layout); connect(mWebView, SIGNAL(loadStarted()), mLoadProgressIndicator, SLOT(start())); - connect(mWebView, SIGNAL(loadFinished(bool)), mLoadProgressIndicator, SLOT(stop())); + connect(mWebView, SIGNAL(loadFinished(bool)), this, SLOT(onPageLoad())); connect(mWebView, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(onContextMenuRequest(QPoint))); - connect(webPage->action(QWebEnginePage::Reload), SIGNAL(triggered(bool)), this, SLOT(onReload())); - connect(webPage, SIGNAL(jsConsoleMsg(QString, int, QString)), this, SLOT(onJsConsoleMsg(QString, int, QString))); + mWebView->setOverrideNavigation(true); + connect(mWebView->page(), SIGNAL(navigationRequested(const QUrl&, QWebEnginePage::NavigationType, bool)), this, + SLOT(onLinkClicked(const QUrl&, QWebEnginePage::NavigationType, bool))); + mWebView->setDelegateReload(true); + connect(mWebView->page()->action(QWebEnginePage::Reload), SIGNAL(triggered(bool)), this, SLOT(onReload())); + connect(mWebView, SIGNAL(jsConsoleMsg(QString, int, QString)), this, SLOT(onJsConsoleMsg(QString, int, QString))); ScProcess* scProcess = Main::scProcess(); connect(scProcess, SIGNAL(response(QString, QString)), this, SLOT(onScResponse(QString, QString))); @@ -104,7 +98,7 @@ HelpBrowser::HelpBrowser(QWidget* parent): QWidget(parent) { // Delete the help browser's page to avoid an assert/crash during shutdown. See QTBUG-56441, QTBUG-50160. // Note that putting this in the destructor doesn't work. - connect(QApplication::instance(), &QApplication::aboutToQuit, [webPage]() { delete webPage; }); + connect(QApplication::instance(), &QApplication::aboutToQuit, [this]() { delete mWebView->page(); }); createActions(); @@ -114,9 +108,10 @@ HelpBrowser::HelpBrowser(QWidget* parent): QWidget(parent) { } void HelpBrowser::onPageLoad() { - if (mServerPort) { - mWebView->page()->runJavaScript(QString("setUpWebChannel(%1)").arg(mServerPort)); - } + mLoadProgressIndicator->stop(); + // add these actions to weview's renderer, to capture shift+enter and possibly other swallowed shortcuts + static_cast(mActions[EvaluateRegion])->addToWidget(mWebView->focusProxy()); + static_cast(mActions[Evaluate])->addToWidget(mWebView->focusProxy()); } void HelpBrowser::createActions() { @@ -142,10 +137,11 @@ void HelpBrowser::createActions() { connect(ovrAction, SIGNAL(triggered()), this, SLOT(resetZoom())); ovrAction->addToWidget(this); + // eval actions are added to mWebView->focusProxy() in onPageLoad() mActions[Evaluate] = ovrAction = new OverridingAction(tr("Evaluate as Code"), this); connect(ovrAction, SIGNAL(triggered()), this, SLOT(evaluateSelection())); - ovrAction->addToWidget(this); - + mActions[EvaluateRegion] = new OverridingAction(tr("Evaluate as Code Region"), this); + connect(mActions[EvaluateRegion], &OverridingAction::triggered, this, [=]() { this->evaluateSelection(true); }); // For the sake of display: mWebView->pageAction(QWebEnginePage::Copy)->setShortcut(QKeySequence::Copy); mWebView->pageAction(QWebEnginePage::Paste)->setShortcut(QKeySequence::Paste); @@ -167,10 +163,10 @@ void HelpBrowser::applySettings(Settings::Manager* settings) { mActions[ResetZoom]->setShortcut(settings->shortcut("editor-reset-font-size")); QList evalShortcuts; - evalShortcuts.append(settings->shortcut("editor-eval-smart")); evalShortcuts.append(settings->shortcut("editor-eval-line")); evalShortcuts.append(QKeySequence(Qt::Key_Enter)); mActions[Evaluate]->setShortcuts(evalShortcuts); + mActions[EvaluateRegion]->setShortcut(settings->shortcut("editor-eval-smart")); settings->endGroup(); @@ -248,7 +244,7 @@ void HelpBrowser::findText(const QString& text, bool backwards) { QWebEnginePage::FindFlags flags; if (backwards) flags |= QWebEnginePage::FindBackward; - mWebView->findText(text, flags); + mWebView->findText(text, backwards); } bool HelpBrowser::helpBrowserHasFocus() const { @@ -284,12 +280,8 @@ bool HelpBrowser::eventFilter(QObject* object, QEvent* event) { break; } case QEvent::ShortcutOverride: { - QKeyEvent* kevent = static_cast(event); - if (kevent == QKeySequence::Copy || kevent == QKeySequence::Paste) { - kevent->accept(); - return true; - } - break; + event->accept(); + return true; } default: break; @@ -352,7 +344,7 @@ void HelpBrowser::evaluateSelection(bool evaluateRegion) { } void HelpBrowser::onJsConsoleMsg(const QString& arg1, int arg2, const QString& arg3) { - qWarning() << "*** ERROR in JavaScript:" << arg1; + qWarning() << "*** JavaScript Message:" << arg1; qWarning() << "* line:" << arg2; qWarning() << "* source ID:" << arg3; } @@ -378,7 +370,9 @@ void HelpBrowser::onContextMenuRequest(const QPoint& pos) { menu.addAction(mWebView->pageAction(QWebEnginePage::Forward)); menu.addAction(mWebView->pageAction(QWebEnginePage::Reload)); - if (!contextData.selectedText().isEmpty()) + if (contextData.selectedText().isEmpty()) + menu.addAction(mActions[EvaluateRegion]); + else menu.addAction(mActions[Evaluate]); menu.addSeparator(); diff --git a/editors/sc-ide/widgets/help_browser.hpp b/editors/sc-ide/widgets/help_browser.hpp index 652234e3bf0..d833a213dba 100644 --- a/editors/sc-ide/widgets/help_browser.hpp +++ b/editors/sc-ide/widgets/help_browser.hpp @@ -22,6 +22,7 @@ #include "util/docklet.hpp" #include "QtCollider/widgets/web_page.hpp" +#include "QtCollider/widgets/QcWebView.h" #include #include @@ -75,15 +76,6 @@ public slots: int mDotCount; }; -class HelpWebPage : public QtCollider::WebPage { - Q_OBJECT - -public: - HelpWebPage(HelpBrowser* browser); - -private: - HelpBrowser* mBrowser; -}; class HelpBrowser : public QWidget { Q_OBJECT @@ -96,6 +88,7 @@ class HelpBrowser : public QWidget { ZoomOut, ResetZoom, Evaluate, + EvaluateRegion, ActionCount }; @@ -148,7 +141,7 @@ private slots: void sendRequest(const QString& code); QString symbolUnderCursor(); - QWebEngineView* mWebView; + QtCollider::WebView* mWebView; LoadProgressIndicator* mLoadProgressIndicator; QSize mSizeHint;