Room Drawer Media Tab
Add a tab bar to the room drawer which includes a new media tab in addition to the room information tab. This mr completes the architecture for adding others easily later e.g. message highlights or threads. To put this together I had to make sure things like the menus and the maximize delegate were available to both the room drawer and page so there is some rework there to put it all together. Wide\  Mobile\ 
This commit is contained in:
@@ -2,12 +2,17 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||||
|
|
||||||
#include "mediamessagefiltermodel.h"
|
#include "mediamessagefiltermodel.h"
|
||||||
#include "models/messageeventmodel.h"
|
|
||||||
#include <Quotient/room.h>
|
#include <Quotient/room.h>
|
||||||
|
|
||||||
MediaMessageFilterModel::MediaMessageFilterModel(QObject *parent)
|
#include "messageeventmodel.h"
|
||||||
|
#include "messagefiltermodel.h"
|
||||||
|
|
||||||
|
MediaMessageFilterModel::MediaMessageFilterModel(QObject *parent, MessageFilterModel *sourceMediaModel)
|
||||||
: QSortFilterProxyModel(parent)
|
: QSortFilterProxyModel(parent)
|
||||||
{
|
{
|
||||||
|
Q_ASSERT(sourceMediaModel);
|
||||||
|
setSourceModel(sourceMediaModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MediaMessageFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
|
bool MediaMessageFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
|
||||||
@@ -43,9 +48,9 @@ QVariant MediaMessageFilterModel::data(const QModelIndex &index, int role) const
|
|||||||
}
|
}
|
||||||
if (role == TypeRole) {
|
if (role == TypeRole) {
|
||||||
if (mapToSource(index).data(MessageEventModel::DelegateTypeRole).toInt() == MessageEventModel::DelegateType::Image) {
|
if (mapToSource(index).data(MessageEventModel::DelegateTypeRole).toInt() == MessageEventModel::DelegateType::Image) {
|
||||||
return 0;
|
return MediaType::Image;
|
||||||
} else {
|
} else {
|
||||||
return 1;
|
return MediaType::Video;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (role == CaptionRole) {
|
if (role == CaptionRole) {
|
||||||
@@ -57,6 +62,13 @@ QVariant MediaMessageFilterModel::data(const QModelIndex &index, int role) const
|
|||||||
if (role == SourceHeightRole) {
|
if (role == SourceHeightRole) {
|
||||||
return mapToSource(index).data(MessageEventModel::MediaInfoRole).toMap()[QStringLiteral("height")].toFloat();
|
return mapToSource(index).data(MessageEventModel::MediaInfoRole).toMap()[QStringLiteral("height")].toFloat();
|
||||||
}
|
}
|
||||||
|
// We need to catch this one and return true if the next media object was
|
||||||
|
// on a different day.
|
||||||
|
if (role == MessageEventModel::ShowSectionRole) {
|
||||||
|
const auto day = mapToSource(index).data(MessageEventModel::TimeRole).toDateTime().toLocalTime().date();
|
||||||
|
const auto previousEventDay = mapToSource(this->index(index.row() + 1, 0)).data(MessageEventModel::TimeRole).toDateTime().toLocalTime().date();
|
||||||
|
return day != previousEventDay;
|
||||||
|
}
|
||||||
|
|
||||||
return sourceModel()->data(mapToSource(index), role);
|
return sourceModel()->data(mapToSource(index), role);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,9 +4,12 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <QSortFilterProxyModel>
|
#include <QSortFilterProxyModel>
|
||||||
|
#include <qobjectdefs.h>
|
||||||
|
|
||||||
#include "models/messagefiltermodel.h"
|
#include "models/messagefiltermodel.h"
|
||||||
|
|
||||||
|
class MessageFilterModel;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @class MediaMessageFilterModel
|
* @class MediaMessageFilterModel
|
||||||
*
|
*
|
||||||
@@ -18,6 +21,12 @@ class MediaMessageFilterModel : public QSortFilterProxyModel
|
|||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
|
enum MediaType {
|
||||||
|
Image = 0,
|
||||||
|
Video,
|
||||||
|
};
|
||||||
|
Q_ENUM(MediaType)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Defines the model roles.
|
* @brief Defines the model roles.
|
||||||
*/
|
*/
|
||||||
@@ -31,7 +40,7 @@ public:
|
|||||||
};
|
};
|
||||||
Q_ENUM(Roles)
|
Q_ENUM(Roles)
|
||||||
|
|
||||||
explicit MediaMessageFilterModel(QObject *parent = nullptr);
|
explicit MediaMessageFilterModel(QObject *parent = nullptr, MessageFilterModel *sourceMediaModel = nullptr);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Custom filter to show only image and video messages.
|
* @brief Custom filter to show only image and video messages.
|
||||||
@@ -43,7 +52,7 @@ public:
|
|||||||
*
|
*
|
||||||
* @sa QSortFilterProxyModel::data
|
* @sa QSortFilterProxyModel::data
|
||||||
*/
|
*/
|
||||||
[[nodiscard]] QVariant data(const QModelIndex &idx, int role = Qt::DisplayRole) const override;
|
[[nodiscard]] QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Returns a mapping from Role enum values to role names.
|
* @brief Returns a mapping from Role enum values to role names.
|
||||||
|
|||||||
@@ -10,9 +10,12 @@
|
|||||||
|
|
||||||
using namespace Quotient;
|
using namespace Quotient;
|
||||||
|
|
||||||
MessageFilterModel::MessageFilterModel(QObject *parent)
|
MessageFilterModel::MessageFilterModel(QObject *parent, MessageEventModel *sourceMessageModel)
|
||||||
: QSortFilterProxyModel(parent)
|
: QSortFilterProxyModel(parent)
|
||||||
{
|
{
|
||||||
|
Q_ASSERT(sourceMessageModel);
|
||||||
|
setSourceModel(sourceMessageModel);
|
||||||
|
|
||||||
connect(NeoChatConfig::self(), &NeoChatConfig::ShowStateEventChanged, this, [this] {
|
connect(NeoChatConfig::self(), &NeoChatConfig::ShowStateEventChanged, this, [this] {
|
||||||
invalidateFilter();
|
invalidateFilter();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ public:
|
|||||||
LastRole, // Keep this last
|
LastRole, // Keep this last
|
||||||
};
|
};
|
||||||
|
|
||||||
explicit MessageFilterModel(QObject *parent = nullptr);
|
explicit MessageFilterModel(QObject *parent = nullptr, MessageEventModel *sourceMessageModel = nullptr);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Custom filter function to remove hidden messages.
|
* @brief Custom filter function to remove hidden messages.
|
||||||
|
|||||||
@@ -100,6 +100,11 @@ Components.AlbumMaximizeComponent {
|
|||||||
dialog.currentFile = dialog.folder + "/" + currentRoom.fileNameToDownload(root.currentEventId)
|
dialog.currentFile = dialog.folder + "/" + currentRoom.fileNameToDownload(root.currentEventId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: fileDelegateContextMenu
|
||||||
|
FileDelegateContextMenu {}
|
||||||
|
}
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: saveAsDialog
|
id: saveAsDialog
|
||||||
Platform.FileDialog {
|
Platform.FileDialog {
|
||||||
|
|||||||
@@ -129,13 +129,19 @@ TimelineContainer {
|
|||||||
|
|
||||||
TapHandler {
|
TapHandler {
|
||||||
acceptedButtons: Qt.LeftButton
|
acceptedButtons: Qt.LeftButton
|
||||||
|
gesturePolicy: TapHandler.ReleaseWithinBounds | TapHandler.WithinBounds
|
||||||
onTapped: {
|
onTapped: {
|
||||||
imageContainer.QQC2.ToolTip.hide()
|
imageContainer.QQC2.ToolTip.hide()
|
||||||
if (root.mediaInfo.animated) {
|
if (root.mediaInfo.animated) {
|
||||||
imageContainer.imageItem.paused = true
|
imageContainer.imageItem.paused = true
|
||||||
}
|
}
|
||||||
root.ListView.view.interactive = false
|
root.ListView.view.interactive = false
|
||||||
root.ListView.view.showMaximizedMedia(root.index)
|
// We need to make sure the index is that of the MediaMessageFilterModel.
|
||||||
|
if (root.ListView.view.model instanceof MessageFilterModel) {
|
||||||
|
RoomManager.maximizeMedia(RoomManager.mediaMessageFilterModel.getRowForSourceItem(root.index))
|
||||||
|
} else {
|
||||||
|
RoomManager.maximizeMedia(root.index)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,7 +150,7 @@ TimelineContainer {
|
|||||||
openSavedFile()
|
openSavedFile()
|
||||||
} else {
|
} else {
|
||||||
openOnFinished = true
|
openOnFinished = true
|
||||||
currentRoom.downloadFile(root.eventId, StandardPaths.writableLocation(StandardPaths.CacheLocation) + "/" + root.eventId.replace(":", "_").replace("/", "_").replace("+", "_") + currentRoom.fileNameToDownload(root.eventId))
|
ListView.view.currentRoom.downloadFile(root.eventId, StandardPaths.writableLocation(StandardPaths.CacheLocation) + "/" + root.eventId.replace(":", "_").replace("/", "_").replace("+", "_") + ListView.view.currentRoom.fileNameToDownload(root.eventId))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ QQC2.ItemDelegate {
|
|||||||
property alias labelText: sectionLabel.text
|
property alias labelText: sectionLabel.text
|
||||||
property var maxWidth: Number.POSITIVE_INFINITY
|
property var maxWidth: Number.POSITIVE_INFINITY
|
||||||
|
|
||||||
|
property int colorSet: Kirigami.Theme.Window
|
||||||
|
|
||||||
topPadding: Kirigami.Units.largeSpacing
|
topPadding: Kirigami.Units.largeSpacing
|
||||||
bottomPadding: 0 // Note not 0 by default
|
bottomPadding: 0 // Note not 0 by default
|
||||||
|
|
||||||
@@ -42,6 +44,6 @@ QQC2.ItemDelegate {
|
|||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
color: Config.blur ? "transparent" : Kirigami.Theme.backgroundColor
|
color: Config.blur ? "transparent" : Kirigami.Theme.backgroundColor
|
||||||
Kirigami.Theme.inherit: false
|
Kirigami.Theme.inherit: false
|
||||||
Kirigami.Theme.colorSet: Config.compactLayout ? Kirigami.Theme.View : Kirigami.Theme.Window
|
Kirigami.Theme.colorSet: sectionDelegate.colorSet
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,6 +65,16 @@ ColumnLayout {
|
|||||||
*/
|
*/
|
||||||
required property bool showAuthor
|
required property bool showAuthor
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Whether the author should always be shown.
|
||||||
|
*
|
||||||
|
* This is primarily used when these delegates are used in a filtered list of
|
||||||
|
* events rather than a sequential timeline, e.g. the media model view.
|
||||||
|
*
|
||||||
|
* @note This setting still respects the avatar configuration settings.
|
||||||
|
*/
|
||||||
|
property bool alwaysShowAuthor: false
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief The delegate type of the message.
|
* @brief The delegate type of the message.
|
||||||
*/
|
*/
|
||||||
@@ -262,12 +272,17 @@ ColumnLayout {
|
|||||||
*/
|
*/
|
||||||
property bool cardBackground: true
|
property bool cardBackground: true
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Whether the delegate should always stretch to the maximum availabel width.
|
||||||
|
*/
|
||||||
|
property bool alwaysMaxWidth: false
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Whether local user messages should be aligned right.
|
* @brief Whether local user messages should be aligned right.
|
||||||
*
|
*
|
||||||
* TODO: make private
|
* TODO: make private
|
||||||
*/
|
*/
|
||||||
property bool showUserMessageOnRight: Config.showLocalMessagesOnRight && root.author.isLocalUser && !Config.compactLayout
|
property bool showUserMessageOnRight: Config.showLocalMessagesOnRight && root.author.isLocalUser && !Config.compactLayout && !alwaysMaxWidth
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Whether the message should be highlighted.
|
* @brief Whether the message should be highlighted.
|
||||||
@@ -296,7 +311,7 @@ ColumnLayout {
|
|||||||
width: parent ? timelineDelegateSizeHelper.currentWidth : 0
|
width: parent ? timelineDelegateSizeHelper.currentWidth : 0
|
||||||
spacing: Kirigami.Units.smallSpacing
|
spacing: Kirigami.Units.smallSpacing
|
||||||
|
|
||||||
state: Config.compactLayout ? "alignLeft" : "alignCenter"
|
state: Config.compactLayout || root.alwaysMaxWidth ? "alignLeft" : "alignCenter"
|
||||||
// Align left when in compact mode and center when using bubbles
|
// Align left when in compact mode and center when using bubbles
|
||||||
states: [
|
states: [
|
||||||
State {
|
State {
|
||||||
@@ -325,21 +340,21 @@ ColumnLayout {
|
|||||||
|
|
||||||
SectionDelegate {
|
SectionDelegate {
|
||||||
id: sectionDelegate
|
id: sectionDelegate
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
visible: root.showSection
|
visible: root.showSection
|
||||||
labelText: root.section
|
labelText: root.section
|
||||||
|
colorSet: Config.compactLayout || root.alwaysMaxWidth ? Kirigami.Theme.View : Kirigami.Theme.Window
|
||||||
}
|
}
|
||||||
|
|
||||||
QQC2.ItemDelegate {
|
QQC2.ItemDelegate {
|
||||||
id: mainContainer
|
id: mainContainer
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.topMargin: root.showAuthor ? Kirigami.Units.largeSpacing : (Config.compactLayout ? 1 : Kirigami.Units.smallSpacing)
|
Layout.topMargin: root.showAuthor || root.alwaysShowAuthor ? Kirigami.Units.largeSpacing : (Config.compactLayout ? 1 : Kirigami.Units.smallSpacing)
|
||||||
Layout.leftMargin: Kirigami.Units.smallSpacing
|
Layout.leftMargin: Kirigami.Units.smallSpacing
|
||||||
Layout.rightMargin: Kirigami.Units.smallSpacing
|
Layout.rightMargin: Kirigami.Units.smallSpacing
|
||||||
|
|
||||||
implicitHeight: Math.max(root.showAuthor ? avatar.implicitHeight : 0, bubble.height)
|
implicitHeight: Math.max(root.showAuthor || root.alwaysShowAuthor ? avatar.implicitHeight : 0, bubble.height)
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
if (root.isReply && root.reply === undefined) {
|
if (root.isReply && root.reply === undefined) {
|
||||||
@@ -365,7 +380,7 @@ ColumnLayout {
|
|||||||
topMargin: Kirigami.Units.smallSpacing
|
topMargin: Kirigami.Units.smallSpacing
|
||||||
}
|
}
|
||||||
|
|
||||||
visible: root.showAuthor &&
|
visible: (root.showAuthor || root.alwaysShowAuthor) &&
|
||||||
Config.showAvatarInTimeline &&
|
Config.showAvatarInTimeline &&
|
||||||
(Config.compactLayout || !showUserMessageOnRight)
|
(Config.compactLayout || !showUserMessageOnRight)
|
||||||
name: root.author.displayName
|
name: root.author.displayName
|
||||||
@@ -395,7 +410,7 @@ ColumnLayout {
|
|||||||
rightMargin: Kirigami.Units.largeSpacing
|
rightMargin: Kirigami.Units.largeSpacing
|
||||||
}
|
}
|
||||||
// HACK: anchoring didn't reset anchors.right when switching from parent.right to undefined reliably
|
// HACK: anchoring didn't reset anchors.right when switching from parent.right to undefined reliably
|
||||||
width: Config.compactLayout ? mainContainer.width - (Config.showAvatarInTimeline ? Kirigami.Units.gridUnit * 2 : 0) + Kirigami.Units.largeSpacing * 2 : implicitWidth
|
width: Config.compactLayout || root.alwaysMaxWidth ? mainContainer.width - (Config.showAvatarInTimeline ? Kirigami.Units.gridUnit * 2 : 0) + Kirigami.Units.largeSpacing * 2 : implicitWidth
|
||||||
|
|
||||||
state: showUserMessageOnRight ? "userMessageOnRight" : "userMessageOnLeft"
|
state: showUserMessageOnRight ? "userMessageOnRight" : "userMessageOnLeft"
|
||||||
// states for anchor animations on window resize
|
// states for anchor animations on window resize
|
||||||
@@ -440,7 +455,7 @@ ColumnLayout {
|
|||||||
id: rowLayout
|
id: rowLayout
|
||||||
|
|
||||||
spacing: Kirigami.Units.smallSpacing
|
spacing: Kirigami.Units.smallSpacing
|
||||||
visible: root.showAuthor
|
visible: root.showAuthor || root.alwaysShowAuthor
|
||||||
|
|
||||||
QQC2.Label {
|
QQC2.Label {
|
||||||
id: nameLabel
|
id: nameLabel
|
||||||
@@ -535,7 +550,7 @@ ColumnLayout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
visible: mainContainer.hovered && Config.compactLayout
|
visible: mainContainer.hovered && (Config.compactLayout || root.alwaysMaxWidth)
|
||||||
color: Kirigami.ColorUtils.tintWithAlpha(Kirigami.Theme.backgroundColor, Kirigami.Theme.highlightColor, 0.15)
|
color: Kirigami.ColorUtils.tintWithAlpha(Kirigami.Theme.backgroundColor, Kirigami.Theme.highlightColor, 0.15)
|
||||||
radius: Kirigami.Units.smallSpacing
|
radius: Kirigami.Units.smallSpacing
|
||||||
}
|
}
|
||||||
@@ -577,6 +592,16 @@ ColumnLayout {
|
|||||||
return (yoff + height > 0 && yoff < ListView.view.height)
|
return (yoff + height > 0 && yoff < ListView.view.height)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: messageDelegateContextMenu
|
||||||
|
MessageDelegateContextMenu {}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: fileDelegateContextMenu
|
||||||
|
FileDelegateContextMenu {}
|
||||||
|
}
|
||||||
|
|
||||||
/// Open message context dialog for file and videos
|
/// Open message context dialog for file and videos
|
||||||
function openFileContext(file) {
|
function openFileContext(file) {
|
||||||
const contextMenu = fileDelegateContextMenu.createObject(root, {
|
const contextMenu = fileDelegateContextMenu.createObject(root, {
|
||||||
@@ -616,8 +641,8 @@ ColumnLayout {
|
|||||||
startBreakpoint: Kirigami.Units.gridUnit * 46
|
startBreakpoint: Kirigami.Units.gridUnit * 46
|
||||||
endBreakpoint: Kirigami.Units.gridUnit * 66
|
endBreakpoint: Kirigami.Units.gridUnit * 66
|
||||||
startPercentWidth: 100
|
startPercentWidth: 100
|
||||||
endPercentWidth: Config.compactLayout ? 100 : 85
|
endPercentWidth: Config.compactLayout || root.alwaysMaxWidth ? 100 : 85
|
||||||
maxWidth: Config.compactLayout ? -1 : Kirigami.Units.gridUnit * 60
|
maxWidth: Config.compactLayout || root.alwaysMaxWidth ? -1 : Kirigami.Units.gridUnit * 60
|
||||||
|
|
||||||
parentWidth: root.parent ? root.parent.width - (Config.compactLayout && root.ListView.view.width >= Kirigami.Units.gridUnit * 20 ? Kirigami.Units.gridUnit * 2 + Kirigami.Units.largeSpacing : 0) : 0
|
parentWidth: root.parent ? root.parent.width - (Config.compactLayout && root.ListView.view.width >= Kirigami.Units.gridUnit * 20 ? Kirigami.Units.gridUnit * 2 + Kirigami.Units.largeSpacing : 0) : 0
|
||||||
}
|
}
|
||||||
@@ -625,8 +650,8 @@ ColumnLayout {
|
|||||||
id: bubbleSizeHelper
|
id: bubbleSizeHelper
|
||||||
startBreakpoint: Kirigami.Units.gridUnit * 25
|
startBreakpoint: Kirigami.Units.gridUnit * 25
|
||||||
endBreakpoint: Kirigami.Units.gridUnit * 40
|
endBreakpoint: Kirigami.Units.gridUnit * 40
|
||||||
startPercentWidth: Config.compactLayout ? 100 : 90
|
startPercentWidth: Config.compactLayout || root.alwaysMaxWidth ? 100 : 90
|
||||||
endPercentWidth: Config.compactLayout ? 100 : 60
|
endPercentWidth: Config.compactLayout || root.alwaysMaxWidth ? 100 : 60
|
||||||
|
|
||||||
parentWidth: mainContainer.availableWidth - (Config.showAvatarInTimeline ? avatar.width + bubble.anchors.leftMargin : 0)
|
parentWidth: mainContainer.availableWidth - (Config.showAvatarInTimeline ? avatar.width + bubble.anchors.leftMargin : 0)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -292,7 +292,12 @@ TimelineContainer {
|
|||||||
onTriggered: {
|
onTriggered: {
|
||||||
root.ListView.view.interactive = false
|
root.ListView.view.interactive = false
|
||||||
vid.pause()
|
vid.pause()
|
||||||
root.ListView.view.showMaximizedMedia(root.index)
|
// We need to make sure the index is that of the MediaMessageFilterModel.
|
||||||
|
if (root.ListView.view.model instanceof MessageFilterModel) {
|
||||||
|
RoomManager.maximizeMedia(RoomManager.mediaMessageFilterModel.getRowForSourceItem(root.index))
|
||||||
|
} else {
|
||||||
|
RoomManager.maximizeMedia(root.index)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -328,6 +333,7 @@ TimelineContainer {
|
|||||||
|
|
||||||
TapHandler {
|
TapHandler {
|
||||||
acceptedButtons: Qt.LeftButton
|
acceptedButtons: Qt.LeftButton
|
||||||
|
gesturePolicy: TapHandler.ReleaseWithinBounds | TapHandler.WithinBounds
|
||||||
onTapped: if (root.progressInfo.completed) {
|
onTapped: if (root.progressInfo.completed) {
|
||||||
if (vid.playbackState == MediaPlayer.PlayingState) {
|
if (vid.playbackState == MediaPlayer.PlayingState) {
|
||||||
vid.pause()
|
vid.pause()
|
||||||
@@ -352,7 +358,7 @@ TimelineContainer {
|
|||||||
playSavedFile()
|
playSavedFile()
|
||||||
} else {
|
} else {
|
||||||
playOnFinished = true
|
playOnFinished = true
|
||||||
currentRoom.downloadFile(root.eventId, Platform.StandardPaths.writableLocation(Platform.StandardPaths.CacheLocation) + "/" + root.eventId.replace(":", "_").replace("/", "_").replace("+", "_") + currentRoom.fileNameToDownload(root.eventId))
|
ListView.view.currentRoom.downloadFile(root.eventId, Platform.StandardPaths.writableLocation(Platform.StandardPaths.CacheLocation) + "/" + root.eventId.replace(":", "_").replace("/", "_").replace("+", "_") + ListView.view.currentRoom.fileNameToDownload(root.eventId))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,9 @@ QQC2.ScrollView {
|
|||||||
|
|
||||||
ListView {
|
ListView {
|
||||||
id: messageListView
|
id: messageListView
|
||||||
|
// So that delegates can access the current room properly.
|
||||||
|
readonly property NeoChatRoom currentRoom: root.currentRoom
|
||||||
|
|
||||||
readonly property int largestVisibleIndex: count > 0 ? indexAt(contentX + (width / 2), contentY + height - 1) : -1
|
readonly property int largestVisibleIndex: count > 0 ? indexAt(contentX + (width / 2), contentY + height - 1) : -1
|
||||||
readonly property var sectionBannerItem: contentHeight >= height ? itemAtIndex(sectionBannerIndex()) : undefined
|
readonly property var sectionBannerItem: contentHeight >= height ? itemAtIndex(sectionBannerIndex()) : undefined
|
||||||
|
|
||||||
@@ -47,33 +50,23 @@ QQC2.ScrollView {
|
|||||||
interactive: Kirigami.Settings.isMobile
|
interactive: Kirigami.Settings.isMobile
|
||||||
bottomMargin: Kirigami.Units.largeSpacing + Math.round(Kirigami.Theme.defaultFont.pointSize * 2)
|
bottomMargin: Kirigami.Units.largeSpacing + Math.round(Kirigami.Theme.defaultFont.pointSize * 2)
|
||||||
|
|
||||||
model: sortedMessageEventModel
|
model: RoomManager.messageFilterModel
|
||||||
|
|
||||||
MessageEventModel {
|
|
||||||
id: messageEventModel
|
|
||||||
room: root.currentRoom
|
|
||||||
}
|
|
||||||
|
|
||||||
MessageFilterModel {
|
|
||||||
id: sortedMessageEventModel
|
|
||||||
sourceModel: messageEventModel
|
|
||||||
}
|
|
||||||
|
|
||||||
Timer {
|
Timer {
|
||||||
interval: 1000
|
interval: 1000
|
||||||
running: messageListView.atYBeginning
|
running: messageListView.atYBeginning
|
||||||
triggeredOnStart: true
|
triggeredOnStart: true
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
if (messageListView.atYBeginning && messageEventModel.canFetchMore(messageEventModel.index(0, 0))) {
|
if (messageListView.atYBeginning && RoomManager.messageEventModel.canFetchMore(RoomManager.messageEventModel.index(0, 0))) {
|
||||||
messageEventModel.fetchMore(messageEventModel.index(0, 0));
|
RoomManager.messageEventModel.fetchMore(RoomManager.messageEventModel.index(0, 0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
repeat: true
|
repeat: true
|
||||||
}
|
}
|
||||||
|
|
||||||
// HACK: The view should do this automatically but doesn't.
|
// HACK: The view should do this automatically but doesn't.
|
||||||
onAtYBeginningChanged: if (atYBeginning && messageEventModel.canFetchMore(messageEventModel.index(0, 0))) {
|
onAtYBeginningChanged: if (atYBeginning && RoomManager.messageEventModel.canFetchMore(RoomManager.messageEventModel.index(0, 0))) {
|
||||||
messageEventModel.fetchMore(messageEventModel.index(0, 0));
|
RoomManager.messageEventModel.fetchMore(RoomManager.messageEventModel.index(0, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
Timer {
|
Timer {
|
||||||
@@ -207,18 +200,6 @@ QQC2.ScrollView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Component {
|
|
||||||
id: messageDelegateContextMenu
|
|
||||||
|
|
||||||
MessageDelegateContextMenu {}
|
|
||||||
}
|
|
||||||
|
|
||||||
Component {
|
|
||||||
id: fileDelegateContextMenu
|
|
||||||
|
|
||||||
FileDelegateContextMenu {}
|
|
||||||
}
|
|
||||||
|
|
||||||
TypingPane {
|
TypingPane {
|
||||||
id: typingPane
|
id: typingPane
|
||||||
visible: root.currentRoom && root.currentRoom.usersTyping.length > 0
|
visible: root.currentRoom && root.currentRoom.usersTyping.length > 0
|
||||||
@@ -285,7 +266,7 @@ QQC2.ScrollView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: messageEventModel
|
target: RoomManager.messageEventModel
|
||||||
|
|
||||||
function onRowsInserted() {
|
function onRowsInserted() {
|
||||||
markReadIfVisibleTimer.restart()
|
markReadIfVisibleTimer.restart()
|
||||||
@@ -326,7 +307,7 @@ QQC2.ScrollView {
|
|||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
//enabled: Config.showFancyEffects
|
//enabled: Config.showFancyEffects
|
||||||
target: messageEventModel
|
target: RoomManager.messageEventModel
|
||||||
|
|
||||||
function onFancyEffectsReasonFound(fancyEffect) {
|
function onFancyEffectsReasonFound(fancyEffect) {
|
||||||
fancyEffectsContainer.processFancyEffectsReason(fancyEffect)
|
fancyEffectsContainer.processFancyEffectsReason(fancyEffect)
|
||||||
@@ -344,21 +325,23 @@ QQC2.ScrollView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MediaMessageFilterModel {
|
|
||||||
id: mediaMessageFilterModel
|
|
||||||
sourceModel: sortedMessageEventModel
|
|
||||||
}
|
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: maximizeComponent
|
id: maximizeComponent
|
||||||
NeochatMaximizeComponent {
|
NeochatMaximizeComponent {
|
||||||
model: mediaMessageFilterModel
|
model: RoomManager.mediaMessageFilterModel
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: RoomManager
|
||||||
|
function onShowMaximizedMedia(index) {
|
||||||
|
messageListView.showMaximizedMedia(index)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function showMaximizedMedia(index) {
|
function showMaximizedMedia(index) {
|
||||||
var popup = maximizeComponent.createObject(QQC2.ApplicationWindow.overlay, {
|
var popup = maximizeComponent.createObject(QQC2.ApplicationWindow.overlay, {
|
||||||
initialIndex: index === -1 ? -1 : mediaMessageFilterModel.getRowForSourceItem(index)
|
initialIndex: index
|
||||||
})
|
})
|
||||||
popup.closed.connect(() => {
|
popup.closed.connect(() => {
|
||||||
messageListView.interactive = true
|
messageListView.interactive = true
|
||||||
@@ -374,10 +357,10 @@ QQC2.ScrollView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function eventToIndex(eventID) {
|
function eventToIndex(eventID) {
|
||||||
const index = messageEventModel.eventIdToRow(eventID)
|
const index = RoomManager.messageEventModel.eventIdToRow(eventID)
|
||||||
if (index === -1)
|
if (index === -1)
|
||||||
return -1
|
return -1
|
||||||
return sortedMessageEventModel.mapFromSource(messageEventModel.index(index, 0)).row
|
return RoomManager.messageFilterModel.mapFromSource(RoomManager.messageEventModel.index(index, 0)).row
|
||||||
}
|
}
|
||||||
|
|
||||||
function firstVisibleIndex() {
|
function firstVisibleIndex() {
|
||||||
|
|||||||
@@ -65,9 +65,12 @@ Kirigami.OverlayDrawer {
|
|||||||
onAnimatingChanged: if (dim === false) dim = undefined
|
onAnimatingChanged: if (dim === false) dim = undefined
|
||||||
|
|
||||||
topPadding: 0
|
topPadding: 0
|
||||||
|
bottomPadding: 0
|
||||||
leftPadding: 0
|
leftPadding: 0
|
||||||
rightPadding: 0
|
rightPadding: 0
|
||||||
|
|
||||||
Kirigami.Theme.colorSet: Kirigami.Theme.View
|
Kirigami.Theme.colorSet: Kirigami.Theme.View
|
||||||
|
|
||||||
contentItem: Loader {
|
contentItem: Loader {
|
||||||
id: loader
|
id: loader
|
||||||
active: root.drawerOpen
|
active: root.drawerOpen
|
||||||
@@ -75,6 +78,8 @@ Kirigami.OverlayDrawer {
|
|||||||
sourceComponent: ColumnLayout {
|
sourceComponent: ColumnLayout {
|
||||||
spacing: 0
|
spacing: 0
|
||||||
|
|
||||||
|
Component.onCompleted: infoAction.toggle()
|
||||||
|
|
||||||
QQC2.ToolBar {
|
QQC2.ToolBar {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
||||||
@@ -83,7 +88,7 @@ Kirigami.OverlayDrawer {
|
|||||||
contentItem: RowLayout {
|
contentItem: RowLayout {
|
||||||
Kirigami.Heading {
|
Kirigami.Heading {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
text: i18n("Room information")
|
text: drawerItemLoader.item ? drawerItemLoader.item.title : ""
|
||||||
}
|
}
|
||||||
|
|
||||||
QQC2.ToolButton {
|
QQC2.ToolButton {
|
||||||
@@ -102,18 +107,47 @@ Kirigami.OverlayDrawer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QQC2.ScrollView {
|
Loader {
|
||||||
|
id: drawerItemLoader
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
|
sourceComponent: roomInformation
|
||||||
|
}
|
||||||
|
|
||||||
// HACK: Hide unnecessary horizontal scrollbar (https://bugreports.qt.io/browse/QTBUG-83890)
|
Component {
|
||||||
QQC2.ScrollBar.horizontal.policy: QQC2.ScrollBar.AlwaysOff
|
id: roomInformation
|
||||||
|
|
||||||
RoomInformation {
|
RoomInformation {
|
||||||
id: roomInformation
|
|
||||||
room: root.room
|
room: root.room
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: roomMedia
|
||||||
|
RoomMedia {
|
||||||
|
currentRoom: root.room
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Kirigami.NavigationTabBar {
|
||||||
|
id: navigationBar
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Kirigami.Theme.colorSet: Kirigami.Theme.Window
|
||||||
|
Kirigami.Theme.inherit: false
|
||||||
|
|
||||||
|
actions: [
|
||||||
|
Kirigami.Action {
|
||||||
|
id: infoAction
|
||||||
|
text: i18n("Information")
|
||||||
|
icon.name: "documentinfo"
|
||||||
|
onTriggered: drawerItemLoader.sourceComponent = roomInformation
|
||||||
|
},
|
||||||
|
Kirigami.Action {
|
||||||
|
text: i18n("Media")
|
||||||
|
icon.name: "mail-attachment-symbollic"
|
||||||
|
onTriggered: drawerItemLoader.sourceComponent = roomMedia
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import org.kde.neochat 1.0
|
|||||||
*
|
*
|
||||||
* @sa RoomDrawer
|
* @sa RoomDrawer
|
||||||
*/
|
*/
|
||||||
Kirigami.ScrollablePage {
|
Kirigami.Page {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -26,18 +26,66 @@ Kirigami.ScrollablePage {
|
|||||||
*/
|
*/
|
||||||
readonly property NeoChatRoom room: RoomManager.currentRoom
|
readonly property NeoChatRoom room: RoomManager.currentRoom
|
||||||
|
|
||||||
title: roomInformation.title
|
title: drawerItemLoader.item ? drawerItemLoader.item.title : ""
|
||||||
|
|
||||||
|
topPadding: 0
|
||||||
|
bottomPadding: 0
|
||||||
|
leftPadding: 0
|
||||||
|
rightPadding: 0
|
||||||
|
|
||||||
|
Kirigami.Theme.colorSet: Kirigami.Theme.View
|
||||||
|
Kirigami.Theme.inherit: false
|
||||||
|
|
||||||
|
Component.onCompleted: infoAction.toggle()
|
||||||
|
|
||||||
actions {
|
actions {
|
||||||
main: Kirigami.Action {
|
main: Kirigami.Action {
|
||||||
|
displayHint: Kirigami.DisplayHint.IconOnly
|
||||||
|
text: i18n("Settings")
|
||||||
icon.name: "settings-configure"
|
icon.name: "settings-configure"
|
||||||
onTriggered: applicationWindow().pageStack.pushDialogLayer('qrc:/Categories.qml', {room: root.room}, { title: i18n("Room Settings") })
|
onTriggered: applicationWindow().pageStack.pushDialogLayer('qrc:/Categories.qml', {room: root.room}, { title: i18n("Room Settings") })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RoomInformation {
|
Loader {
|
||||||
|
id: drawerItemLoader
|
||||||
|
width: parent.width
|
||||||
|
height: parent.height
|
||||||
|
sourceComponent: roomInformation
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
id: roomInformation
|
id: roomInformation
|
||||||
room: root.room
|
RoomInformation {
|
||||||
|
room: root.room
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: roomMedia
|
||||||
|
RoomMedia {
|
||||||
|
currentRoom: root.room
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
footer: Kirigami.NavigationTabBar {
|
||||||
|
id: navigationBar
|
||||||
|
Kirigami.Theme.colorSet: Kirigami.Theme.Window
|
||||||
|
Kirigami.Theme.inherit: false
|
||||||
|
|
||||||
|
actions: [
|
||||||
|
Kirigami.Action {
|
||||||
|
id: infoAction
|
||||||
|
text: i18n("Information")
|
||||||
|
icon.name: "documentinfo"
|
||||||
|
onTriggered: drawerItemLoader.sourceComponent = roomInformation
|
||||||
|
},
|
||||||
|
Kirigami.Action {
|
||||||
|
text: i18n("Media")
|
||||||
|
icon.name: "mail-attachment-symbollic"
|
||||||
|
onTriggered: drawerItemLoader.sourceComponent = roomMedia
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ import org.kde.neochat 1.0
|
|||||||
*
|
*
|
||||||
* @sa RoomDrawer, RoomDrawerPage
|
* @sa RoomDrawer, RoomDrawerPage
|
||||||
*/
|
*/
|
||||||
ListView {
|
QQC2.ScrollView {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -37,206 +37,212 @@ ListView {
|
|||||||
*/
|
*/
|
||||||
readonly property string title: i18nc("@action:title", "Room information")
|
readonly property string title: i18nc("@action:title", "Room information")
|
||||||
|
|
||||||
header: ColumnLayout {
|
// HACK: Hide unnecessary horizontal scrollbar (https://bugreports.qt.io/browse/QTBUG-83890)
|
||||||
id: columnLayout
|
QQC2.ScrollBar.horizontal.policy: QQC2.ScrollBar.AlwaysOff
|
||||||
|
|
||||||
property alias userListSearchField: userListSearchField
|
ListView {
|
||||||
|
id: userList
|
||||||
|
header: ColumnLayout {
|
||||||
|
id: columnLayout
|
||||||
|
|
||||||
spacing: 0
|
property alias userListSearchField: userListSearchField
|
||||||
width: root.width
|
|
||||||
|
|
||||||
Loader {
|
|
||||||
active: true
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.topMargin: Kirigami.Units.smallSpacing
|
|
||||||
sourceComponent: root.room.isDirectChat() ? directChatDrawerHeader : groupChatDrawerHeader
|
|
||||||
onItemChanged: if (item) {
|
|
||||||
root.positionViewAtBeginning();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Kirigami.ListSectionHeader {
|
|
||||||
label: i18n("Options")
|
|
||||||
activeFocusOnTab: false
|
|
||||||
|
|
||||||
Layout.fillWidth: true
|
|
||||||
}
|
|
||||||
|
|
||||||
Delegates.RoundedItemDelegate {
|
|
||||||
id: devtoolsButton
|
|
||||||
|
|
||||||
icon.name: "tools"
|
|
||||||
text: i18n("Open developer tools")
|
|
||||||
visible: Config.developerTools
|
|
||||||
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
applicationWindow().pageStack.pushDialogLayer("qrc:/DevtoolsPage.qml", {room: root.room}, {title: i18n("Developer Tools")})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Delegates.RoundedItemDelegate {
|
|
||||||
id: searchButton
|
|
||||||
|
|
||||||
icon.name: "search"
|
|
||||||
text: i18n("Search in this room")
|
|
||||||
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
pageStack.pushDialogLayer("qrc:/SearchPage.qml", {
|
|
||||||
currentRoom: root.room
|
|
||||||
}, {
|
|
||||||
title: i18nc("@action:title", "Search")
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Delegates.RoundedItemDelegate {
|
|
||||||
id: favouriteButton
|
|
||||||
|
|
||||||
icon.name: root.room && root.room.isFavourite ? "rating" : "rating-unrated"
|
|
||||||
text: root.room && root.room.isFavourite ? i18n("Remove room from favorites") : i18n("Make room favorite")
|
|
||||||
|
|
||||||
onClicked: root.room.isFavourite ? root.room.removeTag("m.favourite") : root.room.addTag("m.favourite", 1.0)
|
|
||||||
|
|
||||||
Layout.fillWidth: true
|
|
||||||
}
|
|
||||||
|
|
||||||
Delegates.RoundedItemDelegate {
|
|
||||||
id: locationsButton
|
|
||||||
|
|
||||||
icon.name: "map-flat"
|
|
||||||
text: i18n("Show locations for this room")
|
|
||||||
|
|
||||||
onClicked: pageStack.pushDialogLayer("qrc:/LocationsPage.qml", {
|
|
||||||
room: root.room
|
|
||||||
}, {
|
|
||||||
title: i18nc("Locations on a map", "Locations")
|
|
||||||
})
|
|
||||||
|
|
||||||
Layout.fillWidth: true
|
|
||||||
}
|
|
||||||
|
|
||||||
Kirigami.ListSectionHeader {
|
|
||||||
label: i18n("Members")
|
|
||||||
activeFocusOnTab: false
|
|
||||||
spacing: 0
|
spacing: 0
|
||||||
visible: !root.room.isDirectChat()
|
width: root.width
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Loader {
|
||||||
|
active: true
|
||||||
QQC2.ToolButton {
|
Layout.fillWidth: true
|
||||||
id: memberSearchToggle
|
Layout.topMargin: Kirigami.Units.smallSpacing
|
||||||
checkable: true
|
sourceComponent: root.room.isDirectChat() ? directChatDrawerHeader : groupChatDrawerHeader
|
||||||
icon.name: "search"
|
onItemChanged: if (item) {
|
||||||
QQC2.ToolTip.text: i18n("Search user in room")
|
userList.positionViewAtBeginning();
|
||||||
QQC2.ToolTip.visible: hovered
|
|
||||||
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
|
||||||
onToggled: {
|
|
||||||
userListSearchField.text = "";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QQC2.ToolButton {
|
Kirigami.ListSectionHeader {
|
||||||
visible: root.room.canSendState("invite")
|
label: i18n("Options")
|
||||||
icon.name: "list-add-user"
|
activeFocusOnTab: false
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
applicationWindow().pageStack.pushDialogLayer("qrc:/InviteUserPage.qml", {room: root.room}, {title: i18nc("@title", "Invite a User")})
|
|
||||||
}
|
|
||||||
|
|
||||||
QQC2.ToolTip.text: i18n("Invite user to room")
|
|
||||||
QQC2.ToolTip.visible: hovered
|
|
||||||
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
|
||||||
}
|
|
||||||
|
|
||||||
QQC2.Label {
|
|
||||||
Layout.alignment: Qt.AlignRight
|
|
||||||
text: root.room ? i18np("%1 member", "%1 members", root.room.joinedCount) : i18n("No member count")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Kirigami.SearchField {
|
|
||||||
id: userListSearchField
|
|
||||||
visible: memberSearchToggle.checked
|
|
||||||
|
|
||||||
onVisibleChanged: if (visible) forceActiveFocus()
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.leftMargin: Kirigami.Units.largeSpacing - 1
|
|
||||||
Layout.rightMargin: Kirigami.Units.largeSpacing - 1
|
|
||||||
Layout.bottomMargin: Kirigami.Units.smallSpacing
|
|
||||||
|
|
||||||
focusSequence: "Ctrl+Shift+F"
|
|
||||||
|
|
||||||
onAccepted: sortedMessageEventModel.filterString = text;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
KSortFilterProxyModel {
|
|
||||||
id: sortedMessageEventModel
|
|
||||||
|
|
||||||
sourceModel: UserListModel {
|
|
||||||
room: root.room
|
|
||||||
}
|
|
||||||
|
|
||||||
sortRole: "powerLevel"
|
|
||||||
sortOrder: Qt.DescendingOrder
|
|
||||||
filterRole: "name"
|
|
||||||
filterCaseSensitivity: Qt.CaseInsensitive
|
|
||||||
}
|
|
||||||
|
|
||||||
model: root.room.isDirectChat() ? 0 : sortedMessageEventModel
|
|
||||||
|
|
||||||
clip: true
|
|
||||||
activeFocusOnTab: true
|
|
||||||
|
|
||||||
delegate: Delegates.RoundedItemDelegate {
|
|
||||||
id: userDelegate
|
|
||||||
|
|
||||||
required property string name
|
|
||||||
required property string userId
|
|
||||||
required property string avatar
|
|
||||||
required property int powerLevel
|
|
||||||
required property string powerLevelString
|
|
||||||
|
|
||||||
implicitHeight: Kirigami.Units.gridUnit * 2
|
|
||||||
|
|
||||||
text: name
|
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
userDelegate.highlighted = true;
|
|
||||||
RoomManager.visitUser(room.getUser(userDelegate.userId).object, "mention")
|
|
||||||
}
|
|
||||||
|
|
||||||
contentItem: RowLayout {
|
|
||||||
KirigamiComponents.Avatar {
|
|
||||||
implicitWidth: height
|
|
||||||
sourceSize {
|
|
||||||
height: Kirigami.Units.gridUnit + Kirigami.Units.smallSpacing * 2.5
|
|
||||||
width: Kirigami.Units.gridUnit + Kirigami.Units.smallSpacing * 2.5
|
|
||||||
}
|
|
||||||
source: userDelegate.avatar
|
|
||||||
name: userDelegate.userId
|
|
||||||
|
|
||||||
Layout.fillHeight: true
|
|
||||||
}
|
|
||||||
|
|
||||||
QQC2.Label {
|
|
||||||
text: userDelegate.name
|
|
||||||
textFormat: Text.PlainText
|
|
||||||
elide: Text.ElideRight
|
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
}
|
}
|
||||||
|
|
||||||
QQC2.Label {
|
Delegates.RoundedItemDelegate {
|
||||||
visible: userDelegate.powerLevel > 0
|
id: devtoolsButton
|
||||||
|
|
||||||
text: userDelegate.powerLevelString
|
icon.name: "tools"
|
||||||
color: Kirigami.Theme.disabledTextColor
|
text: i18n("Open developer tools")
|
||||||
textFormat: Text.PlainText
|
visible: Config.developerTools
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
applicationWindow().pageStack.pushDialogLayer("qrc:/DevtoolsPage.qml", {room: root.room}, {title: i18n("Developer Tools")})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Delegates.RoundedItemDelegate {
|
||||||
|
id: searchButton
|
||||||
|
|
||||||
|
icon.name: "search"
|
||||||
|
text: i18n("Search in this room")
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
pageStack.pushDialogLayer("qrc:/SearchPage.qml", {
|
||||||
|
currentRoom: root.room
|
||||||
|
}, {
|
||||||
|
title: i18nc("@action:title", "Search")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Delegates.RoundedItemDelegate {
|
||||||
|
id: favouriteButton
|
||||||
|
|
||||||
|
icon.name: root.room && root.room.isFavourite ? "rating" : "rating-unrated"
|
||||||
|
text: root.room && root.room.isFavourite ? i18n("Remove room from favorites") : i18n("Make room favorite")
|
||||||
|
|
||||||
|
onClicked: root.room.isFavourite ? root.room.removeTag("m.favourite") : root.room.addTag("m.favourite", 1.0)
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
|
||||||
|
Delegates.RoundedItemDelegate {
|
||||||
|
id: locationsButton
|
||||||
|
|
||||||
|
icon.name: "map-flat"
|
||||||
|
text: i18n("Show locations for this room")
|
||||||
|
|
||||||
|
onClicked: pageStack.pushDialogLayer("qrc:/LocationsPage.qml", {
|
||||||
|
room: root.room
|
||||||
|
}, {
|
||||||
|
title: i18nc("Locations on a map", "Locations")
|
||||||
|
})
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
|
||||||
|
Kirigami.ListSectionHeader {
|
||||||
|
label: i18n("Members")
|
||||||
|
activeFocusOnTab: false
|
||||||
|
spacing: 0
|
||||||
|
visible: !root.room.isDirectChat()
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
QQC2.ToolButton {
|
||||||
|
id: memberSearchToggle
|
||||||
|
checkable: true
|
||||||
|
icon.name: "search"
|
||||||
|
QQC2.ToolTip.text: i18n("Search user in room")
|
||||||
|
QQC2.ToolTip.visible: hovered
|
||||||
|
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
||||||
|
onToggled: {
|
||||||
|
userListSearchField.text = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QQC2.ToolButton {
|
||||||
|
visible: root.room.canSendState("invite")
|
||||||
|
icon.name: "list-add-user"
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
applicationWindow().pageStack.pushDialogLayer("qrc:/InviteUserPage.qml", {room: root.room}, {title: i18nc("@title", "Invite a User")})
|
||||||
|
}
|
||||||
|
|
||||||
|
QQC2.ToolTip.text: i18n("Invite user to room")
|
||||||
|
QQC2.ToolTip.visible: hovered
|
||||||
|
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
||||||
|
}
|
||||||
|
|
||||||
|
QQC2.Label {
|
||||||
|
Layout.alignment: Qt.AlignRight
|
||||||
|
text: root.room ? i18np("%1 member", "%1 members", root.room.joinedCount) : i18n("No member count")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Kirigami.SearchField {
|
||||||
|
id: userListSearchField
|
||||||
|
visible: memberSearchToggle.checked
|
||||||
|
|
||||||
|
onVisibleChanged: if (visible) forceActiveFocus()
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.leftMargin: Kirigami.Units.largeSpacing - 1
|
||||||
|
Layout.rightMargin: Kirigami.Units.largeSpacing - 1
|
||||||
|
Layout.bottomMargin: Kirigami.Units.smallSpacing
|
||||||
|
|
||||||
|
focusSequence: "Ctrl+Shift+F"
|
||||||
|
|
||||||
|
onAccepted: sortedMessageEventModel.filterString = text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
KSortFilterProxyModel {
|
||||||
|
id: sortedMessageEventModel
|
||||||
|
|
||||||
|
sourceModel: UserListModel {
|
||||||
|
room: root.room
|
||||||
|
}
|
||||||
|
|
||||||
|
sortRole: "powerLevel"
|
||||||
|
sortOrder: Qt.DescendingOrder
|
||||||
|
filterRole: "name"
|
||||||
|
filterCaseSensitivity: Qt.CaseInsensitive
|
||||||
|
}
|
||||||
|
|
||||||
|
model: root.room.isDirectChat() ? 0 : sortedMessageEventModel
|
||||||
|
|
||||||
|
clip: true
|
||||||
|
activeFocusOnTab: true
|
||||||
|
|
||||||
|
delegate: Delegates.RoundedItemDelegate {
|
||||||
|
id: userDelegate
|
||||||
|
|
||||||
|
required property string name
|
||||||
|
required property string userId
|
||||||
|
required property string avatar
|
||||||
|
required property int powerLevel
|
||||||
|
required property string powerLevelString
|
||||||
|
|
||||||
|
implicitHeight: Kirigami.Units.gridUnit * 2
|
||||||
|
|
||||||
|
text: name
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
userDelegate.highlighted = true;
|
||||||
|
RoomManager.visitUser(room.getUser(userDelegate.userId).object, "mention")
|
||||||
|
}
|
||||||
|
|
||||||
|
contentItem: RowLayout {
|
||||||
|
KirigamiComponents.Avatar {
|
||||||
|
implicitWidth: height
|
||||||
|
sourceSize {
|
||||||
|
height: Kirigami.Units.gridUnit + Kirigami.Units.smallSpacing * 2.5
|
||||||
|
width: Kirigami.Units.gridUnit + Kirigami.Units.smallSpacing * 2.5
|
||||||
|
}
|
||||||
|
source: userDelegate.avatar
|
||||||
|
name: userDelegate.userId
|
||||||
|
|
||||||
|
Layout.fillHeight: true
|
||||||
|
}
|
||||||
|
|
||||||
|
QQC2.Label {
|
||||||
|
text: userDelegate.name
|
||||||
|
textFormat: Text.PlainText
|
||||||
|
elide: Text.ElideRight
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
|
||||||
|
QQC2.Label {
|
||||||
|
visible: userDelegate.powerLevel > 0
|
||||||
|
|
||||||
|
text: userDelegate.powerLevelString
|
||||||
|
color: Kirigami.Theme.disabledTextColor
|
||||||
|
textFormat: Text.PlainText
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -255,6 +261,6 @@ ListView {
|
|||||||
if (root.headerItem) {
|
if (root.headerItem) {
|
||||||
root.headerItem.userListSearchField.text = "";
|
root.headerItem.userListSearchField.text = "";
|
||||||
}
|
}
|
||||||
root.currentIndex = -1
|
userList.currentIndex = -1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
71
src/qml/RoomDrawer/RoomMedia.qml
Normal file
71
src/qml/RoomDrawer/RoomMedia.qml
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2023 James Graham <james.h.graham@protonmail.com>
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||||
|
|
||||||
|
import QtQuick 2.15
|
||||||
|
import QtQuick.Controls 2.15 as QQC2
|
||||||
|
import QtQuick.Layouts 1.15
|
||||||
|
import Qt.labs.qmlmodels 1.0
|
||||||
|
|
||||||
|
import org.kde.kirigami 2.20 as Kirigami
|
||||||
|
|
||||||
|
import org.kde.neochat 1.0
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Component for visualising the loaded media items in the room.
|
||||||
|
*
|
||||||
|
* The component is a simple list of media delegates (videos or images) with the
|
||||||
|
* ability to open them in the mamimize component.
|
||||||
|
*
|
||||||
|
* @note This component is only the contents, it will need to be placed in either
|
||||||
|
* a drawer (desktop) or page (mobile) to be used.
|
||||||
|
*
|
||||||
|
* @sa RoomDrawer, RoomDrawerPage
|
||||||
|
*/
|
||||||
|
QQC2.ScrollView {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The title that should be displayed for this component if available.
|
||||||
|
*/
|
||||||
|
readonly property string title: i18nc("@action:title", "Room Media")
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The current room that user is viewing.
|
||||||
|
*/
|
||||||
|
required property NeoChatRoom currentRoom
|
||||||
|
|
||||||
|
// HACK: Hide unnecessary horizontal scrollbar (https://bugreports.qt.io/browse/QTBUG-83890)
|
||||||
|
QQC2.ScrollBar.horizontal.policy: QQC2.ScrollBar.AlwaysOff
|
||||||
|
|
||||||
|
ListView {
|
||||||
|
// So that delegates can access current room properly.
|
||||||
|
readonly property NeoChatRoom currentRoom: root.currentRoom
|
||||||
|
|
||||||
|
clip: true
|
||||||
|
verticalLayoutDirection: ListView.BottomToTop
|
||||||
|
|
||||||
|
model: RoomManager.mediaMessageFilterModel
|
||||||
|
|
||||||
|
delegate: DelegateChooser {
|
||||||
|
role: "type"
|
||||||
|
|
||||||
|
DelegateChoice {
|
||||||
|
roleValue: 0//MediaMessageFilterModel.Image
|
||||||
|
delegate: ImageDelegate {
|
||||||
|
alwaysShowAuthor: true
|
||||||
|
alwaysMaxWidth: true
|
||||||
|
cardBackground: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DelegateChoice {
|
||||||
|
roleValue: 1//MediaMessageFilterModel.Video
|
||||||
|
delegate: VideoDelegate {
|
||||||
|
alwaysShowAuthor: true
|
||||||
|
alwaysMaxWidth: true
|
||||||
|
cardBackground: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -141,6 +141,7 @@
|
|||||||
<file alias="DirectChatDrawerHeader.qml">qml/RoomDrawer/DirectChatDrawerHeader.qml</file>
|
<file alias="DirectChatDrawerHeader.qml">qml/RoomDrawer/DirectChatDrawerHeader.qml</file>
|
||||||
<file alias="GroupChatDrawerHeader.qml">qml/RoomDrawer/GroupChatDrawerHeader.qml</file>
|
<file alias="GroupChatDrawerHeader.qml">qml/RoomDrawer/GroupChatDrawerHeader.qml</file>
|
||||||
<file alias="RoomInformation.qml">qml/RoomDrawer/RoomInformation.qml</file>
|
<file alias="RoomInformation.qml">qml/RoomDrawer/RoomInformation.qml</file>
|
||||||
|
<file alias="RoomMedia.qml">qml/RoomDrawer/RoomMedia.qml</file>
|
||||||
<file alias="ChooseRoomDialog.qml">qml/Page/ChooseRoomDialog.qml</file>
|
<file alias="ChooseRoomDialog.qml">qml/Page/ChooseRoomDialog.qml</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#include "roommanager.h"
|
#include "roommanager.h"
|
||||||
|
|
||||||
#include "controller.h"
|
#include "controller.h"
|
||||||
|
#include "models/messageeventmodel.h"
|
||||||
#include "neochatconfig.h"
|
#include "neochatconfig.h"
|
||||||
#include "neochatroom.h"
|
#include "neochatroom.h"
|
||||||
#include <KLocalizedString>
|
#include <KLocalizedString>
|
||||||
@@ -26,8 +27,15 @@ RoomManager::RoomManager(QObject *parent)
|
|||||||
, m_currentRoom(nullptr)
|
, m_currentRoom(nullptr)
|
||||||
, m_lastCurrentRoom(nullptr)
|
, m_lastCurrentRoom(nullptr)
|
||||||
, m_config(KConfig(QStringLiteral("data"), KConfig::SimpleConfig, QStandardPaths::AppDataLocation))
|
, m_config(KConfig(QStringLiteral("data"), KConfig::SimpleConfig, QStandardPaths::AppDataLocation))
|
||||||
|
, m_messageEventModel(new MessageEventModel(this))
|
||||||
|
, m_messageFilterModel(new MessageFilterModel(this, m_messageEventModel))
|
||||||
|
, m_mediaMessageFilterModel(new MediaMessageFilterModel(this, m_messageFilterModel))
|
||||||
{
|
{
|
||||||
m_lastRoomConfig = m_config.group(QStringLiteral("LastOpenRoom"));
|
m_lastRoomConfig = m_config.group(QStringLiteral("LastOpenRoom"));
|
||||||
|
|
||||||
|
connect(this, &RoomManager::currentRoomChanged, this, [this]() {
|
||||||
|
m_messageEventModel->setRoom(m_currentRoom);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
RoomManager::~RoomManager()
|
RoomManager::~RoomManager()
|
||||||
@@ -45,6 +53,21 @@ NeoChatRoom *RoomManager::currentRoom() const
|
|||||||
return m_currentRoom;
|
return m_currentRoom;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MessageEventModel *RoomManager::messageEventModel() const
|
||||||
|
{
|
||||||
|
return m_messageEventModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
MessageFilterModel *RoomManager::messageFilterModel() const
|
||||||
|
{
|
||||||
|
return m_messageFilterModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
MediaMessageFilterModel *RoomManager::mediaMessageFilterModel() const
|
||||||
|
{
|
||||||
|
return m_mediaMessageFilterModel;
|
||||||
|
}
|
||||||
|
|
||||||
void RoomManager::openResource(const QString &idOrUri, const QString &action)
|
void RoomManager::openResource(const QString &idOrUri, const QString &action)
|
||||||
{
|
{
|
||||||
Uri uri{idOrUri};
|
Uri uri{idOrUri};
|
||||||
@@ -72,6 +95,14 @@ void RoomManager::openResource(const QString &idOrUri, const QString &action)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RoomManager::maximizeMedia(int index)
|
||||||
|
{
|
||||||
|
if (index < -1 || index > m_mediaMessageFilterModel->rowCount()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Q_EMIT showMaximizedMedia(index);
|
||||||
|
}
|
||||||
|
|
||||||
bool RoomManager::hasOpenRoom() const
|
bool RoomManager::hasOpenRoom() const
|
||||||
{
|
{
|
||||||
return m_currentRoom != nullptr;
|
return m_currentRoom != nullptr;
|
||||||
@@ -266,7 +297,6 @@ void RoomManager::leaveRoom(NeoChatRoom *room)
|
|||||||
if (m_currentRoom && m_currentRoom->id() == room->id()) {
|
if (m_currentRoom && m_currentRoom->id() == room->id()) {
|
||||||
m_currentRoom = m_lastCurrentRoom;
|
m_currentRoom = m_lastCurrentRoom;
|
||||||
m_lastCurrentRoom = nullptr;
|
m_lastCurrentRoom = nullptr;
|
||||||
|
|
||||||
Q_EMIT currentRoomChanged();
|
Q_EMIT currentRoomChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,9 @@
|
|||||||
#include <Quotient/uriresolver.h>
|
#include <Quotient/uriresolver.h>
|
||||||
|
|
||||||
#include "chatdocumenthandler.h"
|
#include "chatdocumenthandler.h"
|
||||||
|
#include "models/mediamessagefiltermodel.h"
|
||||||
|
#include "models/messageeventmodel.h"
|
||||||
|
#include "models/messagefiltermodel.h"
|
||||||
|
|
||||||
class NeoChatRoom;
|
class NeoChatRoom;
|
||||||
|
|
||||||
@@ -36,6 +39,34 @@ class RoomManager : public QObject, public UriResolverBase
|
|||||||
*/
|
*/
|
||||||
Q_PROPERTY(NeoChatRoom *currentRoom READ currentRoom NOTIFY currentRoomChanged)
|
Q_PROPERTY(NeoChatRoom *currentRoom READ currentRoom NOTIFY currentRoomChanged)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The MessageEventModel that should be used for room message visualisation.
|
||||||
|
*
|
||||||
|
* The room object the model uses to get the data will be updated by this class
|
||||||
|
* so there is no need to do this manually or replace the model when a room
|
||||||
|
* changes.
|
||||||
|
*
|
||||||
|
* @note Available here so that the room page and drawer both have access to the
|
||||||
|
* same model.
|
||||||
|
*/
|
||||||
|
Q_PROPERTY(MessageEventModel *messageEventModel READ messageEventModel CONSTANT)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The MessageFilterModel that should be used for room message visualisation.
|
||||||
|
*
|
||||||
|
* @note Available here so that the room page and drawer both have access to the
|
||||||
|
* same model.
|
||||||
|
*/
|
||||||
|
Q_PROPERTY(MessageFilterModel *messageFilterModel READ messageFilterModel CONSTANT)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The MediaMessageFilterModel that should be used for room media message visualisation.
|
||||||
|
*
|
||||||
|
* @note Available here so that the room page and drawer both have access to the
|
||||||
|
* same model.
|
||||||
|
*/
|
||||||
|
Q_PROPERTY(MediaMessageFilterModel *mediaMessageFilterModel READ mediaMessageFilterModel CONSTANT)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Whether a room is currently open in NeoChat.
|
* @brief Whether a room is currently open in NeoChat.
|
||||||
*
|
*
|
||||||
@@ -57,6 +88,10 @@ public:
|
|||||||
|
|
||||||
NeoChatRoom *currentRoom() const;
|
NeoChatRoom *currentRoom() const;
|
||||||
|
|
||||||
|
MessageEventModel *messageEventModel() const;
|
||||||
|
MessageFilterModel *messageFilterModel() const;
|
||||||
|
MediaMessageFilterModel *mediaMessageFilterModel() const;
|
||||||
|
|
||||||
bool hasOpenRoom() const;
|
bool hasOpenRoom() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -151,6 +186,15 @@ public:
|
|||||||
*/
|
*/
|
||||||
Q_INVOKABLE void openResource(const QString &idOrUri, const QString &action = {});
|
Q_INVOKABLE void openResource(const QString &idOrUri, const QString &action = {});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Show a media item maximized.
|
||||||
|
*
|
||||||
|
* @param index the index to open the maximize delegate model at. This is the
|
||||||
|
* index in the MediaMessageFilterModel owned by this RoomManager. A value
|
||||||
|
* of -1 opens a the default item.
|
||||||
|
*/
|
||||||
|
Q_INVOKABLE void maximizeMedia(int index);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Call this when the current used connection is dropped.
|
* @brief Call this when the current used connection is dropped.
|
||||||
*/
|
*/
|
||||||
@@ -208,6 +252,15 @@ Q_SIGNALS:
|
|||||||
*/
|
*/
|
||||||
void showUserDetail(const Quotient::User *user);
|
void showUserDetail(const Quotient::User *user);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Request a media item is shown maximized.
|
||||||
|
*
|
||||||
|
* @param index the index to open the maximize delegate model at. This is the
|
||||||
|
* index in the MediaMessageFilterModel owned by this RoomManager. A value
|
||||||
|
* of -1 opens a the default item.
|
||||||
|
*/
|
||||||
|
void showMaximizedMedia(int index);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Show the direct chat confirmation dialog.
|
* @brief Show the direct chat confirmation dialog.
|
||||||
*
|
*
|
||||||
@@ -232,4 +285,8 @@ private:
|
|||||||
KConfig m_config;
|
KConfig m_config;
|
||||||
KConfigGroup m_lastRoomConfig;
|
KConfigGroup m_lastRoomConfig;
|
||||||
QPointer<ChatDocumentHandler> m_chatDocumentHandler;
|
QPointer<ChatDocumentHandler> m_chatDocumentHandler;
|
||||||
|
|
||||||
|
MessageEventModel *m_messageEventModel;
|
||||||
|
MessageFilterModel *m_messageFilterModel;
|
||||||
|
MediaMessageFilterModel *m_mediaMessageFilterModel;
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user