mirror of
https://github.com/Nheko-Reborn/nheko.git
synced 2024-11-25 20:48:52 +03:00
Fix hover handling in the timeline
This commit is contained in:
parent
0d61f4bff1
commit
d43607d01c
16 changed files with 127 additions and 67 deletions
|
@ -281,6 +281,7 @@ set(SRC_FILES
|
||||||
src/ui/InfoMessage.cpp
|
src/ui/InfoMessage.cpp
|
||||||
src/ui/Label.cpp
|
src/ui/Label.cpp
|
||||||
src/ui/LoadingIndicator.cpp
|
src/ui/LoadingIndicator.cpp
|
||||||
|
src/ui/NhekoCursorShape.cpp
|
||||||
src/ui/NhekoDropArea.cpp
|
src/ui/NhekoDropArea.cpp
|
||||||
src/ui/OverlayModal.cpp
|
src/ui/OverlayModal.cpp
|
||||||
src/ui/OverlayWidget.cpp
|
src/ui/OverlayWidget.cpp
|
||||||
|
@ -495,6 +496,7 @@ qt5_wrap_cpp(MOC_HEADERS
|
||||||
src/ui/Label.h
|
src/ui/Label.h
|
||||||
src/ui/FloatingButton.h
|
src/ui/FloatingButton.h
|
||||||
src/ui/Menu.h
|
src/ui/Menu.h
|
||||||
|
src/ui/NhekoCursorShape.h
|
||||||
src/ui/NhekoDropArea.h
|
src/ui/NhekoDropArea.h
|
||||||
src/ui/OverlayWidget.h
|
src/ui/OverlayWidget.h
|
||||||
src/ui/SnackBar.h
|
src/ui/SnackBar.h
|
||||||
|
|
|
@ -116,9 +116,7 @@ brew install --cask nheko
|
||||||
|
|
||||||
### Build Requirements
|
### Build Requirements
|
||||||
|
|
||||||
- Qt5 (5.10 or greater). Qt 5.7 adds support for color font rendering with
|
- Qt5 (5.12 or greater). Required for overlapping hover handlers in Qml.
|
||||||
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.
|
|
||||||
- CMake 3.15 or greater. (Lower version may work, but may break boost linking)
|
- CMake 3.15 or greater. (Lower version may work, but may break boost linking)
|
||||||
- [mtxclient](https://github.com/Nheko-Reborn/mtxclient)
|
- [mtxclient](https://github.com/Nheko-Reborn/mtxclient)
|
||||||
- [LMDB](https://symas.com/lightning-memory-mapped-database/)
|
- [LMDB](https://symas.com/lightning-memory-mapped-database/)
|
||||||
|
|
|
@ -90,4 +90,9 @@ Rectangle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CursorShape {
|
||||||
|
anchors.fill: parent
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import QtQuick 2.5
|
import QtQuick 2.12
|
||||||
import QtQuick.Controls 2.1
|
import QtQuick.Controls 2.1
|
||||||
import im.nheko 1.0
|
import im.nheko 1.0
|
||||||
|
|
||||||
|
@ -24,14 +24,11 @@ Rectangle {
|
||||||
color: "transparent"
|
color: "transparent"
|
||||||
width: 16
|
width: 16
|
||||||
height: 16
|
height: 16
|
||||||
ToolTip.visible: ma.containsMouse && indicator.visible
|
ToolTip.visible: ma.hovered && indicator.visible
|
||||||
ToolTip.text: getEncryptionTooltip()
|
ToolTip.text: getEncryptionTooltip()
|
||||||
|
|
||||||
MouseArea {
|
HoverHandler {
|
||||||
id: ma
|
id: ma
|
||||||
|
|
||||||
anchors.fill: parent
|
|
||||||
hoverEnabled: true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import "./ui"
|
import "./ui"
|
||||||
import QtQuick 2.3
|
import QtQuick 2.3
|
||||||
import QtQuick.Controls 2.3
|
import QtQuick.Controls 2.3
|
||||||
|
import im.nheko 1.0 // for cursor shape
|
||||||
|
|
||||||
AbstractButton {
|
AbstractButton {
|
||||||
id: button
|
id: button
|
||||||
|
@ -23,11 +24,10 @@ AbstractButton {
|
||||||
source: image != "" ? ("image://colorimage/" + image + "?" + ((button.hovered && changeColorOnHover) ? highlightColor : buttonTextColor)) : ""
|
source: image != "" ? ("image://colorimage/" + image + "?" + ((button.hovered && changeColorOnHover) ? highlightColor : buttonTextColor)) : ""
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
CursorShape {
|
||||||
id: mouseArea
|
id: mouseArea
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
onPressed: mouse.accepted = false
|
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@ TextEdit {
|
||||||
focus: false
|
focus: false
|
||||||
wrapMode: Text.Wrap
|
wrapMode: Text.Wrap
|
||||||
selectByMouse: !Settings.mobileMode
|
selectByMouse: !Settings.mobileMode
|
||||||
|
enabled: selectByMouse
|
||||||
color: colors.text
|
color: colors.text
|
||||||
onLinkActivated: {
|
onLinkActivated: {
|
||||||
if (/^https:\/\/matrix.to\/#\/(@.*)$/.test(link)) {
|
if (/^https:\/\/matrix.to\/#\/(@.*)$/.test(link)) {
|
||||||
|
@ -25,12 +26,9 @@ TextEdit {
|
||||||
ToolTip.visible: hoveredLink
|
ToolTip.visible: hoveredLink
|
||||||
ToolTip.text: hoveredLink
|
ToolTip.text: hoveredLink
|
||||||
|
|
||||||
MouseArea {
|
CursorShape {
|
||||||
id: ma
|
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
acceptedButtons: Qt.NoButton
|
cursorShape: hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||||
cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import "./delegates"
|
import "./delegates"
|
||||||
import QtGraphicalEffects 1.0
|
import QtGraphicalEffects 1.0
|
||||||
import QtQuick 2.9
|
import QtQuick 2.12
|
||||||
import QtQuick.Controls 2.3
|
import QtQuick.Controls 2.3
|
||||||
import QtQuick.Layouts 1.2
|
import QtQuick.Layouts 1.2
|
||||||
import QtQuick.Window 2.2
|
import QtQuick.Window 2.2
|
||||||
|
@ -153,12 +153,15 @@ ScrollView {
|
||||||
color: TimelineManager.userColor(modelData ? modelData.userId : "", colors.window)
|
color: TimelineManager.userColor(modelData ? modelData.userId : "", colors.window)
|
||||||
textFormat: Text.RichText
|
textFormat: Text.RichText
|
||||||
|
|
||||||
MouseArea {
|
TapHandler {
|
||||||
|
//cursorShape: Qt.PointingHandCursor
|
||||||
|
|
||||||
|
onSingleTapped: chat.model.openUserProfile(modelData.userId)
|
||||||
|
}
|
||||||
|
|
||||||
|
CursorShape {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
onClicked: chat.model.openUserProfile(modelData.userId)
|
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
propagateComposedEvents: true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import "./delegates"
|
import "./delegates"
|
||||||
import "./emoji"
|
import "./emoji"
|
||||||
import QtQuick 2.6
|
import QtQuick 2.12
|
||||||
import QtQuick.Controls 2.3
|
import QtQuick.Controls 2.3
|
||||||
import QtQuick.Layouts 1.2
|
import QtQuick.Layouts 1.2
|
||||||
import QtQuick.Window 2.2
|
import QtQuick.Window 2.2
|
||||||
|
@ -12,27 +12,23 @@ Item {
|
||||||
height: row.height
|
height: row.height
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
color: (Settings.messageHoverHighlight && hoverHandler.containsMouse) ? colors.alternateBase : "transparent"
|
color: (Settings.messageHoverHighlight && hoverHandler.hovered) ? colors.alternateBase : "transparent"
|
||||||
anchors.fill: row
|
anchors.fill: row
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
HoverHandler {
|
||||||
id: hoverHandler
|
id: hoverHandler
|
||||||
|
|
||||||
anchors.fill: parent
|
acceptedDevices: PointerDevice.GenericPointer
|
||||||
propagateComposedEvents: true
|
}
|
||||||
preventStealing: false
|
|
||||||
hoverEnabled: true
|
TapHandler {
|
||||||
acceptedButtons: Qt.AllButtons
|
acceptedButtons: Qt.RightButton
|
||||||
onClicked: {
|
onSingleTapped: messageContextMenu.show(model.id, model.type, model.isEncrypted, model.isEditable, row, mapToItem(timelineRoot, eventPoint.position.x, eventPoint.position.y))
|
||||||
if (mouse.button === Qt.RightButton)
|
}
|
||||||
messageContextMenu.show(model.id, model.type, model.isEncrypted, model.isEditable, row);
|
|
||||||
else
|
TapHandler {
|
||||||
mouse.accepted = false;
|
onLongPressed: messageContextMenu.show(model.id, model.type, model.isEncrypted, model.isEditable, row, mapToItem(timelineRoot, point.position.x, point.position.y))
|
||||||
}
|
|
||||||
onPressAndHold: {
|
|
||||||
messageContextMenu.show(model.id, model.type, model.isEncrypted, model.isEditable, row, mapToItem(timelineRoot, mouse.x, mouse.y));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
|
@ -151,15 +147,11 @@ Item {
|
||||||
text: model.timestamp.toLocaleTimeString("HH:mm")
|
text: model.timestamp.toLocaleTimeString("HH:mm")
|
||||||
width: Math.max(implicitWidth, text.length * fontMetrics.maximumCharacterWidth)
|
width: Math.max(implicitWidth, text.length * fontMetrics.maximumCharacterWidth)
|
||||||
color: inactiveColors.text
|
color: inactiveColors.text
|
||||||
ToolTip.visible: ma.containsMouse
|
ToolTip.visible: ma.hovered
|
||||||
ToolTip.text: Qt.formatDateTime(model.timestamp, Qt.DefaultLocaleLongDate)
|
ToolTip.text: Qt.formatDateTime(model.timestamp, Qt.DefaultLocaleLongDate)
|
||||||
|
|
||||||
MouseArea {
|
HoverHandler {
|
||||||
id: ma
|
id: ma
|
||||||
|
|
||||||
anchors.fill: parent
|
|
||||||
hoverEnabled: true
|
|
||||||
propagateComposedEvents: true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import QtQuick 2.6
|
import QtQuick 2.12
|
||||||
import QtQuick.Layouts 1.2
|
import QtQuick.Layouts 1.2
|
||||||
import im.nheko 1.0
|
import im.nheko 1.0
|
||||||
|
|
||||||
|
@ -31,7 +31,15 @@ Item {
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
anchors.fill: parent
|
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
|
cursorShape: Qt.PointingHandCursor
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import QtQuick 2.6
|
import QtQuick 2.12
|
||||||
import im.nheko 1.0
|
import im.nheko 1.0
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
|
@ -32,20 +32,20 @@ Item {
|
||||||
smooth: true
|
smooth: true
|
||||||
mipmap: true
|
mipmap: true
|
||||||
|
|
||||||
MouseArea {
|
TapHandler {
|
||||||
id: mouseArea
|
|
||||||
|
|
||||||
enabled: model.data.type == MtxEvent.ImageMessage && img.status == Image.Ready
|
enabled: model.data.type == MtxEvent.ImageMessage && img.status == Image.Ready
|
||||||
hoverEnabled: true
|
onSingleTapped: TimelineManager.openImageOverlay(model.data.url, model.data.id)
|
||||||
anchors.fill: parent
|
}
|
||||||
onClicked: TimelineManager.openImageOverlay(model.data.url, model.data.id)
|
|
||||||
|
HoverHandler {
|
||||||
|
id: mouseArea
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: overlay
|
id: overlay
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
visible: mouseArea.containsMouse
|
visible: mouseArea.hovered
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: container
|
id: container
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import QtMultimedia 5.6
|
import QtMultimedia 5.6
|
||||||
import QtQuick 2.6
|
import QtQuick 2.12
|
||||||
import QtQuick.Controls 2.1
|
import QtQuick.Controls 2.1
|
||||||
import QtQuick.Layouts 1.2
|
import QtQuick.Layouts 1.2
|
||||||
import im.nheko 1.0
|
import im.nheko 1.0
|
||||||
|
@ -140,9 +140,8 @@ Rectangle {
|
||||||
fillMode: Image.Pad
|
fillMode: Image.Pad
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
TapHandler {
|
||||||
anchors.fill: parent
|
onSingleTapped: {
|
||||||
onClicked: {
|
|
||||||
switch (button.state) {
|
switch (button.state) {
|
||||||
case "":
|
case "":
|
||||||
TimelineManager.timeline.cacheMedia(model.data.id);
|
TimelineManager.timeline.cacheMedia(model.data.id);
|
||||||
|
@ -159,6 +158,10 @@ Rectangle {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CursorShape {
|
||||||
|
anchors.fill: parent
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import QtQuick 2.6
|
import QtQuick 2.12
|
||||||
import QtQuick.Controls 2.3
|
import QtQuick.Controls 2.3
|
||||||
import QtQuick.Layouts 1.2
|
import QtQuick.Layouts 1.2
|
||||||
import QtQuick.Window 2.2
|
import QtQuick.Window 2.2
|
||||||
|
@ -13,10 +13,12 @@ Item {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: replyContainer.height
|
height: replyContainer.height
|
||||||
|
|
||||||
MouseArea {
|
TapHandler {
|
||||||
|
onSingleTapped: chat.positionViewAtIndex(chat.model.idToIndex(modelData.id), ListView.Contain)
|
||||||
|
}
|
||||||
|
|
||||||
|
CursorShape {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
preventStealing: false
|
|
||||||
onClicked: chat.positionViewAtIndex(chat.model.idToIndex(modelData.id), ListView.Contain)
|
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,10 +45,8 @@ Item {
|
||||||
color: replyComponent.userColor
|
color: replyComponent.userColor
|
||||||
textFormat: Text.RichText
|
textFormat: Text.RichText
|
||||||
|
|
||||||
MouseArea {
|
TapHandler {
|
||||||
anchors.fill: parent
|
onSingleTapped: chat.model.openUserProfile(reply.modelData.userId)
|
||||||
onClicked: chat.model.openUserProfile(reply.modelData.userId)
|
|
||||||
cursorShape: Qt.PointingHandCursor
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,5 +8,6 @@ MatrixText {
|
||||||
width: parent ? parent.width : undefined
|
width: parent ? parent.width : undefined
|
||||||
height: isReply ? Math.round(Math.min(timelineRoot.height / 8, implicitHeight)) : undefined
|
height: isReply ? Math.round(Math.min(timelineRoot.height / 8, implicitHeight)) : undefined
|
||||||
clip: isReply
|
clip: isReply
|
||||||
|
selectByMouse: !Settings.mobileMode && !isReply
|
||||||
font.pointSize: (Settings.enlargeEmojiOnlyMessages && model.data.isOnlyEmoji > 0 && model.data.isOnlyEmoji < 4) ? Settings.fontSize * 3 : Settings.fontSize
|
font.pointSize: (Settings.enlargeEmojiOnlyMessages && model.data.isOnlyEmoji > 0 && model.data.isOnlyEmoji < 4) ? Settings.fontSize * 3 : Settings.fontSize
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
#include "dialogs/ImageOverlay.h"
|
#include "dialogs/ImageOverlay.h"
|
||||||
#include "emoji/EmojiModel.h"
|
#include "emoji/EmojiModel.h"
|
||||||
#include "emoji/Provider.h"
|
#include "emoji/Provider.h"
|
||||||
|
#include "ui/NhekoCursorShape.h"
|
||||||
#include "ui/NhekoDropArea.h"
|
#include "ui/NhekoDropArea.h"
|
||||||
|
|
||||||
#include <iostream> //only for debugging
|
#include <iostream> //only for debugging
|
||||||
|
@ -118,6 +119,7 @@ TimelineViewManager::TimelineViewManager(CallManager *callManager, ChatPage *par
|
||||||
qmlRegisterType<DelegateChoice>("im.nheko", 1, 0, "DelegateChoice");
|
qmlRegisterType<DelegateChoice>("im.nheko", 1, 0, "DelegateChoice");
|
||||||
qmlRegisterType<DelegateChooser>("im.nheko", 1, 0, "DelegateChooser");
|
qmlRegisterType<DelegateChooser>("im.nheko", 1, 0, "DelegateChooser");
|
||||||
qmlRegisterType<NhekoDropArea>("im.nheko", 1, 0, "NhekoDropArea");
|
qmlRegisterType<NhekoDropArea>("im.nheko", 1, 0, "NhekoDropArea");
|
||||||
|
qmlRegisterType<NhekoCursorShape>("im.nheko", 1, 0, "CursorShape");
|
||||||
qmlRegisterUncreatableType<DeviceVerificationFlow>(
|
qmlRegisterUncreatableType<DeviceVerificationFlow>(
|
||||||
"im.nheko", 1, 0, "DeviceVerificationFlow", "Can't create verification flow from QML!");
|
"im.nheko", 1, 0, "DeviceVerificationFlow", "Can't create verification flow from QML!");
|
||||||
qmlRegisterUncreatableType<UserProfile>(
|
qmlRegisterUncreatableType<UserProfile>(
|
||||||
|
@ -548,4 +550,4 @@ void
|
||||||
TimelineViewManager::focusMessageInput()
|
TimelineViewManager::focusMessageInput()
|
||||||
{
|
{
|
||||||
emit focusInput();
|
emit focusInput();
|
||||||
}
|
}
|
||||||
|
|
25
src/ui/NhekoCursorShape.cpp
Normal file
25
src/ui/NhekoCursorShape.cpp
Normal 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
26
src/ui/NhekoCursorShape.h
Normal 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();
|
||||||
|
};
|
Loading…
Reference in a new issue