Fix hover handling in the timeline

This commit is contained in:
Nicolas Werner 2021-02-14 01:28:28 +01:00
parent 0d61f4bff1
commit d43607d01c
16 changed files with 127 additions and 67 deletions

View file

@ -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

View file

@ -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/)

View file

@ -90,4 +90,9 @@ Rectangle {
}
}
CursorShape {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
}
}

View file

@ -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 {

View file

@ -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
}

View file

@ -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
}
}

View file

@ -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
}
}

View file

@ -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
}
}

View file

@ -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
}

View file

@ -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

View file

@ -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
}

View file

@ -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)
}
}

View file

@ -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
}

View file

@ -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();
}
}

View file

@ -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();
}

26
src/ui/NhekoCursorShape.h Normal file
View file

@ -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();
};