diff --git a/CMakeLists.txt b/CMakeLists.txt
index 2d3c189f..72190947 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -281,6 +281,7 @@ set(SRC_FILES
src/ui/InfoMessage.cpp
src/ui/Label.cpp
src/ui/LoadingIndicator.cpp
+ src/ui/NhekoCursorShape.cpp
src/ui/NhekoDropArea.cpp
src/ui/OverlayModal.cpp
src/ui/OverlayWidget.cpp
@@ -495,6 +496,7 @@ qt5_wrap_cpp(MOC_HEADERS
src/ui/Label.h
src/ui/FloatingButton.h
src/ui/Menu.h
+ src/ui/NhekoCursorShape.h
src/ui/NhekoDropArea.h
src/ui/OverlayWidget.h
src/ui/SnackBar.h
diff --git a/README.md b/README.md
index 2ee06940..b9690fc5 100644
--- a/README.md
+++ b/README.md
@@ -116,9 +116,7 @@ brew install --cask nheko
### Build Requirements
-- Qt5 (5.10 or greater). Qt 5.7 adds support for color font rendering with
- Freetype, which is essential to properly support emoji, 5.8 adds some features
- to make interopability with Qml easier, 5.10 makes sliders actually visible with different palettes.
+- Qt5 (5.12 or greater). Required for overlapping hover handlers in Qml.
- CMake 3.15 or greater. (Lower version may work, but may break boost linking)
- [mtxclient](https://github.com/Nheko-Reborn/mtxclient)
- [LMDB](https://symas.com/lightning-memory-mapped-database/)
diff --git a/resources/qml/Avatar.qml b/resources/qml/Avatar.qml
index a459fe5a..f01911bb 100644
--- a/resources/qml/Avatar.qml
+++ b/resources/qml/Avatar.qml
@@ -90,4 +90,9 @@ Rectangle {
}
}
+ CursorShape {
+ anchors.fill: parent
+ cursorShape: Qt.PointingHandCursor
+ }
+
}
diff --git a/resources/qml/EncryptionIndicator.qml b/resources/qml/EncryptionIndicator.qml
index 46ca62c5..00efe9e4 100644
--- a/resources/qml/EncryptionIndicator.qml
+++ b/resources/qml/EncryptionIndicator.qml
@@ -1,4 +1,4 @@
-import QtQuick 2.5
+import QtQuick 2.12
import QtQuick.Controls 2.1
import im.nheko 1.0
@@ -24,14 +24,11 @@ Rectangle {
color: "transparent"
width: 16
height: 16
- ToolTip.visible: ma.containsMouse && indicator.visible
+ ToolTip.visible: ma.hovered && indicator.visible
ToolTip.text: getEncryptionTooltip()
- MouseArea {
+ HoverHandler {
id: ma
-
- anchors.fill: parent
- hoverEnabled: true
}
Image {
diff --git a/resources/qml/ImageButton.qml b/resources/qml/ImageButton.qml
index 9c0faef3..159c750f 100644
--- a/resources/qml/ImageButton.qml
+++ b/resources/qml/ImageButton.qml
@@ -1,6 +1,7 @@
import "./ui"
import QtQuick 2.3
import QtQuick.Controls 2.3
+import im.nheko 1.0 // for cursor shape
AbstractButton {
id: button
@@ -23,11 +24,10 @@ AbstractButton {
source: image != "" ? ("image://colorimage/" + image + "?" + ((button.hovered && changeColorOnHover) ? highlightColor : buttonTextColor)) : ""
}
- MouseArea {
+ CursorShape {
id: mouseArea
anchors.fill: parent
- onPressed: mouse.accepted = false
cursorShape: Qt.PointingHandCursor
}
diff --git a/resources/qml/MatrixText.qml b/resources/qml/MatrixText.qml
index bb3b4296..4ea15518 100644
--- a/resources/qml/MatrixText.qml
+++ b/resources/qml/MatrixText.qml
@@ -8,6 +8,7 @@ TextEdit {
focus: false
wrapMode: Text.Wrap
selectByMouse: !Settings.mobileMode
+ enabled: selectByMouse
color: colors.text
onLinkActivated: {
if (/^https:\/\/matrix.to\/#\/(@.*)$/.test(link)) {
@@ -25,12 +26,9 @@ TextEdit {
ToolTip.visible: hoveredLink
ToolTip.text: hoveredLink
- MouseArea {
- id: ma
-
+ CursorShape {
anchors.fill: parent
- acceptedButtons: Qt.NoButton
- cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
+ cursorShape: hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
}
}
diff --git a/resources/qml/MessageView.qml b/resources/qml/MessageView.qml
index 179f6f54..e1641a36 100644
--- a/resources/qml/MessageView.qml
+++ b/resources/qml/MessageView.qml
@@ -1,6 +1,6 @@
import "./delegates"
import QtGraphicalEffects 1.0
-import QtQuick 2.9
+import QtQuick 2.12
import QtQuick.Controls 2.3
import QtQuick.Layouts 1.2
import QtQuick.Window 2.2
@@ -153,12 +153,15 @@ ScrollView {
color: TimelineManager.userColor(modelData ? modelData.userId : "", colors.window)
textFormat: Text.RichText
- MouseArea {
+ TapHandler {
+ //cursorShape: Qt.PointingHandCursor
+
+ onSingleTapped: chat.model.openUserProfile(modelData.userId)
+ }
+
+ CursorShape {
anchors.fill: parent
- Layout.alignment: Qt.AlignHCenter
- onClicked: chat.model.openUserProfile(modelData.userId)
cursorShape: Qt.PointingHandCursor
- propagateComposedEvents: true
}
}
diff --git a/resources/qml/TimelineRow.qml b/resources/qml/TimelineRow.qml
index 5ec23d62..3a2ed627 100644
--- a/resources/qml/TimelineRow.qml
+++ b/resources/qml/TimelineRow.qml
@@ -1,6 +1,6 @@
import "./delegates"
import "./emoji"
-import QtQuick 2.6
+import QtQuick 2.12
import QtQuick.Controls 2.3
import QtQuick.Layouts 1.2
import QtQuick.Window 2.2
@@ -12,27 +12,23 @@ Item {
height: row.height
Rectangle {
- color: (Settings.messageHoverHighlight && hoverHandler.containsMouse) ? colors.alternateBase : "transparent"
+ color: (Settings.messageHoverHighlight && hoverHandler.hovered) ? colors.alternateBase : "transparent"
anchors.fill: row
}
- MouseArea {
+ HoverHandler {
id: hoverHandler
- anchors.fill: parent
- propagateComposedEvents: true
- preventStealing: false
- hoverEnabled: true
- acceptedButtons: Qt.AllButtons
- onClicked: {
- if (mouse.button === Qt.RightButton)
- messageContextMenu.show(model.id, model.type, model.isEncrypted, model.isEditable, row);
- else
- mouse.accepted = false;
- }
- onPressAndHold: {
- messageContextMenu.show(model.id, model.type, model.isEncrypted, model.isEditable, row, mapToItem(timelineRoot, mouse.x, mouse.y));
- }
+ acceptedDevices: PointerDevice.GenericPointer
+ }
+
+ TapHandler {
+ acceptedButtons: Qt.RightButton
+ onSingleTapped: messageContextMenu.show(model.id, model.type, model.isEncrypted, model.isEditable, row, mapToItem(timelineRoot, eventPoint.position.x, eventPoint.position.y))
+ }
+
+ TapHandler {
+ onLongPressed: messageContextMenu.show(model.id, model.type, model.isEncrypted, model.isEditable, row, mapToItem(timelineRoot, point.position.x, point.position.y))
}
RowLayout {
@@ -151,15 +147,11 @@ Item {
text: model.timestamp.toLocaleTimeString("HH:mm")
width: Math.max(implicitWidth, text.length * fontMetrics.maximumCharacterWidth)
color: inactiveColors.text
- ToolTip.visible: ma.containsMouse
+ ToolTip.visible: ma.hovered
ToolTip.text: Qt.formatDateTime(model.timestamp, Qt.DefaultLocaleLongDate)
- MouseArea {
+ HoverHandler {
id: ma
-
- anchors.fill: parent
- hoverEnabled: true
- propagateComposedEvents: true
}
}
diff --git a/resources/qml/delegates/FileMessage.qml b/resources/qml/delegates/FileMessage.qml
index ffd1e82b..4bc202eb 100644
--- a/resources/qml/delegates/FileMessage.qml
+++ b/resources/qml/delegates/FileMessage.qml
@@ -1,4 +1,4 @@
-import QtQuick 2.6
+import QtQuick 2.12
import QtQuick.Layouts 1.2
import im.nheko 1.0
@@ -31,7 +31,15 @@ Item {
MouseArea {
anchors.fill: parent
- onClicked: TimelineManager.timeline.saveMedia(model.data.id)
+ cursorShape: Qt.PointingHandCursor
+ }
+
+ TapHandler {
+ onSingleTapped: TimelineManager.timeline.saveMedia(model.data.id)
+ }
+
+ CursorShape {
+ anchors.fill: parent
cursorShape: Qt.PointingHandCursor
}
diff --git a/resources/qml/delegates/ImageMessage.qml b/resources/qml/delegates/ImageMessage.qml
index e8e325f0..3bb9eb05 100644
--- a/resources/qml/delegates/ImageMessage.qml
+++ b/resources/qml/delegates/ImageMessage.qml
@@ -1,4 +1,4 @@
-import QtQuick 2.6
+import QtQuick 2.12
import im.nheko 1.0
Item {
@@ -32,20 +32,20 @@ Item {
smooth: true
mipmap: true
- MouseArea {
- id: mouseArea
-
+ TapHandler {
enabled: model.data.type == MtxEvent.ImageMessage && img.status == Image.Ready
- hoverEnabled: true
- anchors.fill: parent
- onClicked: TimelineManager.openImageOverlay(model.data.url, model.data.id)
+ onSingleTapped: TimelineManager.openImageOverlay(model.data.url, model.data.id)
+ }
+
+ HoverHandler {
+ id: mouseArea
}
Item {
id: overlay
anchors.fill: parent
- visible: mouseArea.containsMouse
+ visible: mouseArea.hovered
Rectangle {
id: container
diff --git a/resources/qml/delegates/PlayableMediaMessage.qml b/resources/qml/delegates/PlayableMediaMessage.qml
index 1534da2e..70f39e43 100644
--- a/resources/qml/delegates/PlayableMediaMessage.qml
+++ b/resources/qml/delegates/PlayableMediaMessage.qml
@@ -1,5 +1,5 @@
import QtMultimedia 5.6
-import QtQuick 2.6
+import QtQuick 2.12
import QtQuick.Controls 2.1
import QtQuick.Layouts 1.2
import im.nheko 1.0
@@ -140,9 +140,8 @@ Rectangle {
fillMode: Image.Pad
}
- MouseArea {
- anchors.fill: parent
- onClicked: {
+ TapHandler {
+ onSingleTapped: {
switch (button.state) {
case "":
TimelineManager.timeline.cacheMedia(model.data.id);
@@ -159,6 +158,10 @@ Rectangle {
break;
}
}
+ }
+
+ CursorShape {
+ anchors.fill: parent
cursorShape: Qt.PointingHandCursor
}
diff --git a/resources/qml/delegates/Reply.qml b/resources/qml/delegates/Reply.qml
index ff1fa657..28c4bf6e 100644
--- a/resources/qml/delegates/Reply.qml
+++ b/resources/qml/delegates/Reply.qml
@@ -1,4 +1,4 @@
-import QtQuick 2.6
+import QtQuick 2.12
import QtQuick.Controls 2.3
import QtQuick.Layouts 1.2
import QtQuick.Window 2.2
@@ -13,10 +13,12 @@ Item {
width: parent.width
height: replyContainer.height
- MouseArea {
+ TapHandler {
+ onSingleTapped: chat.positionViewAtIndex(chat.model.idToIndex(modelData.id), ListView.Contain)
+ }
+
+ CursorShape {
anchors.fill: parent
- preventStealing: false
- onClicked: chat.positionViewAtIndex(chat.model.idToIndex(modelData.id), ListView.Contain)
cursorShape: Qt.PointingHandCursor
}
@@ -43,10 +45,8 @@ Item {
color: replyComponent.userColor
textFormat: Text.RichText
- MouseArea {
- anchors.fill: parent
- onClicked: chat.model.openUserProfile(reply.modelData.userId)
- cursorShape: Qt.PointingHandCursor
+ TapHandler {
+ onSingleTapped: chat.model.openUserProfile(reply.modelData.userId)
}
}
diff --git a/resources/qml/delegates/TextMessage.qml b/resources/qml/delegates/TextMessage.qml
index 2c449ca2..82d0d0e4 100644
--- a/resources/qml/delegates/TextMessage.qml
+++ b/resources/qml/delegates/TextMessage.qml
@@ -8,5 +8,6 @@ MatrixText {
width: parent ? parent.width : undefined
height: isReply ? Math.round(Math.min(timelineRoot.height / 8, implicitHeight)) : undefined
clip: isReply
+ selectByMouse: !Settings.mobileMode && !isReply
font.pointSize: (Settings.enlargeEmojiOnlyMessages && model.data.isOnlyEmoji > 0 && model.data.isOnlyEmoji < 4) ? Settings.fontSize * 3 : Settings.fontSize
}
diff --git a/src/timeline/TimelineViewManager.cpp b/src/timeline/TimelineViewManager.cpp
index e1e2b681..b7d2bfb1 100644
--- a/src/timeline/TimelineViewManager.cpp
+++ b/src/timeline/TimelineViewManager.cpp
@@ -21,6 +21,7 @@
#include "dialogs/ImageOverlay.h"
#include "emoji/EmojiModel.h"
#include "emoji/Provider.h"
+#include "ui/NhekoCursorShape.h"
#include "ui/NhekoDropArea.h"
#include <iostream> //only for debugging
@@ -118,6 +119,7 @@ TimelineViewManager::TimelineViewManager(CallManager *callManager, ChatPage *par
qmlRegisterType<DelegateChoice>("im.nheko", 1, 0, "DelegateChoice");
qmlRegisterType<DelegateChooser>("im.nheko", 1, 0, "DelegateChooser");
qmlRegisterType<NhekoDropArea>("im.nheko", 1, 0, "NhekoDropArea");
+ qmlRegisterType<NhekoCursorShape>("im.nheko", 1, 0, "CursorShape");
qmlRegisterUncreatableType<DeviceVerificationFlow>(
"im.nheko", 1, 0, "DeviceVerificationFlow", "Can't create verification flow from QML!");
qmlRegisterUncreatableType<UserProfile>(
@@ -548,4 +550,4 @@ void
TimelineViewManager::focusMessageInput()
{
emit focusInput();
-}
\ No newline at end of file
+}
diff --git a/src/ui/NhekoCursorShape.cpp b/src/ui/NhekoCursorShape.cpp
new file mode 100644
index 00000000..06b0a321
--- /dev/null
+++ b/src/ui/NhekoCursorShape.cpp
@@ -0,0 +1,25 @@
+#include "NhekoCursorShape.h"
+
+#include <QCursor>
+
+NhekoCursorShape::NhekoCursorShape(QQuickItem *parent)
+ : QQuickItem(parent)
+ , currentShape_(Qt::CursorShape::ArrowCursor)
+{}
+
+Qt::CursorShape
+NhekoCursorShape::cursorShape() const
+{
+ return cursor().shape();
+}
+
+void
+NhekoCursorShape::setCursorShape(Qt::CursorShape cursorShape)
+{
+ if (currentShape_ == cursorShape)
+ return;
+
+ currentShape_ = cursorShape;
+ setCursor(cursorShape);
+ emit cursorShapeChanged();
+}
diff --git a/src/ui/NhekoCursorShape.h b/src/ui/NhekoCursorShape.h
new file mode 100644
index 00000000..2eab5e42
--- /dev/null
+++ b/src/ui/NhekoCursorShape.h
@@ -0,0 +1,26 @@
+#pragma once
+
+// see
+// https://stackoverflow.com/questions/27821054/how-to-change-cursor-shape-in-qml-when-mousearea-is-covered-with-another-mousear/29382092#29382092
+
+#include <QQuickItem>
+
+class NhekoCursorShape : public QQuickItem
+{
+ Q_OBJECT
+
+ Q_PROPERTY(Qt::CursorShape cursorShape READ cursorShape WRITE setCursorShape NOTIFY
+ cursorShapeChanged)
+
+public:
+ explicit NhekoCursorShape(QQuickItem *parent = 0);
+
+private:
+ Qt::CursorShape cursorShape() const;
+ void setCursorShape(Qt::CursorShape cursorShape);
+
+ Qt::CursorShape currentShape_;
+
+signals:
+ void cursorShapeChanged();
+};
|