summary refs log tree commit diff
path: root/resources/qml
diff options
context:
space:
mode:
Diffstat (limited to 'resources/qml')
-rw-r--r--resources/qml/ImageButton.qml3
-rw-r--r--resources/qml/Reactions.qml33
-rw-r--r--resources/qml/ScrollHelper.qml2
-rw-r--r--resources/qml/TimelineRow.qml18
-rw-r--r--resources/qml/TimelineView.qml21
-rw-r--r--resources/qml/delegates/Pill.qml1
-rw-r--r--resources/qml/emoji/EmojiButton.qml18
-rw-r--r--resources/qml/emoji/EmojiPicker.qml290
8 files changed, 373 insertions, 13 deletions
diff --git a/resources/qml/ImageButton.qml b/resources/qml/ImageButton.qml

index dd100503..dd67d597 100644 --- a/resources/qml/ImageButton.qml +++ b/resources/qml/ImageButton.qml
@@ -3,7 +3,8 @@ import QtQuick.Controls 2.3 AbstractButton { property string image: undefined - + width: 16 + height: 16 id: button Image { diff --git a/resources/qml/Reactions.qml b/resources/qml/Reactions.qml
index f42e8612..c06dc826 100644 --- a/resources/qml/Reactions.qml +++ b/resources/qml/Reactions.qml
@@ -1,7 +1,19 @@ import QtQuick 2.6 import QtQuick.Controls 2.2 +// This class is for showing Reactions in the timeline row, not for +// adding new reactions via the emoji picker Flow { + id: reactionFlow + + // highlight colors for selfReactedEvent background + property real highlightHue: colors.highlight.hslHue + property real highlightSat: colors.highlight.hslSaturation + property real highlightLight: colors.highlight.hslLightness + + property string eventId + property string roomId + anchors.left: parent.left anchors.right: parent.right spacing: 4 @@ -11,9 +23,8 @@ Flow { Repeater { id: repeater - AbstractButton { + delegate: AbstractButton { id: reaction - text: model.key hoverEnabled: true implicitWidth: contentItem.childrenRect.width + contentItem.leftPadding*2 implicitHeight: contentItem.childrenRect.height @@ -21,6 +32,11 @@ Flow { ToolTip.visible: hovered ToolTip.text: model.users + onClicked: { + console.debug("Picked " + model.key + "in response to " + reactionFlow.eventId + " in room " + reactionFlow.roomId + ". selfReactedEvent: " + model.selfReactedEvent) + timelineManager.reactToMessage(reactionFlow.roomId, reactionFlow.eventId, model.key, model.selfReactedEvent) + } + contentItem: Row { anchors.centerIn: parent @@ -33,13 +49,13 @@ Flow { font.family: settings.emojiFont elide: Text.ElideRight elideWidth: 150 - text: reaction.text + text: model.key } Text { anchors.baseline: reactionCounter.baseline id: reactionText - text: textMetrics.elidedText + (textMetrics.elidedText == textMetrics.text ? "" : "…") + text: textMetrics.elidedText + (textMetrics.elidedText == model.key ? "" : "…") font.family: settings.emojiFont color: reaction.hovered ? colors.highlight : colors.text maximumLineCount: 1 @@ -49,7 +65,7 @@ Flow { id: divider height: Math.floor(reactionCounter.implicitHeight * 1.4) width: 1 - color: reaction.hovered ? colors.highlight : colors.text + color: (reaction.hovered || model.selfReactedEvent !== '') ? colors.highlight : colors.text } Text { @@ -63,10 +79,11 @@ Flow { background: Rectangle { anchors.centerIn: parent + implicitWidth: reaction.implicitWidth - height: reaction.implicitHeight - border.color: (reaction.hovered || model.selfReacted )? colors.highlight : colors.text - color: colors.base + implicitHeight: reaction.implicitHeight + border.color: (reaction.hovered || model.selfReactedEvent !== '') ? colors.highlight : colors.text + color: model.selfReactedEvent !== '' ? Qt.hsla(highlightHue, highlightSat, highlightLight, 0.20) : colors.base border.width: 1 radius: reaction.height / 2.0 } diff --git a/resources/qml/ScrollHelper.qml b/resources/qml/ScrollHelper.qml
index cdb4a23a..30bf9ac2 100644 --- a/resources/qml/ScrollHelper.qml +++ b/resources/qml/ScrollHelper.qml
@@ -71,7 +71,7 @@ MouseArea { pixelDelta = wheel.pixelDelta.y } - pixelDelta = Math.round(pixelDelta) + pixelDelta = Math.round(pixelDelta) if (!pixelDelta) { return flickableItem.contentY; diff --git a/resources/qml/TimelineRow.qml b/resources/qml/TimelineRow.qml
index 528dce85..dfee62dc 100644 --- a/resources/qml/TimelineRow.qml +++ b/resources/qml/TimelineRow.qml
@@ -6,6 +6,7 @@ import QtQuick.Window 2.2 import im.nheko 1.0 import "./delegates" +import "./emoji" MouseArea { anchors.left: parent.left @@ -58,7 +59,10 @@ MouseArea { } Reactions { + id: reactionRow reactions: model.reactions + roomId: model.roomId + eventId: model.id } } @@ -76,7 +80,19 @@ MouseArea { Layout.preferredHeight: 16 width: 16 } - + EmojiButton { + visible: settings.buttonsInTimeline + Layout.alignment: Qt.AlignRight | Qt.AlignTop + Layout.preferredHeight: 16 + width: 16 + id: reactButton + hoverEnabled: true + ToolTip.visible: hovered + ToolTip.text: qsTr("React") + emojiPicker: emojiPopup + room_id: model.roomId + event_id: model.id + } ImageButton { visible: settings.buttonsInTimeline Layout.alignment: Qt.AlignRight | Qt.AlignTop diff --git a/resources/qml/TimelineView.qml b/resources/qml/TimelineView.qml
index ea6bf093..f3d5d219 100644 --- a/resources/qml/TimelineView.qml +++ b/resources/qml/TimelineView.qml
@@ -5,14 +5,19 @@ import QtGraphicalEffects 1.0 import QtQuick.Window 2.2 import im.nheko 1.0 +import im.nheko.EmojiModel 1.0 import "./delegates" +import "./emoji" Page { property var colors: currentActivePalette property var systemInactive: SystemPalette { colorGroup: SystemPalette.Disabled } property var inactiveColors: currentInactivePalette ? currentInactivePalette : systemInactive property int avatarSize: 40 + property real highlightHue: colors.highlight.hslHue + property real highlightSat: colors.highlight.hslSaturation + property real highlightLight: colors.highlight.hslLightness palette: colors @@ -20,6 +25,17 @@ Page { id: fontMetrics } + EmojiPicker { + id: emojiPopup + width: 7 * 52 + 20 + height: 6 * 52 + colors: palette + model: EmojiProxyModel { + category: EmojiCategory.People + sourceModel: EmojiModel {} + } + } + Menu { id: messageContextMenu modal: true @@ -34,7 +50,10 @@ Page { property string eventId property int eventType property bool isEncrypted - + MenuItem { + text: qsTr("React") + onClicked: chat.model.reactAction(messageContextMenu.eventId) + } MenuItem { text: qsTr("Reply") onClicked: chat.model.replyAction(messageContextMenu.eventId) diff --git a/resources/qml/delegates/Pill.qml b/resources/qml/delegates/Pill.qml
index dbf0e5f6..27985b58 100644 --- a/resources/qml/delegates/Pill.qml +++ b/resources/qml/delegates/Pill.qml
@@ -11,5 +11,4 @@ Label { radius: parent.height / 2 color: colors.dark } - } diff --git a/resources/qml/emoji/EmojiButton.qml b/resources/qml/emoji/EmojiButton.qml new file mode 100644
index 00000000..f8f75e3e --- /dev/null +++ b/resources/qml/emoji/EmojiButton.qml
@@ -0,0 +1,18 @@ +import QtQuick 2.10 +import QtQuick.Controls 2.1 +import im.nheko 1.0 +import im.nheko.EmojiModel 1.0 + +import "../" + +ImageButton { + property var colors: currentActivePalette + property var emojiPicker + property string room_id + property string event_id + + image: ":/icons/icons/ui/smile.png" + id: emojiButton + onClicked: emojiPicker.visible ? emojiPicker.close() : emojiPicker.show(emojiButton, room_id, event_id) + +} diff --git a/resources/qml/emoji/EmojiPicker.qml b/resources/qml/emoji/EmojiPicker.qml new file mode 100644
index 00000000..b70923ae --- /dev/null +++ b/resources/qml/emoji/EmojiPicker.qml
@@ -0,0 +1,290 @@ +import QtQuick 2.9 +import QtQuick.Controls 2.9 +import QtQuick.Layouts 1.3 +import QtGraphicalEffects 1.9 + +import im.nheko 1.0 +import im.nheko.EmojiModel 1.0 + +import "../" + +Popup { + + function show(showAt, room_id, event_id) { + console.debug("Showing emojiPicker for " + event_id + "in room " + room_id) + parent = showAt + x = Math.round((showAt.width - width) / 2) + y = showAt.height + emojiPopup.room_id = room_id + emojiPopup.event_id = event_id + open() + } + + property string room_id + property string event_id + property var colors + property alias model: gridView.model + property var textArea + property string emojiCategory: "people" + property real highlightHue: colors.highlight.hslHue + property real highlightSat: colors.highlight.hslSaturation + property real highlightLight: colors.highlight.hslLightness + + id: emojiPopup + + margins: 0 + bottomPadding: 1 + leftPadding: 1 + rightPadding: 1 + + modal: true + focus: true + closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside + + ColumnLayout { + id: columnView + anchors.fill: parent + spacing: 0 + Layout.bottomMargin: 0 + Layout.leftMargin: 3 + Layout.rightMargin: 3 + Layout.topMargin: 2 + + // emoji grid + GridView { + id: gridView + + Layout.preferredHeight: emojiPopup.height + Layout.fillWidth: true + Layout.fillHeight: true + Layout.leftMargin: 4 + + cellWidth: 52 + cellHeight: 52 + + boundsBehavior: Flickable.StopAtBounds + + clip: true + + // Individual emoji + delegate: AbstractButton { + width: 48 + height: 48 + contentItem: Text { + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + font.family: settings.emojiFont + + font.pixelSize: 36 + text: model.unicode + } + + background: Rectangle { + anchors.fill: parent + color: hovered ? colors.highlight : 'transparent' + radius: 5 + } + + hoverEnabled: true + ToolTip.text: model.shortName + ToolTip.visible: hovered + + // give the emoji a little oomf + DropShadow { + width: parent.width; + height: parent.height; + horizontalOffset: 3 + verticalOffset: 3 + radius: 8.0 + samples: 17 + color: "#80000000" + source: parent.contentItem + } + // TODO: maybe add favorites at some point? + onClicked: { + console.debug("Picked " + model.unicode + "in response to " + emojiPopup.event_id + " in room " + emojiPopup.room_id) + emojiPopup.close() + timelineManager.queueReactionMessage(emojiPopup.room_id, emojiPopup.event_id, model.unicode) + } + } + + // Search field + header: TextField { + id: emojiSearch + anchors.left: parent.left + anchors.right: parent.right + anchors.rightMargin: emojiScroll.width + 4 + placeholderText: qsTr("Search") + selectByMouse: true + rightPadding: clearSearch.width + + Timer { + id: searchTimer + interval: 350 // tweak as needed? + onTriggered: { + emojiPopup.model.filter = emojiSearch.text + emojiPopup.model.category = EmojiCategory.Search + } + } + + ToolButton { + id: clearSearch + anchors { + verticalCenter: parent.verticalCenter + right: parent.right + } + // clear the default hover effects. + background: Item {} + visible: emojiSearch.text !== '' + icon.source: "image://colorimage/:/icons/icons/ui/round-remove-button.png?" + (clearSearch.hovered ? colors.highlight : colors.buttonText) + focusPolicy: Qt.NoFocus + onClicked: emojiSearch.clear() + } + + onTextChanged: searchTimer.restart() + onVisibleChanged: if (visible) forceActiveFocus() + } + + ScrollBar.vertical: ScrollBar { + id: emojiScroll + } + } + + // Separator + Rectangle { + Layout.fillWidth: true + Layout.preferredHeight: 1 + + color: emojiPopup.colors.dark + } + + // Category picker row + RowLayout { + Layout.bottomMargin: 0 + Layout.preferredHeight: 42 + implicitHeight: 42 + Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom + // Display the normal categories + Repeater { + model: ListModel { + // TODO: Would like to get 'simple' icons for the categories + ListElement { image: ":/icons/icons/emoji-categories/people.png"; category: EmojiCategory.People } + ListElement { image: ":/icons/icons/emoji-categories/nature.png"; category: EmojiCategory.Nature } + ListElement { image: ":/icons/icons/emoji-categories/foods.png"; category: EmojiCategory.Food } + ListElement { image: ":/icons/icons/emoji-categories/activity.png"; category: EmojiCategory.Activity } + ListElement { image: ":/icons/icons/emoji-categories/travel.png"; category: EmojiCategory.Travel } + ListElement { image: ":/icons/icons/emoji-categories/objects.png"; category: EmojiCategory.Objects } + ListElement { image: ":/icons/icons/emoji-categories/symbols.png"; category: EmojiCategory.Symbols } + ListElement { image: ":/icons/icons/emoji-categories/flags.png"; category: EmojiCategory.Flags } + } + + delegate: AbstractButton { + Layout.preferredWidth: 36 + Layout.preferredHeight: 36 + + contentItem: Image { + horizontalAlignment: Image.AlignHCenter + verticalAlignment: Image.AlignVCenter + fillMode: Image.Pad + sourceSize.width: 32 + sourceSize.height: 32 + source: "image://colorimage/" + model.image + "?" + (hovered ? colors.highlight : colors.buttonText) + } + + MouseArea + { + id: mouseArea + anchors.fill: parent + onPressed: mouse.accepted = false + cursorShape: Qt.PointingHandCursor + } + + background: Rectangle { + anchors.fill: parent + + color: emojiPopup.model.category === model.category ? Qt.hsla(highlightHue, highlightSat, highlightLight, 0.20) : 'transparent' + radius: 5 + border.color: emojiPopup.model.category === model.category ? colors.highlight : 'transparent' + } + + hoverEnabled: true + ToolTip.text: { + switch (model.category) { + case EmojiCategory.People: + return qsTr('People'); + case EmojiCategory.Nature: + return qsTr('Nature'); + case EmojiCategory.Food: + return qsTr('Food'); + case EmojiCategory.Activity: + return qsTr('Activity'); + case EmojiCategory.Travel: + return qsTr('Travel'); + case EmojiCategory.Objects: + return qsTr('Objects'); + case EmojiCategory.Symbols: + return qsTr('Symbols'); + case EmojiCategory.Flags: + return qsTr('Flags'); + } + } + ToolTip.visible: hovered + + onClicked: { + emojiPopup.model.category = model.category + } + } + } + + // Separator + Rectangle { + Layout.fillHeight: true + Layout.preferredWidth: 1 + implicitWidth: 1 + height: parent.height + + color: emojiPopup.colors.dark + } + + // Search Button is special + AbstractButton { + id: searchBtn + hoverEnabled: true + Layout.alignment: Qt.AlignRight + Layout.bottomMargin: 0 + + ToolTip.text: qsTr("Search") + ToolTip.visible: hovered + onClicked: { + // clear any filters + emojiPopup.model.category = EmojiCategory.Search + gridView.positionViewAtBeginning() + emojiSearch.forceActiveFocus() + } + Layout.preferredWidth: 36 + Layout.preferredHeight: 36 + implicitWidth: 36 + implicitHeight: 36 + + contentItem: Image { + anchors.right: parent.right + horizontalAlignment: Image.AlignHCenter + verticalAlignment: Image.AlignVCenter + sourceSize.width: 32 + sourceSize.height: 32 + fillMode: Image.Pad + smooth: true + source: "image://colorimage/:/icons/icons/ui/search.png?" + (parent.hovered ? colors.highlight : colors.buttonText) + } + + MouseArea + { + id: mouseArea + anchors.fill: parent + onPressed: mouse.accepted = false + cursorShape: Qt.PointingHandCursor + } + } + } + } +}