summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/core/web_contents_adapter_client.h3
-rw-r--r--src/core/web_contents_delegate_qt.cpp18
-rw-r--r--src/core/web_contents_delegate_qt.h3
-rw-r--r--src/webengine/api/qquickwebengineview.cpp14
-rw-r--r--src/webengine/api/qquickwebengineview_p_p.h3
-rw-r--r--src/webengine/ui/MessageBubble.qml147
-rw-r--r--src/webengine/ui/ui.pro4
-rw-r--r--src/webengine/ui_delegates_manager.cpp33
-rw-r--r--src/webengine/ui_delegates_manager.h8
-rw-r--r--src/webenginewidgets/api/qwebenginepage.cpp25
-rw-r--r--src/webenginewidgets/api/qwebenginepage_p.h3
-rw-r--r--src/webenginewidgets/api/qwebengineview.cpp8
-rw-r--r--src/webenginewidgets/ui/messagebubblewidget.cpp221
-rw-r--r--src/webenginewidgets/ui/messagebubblewidget_p.h77
-rw-r--r--src/webenginewidgets/webenginewidgets.pro6
15 files changed, 571 insertions, 2 deletions
diff --git a/src/core/web_contents_adapter_client.h b/src/core/web_contents_adapter_client.h
index d67accec2..3ed3ab9ab 100644
--- a/src/core/web_contents_adapter_client.h
+++ b/src/core/web_contents_adapter_client.h
@@ -182,6 +182,9 @@ public:
virtual void runMediaAccessPermissionRequest(const QUrl &securityOrigin, MediaRequestFlags requestFlags) = 0;
virtual void runMouseLockPermissionRequest(const QUrl &securityOrigin) = 0;
virtual WebEngineSettings *webEngineSettings() const = 0;
+ virtual void showValidationMessage(const QRect &anchor, const QString &mainText, const QString &subText) = 0;
+ virtual void hideValidationMessage() = 0;
+ virtual void moveValidationMessage(const QRect &anchor) = 0;
virtual void allowCertificateError(const QSharedPointer<CertificateErrorController> &errorController) = 0;
diff --git a/src/core/web_contents_delegate_qt.cpp b/src/core/web_contents_delegate_qt.cpp
index f69b68af5..e0fb3d7d8 100644
--- a/src/core/web_contents_delegate_qt.cpp
+++ b/src/core/web_contents_delegate_qt.cpp
@@ -354,4 +354,22 @@ void WebContentsDelegateQt::geolocationPermissionReply(const QUrl &origin, bool
}
}
+void WebContentsDelegateQt::ShowValidationMessage(content::WebContents *web_contents, const gfx::Rect &anchor_in_root_view, const base::string16 &main_text, const base::string16 &sub_text)
+{
+ Q_UNUSED(web_contents);
+ m_viewClient->showValidationMessage(toQt(anchor_in_root_view), toQt(main_text), toQt(sub_text));
+}
+
+void WebContentsDelegateQt::HideValidationMessage(content::WebContents *web_contents)
+{
+ Q_UNUSED(web_contents);
+ m_viewClient->hideValidationMessage();
+}
+
+void WebContentsDelegateQt::MoveValidationMessage(content::WebContents *web_contents, const gfx::Rect &anchor_in_root_view)
+{
+ Q_UNUSED(web_contents);
+ m_viewClient->moveValidationMessage(toQt(anchor_in_root_view));
+}
+
} // namespace QtWebEngineCore
diff --git a/src/core/web_contents_delegate_qt.h b/src/core/web_contents_delegate_qt.h
index a200ca4df..254177d24 100644
--- a/src/core/web_contents_delegate_qt.h
+++ b/src/core/web_contents_delegate_qt.h
@@ -86,6 +86,9 @@ public:
virtual void RequestMediaAccessPermission(content::WebContents* web_contents, const content::MediaStreamRequest& request, const content::MediaResponseCallback& callback) Q_DECL_OVERRIDE;
virtual void UpdateTargetURL(content::WebContents* source, const GURL& url) Q_DECL_OVERRIDE;
virtual void RequestToLockMouse(content::WebContents *web_contents, bool user_gesture, bool last_unlocked_by_target) Q_DECL_OVERRIDE;
+ virtual void ShowValidationMessage(content::WebContents *web_contents, const gfx::Rect &anchor_in_root_view, const base::string16 &main_text, const base::string16 &sub_text) Q_DECL_OVERRIDE;
+ virtual void HideValidationMessage(content::WebContents *web_contents) Q_DECL_OVERRIDE;
+ virtual void MoveValidationMessage(content::WebContents *web_contents, const gfx::Rect &anchor_in_root_view) Q_DECL_OVERRIDE;
// WebContentsObserver overrides
virtual void DidStartProvisionalLoadForFrame(content::RenderFrameHost *render_frame_host, const GURL &validated_url, bool is_error_page, bool is_iframe_srcdoc) Q_DECL_OVERRIDE;
diff --git a/src/webengine/api/qquickwebengineview.cpp b/src/webengine/api/qquickwebengineview.cpp
index 4bc6cd087..232d63b30 100644
--- a/src/webengine/api/qquickwebengineview.cpp
+++ b/src/webengine/api/qquickwebengineview.cpp
@@ -766,6 +766,20 @@ void QQuickWebEngineViewPrivate::didFindText(quint64 requestId, int matchCount)
args.append(QJSValue(matchCount));
callback.call(args);
}
+void QQuickWebEngineViewPrivate::showValidationMessage(const QRect &anchor, const QString &mainText, const QString &subText)
+{
+ ui()->showMessageBubble(anchor, mainText, subText);
+}
+
+void QQuickWebEngineViewPrivate::hideValidationMessage()
+{
+ ui()->hideMessageBubble();
+}
+
+void QQuickWebEngineViewPrivate::moveValidationMessage(const QRect &anchor)
+{
+ ui()->moveMessageBubble(anchor);
+}
bool QQuickWebEngineView::isLoading() const
{
diff --git a/src/webengine/api/qquickwebengineview_p_p.h b/src/webengine/api/qquickwebengineview_p_p.h
index 36e590bff..ec8f83468 100644
--- a/src/webengine/api/qquickwebengineview_p_p.h
+++ b/src/webengine/api/qquickwebengineview_p_p.h
@@ -165,6 +165,9 @@ public:
virtual QtWebEngineCore::WebEngineSettings *webEngineSettings() const Q_DECL_OVERRIDE;
virtual void allowCertificateError(const QSharedPointer<CertificateErrorController> &errorController);
virtual void runGeolocationPermissionRequest(QUrl const&) Q_DECL_OVERRIDE;
+ virtual void showValidationMessage(const QRect &anchor, const QString &mainText, const QString &subText) Q_DECL_OVERRIDE;
+ virtual void hideValidationMessage() Q_DECL_OVERRIDE;
+ virtual void moveValidationMessage(const QRect &anchor) Q_DECL_OVERRIDE;
virtual QtWebEngineCore::BrowserContextAdapter *browserContextAdapter() Q_DECL_OVERRIDE;
diff --git a/src/webengine/ui/MessageBubble.qml b/src/webengine/ui/MessageBubble.qml
new file mode 100644
index 000000000..4328eae40
--- /dev/null
+++ b/src/webengine/ui/MessageBubble.qml
@@ -0,0 +1,147 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtWebEngine module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.1
+
+Item {
+ id: bubble
+
+ width: 1
+ height: 1
+
+ property int maxWidth: 0
+ property string mainText: "";
+ property string subText: "";
+
+ property int border: 1
+
+ property int arrowWidth: 18
+ property int arrowHeight: 18
+ property int arrowOffset: 18
+
+ property int marginLeft: border + 8
+ property int marginTop: border + arrowHeight + 6
+ property int marginRight: border + 8
+ property int marginBottom: border + 6
+
+ Column {
+ id: messageColumn
+
+ x: bubble.marginLeft
+ y: bubble.marginTop
+ z: 1
+
+ spacing: 5
+
+ Text {
+ id: message
+ width: bubble.maxWidth
+
+ wrapMode: Text.WordWrap
+ elide: Text.ElideNone
+ clip: true
+
+ font.pointSize: subMessage.font.pointSize + 4
+
+ text: bubble.mainText
+ }
+
+ Text {
+ id: subMessage
+ width: bubble.maxWidth
+
+ wrapMode: Text.WordWrap
+ elide: Text.ElideNone
+ clip: true
+
+ text: bubble.subText
+ }
+ }
+
+ Canvas {
+ id: bubbleCanvas
+
+ property int textWidth: Math.min(bubble.maxWidth, Math.max(message.paintedWidth, subMessage.paintedWidth))
+ property int textHeight: message.paintedHeight + (subMessage.paintedWidth > 0 ? (messageColumn.spacing + subMessage.paintedHeight) : 0)
+
+ width: textWidth + bubble.marginLeft + bubble.marginRight
+ height: textHeight + bubble.marginTop + bubble.marginBottom
+
+ property int cornerRadius: 7
+
+ property int messageBoxLeft: 0
+ property int messageBoxTop: bubble.arrowHeight
+ property int messageBoxRight: width - border
+ property int messageBoxBottom: height - border
+
+ onPaint: {
+ var ctx = getContext("2d")
+
+ ctx.lineWidth = bubble.border
+ ctx.strokeStyle = "#555"
+ ctx.fillStyle = "#ffffe1"
+
+ ctx.beginPath()
+
+ ctx.moveTo(messageBoxLeft + cornerRadius, messageBoxTop)
+
+ // Arrow
+ ctx.lineTo(messageBoxLeft + bubble.arrowOffset, messageBoxTop)
+ ctx.lineTo(messageBoxLeft + bubble.arrowOffset, messageBoxTop - bubble.arrowHeight)
+ ctx.lineTo(messageBoxLeft + bubble.arrowOffset + bubble.arrowWidth, messageBoxTop)
+
+ // Message Box
+ ctx.lineTo(messageBoxRight - cornerRadius, messageBoxTop)
+ ctx.quadraticCurveTo(messageBoxRight, messageBoxTop, messageBoxRight, messageBoxTop + cornerRadius)
+ ctx.lineTo(messageBoxRight, messageBoxBottom - cornerRadius)
+ ctx.quadraticCurveTo(messageBoxRight, messageBoxBottom, messageBoxRight - cornerRadius, messageBoxBottom)
+ ctx.lineTo(messageBoxLeft + cornerRadius, messageBoxBottom)
+ ctx.quadraticCurveTo(messageBoxLeft, messageBoxBottom, messageBoxLeft, messageBoxBottom - cornerRadius)
+ ctx.lineTo(messageBoxLeft, messageBoxTop + cornerRadius)
+ ctx.quadraticCurveTo(messageBoxLeft, messageBoxTop, messageBoxLeft + cornerRadius, messageBoxTop)
+
+ ctx.closePath()
+
+ ctx.fill()
+ ctx.stroke()
+ }
+
+ onPainted: {
+ bubble.width = bubbleCanvas.width
+ bubble.height = bubbleCanvas.height
+ }
+ }
+}
diff --git a/src/webengine/ui/ui.pro b/src/webengine/ui/ui.pro
index a5ae648cb..fdf7a85bc 100644
--- a/src/webengine/ui/ui.pro
+++ b/src/webengine/ui/ui.pro
@@ -9,6 +9,8 @@ QML_FILES += \
# Menus. Based on Qt Quick Controls
Menu.qml \
MenuItem.qml \
- MenuSeparator.qml
+ MenuSeparator.qml \
+ # Message Bubble
+ MessageBubble.qml
load(qml_module)
diff --git a/src/webengine/ui_delegates_manager.cpp b/src/webengine/ui_delegates_manager.cpp
index 615c6024d..d22e6546f 100644
--- a/src/webengine/ui_delegates_manager.cpp
+++ b/src/webengine/ui_delegates_manager.cpp
@@ -138,6 +138,7 @@ void NavigateMenuItem::onTriggered()
UIDelegatesManager::UIDelegatesManager(QQuickWebEngineView *view)
: m_view(view)
+ , m_messageBubbleItem(0)
FOR_EACH_COMPONENT_TYPE(COMPONENT_MEMBER_INIT, NO_SEPARATOR)
{
}
@@ -430,6 +431,38 @@ void UIDelegatesManager::showFilePicker(WebContentsAdapterClient::FileChooserMod
QMetaObject::invokeMethod(filePicker, "open");
}
+void UIDelegatesManager::showMessageBubble(const QRect &anchor, const QString &mainText, const QString &subText)
+{
+ if (!ensureComponentLoaded(MessageBubble))
+ return;
+
+ Q_ASSERT(m_messageBubbleItem.isNull());
+
+ QQmlContext *context = qmlContext(m_view);
+ m_messageBubbleItem.reset(qobject_cast<QQuickItem *>(messageBubbleComponent->beginCreate(context)));
+ m_messageBubbleItem->setParentItem(m_view);
+ messageBubbleComponent->completeCreate();
+
+ QQmlProperty(m_messageBubbleItem.data(), QStringLiteral("maxWidth")).write(anchor.size().width());
+ QQmlProperty(m_messageBubbleItem.data(), QStringLiteral("mainText")).write(mainText);
+ QQmlProperty(m_messageBubbleItem.data(), QStringLiteral("subText")).write(subText);
+ QQmlProperty(m_messageBubbleItem.data(), QStringLiteral("x")).write(anchor.x());
+ QQmlProperty(m_messageBubbleItem.data(), QStringLiteral("y")).write(anchor.y() + anchor.size().height());
+}
+
+void UIDelegatesManager::hideMessageBubble()
+{
+ m_messageBubbleItem.reset();
+}
+
+void UIDelegatesManager::moveMessageBubble(const QRect &anchor)
+{
+ Q_ASSERT(!m_messageBubbleItem.isNull());
+
+ QQmlProperty(m_messageBubbleItem.data(), QStringLiteral("x")).write(anchor.x());
+ QQmlProperty(m_messageBubbleItem.data(), QStringLiteral("y")).write(anchor.y() + anchor.size().height());
+}
+
} // namespace QtWebEngineCore
#include "ui_delegates_manager.moc"
diff --git a/src/webengine/ui_delegates_manager.h b/src/webengine/ui_delegates_manager.h
index b43006381..f2b78f792 100644
--- a/src/webengine/ui_delegates_manager.h
+++ b/src/webengine/ui_delegates_manager.h
@@ -54,7 +54,8 @@
F(AlertDialog, alertDialog) SEPARATOR \
F(ConfirmDialog, confirmDialog) SEPARATOR \
F(PromptDialog, promptDialog) SEPARATOR \
- F(FilePicker, filePicker) SEPARATOR
+ F(FilePicker, filePicker) SEPARATOR \
+ F(MessageBubble, messageBubble) SEPARATOR
#define COMMA_SEPARATOR ,
#define SEMICOLON_SEPARATOR ;
@@ -66,6 +67,7 @@
QT_BEGIN_NAMESPACE
class QObject;
class QQmlContext;
+class QQuickItem;
class QQuickWebEngineView;
QT_END_NAMESPACE
@@ -124,11 +126,15 @@ public:
void showDialog(QSharedPointer<JavaScriptDialogController>);
void showFilePicker(WebContentsAdapterClient::FileChooserMode, const QString &defaultFileName, const QStringList &acceptedMimeTypes
, const QExplicitlySharedDataPointer<WebContentsAdapter> &);
+ void showMessageBubble(const QRect &anchor, const QString &mainText, const QString &subText);
+ void hideMessageBubble();
+ void moveMessageBubble(const QRect &anchor);
private:
bool ensureComponentLoaded(ComponentType);
QQuickWebEngineView *m_view;
+ QScopedPointer<QQuickItem> m_messageBubbleItem;
FOR_EACH_COMPONENT_TYPE(MEMBER_DECLARATION, SEMICOLON_SEPARATOR)
diff --git a/src/webenginewidgets/api/qwebenginepage.cpp b/src/webenginewidgets/api/qwebenginepage.cpp
index 7eab1dc71..65a0a5bed 100644
--- a/src/webenginewidgets/api/qwebenginepage.cpp
+++ b/src/webenginewidgets/api/qwebenginepage.cpp
@@ -38,6 +38,10 @@
#include "web_contents_adapter.h"
#include "web_engine_settings.h"
+#ifdef QT_UI_DELEGATES
+#include "ui/messagebubblewidget_p.h"
+#endif
+
#include <QAction>
#include <QApplication>
#include <QAuthenticator>
@@ -778,6 +782,27 @@ void QWebEnginePagePrivate::javaScriptConsoleMessage(JavaScriptConsoleMessageLev
q->javaScriptConsoleMessage(static_cast<QWebEnginePage::JavaScriptConsoleMessageLevel>(level), message, lineNumber, sourceID);
}
+void QWebEnginePagePrivate::showValidationMessage(const QRect &anchor, const QString &mainText, const QString &subText)
+{
+#ifdef QT_UI_DELEGATES
+ QtWebEngineWidgetUI::MessageBubbleWidget::showBubble(view, anchor, mainText, subText);
+#endif
+}
+
+void QWebEnginePagePrivate::hideValidationMessage()
+{
+#ifdef QT_UI_DELEGATES
+ QtWebEngineWidgetUI::MessageBubbleWidget::hideBubble();
+#endif
+}
+
+void QWebEnginePagePrivate::moveValidationMessage(const QRect &anchor)
+{
+#ifdef QT_UI_DELEGATES
+ QtWebEngineWidgetUI::MessageBubbleWidget::moveBubble(view, anchor);
+#endif
+}
+
namespace {
class SaveToClipboardFunctor
{
diff --git a/src/webenginewidgets/api/qwebenginepage_p.h b/src/webenginewidgets/api/qwebenginepage_p.h
index 087137a78..8f45ecddf 100644
--- a/src/webenginewidgets/api/qwebenginepage_p.h
+++ b/src/webenginewidgets/api/qwebenginepage_p.h
@@ -147,6 +147,9 @@ public:
#endif // QT_NO_ACCESSIBILITY
virtual QtWebEngineCore::WebEngineSettings *webEngineSettings() const Q_DECL_OVERRIDE;
virtual void allowCertificateError(const QSharedPointer<CertificateErrorController> &controller) Q_DECL_OVERRIDE;
+ virtual void showValidationMessage(const QRect &anchor, const QString &mainText, const QString &subText) Q_DECL_OVERRIDE;
+ virtual void hideValidationMessage() Q_DECL_OVERRIDE;
+ virtual void moveValidationMessage(const QRect &anchor) Q_DECL_OVERRIDE;
virtual QtWebEngineCore::BrowserContextAdapter *browserContextAdapter() Q_DECL_OVERRIDE;
diff --git a/src/webenginewidgets/api/qwebengineview.cpp b/src/webenginewidgets/api/qwebengineview.cpp
index 039e18a16..a4a8dd760 100644
--- a/src/webenginewidgets/api/qwebengineview.cpp
+++ b/src/webenginewidgets/api/qwebengineview.cpp
@@ -40,6 +40,10 @@
#include "qwebenginepage_p.h"
#include "web_contents_adapter.h"
+#ifdef QT_UI_DELEGATES
+#include "ui/messagebubblewidget_p.h"
+#endif
+
#include <QAction>
#include <QMenu>
#include <QContextMenuEvent>
@@ -118,6 +122,10 @@ QWebEngineView::~QWebEngineView()
{
Q_D(QWebEngineView);
QWebEngineViewPrivate::bind(0, d->page);
+
+#ifdef QT_UI_DELEGATES
+ QtWebEngineWidgetUI::MessageBubbleWidget::hideBubble();
+#endif
}
QWebEnginePage* QWebEngineView::page() const
diff --git a/src/webenginewidgets/ui/messagebubblewidget.cpp b/src/webenginewidgets/ui/messagebubblewidget.cpp
new file mode 100644
index 000000000..3465f4218
--- /dev/null
+++ b/src/webenginewidgets/ui/messagebubblewidget.cpp
@@ -0,0 +1,221 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtWebEngine module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "messagebubblewidget_p.h"
+
+#include "api/qwebengineview.h"
+
+#include <QBitmap>
+#include <QHBoxLayout>
+#include <QIcon>
+#include <QLabel>
+#include <QStyle>
+
+namespace QtWebEngineWidgetUI {
+
+Q_GLOBAL_STATIC(MessageBubbleWidget, bubbleInstance)
+
+void MessageBubbleWidget::showBubble(QWebEngineView *view, const QRect &anchor, const QString &mainText, const QString &subText)
+{
+ hideBubble();
+ if (mainText.isEmpty())
+ return;
+
+ bubbleInstance->createBubble(anchor.size().width(), mainText, subText);
+ bubbleInstance->moveToAnchor(view, anchor);
+}
+
+void MessageBubbleWidget::hideBubble()
+{
+ bubbleInstance->hide();
+}
+
+void MessageBubbleWidget::moveBubble(QWebEngineView *view, const QRect &anchor)
+{
+ bubbleInstance->moveToAnchor(view, anchor);
+}
+
+MessageBubbleWidget::MessageBubbleWidget()
+ : QWidget(0, Qt::ToolTip)
+ , m_mainLabel(new QLabel)
+ , m_subLabel(new QLabel)
+{
+ QHBoxLayout *hLayout = new QHBoxLayout;
+ hLayout->setAlignment(Qt::AlignTop);
+ hLayout->setSizeConstraint(QLayout::SetFixedSize);
+ hLayout->setMargin(3);
+
+ const int iconSize = 18;
+ QIcon si = style()->standardIcon(QStyle::SP_MessageBoxWarning);
+
+ if (!si.isNull()) {
+ QLabel *iconLabel = new QLabel(this);
+ iconLabel->setPixmap(si.pixmap(iconSize, iconSize));
+ iconLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+ iconLabel->setMargin(2);
+ hLayout->addWidget(iconLabel, 0, Qt::AlignTop);
+ }
+
+ QVBoxLayout *vLayout = new QVBoxLayout;
+
+ m_mainLabel->installEventFilter(this);
+ m_mainLabel->setWordWrap(true);
+ m_mainLabel->setTextFormat(Qt::PlainText);
+ m_mainLabel->setAlignment(Qt::AlignTop | Qt::AlignLeft);
+ vLayout->addWidget(m_mainLabel.data());
+
+ QFont mainFont = m_mainLabel->font();
+ mainFont.setPointSize(mainFont.pointSize() + 4);
+ m_mainLabel->setFont(mainFont);
+
+ m_subLabel->installEventFilter(this);
+ m_subLabel->setWordWrap(true);
+ m_subLabel->setTextFormat(Qt::PlainText);
+ m_subLabel->setAlignment(Qt::AlignBottom | Qt::AlignLeft);
+ vLayout->addWidget(m_subLabel.data());
+
+ hLayout->addLayout(vLayout);
+ setLayout(hLayout);
+
+ QPalette pal = palette();
+ pal.setColor(QPalette::Window, QColor(0xff, 0xff, 0xe1));
+ pal.setColor(QPalette::WindowText, Qt::black);
+ setPalette(pal);
+}
+
+MessageBubbleWidget::~MessageBubbleWidget()
+{
+}
+
+void MessageBubbleWidget::paintEvent(QPaintEvent *)
+{
+ QPainter painter(this);
+ painter.drawPixmap(rect(), m_pixmap);
+}
+
+void MessageBubbleWidget::createBubble(const int maxWidth, const QString &mainText, const QString &subText)
+{
+ m_mainLabel->setText(mainText);
+ m_mainLabel->setMaximumWidth(maxWidth);
+
+ m_subLabel->setText(subText);
+ m_subLabel->setMaximumWidth(maxWidth);
+ m_subLabel->setVisible(!subText.isEmpty());
+
+ int border = 1;
+ int arrowHeight = 18;
+ bool roundedCorners = true;
+
+#if defined(QT_NO_XSHAPE) && defined(Q_WS_X11)
+ // XShape is required for setting the mask, so we just
+ // draw an ugly square when its not available
+ arrowHeight = 0;
+ roundedCorners = false;
+#endif
+
+ setContentsMargins(border + 3, border + arrowHeight + 2, border + 3, border + 2);
+ show(); // The widget should be visible for updateGeometry()
+ updateGeometry();
+ m_pixmap = QPixmap(sizeHint());
+
+ QPainterPath path = drawBoxPath(QPoint(0, arrowHeight), border, roundedCorners);
+
+ // Draw border and set background
+ QPainter painter(&m_pixmap);
+ painter.setPen(QPen(palette().color(QPalette::Window).darker(160), border));
+ painter.setBrush(palette().color(QPalette::Window));
+ painter.drawPath(path);
+}
+
+void MessageBubbleWidget::moveToAnchor(QWebEngineView *view, const QRect &anchor)
+{
+ QPoint topLeft = view->mapToGlobal(anchor.topLeft());
+ move(topLeft.x(), topLeft.y() + anchor.height());
+}
+
+QPainterPath MessageBubbleWidget::drawBoxPath(const QPoint &pos, int border, bool roundedCorners)
+{
+ const int arrowHeight = pos.y();
+ const int arrowOffset = 18;
+ const int arrowWidth = 18;
+
+ const int cornerRadius = roundedCorners ? 7 : 0;
+
+ const int messageBoxLeft = pos.x();
+ const int messageBoxTop = arrowHeight;
+ const int messageBoxRight = m_pixmap.width() - 1;
+ const int messageBoxBottom = m_pixmap.height() - 1;
+
+ QPainterPath path;
+ path.moveTo(messageBoxLeft + cornerRadius, messageBoxTop);
+
+ if (arrowHeight) {
+ path.lineTo(messageBoxLeft + arrowOffset, messageBoxTop);
+ path.lineTo(messageBoxLeft + arrowOffset, messageBoxTop - arrowHeight);
+ path.lineTo(messageBoxLeft + arrowOffset + arrowWidth, messageBoxTop);
+ }
+
+ if (roundedCorners) {
+ path.lineTo(messageBoxRight - cornerRadius, messageBoxTop);
+ path.quadTo(messageBoxRight, messageBoxTop, messageBoxRight, messageBoxTop + cornerRadius);
+ path.lineTo(messageBoxRight, messageBoxBottom - cornerRadius);
+ path.quadTo(messageBoxRight, messageBoxBottom, messageBoxRight - cornerRadius, messageBoxBottom);
+ path.lineTo(messageBoxLeft + cornerRadius, messageBoxBottom);
+ path.quadTo(messageBoxLeft, messageBoxBottom, messageBoxLeft, messageBoxBottom - cornerRadius);
+ path.lineTo(messageBoxLeft, messageBoxTop + cornerRadius);
+ path.quadTo(messageBoxLeft, messageBoxTop, messageBoxLeft + cornerRadius, messageBoxTop);
+ } else {
+ path.lineTo(messageBoxRight, messageBoxTop);
+ path.lineTo(messageBoxRight, messageBoxBottom);
+ path.lineTo(messageBoxLeft, messageBoxBottom);
+ path.moveTo(messageBoxLeft, messageBoxTop);
+ }
+
+ // Set mask
+ if (arrowHeight || roundedCorners) {
+ QBitmap bitmap = QBitmap(sizeHint());
+ bitmap.fill(Qt::color0);
+ QPainter painter(&bitmap);
+ painter.setPen(QPen(Qt::color1, border));
+ painter.setBrush(QBrush(Qt::color1));
+ painter.drawPath(path);
+ setMask(bitmap);
+ }
+
+ return path;
+}
+
+} // namespace QtWebEngineWidgetUI
diff --git a/src/webenginewidgets/ui/messagebubblewidget_p.h b/src/webenginewidgets/ui/messagebubblewidget_p.h
new file mode 100644
index 000000000..7b7a9e309
--- /dev/null
+++ b/src/webenginewidgets/ui/messagebubblewidget_p.h
@@ -0,0 +1,77 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtWebEngine module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef MESSAGEBUBBLEWIDGET_P_H
+#define MESSAGEBUBBLEWIDGET_P_H
+
+#include <QWidget>
+#include <QPainterPath>
+
+QT_BEGIN_NAMESPACE
+class QLabel;
+class QWebEngineView;
+QT_END_NAMESPACE
+
+namespace QtWebEngineWidgetUI {
+
+class MessageBubbleWidget : public QWidget
+{
+ Q_OBJECT
+public:
+ MessageBubbleWidget();
+ ~MessageBubbleWidget();
+
+ static void showBubble(QWebEngineView *view, const QRect &anchor, const QString &mainText, const QString &subText = QString());
+ static void hideBubble();
+ static void moveBubble(QWebEngineView *view, const QRect &anchor);
+
+protected:
+ void paintEvent(QPaintEvent *) Q_DECL_OVERRIDE;
+
+private:
+ void createBubble(const int maxWidth, const QString &mainText, const QString &subText);
+ void moveToAnchor(QWebEngineView *view, const QRect &anchor);
+
+ QPainterPath drawBoxPath(const QPoint &pos, int border, bool roundedCorners);
+
+ QScopedPointer<QLabel> m_mainLabel;
+ QScopedPointer<QLabel> m_subLabel;
+ QPixmap m_pixmap;
+};
+
+} // namespace QtWebEngineWidgetUI
+
+#endif // MESSAGEBUBBLEWIDGET_P_H
diff --git a/src/webenginewidgets/webenginewidgets.pro b/src/webenginewidgets/webenginewidgets.pro
index 02e687c7c..1f7974bd2 100644
--- a/src/webenginewidgets/webenginewidgets.pro
+++ b/src/webenginewidgets/webenginewidgets.pro
@@ -45,4 +45,10 @@ HEADERS = \
api/qwebengineview_p.h \
render_widget_host_view_qt_delegate_widget.h
+!contains(WEBENGINE_CONFIG, no_ui_delegates) {
+ SOURCES += ui/messagebubblewidget.cpp
+ HEADERS += ui/messagebubblewidget_p.h
+ DEFINES += QT_UI_DELEGATES
+}
+
load(qt_module)