diff options
author | Peter Varga <pvarga@inf.u-szeged.hu> | 2018-06-07 09:58:31 +0200 |
---|---|---|
committer | Peter Varga <pvarga@inf.u-szeged.hu> | 2019-01-31 07:51:32 +0000 |
commit | 713cfd8a684b2fd4e1d4a74d19415c36c370adbb (patch) | |
tree | 72b4da49e1dd4d2f5176bd97d8c97a8ec4336fb2 | |
parent | 94d2cba36b8a1c3e02ca2abf16b11963fd1801d5 (diff) |
Implement touch text selection for QQuickWebEngineView
Touch handle and touch selection menu are not implemented for widget.
Task-number: QTBUG-59999
Change-Id: Ia492e58b83d3ad38cdf6877d468724c399f34367
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
24 files changed, 1684 insertions, 6 deletions
diff --git a/src/core/core_chromium.pri b/src/core/core_chromium.pri index 65ca793f9..3918dc0aa 100644 --- a/src/core/core_chromium.pri +++ b/src/core/core_chromium.pri @@ -117,6 +117,9 @@ SOURCES = \ resource_bundle_qt.cpp \ resource_context_qt.cpp \ service/service_qt.cpp \ + touch_handle_drawable_qt.cpp \ + touch_selection_controller_client_qt.cpp \ + touch_selection_menu_controller.cpp \ type_conversion.cpp \ user_script.cpp \ visited_links_manager_qt.cpp \ @@ -218,6 +221,10 @@ HEADERS = \ request_controller.h \ resource_context_qt.h \ service/service_qt.h \ + touch_handle_drawable_client.h \ + touch_handle_drawable_qt.h \ + touch_selection_controller_client_qt.h \ + touch_selection_menu_controller.h \ type_conversion.h \ user_script.h \ visited_links_manager_qt.h \ diff --git a/src/core/render_widget_host_view_qt.cpp b/src/core/render_widget_host_view_qt.cpp index 66276ea6c..09b6a3eca 100644 --- a/src/core/render_widget_host_view_qt.cpp +++ b/src/core/render_widget_host_view_qt.cpp @@ -44,6 +44,9 @@ #include "compositor/compositor.h" #include "qtwebenginecoreglobal_p.h" #include "render_widget_host_view_qt_delegate.h" +#include "touch_handle_drawable_client.h" +#include "touch_selection_controller_client_qt.h" +#include "touch_selection_menu_controller.h" #include "type_conversion.h" #include "web_contents_adapter_client.h" #include "web_event_factory.h" @@ -61,10 +64,13 @@ #include "third_party/blink/public/platform/web_cursor_info.h" #include "ui/events/blink/blink_event_util.h" #include "ui/events/event.h" +#include "ui/events/gesture_detection/gesture_configuration.h" #include "ui/events/gesture_detection/gesture_provider_config_helper.h" #include "ui/events/gesture_detection/motion_event.h" #include "ui/gfx/geometry/size_conversions.h" #include "ui/gfx/image/image_skia.h" +#include "ui/touch_selection/touch_selection_controller.h" + #if defined(USE_OZONE) #include "ui/base/clipboard/scoped_clipboard_writer.h" #endif @@ -278,6 +284,13 @@ RenderWidgetHostViewQt::RenderWidgetHostViewQt(content::RenderWidgetHost *widget if (host()->delegate() && host()->delegate()->GetInputEventRouter()) host()->delegate()->GetInputEventRouter()->AddFrameSinkIdOwner(GetFrameSinkId(), this); + + m_touchSelectionControllerClient.reset(new TouchSelectionControllerClientQt(this)); + ui::TouchSelectionController::Config config; + config.max_tap_duration = base::TimeDelta::FromMilliseconds(ui::GestureConfiguration::GetInstance()->long_press_time_in_ms()); + config.tap_slop = ui::GestureConfiguration::GetInstance()->max_touch_move_in_pixels_for_click(); + config.enable_longpress_drag_selection = false; + m_touchSelectionController.reset(new ui::TouchSelectionController(m_touchSelectionControllerClient.get(), config)); } RenderWidgetHostViewQt::~RenderWidgetHostViewQt() @@ -286,6 +299,9 @@ RenderWidgetHostViewQt::~RenderWidgetHostViewQt() if (text_input_manager_) text_input_manager_->RemoveObserver(this); + + m_touchSelectionController.reset(); + m_touchSelectionControllerClient.reset(); } void RenderWidgetHostViewQt::setDelegate(RenderWidgetHostViewQtDelegate* delegate) @@ -880,7 +896,33 @@ void RenderWidgetHostViewQt::OnGestureEvent(const ui::GestureEventData& gesture) return; } - host()->ForwardGestureEvent(ui::CreateWebGestureEventFromGestureEventData(gesture)); + blink::WebGestureEvent event = ui::CreateWebGestureEventFromGestureEventData(gesture); + + if (m_touchSelectionController && m_touchSelectionControllerClient) { + switch (event.GetType()) { + case blink::WebInputEvent::kGestureLongPress: + m_touchSelectionController->HandleLongPressEvent(event.TimeStamp(), event.PositionInWidget()); + break; + case blink::WebInputEvent::kGestureTap: + m_touchSelectionController->HandleTapEvent(event.PositionInWidget(), event.data.tap.tap_count); + break; + case blink::WebInputEvent::kGestureScrollBegin: + m_touchSelectionControllerClient->onScrollBegin(); + break; + case blink::WebInputEvent::kGestureScrollEnd: + m_touchSelectionControllerClient->onScrollEnd(); + break; + default: + break; + } + } + + host()->ForwardGestureEvent(event); +} + +void RenderWidgetHostViewQt::DidStopFlinging() +{ + m_touchSelectionControllerClient->DidStopFlinging(); } viz::ScopedSurfaceIdAllocator RenderWidgetHostViewQt::DidUpdateVisualProperties(const cc::RenderFrameMetadata &metadata) @@ -1291,9 +1333,9 @@ void RenderWidgetHostViewQt::handleInputMethodEvent(QInputMethodEvent *ev) } if (hasSelection) { - content::RenderFrameHostImpl *frameHost = static_cast<content::RenderFrameHostImpl *>(getFocusedFrameHost()); - if (frameHost) - frameHost->GetFrameInputHandler()->SetEditableSelectionOffsets(selectionRange.start(), selectionRange.end()); + content::mojom::FrameInputHandler *frameInputHandler = getFrameInputHandler(); + if (frameInputHandler) + frameInputHandler->SetEditableSelectionOffsets(selectionRange.start(), selectionRange.end()); } int replacementLength = ev->replacementLength(); @@ -1451,11 +1493,35 @@ void RenderWidgetHostViewQt::handleTouchEvent(QTouchEvent *ev) eventTimestamp += m_eventsToNowDelta; QList<QTouchEvent::TouchPoint> touchPoints = mapTouchPointIds(ev->touchPoints()); + { + ui::MotionEvent::Action action; + switch (touchPoints[0].state()) { + case Qt::TouchPointPressed: + action = ui::MotionEvent::Action::DOWN; + break; + case Qt::TouchPointMoved: + action = ui::MotionEvent::Action::MOVE; + break; + case Qt::TouchPointReleased: + action = ui::MotionEvent::Action::UP; + break; + default: + action = ui::MotionEvent::Action::NONE; + break; + } + + MotionEventQt motionEvent(touchPoints, eventTimestamp, action, ev->modifiers(), dpiScale(), 0); + if (m_touchSelectionController->WillHandleTouchEvent(motionEvent)) { + ev->accept(); + return; + } + } switch (ev->type()) { case QEvent::TouchBegin: m_sendMotionActionDown = true; m_touchMotionStarted = true; + m_touchSelectionControllerClient->onTouchDown(); break; case QEvent::TouchUpdate: m_touchMotionStarted = true; @@ -1482,6 +1548,7 @@ void RenderWidgetHostViewQt::handleTouchEvent(QTouchEvent *ev) } case QEvent::TouchEnd: clearPreviousTouchMotionState(); + m_touchSelectionControllerClient->onTouchUp(); break; default: break; @@ -1632,6 +1699,15 @@ content::RenderFrameHost *RenderWidgetHostViewQt::getFocusedFrameHost() return focusedFrame->current_frame_host(); } +content::mojom::FrameInputHandler *RenderWidgetHostViewQt::getFrameInputHandler() +{ + content::RenderFrameHostImpl *frameHost = static_cast<content::RenderFrameHostImpl *>(getFocusedFrameHost()); + if (!frameHost) + return nullptr; + + return frameHost->GetFrameInputHandler(); +} + ui::TextInputType RenderWidgetHostViewQt::getTextInputType() const { if (text_input_manager_ && text_input_manager_->GetTextInputState()) @@ -1685,4 +1761,16 @@ void RenderWidgetHostViewQt::ResetFallbackToFirstNavigationSurface() Q_UNIMPLEMENTED(); } +void RenderWidgetHostViewQt::OnRenderFrameMetadataChangedAfterActivation() +{ + content::RenderWidgetHostViewBase::OnRenderFrameMetadataChangedAfterActivation(); + + const cc::RenderFrameMetadata &metadata = host()->render_frame_metadata_provider()->LastRenderFrameMetadata(); + if (metadata.selection.start != m_selectionStart || metadata.selection.end != m_selectionEnd) { + m_selectionStart = metadata.selection.start; + m_selectionEnd = metadata.selection.end; + m_touchSelectionControllerClient->UpdateClientSelectionBounds(m_selectionStart, m_selectionEnd); + } +} + } // namespace QtWebEngineCore diff --git a/src/core/render_widget_host_view_qt.h b/src/core/render_widget_host_view_qt.h index 30a1550e9..4c63aaea7 100644 --- a/src/core/render_widget_host_view_qt.h +++ b/src/core/render_widget_host_view_qt.h @@ -65,11 +65,21 @@ QT_END_NAMESPACE namespace content { class RenderFrameHost; class RenderWidgetHostImpl; +namespace mojom { +class FrameInputHandler; +} +} + +namespace ui { +class TouchSelectionController; } namespace QtWebEngineCore { class Compositor; +class TouchHandleDrawableClient; +class TouchSelectionControllerClientQt; +class TouchSelectionMenuController; struct MultipleMouseClickHelper { @@ -106,6 +116,7 @@ public: RenderWidgetHostViewQtDelegate *delegate() { return m_delegate.get(); } void setDelegate(RenderWidgetHostViewQtDelegate *delegate); + WebContentsAdapterClient *adapterClient() { return m_adapterClient; } void setAdapterClient(WebContentsAdapterClient *adapterClient); void InitAsChild(gfx::NativeView) override; @@ -158,6 +169,7 @@ public: void EnsureSurfaceSynchronizedForLayoutTest() override; uint32_t GetCaptureSequenceNumber() const override; void ResetFallbackToFirstNavigationSurface() override; + void DidStopFlinging() override; // Overridden from ui::GestureProviderClient. void OnGestureEvent(const ui::GestureEventData& gesture) override; @@ -207,9 +219,17 @@ public: LoadVisuallyCommittedState getLoadVisuallyCommittedState() const { return m_loadVisuallyCommittedState; } void setLoadVisuallyCommittedState(LoadVisuallyCommittedState state) { m_loadVisuallyCommittedState = state; } + // Overridden from content::RenderFrameMetadataProvider::Observer + void OnRenderFrameMetadataChangedAfterActivation() override; + gfx::SizeF lastContentsSize() const { return m_lastContentsSize; } gfx::Vector2dF lastScrollOffset() const { return m_lastScrollOffset; } + ui::TouchSelectionController *getTouchSelectionController() const { return m_touchSelectionController.get(); } + TouchSelectionControllerClientQt *getTouchSelectionControllerClient() const { return m_touchSelectionControllerClient.get(); } + content::mojom::FrameInputHandler *getFrameInputHandler(); + ui::TextInputType getTextInputType() const; + private: void processMotionEvent(const ui::MotionEvent &motionEvent); void clearPreviousTouchMotionState(); @@ -221,7 +241,6 @@ private: void selectionChanged(); content::RenderFrameHost *getFocusedFrameHost(); - ui::TextInputType getTextInputType() const; ui::FilteredGestureProvider m_gestureProvider; base::TimeDelta m_eventsToNowDelta; @@ -264,6 +283,11 @@ private: uint32_t m_latestCaptureSequenceNumber = 0u; std::string m_editCommand; + + std::unique_ptr<TouchSelectionControllerClientQt> m_touchSelectionControllerClient; + std::unique_ptr<ui::TouchSelectionController> m_touchSelectionController; + gfx::SelectionBound m_selectionStart; + gfx::SelectionBound m_selectionEnd; }; } // namespace QtWebEngineCore diff --git a/src/core/touch_handle_drawable_client.h b/src/core/touch_handle_drawable_client.h new file mode 100644 index 000000000..42d907d75 --- /dev/null +++ b/src/core/touch_handle_drawable_client.h @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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.LGPL3 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-3.0.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 (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef TOUCH_HANDLE_DRAWABLE_CLIENT_H +#define TOUCH_HANDLE_DRAWABLE_CLIENT_H + +#include "qtwebenginecoreglobal_p.h" +#include <QRect> + +namespace QtWebEngineCore { + +class QWEBENGINECORE_PRIVATE_EXPORT TouchHandleDrawableClient { +public: + virtual ~TouchHandleDrawableClient() { } + + virtual void setImage(int orientation) = 0; + virtual void setBounds(const QRect &bounds) = 0; + virtual void setVisible(bool visible) = 0; + virtual void setOpacity(float opacity) = 0; +}; + +} // namespace QtWebEngineCore + +#endif // TOUCH_HANDLE_DRAWABLE_CLIENT_H diff --git a/src/core/touch_handle_drawable_qt.cpp b/src/core/touch_handle_drawable_qt.cpp new file mode 100644 index 000000000..66b1cf40e --- /dev/null +++ b/src/core/touch_handle_drawable_qt.cpp @@ -0,0 +1,211 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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.LGPL3 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-3.0.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 (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE.Chromium file. + +// This implementation is based on chromium/ui/touch_selection/touch_handle_drawable_aura.cc + +#include "render_widget_host_view_qt.h" +#include "touch_handle_drawable_client.h" +#include "touch_handle_drawable_qt.h" +#include "type_conversion.h" +#include "web_contents_adapter_client.h" + +#include "ui/gfx/image/image.h" +#include "ui/base/resource/resource_bundle.h" +#include "ui/resources/grit/ui_resources.h" + +namespace QtWebEngineCore { + +namespace { +// The distance by which a handle image is offset from the focal point (i.e. +// text baseline) downwards. +const int kSelectionHandleVerticalVisualOffset = 2; + +// The padding around the selection handle image can be used to extend the +// handle window so that touch events near the selection handle image are +// targeted to the selection handle window. +const int kSelectionHandlePadding = 0; + +// Epsilon value used to compare float values to zero. +const float kEpsilon = 1e-8f; + +// Returns the appropriate handle image based on the handle orientation. +gfx::Image* GetHandleImage(ui::TouchHandleOrientation orientation) +{ + int resource_id = 0; + switch (orientation) { + case ui::TouchHandleOrientation::LEFT: + resource_id = IDR_TEXT_SELECTION_HANDLE_LEFT; + break; + case ui::TouchHandleOrientation::CENTER: + resource_id = IDR_TEXT_SELECTION_HANDLE_CENTER; + break; + case ui::TouchHandleOrientation::RIGHT: + resource_id = IDR_TEXT_SELECTION_HANDLE_RIGHT; + break; + case ui::TouchHandleOrientation::UNDEFINED: + NOTREACHED() << "Invalid touch handle bound type."; + return nullptr; + }; + return &ui::ResourceBundle::GetSharedInstance().GetImageNamed(resource_id); +} + +bool IsNearlyZero(float value) +{ + return std::abs(value) < kEpsilon; +} + +} // namespace + +TouchHandleDrawableQt::TouchHandleDrawableQt(RenderWidgetHostViewQt *rwhv) + : m_rwhv(rwhv) + , m_enabled(false) + , m_alpha(0) + , m_orientation(ui::TouchHandleOrientation::UNDEFINED) +{ + QMap<int, QImage> images; + for (int orientation = 0; orientation < static_cast<int>(ui::TouchHandleOrientation::UNDEFINED); ++orientation) { + gfx::Image* image = GetHandleImage(static_cast<ui::TouchHandleOrientation>(orientation)); + images.insert(orientation, toQImage(image->AsBitmap())); + } + + Q_ASSERT(m_rwhv); + Q_ASSERT(m_rwhv->adapterClient()); + m_client.reset(m_rwhv->adapterClient()->createTouchHandle(images)); +} + +TouchHandleDrawableQt::~TouchHandleDrawableQt() +{ +} + +void TouchHandleDrawableQt::UpdateBounds() +{ + if (!m_client) + return; + + gfx::RectF newBounds = m_relativeBounds; + newBounds.Offset(m_originPosition.x(), m_originPosition.y()); + m_client->setBounds(toQt(gfx::ToEnclosingRect(newBounds))); +} + +bool TouchHandleDrawableQt::IsVisible() const +{ + return m_enabled && !IsNearlyZero(m_alpha); +} + +void TouchHandleDrawableQt::SetEnabled(bool enabled) +{ + if (!m_client) + return; + + if (enabled == m_enabled) + return; + + m_enabled = enabled; + m_client->setVisible(enabled); +} + +void TouchHandleDrawableQt::SetOrientation(ui::TouchHandleOrientation orientation, bool mirror_vertical, bool mirror_horizontal) +{ + if (!m_client) + return; + + // TODO: Implement adaptive handle orientation logic + DCHECK(!mirror_vertical); + DCHECK(!mirror_horizontal); + + if (m_orientation == orientation) + return; + m_orientation = orientation; + gfx::Image* image = GetHandleImage(orientation); + m_client->setImage(static_cast<int>(orientation)); + + // Calculate the relative bounds. + gfx::Size image_size = image->Size(); + int window_width = image_size.width() + 2 * kSelectionHandlePadding; + int window_height = image_size.height() + 2 * kSelectionHandlePadding; + m_relativeBounds = + gfx::RectF(-kSelectionHandlePadding, + kSelectionHandleVerticalVisualOffset - kSelectionHandlePadding, + window_width, window_height); + UpdateBounds(); +} + +void TouchHandleDrawableQt::SetOrigin(const gfx::PointF& position) +{ + m_originPosition = position; + UpdateBounds(); +} + +void TouchHandleDrawableQt::SetAlpha(float alpha) +{ + if (!m_client) + return; + + if (alpha == m_alpha) + return; + + m_alpha = alpha; + m_client->setOpacity(m_alpha); + m_client->setVisible(IsVisible()); +} + +gfx::RectF TouchHandleDrawableQt::GetVisibleBounds() const +{ + gfx::RectF bounds = m_relativeBounds; + bounds.Offset(m_originPosition.x(), m_originPosition.y()); + + gfx::RectF visibleBounds(bounds); + visibleBounds.Inset(kSelectionHandlePadding, + kSelectionHandlePadding + kSelectionHandleVerticalVisualOffset, + kSelectionHandlePadding, + kSelectionHandlePadding); + return visibleBounds; +} + +float TouchHandleDrawableQt::GetDrawableHorizontalPaddingRatio() const +{ + // Qt does not have any transparent padding for its handle drawable. + return 0.0; +} + +} // namespace QtWebEngineCore diff --git a/src/core/touch_handle_drawable_qt.h b/src/core/touch_handle_drawable_qt.h new file mode 100644 index 000000000..46fa217b7 --- /dev/null +++ b/src/core/touch_handle_drawable_qt.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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.LGPL3 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-3.0.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 (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef TOUCH_HANDLE_DRAWABLE_QT_H +#define TOUCH_HANDLE_DRAWABLE_QT_H + +#include "ui/touch_selection/touch_handle.h" +#include "ui/touch_selection/touch_handle_orientation.h" + +#include <QtCore/QScopedPointer> + +namespace QtWebEngineCore { + +class RenderWidgetHostViewQt; +class TouchHandleDrawableClient; + +class TouchHandleDrawableQt : public ui::TouchHandleDrawable +{ +public: + explicit TouchHandleDrawableQt(RenderWidgetHostViewQt *rwhv); + ~TouchHandleDrawableQt() override; + +private: + void UpdateBounds(); + bool IsVisible() const; + + // ui::TouchHandleDrawable overrides + void SetEnabled(bool enabled) override; + void SetOrientation(ui::TouchHandleOrientation orientation, + bool mirror_vertical, + bool mirror_horizontal) override; + void SetOrigin(const gfx::PointF& position) override; + void SetAlpha(float alpha) override; + gfx::RectF GetVisibleBounds() const override; + float GetDrawableHorizontalPaddingRatio() const override; + + RenderWidgetHostViewQt *m_rwhv; + QScopedPointer<TouchHandleDrawableClient> m_client; + + bool m_enabled; + float m_alpha; + ui::TouchHandleOrientation m_orientation; + gfx::RectF m_relativeBounds; + gfx::PointF m_originPosition; + + DISALLOW_COPY_AND_ASSIGN(TouchHandleDrawableQt); +}; + +} // namespace QtWebEngineCore + +#endif // TOUCH_HANDLE_DRAWABLE_QT_H diff --git a/src/core/touch_selection_controller_client_qt.cpp b/src/core/touch_selection_controller_client_qt.cpp new file mode 100644 index 000000000..da3c78b8a --- /dev/null +++ b/src/core/touch_selection_controller_client_qt.cpp @@ -0,0 +1,343 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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.LGPL3 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-3.0.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 (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "render_widget_host_view_qt.h" +#include "touch_handle_drawable_qt.h" +#include "touch_selection_controller_client_qt.h" +#include "touch_selection_menu_controller.h" +#include "type_conversion.h" +#include "web_contents_adapter.h" +#include "web_contents_adapter_client.h" + +#include "content/browser/frame_host/render_frame_host_impl.h" +#include "content/browser/renderer_host/render_widget_host_impl.h" +#include "ui/gfx/geometry/size_conversions.h" + +#include <QClipboard> +#include <QGuiApplication> + +namespace QtWebEngineCore { + +TouchSelectionControllerClientQt::TouchSelectionControllerClientQt(RenderWidgetHostViewQt *rwhv) + : m_rwhv(rwhv) + , m_menuController(new TouchSelectionMenuController(this)) + , m_menuShowing(false) + , m_menuRequested(false) + , m_touchDown(false) + , m_scrollInProgress(false) + , m_handleDragInProgress(false) +{ + Q_ASSERT(rwhv); +} + +TouchSelectionControllerClientQt::~TouchSelectionControllerClientQt() +{ +} + +bool TouchSelectionControllerClientQt::handleContextMenu(const content::ContextMenuParams& params) +{ + if ((params.source_type == ui::MENU_SOURCE_LONG_PRESS || + params.source_type == ui::MENU_SOURCE_LONG_TAP) && + params.is_editable && params.selection_text.empty()) { + m_menuRequested = true; + updateMenu(); + return true; + } + + const bool from_touch = params.source_type == ui::MENU_SOURCE_LONG_PRESS || + params.source_type == ui::MENU_SOURCE_LONG_TAP || + params.source_type == ui::MENU_SOURCE_TOUCH; + if (from_touch && !params.selection_text.empty()) + return true; + + GetTouchSelectionController()->HideAndDisallowShowingAutomatically(); + return false; +} + +void TouchSelectionControllerClientQt::onTouchDown() +{ + m_touchDown = true; + updateMenu(); +} + +void TouchSelectionControllerClientQt::onTouchUp() +{ + m_touchDown = false; + updateMenu(); +} + +void TouchSelectionControllerClientQt::onScrollBegin() +{ + m_scrollInProgress = true; + GetTouchSelectionController()->SetTemporarilyHidden(true); + updateMenu(); +} + +void TouchSelectionControllerClientQt::onScrollEnd() +{ + m_scrollInProgress = false; + GetTouchSelectionController()->SetTemporarilyHidden(false); + updateMenu(); +} + +bool TouchSelectionControllerClientQt::IsCommandIdEnabled(int command_id) const +{ + bool editable = m_rwhv->getTextInputType() != ui::TEXT_INPUT_TYPE_NONE; + bool readable = m_rwhv->getTextInputType() != ui::TEXT_INPUT_TYPE_PASSWORD; + bool hasSelection = !m_rwhv->GetSelectedText().empty(); + + switch (command_id) { + case TouchSelectionMenuController::Cut: + return editable && readable && hasSelection; + case TouchSelectionMenuController::Copy: + return readable && hasSelection; + case TouchSelectionMenuController::Paste: + return editable && !QGuiApplication::clipboard()->text().isEmpty(); + default: + return false; + } +} + +void TouchSelectionControllerClientQt::ExecuteCommand(int command_id, int event_flags) +{ + Q_UNUSED(event_flags); + GetTouchSelectionController()->HideAndDisallowShowingAutomatically(); + + WebContentsAdapterClient *adapterClient = m_rwhv->adapterClient(); + Q_ASSERT(adapterClient); + WebContentsAdapter *adapter = adapterClient->webContentsAdapter(); + Q_ASSERT(adapter); + + switch (command_id) { + case TouchSelectionMenuController::Cut: + adapter->cut(); + break; + case TouchSelectionMenuController::Copy: + adapter->copy(); + break; + case TouchSelectionMenuController::Paste: + adapter->paste(); + break; + default: + NOTREACHED(); + break; + } +} + +void TouchSelectionControllerClientQt::RunContextMenu() +{ + gfx::RectF anchorRect = GetTouchSelectionController()->GetRectBetweenBounds(); + gfx::PointF anchorPoint = gfx::PointF(anchorRect.CenterPoint().x(), anchorRect.y()); + + content::RenderWidgetHostImpl *host = m_rwhv->host(); + host->ShowContextMenuAtPoint(gfx::ToRoundedPoint(anchorPoint), + ui::MENU_SOURCE_TOUCH_EDIT_MENU); + + // Hide selection handles after getting rect-between-bounds from touch + // selection controller; otherwise, rect would be empty and the above + // calculations would be invalid. + GetTouchSelectionController()->HideAndDisallowShowingAutomatically(); +} + +void TouchSelectionControllerClientQt::DidStopFlinging() +{ + onScrollEnd(); +} + +void TouchSelectionControllerClientQt::UpdateClientSelectionBounds(const gfx::SelectionBound& start, + const gfx::SelectionBound& end) +{ + UpdateClientSelectionBounds(start, end, this, this); +} + +void TouchSelectionControllerClientQt::UpdateClientSelectionBounds(const gfx::SelectionBound& start, + const gfx::SelectionBound& end, + ui::TouchSelectionControllerClient* client, + ui::TouchSelectionMenuClient* menu_client) +{ + Q_UNUSED(client); + Q_UNUSED(menu_client); + + GetTouchSelectionController()->OnSelectionBoundsChanged(start, end); +} + +void TouchSelectionControllerClientQt::InvalidateClient(ui::TouchSelectionControllerClient* client) +{ + Q_UNUSED(client); +} + +ui::TouchSelectionController* TouchSelectionControllerClientQt::GetTouchSelectionController() +{ + return m_rwhv->getTouchSelectionController(); +} + +void TouchSelectionControllerClientQt::AddObserver(Observer* observer) +{ + Q_UNUSED(observer); +} + +void TouchSelectionControllerClientQt::RemoveObserver(Observer* observer) +{ + Q_UNUSED(observer); +} + +bool TouchSelectionControllerClientQt::SupportsAnimation() const +{ + return false; +} + +void TouchSelectionControllerClientQt::SetNeedsAnimate() +{ + NOTREACHED(); +} + +void TouchSelectionControllerClientQt::MoveCaret(const gfx::PointF& position) +{ + content::mojom::FrameInputHandler *frameInputHandler = m_rwhv->getFrameInputHandler(); + if (!frameInputHandler) + return; + + frameInputHandler->MoveCaret(gfx::ToRoundedPoint(position)); +} + +void TouchSelectionControllerClientQt::MoveRangeSelectionExtent(const gfx::PointF& extent) +{ + content::mojom::FrameInputHandler *frameInputHandler = m_rwhv->getFrameInputHandler(); + if (!frameInputHandler) + return; + + frameInputHandler->MoveRangeSelectionExtent(gfx::ToRoundedPoint(extent)); +} + +void TouchSelectionControllerClientQt::SelectBetweenCoordinates(const gfx::PointF& base, const gfx::PointF& extent) +{ + content::mojom::FrameInputHandler *frameInputHandler = m_rwhv->getFrameInputHandler(); + if (!frameInputHandler) + return; + + frameInputHandler->SelectRange(gfx::ToRoundedPoint(base), gfx::ToRoundedPoint(extent)); +} + +void TouchSelectionControllerClientQt::OnSelectionEvent(ui::SelectionEventType event) +{ + switch (event) { + case ui::SELECTION_HANDLES_SHOWN: + m_menuRequested = true; + break; + case ui::INSERTION_HANDLE_SHOWN: + break; + case ui::SELECTION_HANDLES_CLEARED: + case ui::INSERTION_HANDLE_CLEARED: + m_menuRequested = false; + break; + case ui::SELECTION_HANDLE_DRAG_STARTED: + case ui::INSERTION_HANDLE_DRAG_STARTED: + m_handleDragInProgress = true; + break; + case ui::SELECTION_HANDLE_DRAG_STOPPED: + case ui::INSERTION_HANDLE_DRAG_STOPPED: + m_handleDragInProgress = false; + break; + case ui::SELECTION_HANDLES_MOVED: + case ui::INSERTION_HANDLE_MOVED: + break; + case ui::INSERTION_HANDLE_TAPPED: + m_menuRequested = !m_menuRequested; + break; + } + + updateMenu(); +} + +void TouchSelectionControllerClientQt::OnDragUpdate(const gfx::PointF& position) +{ + Q_UNUSED(position); +} + +std::unique_ptr<ui::TouchHandleDrawable> TouchSelectionControllerClientQt::CreateDrawable() +{ + return std::unique_ptr<ui::TouchHandleDrawable>(new TouchHandleDrawableQt(m_rwhv)); +} + +void TouchSelectionControllerClientQt::DidScroll() +{ +} + +void TouchSelectionControllerClientQt::showMenu() +{ + gfx::RectF rect = GetTouchSelectionController()->GetRectBetweenBounds(); + gfx::PointF origin = rect.origin(); + gfx::PointF bottom_right = rect.bottom_right(); + + gfx::Vector2dF diagonal = bottom_right - origin; + gfx::SizeF size(diagonal.x(), diagonal.y()); + gfx::RectF anchor_rect(origin, size); + + // Calculate maximum handle image size; + gfx::SizeF max_handle_size = GetTouchSelectionController()->GetStartHandleRect().size(); + max_handle_size.SetToMax(GetTouchSelectionController()->GetEndHandleRect().size()); + + WebContentsAdapterClient *adapterClient = m_rwhv->adapterClient(); + Q_ASSERT(adapterClient); + adapterClient->showTouchSelectionMenu(m_menuController.get(), + QRect(toQt(gfx::ToEnclosingRect(anchor_rect))), + QSize(toQt(gfx::ToRoundedSize(max_handle_size)))); + m_menuShowing = true; +} + +void TouchSelectionControllerClientQt::hideMenu() +{ + WebContentsAdapterClient *adapterClient = m_rwhv->adapterClient(); + Q_ASSERT(adapterClient); + adapterClient->hideTouchSelectionMenu(); + m_menuShowing = false; +} + +void TouchSelectionControllerClientQt::updateMenu() +{ + if (m_menuShowing) + hideMenu(); + + if (m_menuRequested && !m_touchDown && + !m_scrollInProgress && !m_handleDragInProgress) { + showMenu(); + } +} + +} // namespace QtWebEngineCore diff --git a/src/core/touch_selection_controller_client_qt.h b/src/core/touch_selection_controller_client_qt.h new file mode 100644 index 000000000..cdc45cac3 --- /dev/null +++ b/src/core/touch_selection_controller_client_qt.h @@ -0,0 +1,116 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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.LGPL3 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-3.0.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 (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef TOUCH_SELECTION_CONTROLLER_CLIENT_QT_H +#define TOUCH_SELECTION_CONTROLLER_CLIENT_QT_H + +#include "content/public/browser/touch_selection_controller_client_manager.h" +#include "content/public/common/context_menu_params.h" +#include "ui/touch_selection/touch_selection_controller.h" +#include "ui/touch_selection/touch_selection_menu_runner.h" + +#include <QtCore/QScopedPointer> + +namespace QtWebEngineCore { + +class RenderWidgetHostViewQt; +class TouchSelectionMenuController; + +class TouchSelectionControllerClientQt + : public ui::TouchSelectionControllerClient + , public ui::TouchSelectionMenuClient + , public content::TouchSelectionControllerClientManager +{ +public: + explicit TouchSelectionControllerClientQt(RenderWidgetHostViewQt *rwhv); + ~TouchSelectionControllerClientQt() override; + + void UpdateClientSelectionBounds(const gfx::SelectionBound& start, + const gfx::SelectionBound& end); + bool handleContextMenu(const content::ContextMenuParams& params); + void onTouchDown(); + void onTouchUp(); + void onScrollBegin(); + void onScrollEnd(); + + // ui::TouchSelectionMenuClient overrides + bool IsCommandIdEnabled(int command_id) const override; + void ExecuteCommand(int command_id, int event_flags) override; + void RunContextMenu() override; + + // content::TouchSelectionControllerClientManager overrides + void DidStopFlinging() override; + void UpdateClientSelectionBounds(const gfx::SelectionBound& start, + const gfx::SelectionBound& end, + ui::TouchSelectionControllerClient* client, + ui::TouchSelectionMenuClient* menu_client) override; + void InvalidateClient(ui::TouchSelectionControllerClient* client) override; + ui::TouchSelectionController* GetTouchSelectionController() override; + void AddObserver(Observer* observer) override; + void RemoveObserver(Observer* observer) override; + + // ui::TouchSelectionControllerClient overrides + bool SupportsAnimation() const override; + void SetNeedsAnimate() override; + void MoveCaret(const gfx::PointF& position) override; + void MoveRangeSelectionExtent(const gfx::PointF& extent) override; + void SelectBetweenCoordinates(const gfx::PointF& base, const gfx::PointF& extent) override; + void OnSelectionEvent(ui::SelectionEventType event) override; + void OnDragUpdate(const gfx::PointF& position) override; + std::unique_ptr<ui::TouchHandleDrawable> CreateDrawable() override; + void DidScroll() override; + +private: + void showMenu(); + void hideMenu(); + void updateMenu(); + + RenderWidgetHostViewQt *m_rwhv; + QScopedPointer<TouchSelectionMenuController> m_menuController; + + bool m_menuShowing; + bool m_menuRequested; + bool m_touchDown; + bool m_scrollInProgress; + bool m_handleDragInProgress; +}; + +} // namespace QtWebEngineCore + +#endif // TOUCH_SELECTION_CONTROLLER_CLIENT_QT_H diff --git a/src/core/touch_selection_menu_controller.cpp b/src/core/touch_selection_menu_controller.cpp new file mode 100644 index 000000000..cdec9a064 --- /dev/null +++ b/src/core/touch_selection_menu_controller.cpp @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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.LGPL3 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-3.0.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 (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "touch_selection_controller_client_qt.h" +#include "touch_selection_menu_controller.h" + +namespace QtWebEngineCore { + +TouchSelectionMenuController::TouchSelectionMenuController(TouchSelectionControllerClientQt *touchSelectionControllerClient) + : m_touchSelectionControllerClient(touchSelectionControllerClient) +{ +} + +TouchSelectionMenuController::~TouchSelectionMenuController() +{ +} + +int TouchSelectionMenuController::buttonCount() +{ + int buttonCount = 1; + + for (int commandId = 0; commandId <= static_cast<int>(Paste); ++commandId) { + if (m_touchSelectionControllerClient->IsCommandIdEnabled(commandId)) + buttonCount++; + } + + return buttonCount; +} + +bool TouchSelectionMenuController::isCommandEnabled(TouchSelectionCommand command) +{ + return m_touchSelectionControllerClient->IsCommandIdEnabled(static_cast<int>(command)); +} + +void TouchSelectionMenuController::cut() +{ + m_touchSelectionControllerClient->ExecuteCommand(static_cast<int>(Cut), 0); +} + +void TouchSelectionMenuController::copy() +{ + m_touchSelectionControllerClient->ExecuteCommand(static_cast<int>(Copy), 0); +} + +void TouchSelectionMenuController::paste() +{ + m_touchSelectionControllerClient->ExecuteCommand(static_cast<int>(Paste), 0); +} + +void TouchSelectionMenuController::runContextMenu() +{ + return m_touchSelectionControllerClient->RunContextMenu(); +} + +} // namespace QtWebEngineCore diff --git a/src/core/touch_selection_menu_controller.h b/src/core/touch_selection_menu_controller.h new file mode 100644 index 000000000..fd61ae709 --- /dev/null +++ b/src/core/touch_selection_menu_controller.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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.LGPL3 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-3.0.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 (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef TOUCH_SELECTION_MENU_CONTROLLER_H +#define TOUCH_SELECTION_MENU_CONTROLLER_H + +#include "qtwebenginecoreglobal_p.h" +#include <QtCore/QObject> + +namespace QtWebEngineCore { + +class TouchSelectionControllerClientQt; + +class QWEBENGINECORE_PRIVATE_EXPORT TouchSelectionMenuController : public QObject { + Q_OBJECT +public: + enum TouchSelectionCommand { + Cut, + Copy, + Paste + }; + + TouchSelectionMenuController(TouchSelectionControllerClientQt *touchSelectionControllerClient); + ~TouchSelectionMenuController(); + int buttonCount(); + bool isCommandEnabled(TouchSelectionCommand); + +public Q_SLOTS: + void cut(); + void copy(); + void paste(); + void runContextMenu(); + +private: + TouchSelectionControllerClientQt *m_touchSelectionControllerClient; +}; + +} // namespace QtWebEngineCore + +#endif // TOUCH_SELECTION_CONTROLLER_CLIENT_QT_H diff --git a/src/core/web_contents_adapter_client.h b/src/core/web_contents_adapter_client.h index f9d0c0781..4a36e77f9 100644 --- a/src/core/web_contents_adapter_client.h +++ b/src/core/web_contents_adapter_client.h @@ -82,6 +82,8 @@ class JavaScriptDialogController; class RenderWidgetHostViewQt; class RenderWidgetHostViewQtDelegate; class RenderWidgetHostViewQtDelegateClient; +class TouchHandleDrawableClient; +class TouchSelectionMenuController; class WebContentsAdapter; class WebContentsDelegateQt; class WebEngineSettings; @@ -477,6 +479,9 @@ public: virtual void printRequested() = 0; virtual void widgetChanged(RenderWidgetHostViewQtDelegate *newWidget) = 0; virtual void interceptRequest(QWebEngineUrlRequestInfo &) { } + virtual TouchHandleDrawableClient *createTouchHandle(const QMap<int, QImage> &images) = 0; + virtual void showTouchSelectionMenu(TouchSelectionMenuController *menuController, const QRect &bounds, const QSize &handleSize) = 0; + virtual void hideTouchSelectionMenu() = 0; virtual ProfileAdapter *profileAdapter() = 0; virtual WebContentsAdapter* webContentsAdapter() = 0; diff --git a/src/core/web_contents_view_qt.cpp b/src/core/web_contents_view_qt.cpp index f44668019..648e0fd8f 100644 --- a/src/core/web_contents_view_qt.cpp +++ b/src/core/web_contents_view_qt.cpp @@ -44,6 +44,7 @@ #include "render_widget_host_view_qt.h" #include "render_widget_host_view_qt_delegate.h" #include "render_widget_host_view_qt.h" +#include "touch_selection_controller_client_qt.h" #include "type_conversion.h" #include "web_contents_adapter_client.h" #include "web_contents_adapter.h" @@ -189,6 +190,11 @@ static inline WebEngineContextMenuData fromParams(const content::ContextMenuPara void WebContentsViewQt::ShowContextMenu(content::RenderFrameHost *, const content::ContextMenuParams ¶ms) { + if (auto rwhv = static_cast<RenderWidgetHostViewQt *>(m_webContents->GetRenderWidgetHostView())) { + if (rwhv && rwhv->getTouchSelectionControllerClient()->handleContextMenu(params)) + return; + } + WebEngineContextMenuData contextMenuData(fromParams(params)); #if QT_CONFIG(webengine_spellchecker) // Do not use params.spellcheck_enabled, since it is never diff --git a/src/webengine/api/qquickwebenginetouchhandleprovider.cpp b/src/webengine/api/qquickwebenginetouchhandleprovider.cpp new file mode 100644 index 000000000..80f4727b6 --- /dev/null +++ b/src/webengine/api/qquickwebenginetouchhandleprovider.cpp @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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.LGPL3 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-3.0.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 (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickwebenginetouchhandleprovider_p_p.h" + +// static +QString QQuickWebEngineTouchHandleProvider::identifier() +{ + return QStringLiteral("touchhandle"); +} + +// static +QUrl QQuickWebEngineTouchHandleProvider::url(int orientation) +{ + return QUrl(QStringLiteral("image://%1/%2").arg(identifier(), QString::number(orientation))); +} + +QQuickWebEngineTouchHandleProvider::QQuickWebEngineTouchHandleProvider() + : QQuickImageProvider(QQuickImageProvider::Image) +{ +} + +QQuickWebEngineTouchHandleProvider::~QQuickWebEngineTouchHandleProvider() +{ +} + +void QQuickWebEngineTouchHandleProvider::init(const QMap<int, QImage> &images) +{ + if (!m_touchHandleMap.empty()) { + Q_ASSERT(images.size() == m_touchHandleMap.size()); + return; + } + + m_touchHandleMap.unite(images); +} + +QImage QQuickWebEngineTouchHandleProvider::requestImage(const QString &id, QSize *size, const QSize &requestedSize) +{ + Q_UNUSED(size); + Q_UNUSED(requestedSize); + + Q_ASSERT(m_touchHandleMap.contains(id.toInt())); + return m_touchHandleMap.value(id.toInt()); +} diff --git a/src/webengine/api/qquickwebenginetouchhandleprovider_p_p.h b/src/webengine/api/qquickwebenginetouchhandleprovider_p_p.h new file mode 100644 index 000000000..277436289 --- /dev/null +++ b/src/webengine/api/qquickwebenginetouchhandleprovider_p_p.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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.LGPL3 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-3.0.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 (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKWEBENGINETOUCHHANDLEPROVIDER_P_P_H +#define QQUICKWEBENGINETOUCHHANDLEPROVIDER_P_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtQuick/QQuickImageProvider> +#include <QtWebEngine/private/qtwebengineglobal_p.h> + +QT_BEGIN_NAMESPACE + +class Q_WEBENGINE_PRIVATE_EXPORT QQuickWebEngineTouchHandleProvider : public QQuickImageProvider { +public: + static QString identifier(); + static QUrl url(int orientation); + + QQuickWebEngineTouchHandleProvider(); + ~QQuickWebEngineTouchHandleProvider(); + + void init(const QMap<int, QImage> &images); + virtual QImage requestImage(const QString &id, QSize *size, const QSize &requestedSize); + +private: + QMap<int, QImage> m_touchHandleMap; +}; + + +QT_END_NAMESPACE + +#endif // QQUICKWEBENGINETOUCHHANDLEPROVIDER_P_P_H diff --git a/src/webengine/api/qquickwebengineview.cpp b/src/webengine/api/qquickwebengineview.cpp index a6913e2a7..59431cadc 100644 --- a/src/webengine/api/qquickwebengineview.cpp +++ b/src/webengine/api/qquickwebengineview.cpp @@ -44,6 +44,7 @@ #include "certificate_error_controller.h" #include "file_picker_controller.h" #include "javascript_dialog_controller.h" +#include "touch_selection_menu_controller.h" #include "qquickwebengineaction_p.h" #include "qquickwebengineaction_p_p.h" @@ -59,6 +60,7 @@ #include "qquickwebengineprofile_p.h" #include "qquickwebenginesettings_p.h" #include "qquickwebenginescript_p.h" +#include "qquickwebenginetouchhandleprovider_p_p.h" #include "qwebenginequotarequest.h" #include "qwebengineregisterprotocolhandlerrequest.h" @@ -1211,6 +1213,39 @@ void QQuickWebEngineViewPrivate::setToolTip(const QString &toolTipText) ui()->showToolTip(toolTipText); } +QtWebEngineCore::TouchHandleDrawableClient *QQuickWebEngineViewPrivate::createTouchHandle(const QMap<int, QImage> &images) +{ + return new QQuickWebEngineTouchHandle(ui(), images); +} + +void QQuickWebEngineViewPrivate::showTouchSelectionMenu(QtWebEngineCore::TouchSelectionMenuController *menuController, const QRect &selectionBounds, const QSize &handleSize) +{ + Q_UNUSED(handleSize); + + const int kSpacingBetweenButtons = 2; + const int kMenuButtonMinWidth = 63; + const int kMenuButtonMinHeight = 38; + + int buttonCount = menuController->buttonCount(); + if (buttonCount == 1) { + menuController->runContextMenu(); + return; + } + + int width = (kSpacingBetweenButtons * (buttonCount + 1)) + (kMenuButtonMinWidth * buttonCount); + int height = kMenuButtonMinHeight + kSpacingBetweenButtons; + int x = (selectionBounds.x() + selectionBounds.x() + selectionBounds.width() - width) / 2; + int y = selectionBounds.y() - height - 2; + + QRect bounds(x, y, width, height); + ui()->showTouchSelectionMenu(menuController, bounds, kSpacingBetweenButtons); +} + +void QQuickWebEngineViewPrivate::hideTouchSelectionMenu() +{ + ui()->hideTouchSelectionMenu(); +} + bool QQuickWebEngineView::isLoading() const { Q_D(const QQuickWebEngineView); @@ -2285,5 +2320,43 @@ bool QQuickContextMenuBuilder::isMenuItemEnabled(ContextMenuItem menuItem) Q_UNREACHABLE(); } + +QQuickWebEngineTouchHandle::QQuickWebEngineTouchHandle(QtWebEngineCore::UIDelegatesManager *ui, const QMap<int, QImage> &images) +{ + Q_ASSERT(ui); + m_item.reset(ui->createTouchHandle()); + + QQmlEngine *engine = qmlEngine(m_item.data()); + Q_ASSERT(engine); + QQuickWebEngineTouchHandleProvider *touchHandleProvider = + static_cast<QQuickWebEngineTouchHandleProvider *>(engine->imageProvider(QQuickWebEngineTouchHandleProvider::identifier())); + Q_ASSERT(touchHandleProvider); + touchHandleProvider->init(images); +} + +void QQuickWebEngineTouchHandle::setImage(int orientation) +{ + QUrl url = QQuickWebEngineTouchHandleProvider::url(orientation); + m_item->setProperty("source", url); +} + +void QQuickWebEngineTouchHandle::setBounds(const QRect &bounds) +{ + m_item->setProperty("x", bounds.x()); + m_item->setProperty("y", bounds.y()); + m_item->setProperty("width", bounds.width()); + m_item->setProperty("height", bounds.height()); +} + +void QQuickWebEngineTouchHandle::setVisible(bool visible) +{ + m_item->setProperty("visible", visible); +} + +void QQuickWebEngineTouchHandle::setOpacity(float opacity) +{ + m_item->setProperty("opacity", opacity); +} + QT_END_NAMESPACE diff --git a/src/webengine/api/qquickwebengineview_p_p.h b/src/webengine/api/qquickwebengineview_p_p.h index 88a670867..3c985cba1 100644 --- a/src/webengine/api/qquickwebengineview_p_p.h +++ b/src/webengine/api/qquickwebengineview_p_p.h @@ -53,6 +53,7 @@ #include "qquickwebengineview_p.h" #include "render_view_context_menu_qt.h" +#include "touch_handle_drawable_client.h" #include "web_contents_adapter_client.h" #include <QPointer> @@ -64,6 +65,8 @@ namespace QtWebEngineCore { class RenderWidgetHostViewQtDelegateQuick; +class TouchHandleDrawableClient; +class TouchSelectionMenuController; class UIDelegatesManager; class WebContentsAdapter; } @@ -76,6 +79,7 @@ class QQuickWebEngineContextMenuRequest; class QQuickWebEngineSettings; class QQuickWebEngineFaviconProvider; class QQuickWebEngineProfilePrivate; +class QQuickWebEngineTouchHandleProvider; QQuickWebEngineView::WebAction editorActionForKeyEvent(QKeyEvent* event); @@ -152,6 +156,9 @@ public: bool supportsDragging() const override; bool isEnabled() const override; void setToolTip(const QString &toolTipText) override; + QtWebEngineCore::TouchHandleDrawableClient *createTouchHandle(const QMap<int, QImage> &images) override; + void showTouchSelectionMenu(QtWebEngineCore::TouchSelectionMenuController *, const QRect &, const QSize &) override; + void hideTouchSelectionMenu() override; const QObject *holdingQObject() const override; ClientType clientType() override { return QtWebEngineCore::WebContentsAdapterClient::QmlClient; } @@ -253,6 +260,19 @@ private: QObject *m_menu; }; +class Q_WEBENGINE_PRIVATE_EXPORT QQuickWebEngineTouchHandle : public QtWebEngineCore::TouchHandleDrawableClient { +public: + QQuickWebEngineTouchHandle(QtWebEngineCore::UIDelegatesManager *ui, const QMap<int, QImage> &images); + + void setImage(int orientation) override; + void setBounds(const QRect &bounds) override; + void setVisible(bool visible) override; + void setOpacity(float opacity) override; + +private: + QScopedPointer<QQuickItem> m_item; +}; + QT_END_NAMESPACE #endif // QQUICKWEBENGINEVIEW_P_P_H diff --git a/src/webengine/module.pro b/src/webengine/module.pro index 5bc196049..49a1086b2 100644 --- a/src/webengine/module.pro +++ b/src/webengine/module.pro @@ -30,6 +30,7 @@ SOURCES = \ api/qquickwebenginescript.cpp \ api/qquickwebenginesettings.cpp \ api/qquickwebenginesingleton.cpp \ + api/qquickwebenginetouchhandleprovider.cpp \ api/qquickwebengineview.cpp \ api/qtwebengineglobal.cpp \ render_widget_host_view_qt_delegate_quick.cpp \ @@ -58,6 +59,7 @@ HEADERS = \ api/qquickwebenginescript_p.h \ api/qquickwebenginesettings_p.h \ api/qquickwebenginesingleton_p.h \ + api/qquickwebenginetouchhandleprovider_p_p.h \ api/qquickwebengineview_p.h \ api/qquickwebengineview_p_p.h \ render_widget_host_view_qt_delegate_quick.h \ diff --git a/src/webengine/plugin/plugin.cpp b/src/webengine/plugin/plugin.cpp index b5adef8ab..0e63989ee 100644 --- a/src/webengine/plugin/plugin.cpp +++ b/src/webengine/plugin/plugin.cpp @@ -52,6 +52,7 @@ #include <QtWebEngine/private/qquickwebenginenewviewrequest_p.h> #include <QtWebEngine/private/qquickwebenginesettings_p.h> #include <QtWebEngine/private/qquickwebenginesingleton_p.h> +#include <QtWebEngine/private/qquickwebenginetouchhandleprovider_p_p.h> #include <QtWebEngine/private/qquickwebengineview_p.h> #include <QtWebEngine/private/qquickwebengineaction_p.h> #include <QtWebEngineCore/qwebenginequotarequest.h> @@ -73,6 +74,7 @@ public: { Q_UNUSED(uri); engine->addImageProvider(QQuickWebEngineFaviconProvider::identifier(), new QQuickWebEngineFaviconProvider); + engine->addImageProvider(QQuickWebEngineTouchHandleProvider::identifier(), new QQuickWebEngineTouchHandleProvider); } void registerTypes(const char *uri) override diff --git a/src/webengine/ui/TouchHandle.qml b/src/webengine/ui/TouchHandle.qml new file mode 100644 index 000000000..76a93829e --- /dev/null +++ b/src/webengine/ui/TouchHandle.qml @@ -0,0 +1,42 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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.LGPL3 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-3.0.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 (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.5 + +Image { } diff --git a/src/webengine/ui/TouchSelectionMenu.qml b/src/webengine/ui/TouchSelectionMenu.qml new file mode 100644 index 000000000..7cf16b554 --- /dev/null +++ b/src/webengine/ui/TouchSelectionMenu.qml @@ -0,0 +1,175 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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.LGPL3 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-3.0.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 (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.5 +import QtQuick.Layouts 1.3 + +Rectangle { + id: menu + + signal cutTriggered + signal copyTriggered + signal pasteTriggered + signal contextMenuTriggered + + property bool isCutEnabled: false + property bool isCopyEnabled: false + property bool isPasteEnabled: false + + property color borderColor: "darkGray" + property color bgColor: "white" + + radius: 4 + border.color: borderColor + color: borderColor + antialiasing: true + + RowLayout { + anchors.fill: parent + spacing: parent.border.width + anchors.margins: parent.border.width + + Rectangle { + Layout.fillHeight: true + Layout.fillWidth: true + radius: menu.radius + color: bgColor + visible: isCutEnabled + + Text { + id: cutText + anchors.centerIn: parent + text: "Cut" + } + + MouseArea { + anchors.fill: parent + onPressed: { + parent.color = borderColor; + cutText.color = "white"; + } + onReleased: { + parent.color = bgColor; + cutText.color = "black"; + cutTriggered(); + } + } + } + + Rectangle { + Layout.fillHeight: true + Layout.fillWidth: true + radius: menu.radius + color: bgColor + visible: isCopyEnabled + + Text { + id: copyText + anchors.centerIn: parent + text: "Copy" + } + + MouseArea { + anchors.fill: parent + onPressed: { + parent.color = borderColor; + copyText.color = "white"; + } + onReleased: { + parent.color = bgColor; + copyText.color = "black"; + copyTriggered(); + } + } + } + + Rectangle { + Layout.fillHeight: true + Layout.fillWidth: true + radius: menu.radius + color: bgColor + visible: isPasteEnabled + + Text { + id: pasteText + anchors.centerIn: parent + text: "Paste" + } + + MouseArea { + anchors.fill: parent + onPressed: { + parent.color = borderColor; + pasteText.color = "white"; + } + onReleased: { + parent.color = bgColor; + pasteText.color = "black"; + pasteTriggered(); + } + } + } + + Rectangle { + Layout.fillHeight: true + Layout.fillWidth: true + radius: menu.radius + color: bgColor + + Text { + id: contextMenuText + anchors.centerIn: parent + text: "..." + } + + MouseArea { + anchors.fill: parent + onPressed: { + parent.color = borderColor; + contextMenuText.color = "white"; + } + onReleased: { + parent.color = bgColor; + contextMenuText.color = "black"; + contextMenuTriggered(); + } + } + } + } +} diff --git a/src/webengine/ui/ui.pro b/src/webengine/ui/ui.pro index eb6bf435c..69f754e0c 100644 --- a/src/webengine/ui/ui.pro +++ b/src/webengine/ui/ui.pro @@ -13,6 +13,8 @@ QML_FILES += \ Menu.qml \ MenuItem.qml \ MenuSeparator.qml \ - ToolTip.qml + ToolTip.qml \ + TouchHandle.qml \ + TouchSelectionMenu.qml load(qml_module) diff --git a/src/webengine/ui_delegates_manager.cpp b/src/webengine/ui_delegates_manager.cpp index 7e49bc77d..da120ab69 100644 --- a/src/webengine/ui_delegates_manager.cpp +++ b/src/webengine/ui_delegates_manager.cpp @@ -44,6 +44,7 @@ #include <color_chooser_controller.h> #include <file_picker_controller.h> #include <javascript_dialog_controller.h> +#include <touch_selection_menu_controller.h> #include <web_contents_adapter_client.h> #include <QFileInfo> @@ -54,6 +55,7 @@ #include <QCursor> #include <QList> #include <QScreen> +#include <QTimer> #include <QGuiApplication> // Uncomment for QML debugging @@ -125,6 +127,7 @@ const char *defaultPropertyName(QObject *obj) UIDelegatesManager::UIDelegatesManager(QQuickWebEngineView *view) : m_view(view) , m_toolTip(nullptr) + , m_touchSelectionMenu(nullptr) FOR_EACH_COMPONENT_TYPE(COMPONENT_MEMBER_INIT, NO_SEPARATOR) { } @@ -568,6 +571,82 @@ void UIDelegatesManager::showToolTip(const QString &text) QMetaObject::invokeMethod(m_toolTip.data(), "open"); } +QQuickItem *UIDelegatesManager::createTouchHandle() +{ + if (!ensureComponentLoaded(TouchHandle)) + return nullptr; + + QQmlContext *context = qmlContext(m_view); + QObject *touchHandle = touchHandleComponent->beginCreate(context); + QQuickItem *item = qobject_cast<QQuickItem *>(touchHandle); + Q_ASSERT(item); + item->setParentItem(m_view); + touchHandleComponent->completeCreate(); + + return item; +} + +void UIDelegatesManager::showTouchSelectionMenu(QtWebEngineCore::TouchSelectionMenuController *menuController, const QRect &bounds, const int spacing) +{ + if (!ensureComponentLoaded(TouchSelectionMenu)) + return; + + QQmlContext *context = qmlContext(m_view); + m_touchSelectionMenu.reset(touchSelectionMenuComponent->beginCreate(context)); + if (QQuickItem *item = qobject_cast<QQuickItem *>(m_touchSelectionMenu.data())) + item->setParentItem(m_view); + m_touchSelectionMenu->setParent(m_view); + + QQmlProperty(m_touchSelectionMenu.data(), QStringLiteral("width")).write(bounds.width()); + QQmlProperty(m_touchSelectionMenu.data(), QStringLiteral("height")).write(bounds.height()); + QQmlProperty(m_touchSelectionMenu.data(), QStringLiteral("x")).write(bounds.x()); + QQmlProperty(m_touchSelectionMenu.data(), QStringLiteral("y")).write(bounds.y()); + QQmlProperty(m_touchSelectionMenu.data(), QStringLiteral("border.width")).write(spacing); + + // Cut button + bool cutEnabled = menuController->isCommandEnabled(TouchSelectionMenuController::Cut); + QQmlProperty(m_touchSelectionMenu.data(), QStringLiteral("isCutEnabled")).write(cutEnabled); + if (cutEnabled) { + QQmlProperty cutSignal(m_touchSelectionMenu.data(), QStringLiteral("onCutTriggered")); + CHECK_QML_SIGNAL_PROPERTY(cutSignal, touchSelectionMenuComponent->url()); + int cutIndex = menuController->metaObject()->indexOfSlot("cut()"); + QObject::connect(m_touchSelectionMenu.data(), cutSignal.method(), menuController, menuController->metaObject()->method(cutIndex)); + } + + // Copy button + bool copyEnabled = menuController->isCommandEnabled(TouchSelectionMenuController::Copy); + QQmlProperty(m_touchSelectionMenu.data(), QStringLiteral("isCopyEnabled")).write(copyEnabled); + if (copyEnabled) { + QQmlProperty copySignal(m_touchSelectionMenu.data(), QStringLiteral("onCopyTriggered")); + CHECK_QML_SIGNAL_PROPERTY(copySignal, touchSelectionMenuComponent->url()); + int copyIndex = menuController->metaObject()->indexOfSlot("copy()"); + QObject::connect(m_touchSelectionMenu.data(), copySignal.method(), menuController, menuController->metaObject()->method(copyIndex)); + } + + // Paste button + bool pasteEnabled = menuController->isCommandEnabled(TouchSelectionMenuController::Paste); + QQmlProperty(m_touchSelectionMenu.data(), QStringLiteral("isPasteEnabled")).write(pasteEnabled); + if (pasteEnabled) { + QQmlProperty pasteSignal(m_touchSelectionMenu.data(), QStringLiteral("onPasteTriggered")); + CHECK_QML_SIGNAL_PROPERTY(pasteSignal, touchSelectionMenuComponent->url()); + int pasteIndex = menuController->metaObject()->indexOfSlot("paste()"); + QObject::connect(m_touchSelectionMenu.data(), pasteSignal.method(), menuController, menuController->metaObject()->method(pasteIndex)); + } + + // Context menu button + QQmlProperty contextMenuSignal(m_touchSelectionMenu.data(), QStringLiteral("onContextMenuTriggered")); + CHECK_QML_SIGNAL_PROPERTY(contextMenuSignal, touchSelectionMenuComponent->url()); + int contextMenuIndex = menuController->metaObject()->indexOfSlot("runContextMenu()"); + QObject::connect(m_touchSelectionMenu.data(), contextMenuSignal.method(), menuController, menuController->metaObject()->method(contextMenuIndex)); + + touchSelectionMenuComponent->completeCreate(); +} + +void UIDelegatesManager::hideTouchSelectionMenu() +{ + QTimer::singleShot(0, m_view, [this] { m_touchSelectionMenu.reset(); }); +} + UI2DelegatesManager::UI2DelegatesManager(QQuickWebEngineView *view) : UIDelegatesManager(view) { diff --git a/src/webengine/ui_delegates_manager.h b/src/webengine/ui_delegates_manager.h index 18457e4ed..4b6e291b2 100644 --- a/src/webengine/ui_delegates_manager.h +++ b/src/webengine/ui_delegates_manager.h @@ -61,6 +61,8 @@ F(FilePicker, filePicker) SEPARATOR \ F(AuthenticationDialog, authenticationDialog) SEPARATOR \ F(ToolTip, toolTip) SEPARATOR \ + F(TouchHandle, touchHandle) SEPARATOR \ + F(TouchSelectionMenu, touchSelectionMenu) SEPARATOR \ #define COMMA_SEPARATOR , #define SEMICOLON_SEPARATOR ; @@ -81,6 +83,7 @@ namespace QtWebEngineCore { class AuthenticationDialogController; class JavaScriptDialogController; class FilePickerController; +class TouchSelectionMenuController; const char *defaultPropertyName(QObject *obj); @@ -110,6 +113,9 @@ public: void showFilePicker(QSharedPointer<FilePickerController>); virtual void showMenu(QObject *menu); void showToolTip(const QString &text); + QQuickItem *createTouchHandle(); + void showTouchSelectionMenu(TouchSelectionMenuController *, const QRect &, const int spacing); + void hideTouchSelectionMenu(); protected: bool ensureComponentLoaded(ComponentType); @@ -117,6 +123,7 @@ protected: QQuickWebEngineView *m_view; QScopedPointer<QObject> m_toolTip; QStringList m_importDirs; + QScopedPointer<QObject> m_touchSelectionMenu; FOR_EACH_COMPONENT_TYPE(MEMBER_DECLARATION, SEMICOLON_SEPARATOR) diff --git a/src/webenginewidgets/api/qwebenginepage_p.h b/src/webenginewidgets/api/qwebenginepage_p.h index ddd9e8342..e60438d97 100644 --- a/src/webenginewidgets/api/qwebenginepage_p.h +++ b/src/webenginewidgets/api/qwebenginepage_p.h @@ -66,6 +66,8 @@ namespace QtWebEngineCore { class RenderWidgetHostViewQtDelegate; class RenderWidgetHostViewQtDelegateWidget; +class TouchHandleDrawableClient; +class TouchSelectionMenuController; class WebContentsAdapter; } @@ -149,6 +151,9 @@ public: bool isEnabled() const override; void setToolTip(const QString &toolTipText) override; void printRequested() override; + QtWebEngineCore::TouchHandleDrawableClient *createTouchHandle(const QMap<int, QImage> &) override { return nullptr; } + void showTouchSelectionMenu(QtWebEngineCore::TouchSelectionMenuController *, const QRect &, const QSize &) override { } + void hideTouchSelectionMenu() override { } const QObject *holdingQObject() const override; ClientType clientType() override { return QtWebEngineCore::WebContentsAdapterClient::WidgetsClient; } void interceptRequest(QWebEngineUrlRequestInfo &) override; |