mirror of
https://github.com/Nheko-Reborn/nheko.git
synced 2024-10-30 09:30:47 +03:00
Merge master and fix conflicts
This commit is contained in:
commit
6bb73f84a3
66 changed files with 2535 additions and 460 deletions
|
@ -3,6 +3,7 @@
|
|||
set -ex
|
||||
|
||||
if [ "$FLATPAK" ]; then
|
||||
sudo apt-get -y install flatpak flatpak-builder elfutils
|
||||
flatpak remote-add --user --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo
|
||||
flatpak --noninteractive install --user flathub org.kde.Platform//5.14
|
||||
flatpak --noninteractive install --user flathub org.kde.Sdk//5.14
|
||||
|
|
|
@ -6,7 +6,26 @@ if [ "$FLATPAK" ]; then
|
|||
mkdir -p build-flatpak
|
||||
cd build-flatpak
|
||||
|
||||
flatpak-builder --ccache --repo=repo --subject="Build of Nheko ${VERSION} `date`" app ../io.github.NhekoReborn.Nheko.json
|
||||
jobsarg=""
|
||||
if [ "$ARCH" = "arm64" ]; then
|
||||
jobsarg="--jobs=2"
|
||||
fi
|
||||
|
||||
flatpak-builder --ccache --repo=repo --subject="Build of Nheko ${VERSION} $jobsarg `date`" app ../io.github.NhekoReborn.Nheko.json &
|
||||
|
||||
# to prevent flatpak builder from timing out on arm, run it in the background and print something every minute for up to 30 minutes.
|
||||
minutes=0
|
||||
limit=40
|
||||
while kill -0 $! >/dev/null 2>&1; do
|
||||
if [ $minutes == $limit ]; then
|
||||
break;
|
||||
fi
|
||||
|
||||
minutes=$((minutes+1))
|
||||
|
||||
sleep 60
|
||||
done
|
||||
|
||||
flatpak build-bundle repo nheko-${VERSION}-${ARCH}.flatpak io.github.NhekoReborn.Nheko master
|
||||
|
||||
mkdir ../artifacts
|
||||
|
|
11
.travis.yml
11
.travis.yml
|
@ -113,10 +113,6 @@ matrix:
|
|||
apt:
|
||||
sources:
|
||||
- sourceline: 'ppa:alexlarsson/flatpak'
|
||||
packages:
|
||||
- flatpak
|
||||
- flatpak-builder
|
||||
- elfutils
|
||||
- os: linux
|
||||
arch: arm64
|
||||
env:
|
||||
|
@ -128,20 +124,17 @@ matrix:
|
|||
sources:
|
||||
- sourceline: 'ppa:alexlarsson/flatpak'
|
||||
packages:
|
||||
- flatpak
|
||||
- flatpak-builder
|
||||
- elfutils
|
||||
- librsvg2-bin
|
||||
|
||||
before_install:
|
||||
# Use TRAVIS_TAG if defined, or the short commit SHA otherwise
|
||||
- export VERSION=${TRAVIS_TAG:-$(git rev-parse --short HEAD)}
|
||||
install:
|
||||
- travis_wait ./.ci/install.sh
|
||||
- ./.ci/install.sh
|
||||
- export PATH=/usr/local/bin:${PATH}
|
||||
|
||||
script:
|
||||
- travis_wait ./.ci/script.sh
|
||||
- ./.ci/script.sh
|
||||
- sed -i -e "s/VERSION_NAME_VALUE/${VERSION}/g" ./.ci/bintray-release.json || true
|
||||
- cp ./.ci/bintray-release.json .
|
||||
deploy:
|
||||
|
|
|
@ -10,7 +10,9 @@ set(
|
|||
CACHE
|
||||
FILEPATH "Default toolchain"
|
||||
)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17 CACHE STRING "C++ standard")
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON CACHE BOOL "Require C++ standard to be supported")
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON CACHE BOOL "compile as PIC by default")
|
||||
|
||||
option(HUNTER_ENABLED "Enable Hunter package manager" OFF)
|
||||
include("cmake/HunterGate.cmake")
|
||||
|
@ -333,7 +335,7 @@ if(USE_BUNDLED_MTXCLIENT)
|
|||
FetchContent_Declare(
|
||||
MatrixClient
|
||||
GIT_REPOSITORY https://github.com/Nheko-Reborn/mtxclient.git
|
||||
GIT_TAG f5c78f4331b62a1e25a2d839cb38b07bb9bd7829
|
||||
GIT_TAG 795b6a82d4f10c629ce0eb99803cc677c413f940
|
||||
)
|
||||
FetchContent_MakeAvailable(MatrixClient)
|
||||
else()
|
||||
|
@ -421,7 +423,7 @@ endif()
|
|||
|
||||
# single instance functionality
|
||||
set(QAPPLICATION_CLASS QApplication CACHE STRING "Inheritance class for SingleApplication")
|
||||
add_subdirectory(third_party/SingleApplication-3.0.19/)
|
||||
add_subdirectory(third_party/SingleApplication-3.1.3.1/)
|
||||
|
||||
feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES)
|
||||
|
||||
|
@ -515,6 +517,9 @@ set(TRANSLATION_DEPS ${LANG_QRC} ${QRC} ${QM_SRC})
|
|||
if (APPLE)
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -framework Foundation -framework Cocoa")
|
||||
set(SRC_FILES ${SRC_FILES} src/notifications/ManagerMac.mm src/emoji/MacHelper.mm)
|
||||
if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.16.0")
|
||||
set_source_files_properties( src/notifications/ManagerMac.mm src/emoji/MacHelper.mm PROPERTIES SKIP_PRECOMPILE_HEADERS ON)
|
||||
endif()
|
||||
elseif (WIN32)
|
||||
file(DOWNLOAD
|
||||
"https://raw.githubusercontent.com/mohabouje/WinToast/41ed1c58d5dce0ee9c01dbdeac05be45358d4f57/src/wintoastlib.cpp"
|
||||
|
@ -578,6 +583,13 @@ target_link_libraries(nheko PRIVATE
|
|||
tweeny
|
||||
SingleApplication::SingleApplication)
|
||||
|
||||
if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.16.0")
|
||||
target_precompile_headers(nheko
|
||||
PRIVATE
|
||||
<string>
|
||||
)
|
||||
endif()
|
||||
|
||||
if(MSVC)
|
||||
target_link_libraries(nheko PRIVATE ntdll)
|
||||
endif()
|
||||
|
|
|
@ -131,7 +131,7 @@
|
|||
{
|
||||
"sha256": "59c9b274bc451cf91a9ba1dd2c7fdcaf5d60b1b3aa83f2c9fa143417cc660722",
|
||||
"type": "archive",
|
||||
"url": "https://dl.bintray.com/boostorg/release/1.72.0/source/boost_1_72_0.tar.bz2"
|
||||
"url": "https://sourceforge.net/projects/boost/files/boost/1.72.0/boost_1_72_0.tar.bz2"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -146,9 +146,9 @@
|
|||
"name": "mtxclient",
|
||||
"sources": [
|
||||
{
|
||||
"sha256": "16203a92b03c488178b31bedca9d9015b1d406443f7e5363a2e09171e50f8dfc",
|
||||
"sha256": "7ba85bb477c9e17e2389faf2333034aa9ceae4b1c65d6bf5f7067f2799e9b770",
|
||||
"type": "archive",
|
||||
"url": "https://github.com/Nheko-Reborn/mtxclient/archive/f5c78f4331b62a1e25a2d839cb38b07bb9bd7829.tar.gz"
|
||||
"url": "https://github.com/Nheko-Reborn/mtxclient/archive/795b6a82d4f10c629ce0eb99803cc677c413f940.tar.gz"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
1586
resources/langs/nheko_it.ts
Normal file
1586
resources/langs/nheko_it.ts
Normal file
File diff suppressed because it is too large
Load diff
|
@ -6,7 +6,7 @@ Rectangle {
|
|||
id: avatar
|
||||
width: 48
|
||||
height: 48
|
||||
radius: settings.avatar_circles ? height/2 : 3
|
||||
radius: settings.avatarCircles ? height/2 : 3
|
||||
|
||||
property alias url: img.source
|
||||
property string displayName
|
||||
|
@ -39,7 +39,7 @@ Rectangle {
|
|||
anchors.fill: parent
|
||||
width: avatar.width
|
||||
height: avatar.height
|
||||
radius: settings.avatar_circles ? height/2 : 3
|
||||
radius: settings.avatarCircles ? height/2 : 3
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -57,7 +57,7 @@ Flow {
|
|||
Text {
|
||||
anchors.baseline: reactionCounter.baseline
|
||||
id: reactionText
|
||||
text: textMetrics.elidedText + (textMetrics.elidedText == model.key ? "" : "…")
|
||||
text: textMetrics.elidedText + (textMetrics.elidedText == textMetrics.text ? "" : "…")
|
||||
font.family: settings.emoji_font_family
|
||||
color: reaction.hovered ? colors.highlight : colors.text
|
||||
maximumLineCount: 1
|
||||
|
@ -65,7 +65,7 @@ Flow {
|
|||
|
||||
Rectangle {
|
||||
id: divider
|
||||
height: reactionCounter.implicitHeight * 1.4
|
||||
height: Math.floor(reactionCounter.implicitHeight * 1.4)
|
||||
width: 1
|
||||
color: (reaction.hovered || model.selfReactedEvent !== '') ? colors.highlight : colors.text
|
||||
}
|
||||
|
|
|
@ -26,13 +26,13 @@ MouseArea {
|
|||
messageContextMenu.show(model.id, model.type, model.isEncrypted, row)
|
||||
}
|
||||
Rectangle {
|
||||
color: (timelineSettings.message_hover_highlight && parent.containsMouse) ? colors.base : "transparent"
|
||||
color: (settings.messageHoverHighlight && parent.containsMouse) ? colors.base : "transparent"
|
||||
anchors.fill: row
|
||||
}
|
||||
RowLayout {
|
||||
id: row
|
||||
|
||||
anchors.leftMargin: avatarSize + 4
|
||||
anchors.leftMargin: avatarSize + 16
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
|
@ -97,7 +97,7 @@ MouseArea {
|
|||
event_id: model.id
|
||||
}
|
||||
ImageButton {
|
||||
visible: timelineSettings.buttons
|
||||
visible: settings.buttonsInTimeline
|
||||
Layout.alignment: Qt.AlignRight | Qt.AlignTop
|
||||
Layout.preferredHeight: 16
|
||||
width: 16
|
||||
|
@ -113,7 +113,7 @@ MouseArea {
|
|||
onClicked: chat.model.replyAction(model.id)
|
||||
}
|
||||
ImageButton {
|
||||
visible: timelineSettings.buttons
|
||||
visible: settings.buttonsInTimeline
|
||||
Layout.alignment: Qt.AlignRight | Qt.AlignTop
|
||||
Layout.preferredHeight: 16
|
||||
width: 16
|
||||
|
|
|
@ -3,7 +3,6 @@ import QtQuick.Controls 2.3
|
|||
import QtQuick.Layouts 1.2
|
||||
import QtGraphicalEffects 1.0
|
||||
import QtQuick.Window 2.2
|
||||
import Qt.labs.settings 1.0
|
||||
|
||||
import im.nheko 1.0
|
||||
import im.nheko.EmojiModel 1.0
|
||||
|
@ -122,7 +121,7 @@ Page {
|
|||
BusyIndicator {
|
||||
visible: running
|
||||
anchors.centerIn: parent
|
||||
running: timelineManager.isInitialSync
|
||||
running: timelineManager.isInitialSync
|
||||
height: 200
|
||||
width: 200
|
||||
z: 3
|
||||
|
@ -133,12 +132,12 @@ Page {
|
|||
|
||||
visible: timelineManager.timeline != null
|
||||
|
||||
cacheBuffer: 500
|
||||
cacheBuffer: 400
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: chatFooter.top
|
||||
width: parent.width
|
||||
|
||||
anchors.leftMargin: 4
|
||||
anchors.rightMargin: scrollbar.width
|
||||
|
@ -180,7 +179,7 @@ Page {
|
|||
id: scrollbar
|
||||
parent: chat.parent
|
||||
anchors.top: chat.top
|
||||
anchors.left: chat.right
|
||||
anchors.right: chat.right
|
||||
anchors.bottom: chat.bottom
|
||||
}
|
||||
|
||||
|
@ -195,7 +194,8 @@ Page {
|
|||
|
||||
id: wrapper
|
||||
property Item section
|
||||
width: chat.width
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
width: (settings.timelineMaxWidth > 100 && (parent.width - settings.timelineMaxWidth) > 32) ? settings.timelineMaxWidth : (parent.width - 32)
|
||||
height: section ? section.height + timelinerow.height : timelinerow.height
|
||||
color: "transparent"
|
||||
|
||||
|
@ -265,7 +265,8 @@ Page {
|
|||
}
|
||||
Row {
|
||||
height: userName.height
|
||||
spacing: 4
|
||||
spacing: 8
|
||||
|
||||
Avatar {
|
||||
width: avatarSize
|
||||
height: avatarSize
|
||||
|
|
|
@ -6,4 +6,5 @@ MatrixText {
|
|||
width: parent ? parent.width : undefined
|
||||
height: isReply ? Math.min(chat.height / 8, implicitHeight) : undefined
|
||||
clip: true
|
||||
font.pointSize: (settings.enlargeEmojiOnlyMessages && model.data.isOnlyEmoji > 0 && model.data.isOnlyEmoji < 4) ? settings.fontSize * 3 : settings.fontSize
|
||||
}
|
||||
|
|
67
resources/qml/emoji/EmojiCategory.qml
Normal file
67
resources/qml/emoji/EmojiCategory.qml
Normal file
|
@ -0,0 +1,67 @@
|
|||
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
|
||||
|
||||
GridView {
|
||||
id: root
|
||||
property var category
|
||||
property var emojiPopup
|
||||
property EmojiProxyModel model: EmojiProxyModel {
|
||||
sourceModel: EmojiModel {
|
||||
viewCategory: category
|
||||
}
|
||||
}
|
||||
|
||||
interactive: false
|
||||
|
||||
cellWidth: 52
|
||||
cellHeight: 52
|
||||
height: 52 * ( model.count / 7 + 1 )
|
||||
|
||||
clip: true
|
||||
|
||||
// Individual emoji
|
||||
delegate: AbstractButton {
|
||||
width: 48
|
||||
height: 48
|
||||
contentItem: Text {
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
font.family: settings.emoji_font_family
|
||||
|
||||
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.picked(emojiPopup.room_id, emojiPopup.event_id, model.unicode)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -31,6 +31,7 @@
|
|||
|
||||
#include "Cache.h"
|
||||
#include "Cache_p.h"
|
||||
#include "EventAccessors.h"
|
||||
#include "Logging.h"
|
||||
#include "Utils.h"
|
||||
|
||||
|
@ -1947,13 +1948,14 @@ Cache::saveTimelineMessages(lmdb::txn &txn,
|
|||
|
||||
json obj = json::object();
|
||||
|
||||
obj["event"] = utils::serialize_event(e);
|
||||
obj["event"] = mtx::accessors::serialize_event(e);
|
||||
obj["token"] = res.prev_batch;
|
||||
|
||||
lmdb::dbi_put(txn,
|
||||
db,
|
||||
lmdb::val(std::to_string(utils::event_timestamp(e))),
|
||||
lmdb::val(obj.dump()));
|
||||
lmdb::dbi_put(
|
||||
txn,
|
||||
db,
|
||||
lmdb::val(std::to_string(obj["event"]["origin_server_ts"].get<uint64_t>())),
|
||||
lmdb::val(obj.dump()));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2026,7 +2028,7 @@ Cache::saveTimelineMentions(lmdb::txn &txn,
|
|||
using namespace mtx::events::state;
|
||||
|
||||
for (const auto ¬if : res) {
|
||||
const auto event_id = utils::event_id(notif.event);
|
||||
const auto event_id = mtx::accessors::event_id(notif.event);
|
||||
|
||||
// double check that we have the correct room_id...
|
||||
if (room_id.compare(notif.room_id) != 0) {
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include "Cache.h"
|
||||
#include "Cache_p.h"
|
||||
#include "ChatPage.h"
|
||||
#include "EventAccessors.h"
|
||||
#include "Logging.h"
|
||||
#include "MainWindow.h"
|
||||
#include "MatrixClient.h"
|
||||
|
@ -255,7 +256,7 @@ ChatPage::ChatPage(QSharedPointer<UserSettings> userSettings, QWidget *parent)
|
|||
text_input_, &TextInputWidget::startedTyping, this, &ChatPage::sendTypingNotifications);
|
||||
connect(typingRefresher_, &QTimer::timeout, this, &ChatPage::sendTypingNotifications);
|
||||
connect(text_input_, &TextInputWidget::stoppedTyping, this, [this]() {
|
||||
if (!userSettings_->isTypingNotificationsEnabled())
|
||||
if (!userSettings_->typingNotifications())
|
||||
return;
|
||||
|
||||
typingRefresher_->stop();
|
||||
|
@ -482,7 +483,7 @@ ChatPage::ChatPage(QSharedPointer<UserSettings> userSettings, QWidget *parent)
|
|||
activateWindow();
|
||||
});
|
||||
|
||||
setGroupViewState(userSettings_->isGroupViewEnabled());
|
||||
setGroupViewState(userSettings_->groupView());
|
||||
|
||||
connect(userSettings_.data(),
|
||||
&UserSettings::groupViewStateChanged,
|
||||
|
@ -891,7 +892,7 @@ void
|
|||
ChatPage::sendDesktopNotifications(const mtx::responses::Notifications &res)
|
||||
{
|
||||
for (const auto &item : res.notifications) {
|
||||
const auto event_id = utils::event_id(item.event);
|
||||
const auto event_id = mtx::accessors::event_id(item.event);
|
||||
|
||||
try {
|
||||
if (item.read) {
|
||||
|
@ -901,7 +902,8 @@ ChatPage::sendDesktopNotifications(const mtx::responses::Notifications &res)
|
|||
|
||||
if (!cache::isNotificationSent(event_id)) {
|
||||
const auto room_id = QString::fromStdString(item.room_id);
|
||||
const auto user_id = utils::event_sender(item.event);
|
||||
const auto user_id =
|
||||
QString::fromStdString(mtx::accessors::sender(item.event));
|
||||
|
||||
// We should only sent one notification per event.
|
||||
cache::markSentNotification(event_id);
|
||||
|
@ -1213,7 +1215,7 @@ ChatPage::unbanUser(QString userid, QString reason)
|
|||
void
|
||||
ChatPage::sendTypingNotifications()
|
||||
{
|
||||
if (!userSettings_->isTypingNotificationsEnabled())
|
||||
if (!userSettings_->typingNotifications())
|
||||
return;
|
||||
|
||||
http::client()->start_typing(
|
||||
|
@ -1349,7 +1351,7 @@ ChatPage::hideSideBars()
|
|||
void
|
||||
ChatPage::showSideBars()
|
||||
{
|
||||
if (userSettings_->isGroupViewEnabled())
|
||||
if (userSettings_->groupView())
|
||||
communitiesList_->show();
|
||||
|
||||
sideBar_->show();
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
#include "ui/Theme.h"
|
||||
|
||||
class RippleOverlay;
|
||||
class QPainter;
|
||||
class QMouseEvent;
|
||||
|
||||
class CommunitiesListItem : public QWidget
|
||||
|
|
|
@ -400,3 +400,9 @@ mtx::accessors::media_width(const mtx::events::collections::TimelineEvents &even
|
|||
{
|
||||
return std::visit(EventMediaWidth{}, event);
|
||||
}
|
||||
|
||||
nlohmann::json
|
||||
mtx::accessors::serialize_event(const mtx::events::collections::TimelineEvents &event)
|
||||
{
|
||||
return std::visit([](const auto &e) { return nlohmann::json(e); }, event);
|
||||
}
|
||||
|
|
|
@ -63,4 +63,7 @@ media_height(const mtx::events::collections::TimelineEvents &event);
|
|||
|
||||
uint64_t
|
||||
media_width(const mtx::events::collections::TimelineEvents &event);
|
||||
|
||||
nlohmann::json
|
||||
serialize_event(const mtx::events::collections::TimelineEvents &event);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include <QHBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QPushButton>
|
||||
|
||||
#include "InviteeItem.h"
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
#pragma once
|
||||
|
||||
#include <QLabel>
|
||||
#include <QWidget>
|
||||
|
||||
#include <mtx/identifiers.hpp>
|
||||
|
||||
class QPushButton;
|
||||
class QLabel;
|
||||
|
||||
class InviteeItem : public QWidget
|
||||
{
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
*/
|
||||
|
||||
#include <QDesktopServices>
|
||||
#include <QLabel>
|
||||
#include <QPainter>
|
||||
#include <QStyleOption>
|
||||
|
||||
|
@ -118,7 +119,7 @@ LoginPage::LoginPage(QWidget *parent)
|
|||
deviceName_->setLabel(tr("Device name"));
|
||||
deviceName_->setToolTip(
|
||||
tr("A name for this device, which will be shown to others, when verifying your devices. "
|
||||
"If none is provided, a random string is used for privacy purposes."));
|
||||
"If none is provided a default is used."));
|
||||
|
||||
serverInput_ = new TextField(this);
|
||||
serverInput_->setLabel("Homeserver address");
|
||||
|
@ -132,7 +133,7 @@ LoginPage::LoginPage(QWidget *parent)
|
|||
|
||||
form_layout_->addLayout(matrixidLayout_);
|
||||
form_layout_->addWidget(password_input_);
|
||||
form_layout_->addWidget(deviceName_, Qt::AlignHCenter, nullptr);
|
||||
form_layout_->addWidget(deviceName_, Qt::AlignHCenter);
|
||||
form_layout_->addLayout(serverLayout_);
|
||||
|
||||
button_layout_ = new QHBoxLayout();
|
||||
|
@ -179,6 +180,12 @@ LoginPage::LoginPage(QWidget *parent)
|
|||
connect(serverInput_, SIGNAL(editingFinished()), this, SLOT(onServerAddressEntered()));
|
||||
}
|
||||
|
||||
void
|
||||
LoginPage::loginError(const QString &msg)
|
||||
{
|
||||
error_label_->setText(msg);
|
||||
}
|
||||
|
||||
void
|
||||
LoginPage::onMatrixIdEntered()
|
||||
{
|
||||
|
|
|
@ -17,8 +17,6 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <QLabel>
|
||||
#include <QLayout>
|
||||
#include <QWidget>
|
||||
|
||||
class FlatButton;
|
||||
|
@ -26,6 +24,9 @@ class LoadingIndicator;
|
|||
class OverlayModal;
|
||||
class RaisedButton;
|
||||
class TextField;
|
||||
class QLabel;
|
||||
class QVBoxLayout;
|
||||
class QHBoxLayout;
|
||||
|
||||
namespace mtx {
|
||||
namespace responses {
|
||||
|
@ -65,7 +66,7 @@ protected:
|
|||
|
||||
public slots:
|
||||
// Displays errors produced during the login.
|
||||
void loginError(const QString &msg) { error_label_->setText(msg); }
|
||||
void loginError(const QString &msg);
|
||||
|
||||
private slots:
|
||||
// Callback for the back button.
|
||||
|
|
|
@ -148,7 +148,7 @@ MainWindow::MainWindow(QWidget *parent)
|
|||
|
||||
QSettings settings;
|
||||
|
||||
trayIcon_->setVisible(userSettings_->isTrayEnabled());
|
||||
trayIcon_->setVisible(userSettings_->tray());
|
||||
|
||||
if (hasActiveUser()) {
|
||||
QString token = settings.value("auth/access_token").toString();
|
||||
|
@ -286,7 +286,7 @@ void
|
|||
MainWindow::closeEvent(QCloseEvent *event)
|
||||
{
|
||||
if (!qApp->isSavingSession() && isVisible() && pageSupportsTray() &&
|
||||
userSettings_->isTrayEnabled()) {
|
||||
userSettings_->tray()) {
|
||||
event->ignore();
|
||||
hide();
|
||||
}
|
||||
|
|
|
@ -17,7 +17,8 @@ MxcImageResponse::run()
|
|||
auto data = cache::image(fileName);
|
||||
if (!data.isNull()) {
|
||||
m_image = utils::readImage(&data);
|
||||
m_image = m_image.scaled(m_requestedSize, Qt::KeepAspectRatio);
|
||||
m_image = m_image.scaled(
|
||||
m_requestedSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
||||
m_image.setText("mxc url", "mxc://" + m_id);
|
||||
|
||||
if (!m_image.isNull()) {
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <QLabel>
|
||||
#include <QMetaType>
|
||||
#include <QPainter>
|
||||
#include <QStyleOption>
|
||||
|
@ -106,10 +107,10 @@ RegisterPage::RegisterPage(QWidget *parent)
|
|||
tr("A server that allows registration. Since matrix is decentralized, you need to first "
|
||||
"find a server you can register on or host your own."));
|
||||
|
||||
form_layout_->addWidget(username_input_, Qt::AlignHCenter, nullptr);
|
||||
form_layout_->addWidget(password_input_, Qt::AlignHCenter, nullptr);
|
||||
form_layout_->addWidget(password_confirmation_, Qt::AlignHCenter, nullptr);
|
||||
form_layout_->addWidget(server_input_, Qt::AlignHCenter, nullptr);
|
||||
form_layout_->addWidget(username_input_, Qt::AlignHCenter);
|
||||
form_layout_->addWidget(password_input_, Qt::AlignHCenter);
|
||||
form_layout_->addWidget(password_confirmation_, Qt::AlignHCenter);
|
||||
form_layout_->addWidget(server_input_, Qt::AlignHCenter);
|
||||
|
||||
button_layout_ = new QHBoxLayout();
|
||||
button_layout_->setSpacing(0);
|
||||
|
|
|
@ -17,8 +17,8 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <QLabel>
|
||||
#include <QLayout>
|
||||
#include <QWidget>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <mtx/user_interactive.hpp>
|
||||
|
@ -26,6 +26,9 @@
|
|||
class FlatButton;
|
||||
class RaisedButton;
|
||||
class TextField;
|
||||
class QLabel;
|
||||
class QVBoxLayout;
|
||||
class QHBoxLayout;
|
||||
|
||||
class RegisterPage : public QWidget
|
||||
{
|
||||
|
|
|
@ -451,7 +451,7 @@ RoomInfoListItem::calculateImportance() const
|
|||
// returns ImportanceDisabled or Invite
|
||||
if (isInvite()) {
|
||||
return Invite;
|
||||
} else if (!settings->isSortByImportanceEnabled()) {
|
||||
} else if (!settings->sortByImportance()) {
|
||||
return ImportanceDisabled;
|
||||
} else if (unreadHighlightedMsgCount_) {
|
||||
return NewMentions;
|
||||
|
|
|
@ -21,6 +21,8 @@
|
|||
#include <QObject>
|
||||
#include <QPainter>
|
||||
#include <QScroller>
|
||||
#include <QStyle>
|
||||
#include <QStyleOption>
|
||||
#include <QTimer>
|
||||
|
||||
#include "Logging.h"
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
#include <QIcon>
|
||||
#include <QPainter>
|
||||
#include <QResizeEvent>
|
||||
#include <QStyle>
|
||||
#include <QStyleOption>
|
||||
|
||||
#include <mtx/requests.hpp>
|
||||
|
||||
|
|
|
@ -27,7 +27,6 @@ class Menu;
|
|||
class TextLabel;
|
||||
class OverlayModal;
|
||||
|
||||
class QPainter;
|
||||
class QLabel;
|
||||
class QHBoxLayout;
|
||||
class QVBoxLayout;
|
||||
|
|
|
@ -16,7 +16,10 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <QLabel>
|
||||
#include <QPainter>
|
||||
#include <QStyle>
|
||||
#include <QStyleOption>
|
||||
#include <QTimer>
|
||||
|
||||
#include <iostream>
|
||||
|
|
|
@ -17,13 +17,16 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <QLabel>
|
||||
#include <QLayout>
|
||||
#include <QWidget>
|
||||
|
||||
class Avatar;
|
||||
class FlatButton;
|
||||
class OverlayModal;
|
||||
|
||||
class QLabel;
|
||||
class QHBoxLayout;
|
||||
class QVBoxLayout;
|
||||
|
||||
class UserInfoWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#include <QScrollArea>
|
||||
#include <QScroller>
|
||||
#include <QSettings>
|
||||
#include <QSpinBox>
|
||||
#include <QStandardPaths>
|
||||
#include <QString>
|
||||
#include <QTextStream>
|
||||
|
@ -51,54 +52,206 @@ void
|
|||
UserSettings::load()
|
||||
{
|
||||
QSettings settings;
|
||||
isTrayEnabled_ = settings.value("user/window/tray", false).toBool();
|
||||
hasDesktopNotifications_ = settings.value("user/desktop_notifications", true).toBool();
|
||||
isStartInTrayEnabled_ = settings.value("user/window/start_in_tray", false).toBool();
|
||||
isGroupViewEnabled_ = settings.value("user/group_view", true).toBool();
|
||||
isButtonsInTimelineEnabled_ = settings.value("user/timeline/buttons", true).toBool();
|
||||
isMessageHoverHighlightEnabled_ =
|
||||
tray_ = settings.value("user/window/tray", false).toBool();
|
||||
hasDesktopNotifications_ = settings.value("user/desktop_notifications", true).toBool();
|
||||
startInTray_ = settings.value("user/window/start_in_tray", false).toBool();
|
||||
groupView_ = settings.value("user/group_view", true).toBool();
|
||||
buttonsInTimeline_ = settings.value("user/timeline/buttons", true).toBool();
|
||||
timelineMaxWidth_ = settings.value("user/timeline/max_width", 0).toInt();
|
||||
messageHoverHighlight_ =
|
||||
settings.value("user/timeline/message_hover_highlight", false).toBool();
|
||||
isMarkdownEnabled_ = settings.value("user/markdown_enabled", true).toBool();
|
||||
isTypingNotificationsEnabled_ = settings.value("user/typing_notifications", true).toBool();
|
||||
sortByImportance_ = settings.value("user/sort_by_unread", true).toBool();
|
||||
isReadReceiptsEnabled_ = settings.value("user/read_receipts", true).toBool();
|
||||
theme_ = settings.value("user/theme", defaultTheme_).toString();
|
||||
font_ = settings.value("user/font_family", "default").toString();
|
||||
avatarCircles_ = settings.value("user/avatar_circles", true).toBool();
|
||||
decryptSidebar_ = settings.value("user/decrypt_sidebar", true).toBool();
|
||||
emojiFont_ = settings.value("user/emoji_font_family", "default").toString();
|
||||
baseFontSize_ = settings.value("user/font_size", QFont().pointSizeF()).toDouble();
|
||||
enlargeEmojiOnlyMessages_ =
|
||||
settings.value("user/timeline/enlarge_emoji_only_msg", false).toBool();
|
||||
markdown_ = settings.value("user/markdown_enabled", true).toBool();
|
||||
typingNotifications_ = settings.value("user/typing_notifications", true).toBool();
|
||||
sortByImportance_ = settings.value("user/sort_by_unread", true).toBool();
|
||||
readReceipts_ = settings.value("user/read_receipts", true).toBool();
|
||||
theme_ = settings.value("user/theme", defaultTheme_).toString();
|
||||
font_ = settings.value("user/font_family", "default").toString();
|
||||
avatarCircles_ = settings.value("user/avatar_circles", true).toBool();
|
||||
decryptSidebar_ = settings.value("user/decrypt_sidebar", true).toBool();
|
||||
emojiFont_ = settings.value("user/emoji_font_family", "default").toString();
|
||||
baseFontSize_ = settings.value("user/font_size", QFont().pointSizeF()).toDouble();
|
||||
|
||||
applyTheme();
|
||||
}
|
||||
void
|
||||
UserSettings::setMessageHoverHighlight(bool state)
|
||||
{
|
||||
if (state == messageHoverHighlight_)
|
||||
return;
|
||||
messageHoverHighlight_ = state;
|
||||
emit messageHoverHighlightChanged(state);
|
||||
save();
|
||||
}
|
||||
void
|
||||
UserSettings::setEnlargeEmojiOnlyMessages(bool state)
|
||||
{
|
||||
if (state == enlargeEmojiOnlyMessages_)
|
||||
return;
|
||||
enlargeEmojiOnlyMessages_ = state;
|
||||
emit enlargeEmojiOnlyMessagesChanged(state);
|
||||
save();
|
||||
}
|
||||
void
|
||||
UserSettings::setTray(bool state)
|
||||
{
|
||||
if (state == tray_)
|
||||
return;
|
||||
tray_ = state;
|
||||
emit trayChanged(state);
|
||||
save();
|
||||
}
|
||||
|
||||
void
|
||||
UserSettings::setStartInTray(bool state)
|
||||
{
|
||||
if (state == startInTray_)
|
||||
return;
|
||||
startInTray_ = state;
|
||||
emit startInTrayChanged(state);
|
||||
save();
|
||||
}
|
||||
|
||||
void
|
||||
UserSettings::setGroupView(bool state)
|
||||
{
|
||||
if (groupView_ != state)
|
||||
emit groupViewStateChanged(state);
|
||||
|
||||
groupView_ = state;
|
||||
save();
|
||||
}
|
||||
|
||||
void
|
||||
UserSettings::setMarkdown(bool state)
|
||||
{
|
||||
if (state == markdown_)
|
||||
return;
|
||||
markdown_ = state;
|
||||
emit markdownChanged(state);
|
||||
save();
|
||||
}
|
||||
|
||||
void
|
||||
UserSettings::setReadReceipts(bool state)
|
||||
{
|
||||
if (state == readReceipts_)
|
||||
return;
|
||||
readReceipts_ = state;
|
||||
emit readReceiptsChanged(state);
|
||||
save();
|
||||
}
|
||||
|
||||
void
|
||||
UserSettings::setTypingNotifications(bool state)
|
||||
{
|
||||
if (state == typingNotifications_)
|
||||
return;
|
||||
typingNotifications_ = state;
|
||||
emit typingNotificationsChanged(state);
|
||||
save();
|
||||
}
|
||||
|
||||
void
|
||||
UserSettings::setSortByImportance(bool state)
|
||||
{
|
||||
if (state == sortByImportance_)
|
||||
return;
|
||||
sortByImportance_ = state;
|
||||
emit roomSortingChanged(state);
|
||||
save();
|
||||
}
|
||||
|
||||
void
|
||||
UserSettings::setButtonsInTimeline(bool state)
|
||||
{
|
||||
if (state == buttonsInTimeline_)
|
||||
return;
|
||||
buttonsInTimeline_ = state;
|
||||
emit buttonInTimelineChanged(state);
|
||||
save();
|
||||
}
|
||||
|
||||
void
|
||||
UserSettings::setTimelineMaxWidth(int state)
|
||||
{
|
||||
if (state == timelineMaxWidth_)
|
||||
return;
|
||||
timelineMaxWidth_ = state;
|
||||
emit timelineMaxWidthChanged(state);
|
||||
save();
|
||||
}
|
||||
|
||||
void
|
||||
UserSettings::setDesktopNotifications(bool state)
|
||||
{
|
||||
if (state == hasDesktopNotifications_)
|
||||
return;
|
||||
hasDesktopNotifications_ = state;
|
||||
emit desktopNotificationsChanged(state);
|
||||
save();
|
||||
}
|
||||
|
||||
void
|
||||
UserSettings::setAvatarCircles(bool state)
|
||||
{
|
||||
if (state == avatarCircles_)
|
||||
return;
|
||||
avatarCircles_ = state;
|
||||
emit avatarCirclesChanged(state);
|
||||
save();
|
||||
}
|
||||
|
||||
void
|
||||
UserSettings::setDecryptSidebar(bool state)
|
||||
{
|
||||
if (state == decryptSidebar_)
|
||||
return;
|
||||
decryptSidebar_ = state;
|
||||
emit decryptSidebarChanged(state);
|
||||
save();
|
||||
}
|
||||
|
||||
void
|
||||
UserSettings::setFontSize(double size)
|
||||
{
|
||||
if (size == baseFontSize_)
|
||||
return;
|
||||
baseFontSize_ = size;
|
||||
emit fontSizeChanged(size);
|
||||
save();
|
||||
}
|
||||
|
||||
void
|
||||
UserSettings::setFontFamily(QString family)
|
||||
{
|
||||
if (family == font_)
|
||||
return;
|
||||
font_ = family;
|
||||
emit fontChanged(family);
|
||||
save();
|
||||
}
|
||||
|
||||
void
|
||||
UserSettings::setEmojiFontFamily(QString family)
|
||||
{
|
||||
if (family == emojiFont_)
|
||||
return;
|
||||
emojiFont_ = family;
|
||||
emit emojiFontChanged(family);
|
||||
save();
|
||||
}
|
||||
|
||||
void
|
||||
UserSettings::setTheme(QString theme)
|
||||
{
|
||||
if (theme == theme)
|
||||
return;
|
||||
theme_ = theme;
|
||||
save();
|
||||
applyTheme();
|
||||
emit themeChanged(theme);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -161,29 +314,33 @@ UserSettings::save()
|
|||
settings.beginGroup("user");
|
||||
|
||||
settings.beginGroup("window");
|
||||
settings.setValue("tray", isTrayEnabled_);
|
||||
settings.setValue("start_in_tray", isStartInTrayEnabled_);
|
||||
settings.setValue("tray", tray_);
|
||||
settings.setValue("start_in_tray", startInTray_);
|
||||
settings.endGroup();
|
||||
|
||||
settings.beginGroup("timeline");
|
||||
settings.setValue("buttons", isButtonsInTimelineEnabled_);
|
||||
settings.setValue("message_hover_highlight", isMessageHoverHighlightEnabled_);
|
||||
settings.setValue("buttons", buttonsInTimeline_);
|
||||
settings.setValue("message_hover_highlight", messageHoverHighlight_);
|
||||
settings.setValue("enlarge_emoji_only_msg", enlargeEmojiOnlyMessages_);
|
||||
settings.setValue("max_width", timelineMaxWidth_);
|
||||
settings.endGroup();
|
||||
|
||||
settings.setValue("avatar_circles", avatarCircles_);
|
||||
settings.setValue("decrypt_sidebar", decryptSidebar_);
|
||||
settings.setValue("font_size", baseFontSize_);
|
||||
settings.setValue("typing_notifications", isTypingNotificationsEnabled_);
|
||||
settings.setValue("typing_notifications", typingNotifications_);
|
||||
settings.setValue("minor_events", sortByImportance_);
|
||||
settings.setValue("read_receipts", isReadReceiptsEnabled_);
|
||||
settings.setValue("group_view", isGroupViewEnabled_);
|
||||
settings.setValue("markdown_enabled", isMarkdownEnabled_);
|
||||
settings.setValue("read_receipts", readReceipts_);
|
||||
settings.setValue("group_view", groupView_);
|
||||
settings.setValue("markdown_enabled", markdown_);
|
||||
settings.setValue("desktop_notifications", hasDesktopNotifications_);
|
||||
settings.setValue("theme", theme());
|
||||
settings.setValue("font_family", font_);
|
||||
settings.setValue("emoji_font_family", emojiFont_);
|
||||
|
||||
settings.endGroup();
|
||||
|
||||
settings.sync();
|
||||
}
|
||||
|
||||
HorizontalLine::HorizontalLine(QWidget *parent)
|
||||
|
@ -231,24 +388,26 @@ UserSettingsPage::UserSettingsPage(QSharedPointer<UserSettings> settings, QWidge
|
|||
general_->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed);
|
||||
general_->setFont(font);
|
||||
|
||||
trayToggle_ = new Toggle{this};
|
||||
startInTrayToggle_ = new Toggle{this};
|
||||
avatarCircles_ = new Toggle{this};
|
||||
decryptSidebar_ = new Toggle(this);
|
||||
groupViewToggle_ = new Toggle{this};
|
||||
timelineButtonsToggle_ = new Toggle{this};
|
||||
typingNotifications_ = new Toggle{this};
|
||||
messageHoverHighlight_ = new Toggle{this};
|
||||
sortByImportance_ = new Toggle{this};
|
||||
readReceipts_ = new Toggle{this};
|
||||
markdownEnabled_ = new Toggle{this};
|
||||
desktopNotifications_ = new Toggle{this};
|
||||
scaleFactorCombo_ = new QComboBox{this};
|
||||
fontSizeCombo_ = new QComboBox{this};
|
||||
fontSelectionCombo_ = new QComboBox{this};
|
||||
emojiFontSelectionCombo_ = new QComboBox{this};
|
||||
trayToggle_ = new Toggle{this};
|
||||
startInTrayToggle_ = new Toggle{this};
|
||||
avatarCircles_ = new Toggle{this};
|
||||
decryptSidebar_ = new Toggle(this);
|
||||
groupViewToggle_ = new Toggle{this};
|
||||
timelineButtonsToggle_ = new Toggle{this};
|
||||
typingNotifications_ = new Toggle{this};
|
||||
messageHoverHighlight_ = new Toggle{this};
|
||||
enlargeEmojiOnlyMessages_ = new Toggle{this};
|
||||
sortByImportance_ = new Toggle{this};
|
||||
readReceipts_ = new Toggle{this};
|
||||
markdown_ = new Toggle{this};
|
||||
desktopNotifications_ = new Toggle{this};
|
||||
scaleFactorCombo_ = new QComboBox{this};
|
||||
fontSizeCombo_ = new QComboBox{this};
|
||||
fontSelectionCombo_ = new QComboBox{this};
|
||||
emojiFontSelectionCombo_ = new QComboBox{this};
|
||||
timelineMaxWidthSpin_ = new QSpinBox{this};
|
||||
|
||||
if (!settings_->isTrayEnabled())
|
||||
if (!settings_->tray())
|
||||
startInTrayToggle_->setDisabled(true);
|
||||
|
||||
avatarCircles_->setFixedSize(64, 48);
|
||||
|
@ -291,6 +450,10 @@ UserSettingsPage::UserSettingsPage(QSharedPointer<UserSettings> settings, QWidge
|
|||
int themeIndex = themeCombo_->findText(themeStr);
|
||||
themeCombo_->setCurrentIndex(themeIndex);
|
||||
|
||||
timelineMaxWidthSpin_->setMinimum(0);
|
||||
timelineMaxWidthSpin_->setMaximum(100'000'000);
|
||||
timelineMaxWidthSpin_->setSingleStep(10);
|
||||
|
||||
auto encryptionLabel_ = new QLabel{tr("ENCRYPTION"), this};
|
||||
encryptionLabel_->setFixedHeight(encryptionLabel_->minimumHeight() + LayoutTopMargin);
|
||||
encryptionLabel_->setAlignment(Qt::AlignBottom);
|
||||
|
@ -323,11 +486,15 @@ UserSettingsPage::UserSettingsPage(QSharedPointer<UserSettings> settings, QWidge
|
|||
sessionKeysLayout->addWidget(sessionKeysExportBtn, 0, Qt::AlignRight);
|
||||
sessionKeysLayout->addWidget(sessionKeysImportBtn, 0, Qt::AlignRight);
|
||||
|
||||
auto boxWrap = [this, &font](QString labelText, QWidget *field) {
|
||||
auto boxWrap = [this, &font](QString labelText, QWidget *field, QString tooltipText = "") {
|
||||
auto label = new QLabel{labelText, this};
|
||||
label->setFont(font);
|
||||
label->setMargin(OptionMargin);
|
||||
|
||||
if (!tooltipText.isEmpty()) {
|
||||
label->setToolTip(tooltipText);
|
||||
}
|
||||
|
||||
auto layout = new QHBoxLayout;
|
||||
layout->addWidget(field, 0, Qt::AlignRight);
|
||||
|
||||
|
@ -336,25 +503,70 @@ UserSettingsPage::UserSettingsPage(QSharedPointer<UserSettings> settings, QWidge
|
|||
|
||||
formLayout_->addRow(general_);
|
||||
formLayout_->addRow(new HorizontalLine{this});
|
||||
boxWrap(tr("Minimize to tray"), trayToggle_);
|
||||
boxWrap(tr("Start in tray"), startInTrayToggle_);
|
||||
boxWrap(
|
||||
tr("Minimize to tray"),
|
||||
trayToggle_,
|
||||
tr("Keep the application running in the background after closing the client window."));
|
||||
boxWrap(tr("Start in tray"),
|
||||
startInTrayToggle_,
|
||||
tr("Start the application in the background without showing the client window."));
|
||||
formLayout_->addRow(new HorizontalLine{this});
|
||||
boxWrap(tr("Circular Avatars"), avatarCircles_);
|
||||
boxWrap(tr("Group's sidebar"), groupViewToggle_);
|
||||
boxWrap(tr("Decrypt messages in sidebar"), decryptSidebar_);
|
||||
boxWrap(tr("Show buttons in timeline"), timelineButtonsToggle_);
|
||||
boxWrap(tr("Typing notifications"), typingNotifications_);
|
||||
boxWrap(tr("Sort rooms by unreads"), sortByImportance_);
|
||||
boxWrap(tr("Circular Avatars"),
|
||||
avatarCircles_,
|
||||
tr("Change the appearance of user avatars in chats.\nOFF - square, ON - Circle."));
|
||||
boxWrap(tr("Group's sidebar"),
|
||||
groupViewToggle_,
|
||||
tr("Show a column containing groups and tags next to the room list."));
|
||||
boxWrap(tr("Decrypt messages in sidebar"),
|
||||
decryptSidebar_,
|
||||
tr("Decrypt the messages shown in the sidebar.\nOnly affects messages in "
|
||||
"encrypted chats."));
|
||||
boxWrap(tr("Show buttons in timeline"),
|
||||
timelineButtonsToggle_,
|
||||
tr("Show buttons to quickly reply, react or access additional options next to each "
|
||||
"message."));
|
||||
boxWrap(tr("Limit width of timeline"),
|
||||
timelineMaxWidthSpin_,
|
||||
tr("Set the max width of messages in the timeline (in pixels). This can help "
|
||||
"readability on wide screen, when Nheko is maximised"));
|
||||
boxWrap(tr("Typing notifications"),
|
||||
typingNotifications_,
|
||||
tr("Show who is typing in a room.\nThis will also enable or disable sending typing "
|
||||
"notifications to others."));
|
||||
boxWrap(
|
||||
tr("Sort rooms by unreads"),
|
||||
sortByImportance_,
|
||||
tr(
|
||||
"Display rooms with new messages first.\nIf this is off, the list of rooms will only "
|
||||
"be sorted by the timestamp of the last message in a room.\nIf this is on, rooms which "
|
||||
"have active notifications (the small circle with a number in it) will be sorted on "
|
||||
"top. Rooms, that you have muted, will still be sorted by timestamp, since you don't "
|
||||
"seem to consider them as important as the other rooms."));
|
||||
formLayout_->addRow(new HorizontalLine{this});
|
||||
boxWrap(tr("Read receipts"), readReceipts_);
|
||||
boxWrap(tr("Send messages as Markdown"), markdownEnabled_);
|
||||
boxWrap(tr("Desktop notifications"), desktopNotifications_);
|
||||
boxWrap(tr("Highlight message on hover"), messageHoverHighlight_);
|
||||
boxWrap(tr("Read receipts"),
|
||||
readReceipts_,
|
||||
tr("Show if your message was read.\nStatus is displayed next to timestamps."));
|
||||
boxWrap(
|
||||
tr("Send messages as Markdown"),
|
||||
markdown_,
|
||||
tr("Allow using markdown in messages.\nWhen disabled, all messages are sent as a plain "
|
||||
"text."));
|
||||
boxWrap(tr("Desktop notifications"),
|
||||
desktopNotifications_,
|
||||
tr("Notify about received message when the client is not currently focused."));
|
||||
boxWrap(tr("Highlight message on hover"),
|
||||
messageHoverHighlight_,
|
||||
tr("Change the background color of messages when you hover over them."));
|
||||
boxWrap(tr("Large Emoji in timeline"),
|
||||
enlargeEmojiOnlyMessages_,
|
||||
tr("Make font size larger if messages with only a few emojis are displayed."));
|
||||
formLayout_->addRow(uiLabel_);
|
||||
formLayout_->addRow(new HorizontalLine{this});
|
||||
|
||||
#if !defined(Q_OS_MAC)
|
||||
boxWrap(tr("Scale factor"), scaleFactorCombo_);
|
||||
boxWrap(tr("Scale factor"),
|
||||
scaleFactorCombo_,
|
||||
tr("Change the scale factor of the whole user interface."));
|
||||
#else
|
||||
scaleFactorCombo_->hide();
|
||||
#endif
|
||||
|
@ -400,78 +612,87 @@ UserSettingsPage::UserSettingsPage(QSharedPointer<UserSettings> settings, QWidge
|
|||
topLayout_->addWidget(versionInfo);
|
||||
|
||||
connect(themeCombo_,
|
||||
static_cast<void (QComboBox::*)(const QString &)>(&QComboBox::activated),
|
||||
static_cast<void (QComboBox::*)(const QString &)>(&QComboBox::currentTextChanged),
|
||||
[this](const QString &text) {
|
||||
settings_->setTheme(text.toLower());
|
||||
emit themeChanged();
|
||||
});
|
||||
connect(scaleFactorCombo_,
|
||||
static_cast<void (QComboBox::*)(const QString &)>(&QComboBox::activated),
|
||||
static_cast<void (QComboBox::*)(const QString &)>(&QComboBox::currentTextChanged),
|
||||
[](const QString &factor) { utils::setScaleFactor(factor.toFloat()); });
|
||||
connect(fontSizeCombo_,
|
||||
static_cast<void (QComboBox::*)(const QString &)>(&QComboBox::activated),
|
||||
static_cast<void (QComboBox::*)(const QString &)>(&QComboBox::currentTextChanged),
|
||||
[this](const QString &size) { settings_->setFontSize(size.trimmed().toDouble()); });
|
||||
connect(fontSelectionCombo_,
|
||||
static_cast<void (QComboBox::*)(const QString &)>(&QComboBox::activated),
|
||||
static_cast<void (QComboBox::*)(const QString &)>(&QComboBox::currentTextChanged),
|
||||
[this](const QString &family) { settings_->setFontFamily(family.trimmed()); });
|
||||
connect(emojiFontSelectionCombo_,
|
||||
static_cast<void (QComboBox::*)(const QString &)>(&QComboBox::activated),
|
||||
static_cast<void (QComboBox::*)(const QString &)>(&QComboBox::currentTextChanged),
|
||||
[this](const QString &family) { settings_->setEmojiFontFamily(family.trimmed()); });
|
||||
connect(trayToggle_, &Toggle::toggled, this, [this](bool isDisabled) {
|
||||
settings_->setTray(!isDisabled);
|
||||
if (isDisabled) {
|
||||
connect(trayToggle_, &Toggle::toggled, this, [this](bool disabled) {
|
||||
settings_->setTray(!disabled);
|
||||
if (disabled) {
|
||||
startInTrayToggle_->setDisabled(true);
|
||||
} else {
|
||||
startInTrayToggle_->setEnabled(true);
|
||||
}
|
||||
emit trayOptionChanged(!isDisabled);
|
||||
emit trayOptionChanged(!disabled);
|
||||
});
|
||||
|
||||
connect(startInTrayToggle_, &Toggle::toggled, this, [this](bool isDisabled) {
|
||||
settings_->setStartInTray(!isDisabled);
|
||||
connect(startInTrayToggle_, &Toggle::toggled, this, [this](bool disabled) {
|
||||
settings_->setStartInTray(!disabled);
|
||||
});
|
||||
|
||||
connect(groupViewToggle_, &Toggle::toggled, this, [this](bool isDisabled) {
|
||||
settings_->setGroupView(!isDisabled);
|
||||
connect(groupViewToggle_, &Toggle::toggled, this, [this](bool disabled) {
|
||||
settings_->setGroupView(!disabled);
|
||||
});
|
||||
|
||||
connect(decryptSidebar_, &Toggle::toggled, this, [this](bool isDisabled) {
|
||||
settings_->setDecryptSidebar(!isDisabled);
|
||||
connect(decryptSidebar_, &Toggle::toggled, this, [this](bool disabled) {
|
||||
settings_->setDecryptSidebar(!disabled);
|
||||
emit decryptSidebarChanged();
|
||||
});
|
||||
|
||||
connect(avatarCircles_, &Toggle::toggled, this, [this](bool isDisabled) {
|
||||
settings_->setAvatarCircles(!isDisabled);
|
||||
connect(avatarCircles_, &Toggle::toggled, this, [this](bool disabled) {
|
||||
settings_->setAvatarCircles(!disabled);
|
||||
});
|
||||
|
||||
connect(markdownEnabled_, &Toggle::toggled, this, [this](bool isDisabled) {
|
||||
settings_->setMarkdownEnabled(!isDisabled);
|
||||
connect(markdown_, &Toggle::toggled, this, [this](bool disabled) {
|
||||
settings_->setMarkdown(!disabled);
|
||||
});
|
||||
|
||||
connect(typingNotifications_, &Toggle::toggled, this, [this](bool isDisabled) {
|
||||
settings_->setTypingNotifications(!isDisabled);
|
||||
connect(typingNotifications_, &Toggle::toggled, this, [this](bool disabled) {
|
||||
settings_->setTypingNotifications(!disabled);
|
||||
});
|
||||
|
||||
connect(sortByImportance_, &Toggle::toggled, this, [this](bool isDisabled) {
|
||||
settings_->setSortByImportance(!isDisabled);
|
||||
connect(sortByImportance_, &Toggle::toggled, this, [this](bool disabled) {
|
||||
settings_->setSortByImportance(!disabled);
|
||||
});
|
||||
|
||||
connect(timelineButtonsToggle_, &Toggle::toggled, this, [this](bool isDisabled) {
|
||||
settings_->setButtonsInTimeline(!isDisabled);
|
||||
connect(timelineButtonsToggle_, &Toggle::toggled, this, [this](bool disabled) {
|
||||
settings_->setButtonsInTimeline(!disabled);
|
||||
});
|
||||
|
||||
connect(readReceipts_, &Toggle::toggled, this, [this](bool isDisabled) {
|
||||
settings_->setReadReceipts(!isDisabled);
|
||||
connect(readReceipts_, &Toggle::toggled, this, [this](bool disabled) {
|
||||
settings_->setReadReceipts(!disabled);
|
||||
});
|
||||
|
||||
connect(desktopNotifications_, &Toggle::toggled, this, [this](bool isDisabled) {
|
||||
settings_->setDesktopNotifications(!isDisabled);
|
||||
connect(desktopNotifications_, &Toggle::toggled, this, [this](bool disabled) {
|
||||
settings_->setDesktopNotifications(!disabled);
|
||||
});
|
||||
|
||||
connect(messageHoverHighlight_, &Toggle::toggled, this, [this](bool isDisabled) {
|
||||
settings_->setMessageHoverHighlight(!isDisabled);
|
||||
connect(messageHoverHighlight_, &Toggle::toggled, this, [this](bool disabled) {
|
||||
settings_->setMessageHoverHighlight(!disabled);
|
||||
});
|
||||
|
||||
connect(enlargeEmojiOnlyMessages_, &Toggle::toggled, this, [this](bool disabled) {
|
||||
settings_->setEnlargeEmojiOnlyMessages(!disabled);
|
||||
});
|
||||
|
||||
connect(timelineMaxWidthSpin_,
|
||||
qOverload<int>(&QSpinBox::valueChanged),
|
||||
this,
|
||||
[this](int newValue) { settings_->setTimelineMaxWidth(newValue); });
|
||||
|
||||
connect(
|
||||
sessionKeysImportBtn, &QPushButton::clicked, this, &UserSettingsPage::importSessionKeys);
|
||||
|
||||
|
@ -493,19 +714,21 @@ UserSettingsPage::showEvent(QShowEvent *)
|
|||
utils::restoreCombobox(themeCombo_, settings_->theme());
|
||||
|
||||
// FIXME: Toggle treats true as "off"
|
||||
trayToggle_->setState(!settings_->isTrayEnabled());
|
||||
startInTrayToggle_->setState(!settings_->isStartInTrayEnabled());
|
||||
groupViewToggle_->setState(!settings_->isGroupViewEnabled());
|
||||
decryptSidebar_->setState(!settings_->isDecryptSidebarEnabled());
|
||||
avatarCircles_->setState(!settings_->isAvatarCirclesEnabled());
|
||||
typingNotifications_->setState(!settings_->isTypingNotificationsEnabled());
|
||||
sortByImportance_->setState(!settings_->isSortByImportanceEnabled());
|
||||
timelineButtonsToggle_->setState(!settings_->isButtonsInTimelineEnabled());
|
||||
readReceipts_->setState(!settings_->isReadReceiptsEnabled());
|
||||
markdownEnabled_->setState(!settings_->isMarkdownEnabled());
|
||||
trayToggle_->setState(!settings_->tray());
|
||||
startInTrayToggle_->setState(!settings_->startInTray());
|
||||
groupViewToggle_->setState(!settings_->groupView());
|
||||
decryptSidebar_->setState(!settings_->decryptSidebar());
|
||||
avatarCircles_->setState(!settings_->avatarCircles());
|
||||
typingNotifications_->setState(!settings_->typingNotifications());
|
||||
sortByImportance_->setState(!settings_->sortByImportance());
|
||||
timelineButtonsToggle_->setState(!settings_->buttonsInTimeline());
|
||||
readReceipts_->setState(!settings_->readReceipts());
|
||||
markdown_->setState(!settings_->markdown());
|
||||
desktopNotifications_->setState(!settings_->hasDesktopNotifications());
|
||||
messageHoverHighlight_->setState(!settings_->isMessageHoverHighlightEnabled());
|
||||
messageHoverHighlight_->setState(!settings_->messageHoverHighlight());
|
||||
enlargeEmojiOnlyMessages_->setState(!settings_->enlargeEmojiOnlyMessages());
|
||||
deviceIdValue_->setText(QString::fromStdString(http::client()->device_id()));
|
||||
timelineMaxWidthSpin_->setValue(settings_->timelineMaxWidth());
|
||||
|
||||
deviceFingerprintValue_->setText(
|
||||
utils::humanReadableFingerprint(olm::client()->identity_keys().ed25519));
|
||||
|
|
|
@ -17,17 +17,19 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <QComboBox>
|
||||
#include <QFontDatabase>
|
||||
#include <QFormLayout>
|
||||
#include <QFrame>
|
||||
#include <QLabel>
|
||||
#include <QLayout>
|
||||
#include <QProcessEnvironment>
|
||||
#include <QSharedPointer>
|
||||
#include <QWidget>
|
||||
|
||||
class Toggle;
|
||||
class QLabel;
|
||||
class QFormLayout;
|
||||
class QComboBox;
|
||||
class QSpinBox;
|
||||
class QHBoxLayout;
|
||||
class QVBoxLayout;
|
||||
|
||||
constexpr int OptionMargin = 6;
|
||||
constexpr int LayoutTopMargin = 50;
|
||||
|
@ -37,6 +39,36 @@ class UserSettings : public QObject
|
|||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(QString theme READ theme WRITE setTheme NOTIFY themeChanged)
|
||||
Q_PROPERTY(bool messageHoverHighlight READ messageHoverHighlight WRITE
|
||||
setMessageHoverHighlight NOTIFY messageHoverHighlightChanged)
|
||||
Q_PROPERTY(bool enlargeEmojiOnlyMessages READ enlargeEmojiOnlyMessages WRITE
|
||||
setEnlargeEmojiOnlyMessages NOTIFY enlargeEmojiOnlyMessagesChanged)
|
||||
Q_PROPERTY(bool tray READ tray WRITE setTray NOTIFY trayChanged)
|
||||
Q_PROPERTY(bool startInTray READ startInTray WRITE setStartInTray NOTIFY startInTrayChanged)
|
||||
Q_PROPERTY(bool groupView READ groupView WRITE setGroupView NOTIFY groupViewStateChanged)
|
||||
Q_PROPERTY(bool markdown READ markdown WRITE setMarkdown NOTIFY markdownChanged)
|
||||
Q_PROPERTY(bool typingNotifications READ typingNotifications WRITE setTypingNotifications
|
||||
NOTIFY typingNotificationsChanged)
|
||||
Q_PROPERTY(bool sortByImportance READ sortByImportance WRITE setSortByImportance NOTIFY
|
||||
roomSortingChanged)
|
||||
Q_PROPERTY(bool buttonsInTimeline READ buttonsInTimeline WRITE setButtonsInTimeline NOTIFY
|
||||
buttonInTimelineChanged)
|
||||
Q_PROPERTY(
|
||||
bool readReceipts READ readReceipts WRITE setReadReceipts NOTIFY readReceiptsChanged)
|
||||
Q_PROPERTY(bool desktopNotifications READ hasDesktopNotifications WRITE
|
||||
setDesktopNotifications NOTIFY desktopNotificationsChanged)
|
||||
Q_PROPERTY(
|
||||
bool avatarCircles READ avatarCircles WRITE setAvatarCircles NOTIFY avatarCirclesChanged)
|
||||
Q_PROPERTY(bool decryptSidebar READ decryptSidebar WRITE setDecryptSidebar NOTIFY
|
||||
decryptSidebarChanged)
|
||||
Q_PROPERTY(int timelineMaxWidth READ timelineMaxWidth WRITE setTimelineMaxWidth NOTIFY
|
||||
timelineMaxWidthChanged)
|
||||
Q_PROPERTY(double fontSize READ fontSize WRITE setFontSize NOTIFY fontSizeChanged)
|
||||
Q_PROPERTY(QString font READ font WRITE setFontFamily NOTIFY fontChanged)
|
||||
Q_PROPERTY(
|
||||
QString emojiFont READ emojiFont WRITE setEmojiFontFamily NOTIFY emojiFontChanged)
|
||||
|
||||
public:
|
||||
UserSettings();
|
||||
|
||||
|
@ -44,104 +76,62 @@ public:
|
|||
void load();
|
||||
void applyTheme();
|
||||
void setTheme(QString theme);
|
||||
void setMessageHoverHighlight(bool state)
|
||||
{
|
||||
isMessageHoverHighlightEnabled_ = state;
|
||||
save();
|
||||
}
|
||||
void setTray(bool state)
|
||||
{
|
||||
isTrayEnabled_ = state;
|
||||
save();
|
||||
}
|
||||
|
||||
void setStartInTray(bool state)
|
||||
{
|
||||
isStartInTrayEnabled_ = state;
|
||||
save();
|
||||
}
|
||||
|
||||
void setMessageHoverHighlight(bool state);
|
||||
void setEnlargeEmojiOnlyMessages(bool state);
|
||||
void setTray(bool state);
|
||||
void setStartInTray(bool state);
|
||||
void setFontSize(double size);
|
||||
void setFontFamily(QString family);
|
||||
void setEmojiFontFamily(QString family);
|
||||
|
||||
void setGroupView(bool state)
|
||||
{
|
||||
if (isGroupViewEnabled_ != state)
|
||||
emit groupViewStateChanged(state);
|
||||
|
||||
isGroupViewEnabled_ = state;
|
||||
save();
|
||||
}
|
||||
|
||||
void setMarkdownEnabled(bool state)
|
||||
{
|
||||
isMarkdownEnabled_ = state;
|
||||
save();
|
||||
}
|
||||
|
||||
void setReadReceipts(bool state)
|
||||
{
|
||||
isReadReceiptsEnabled_ = state;
|
||||
save();
|
||||
}
|
||||
|
||||
void setTypingNotifications(bool state)
|
||||
{
|
||||
isTypingNotificationsEnabled_ = state;
|
||||
save();
|
||||
}
|
||||
|
||||
void setSortByImportance(bool state)
|
||||
{
|
||||
sortByImportance_ = state;
|
||||
emit roomSortingChanged();
|
||||
}
|
||||
|
||||
void setButtonsInTimeline(bool state)
|
||||
{
|
||||
isButtonsInTimelineEnabled_ = state;
|
||||
save();
|
||||
}
|
||||
|
||||
void setDesktopNotifications(bool state)
|
||||
{
|
||||
hasDesktopNotifications_ = state;
|
||||
save();
|
||||
}
|
||||
|
||||
void setAvatarCircles(bool state)
|
||||
{
|
||||
avatarCircles_ = state;
|
||||
save();
|
||||
}
|
||||
|
||||
void setDecryptSidebar(bool state)
|
||||
{
|
||||
decryptSidebar_ = state;
|
||||
save();
|
||||
}
|
||||
void setGroupView(bool state);
|
||||
void setMarkdown(bool state);
|
||||
void setReadReceipts(bool state);
|
||||
void setTypingNotifications(bool state);
|
||||
void setSortByImportance(bool state);
|
||||
void setButtonsInTimeline(bool state);
|
||||
void setTimelineMaxWidth(int state);
|
||||
void setDesktopNotifications(bool state);
|
||||
void setAvatarCircles(bool state);
|
||||
void setDecryptSidebar(bool state);
|
||||
|
||||
QString theme() const { return !theme_.isEmpty() ? theme_ : defaultTheme_; }
|
||||
bool isMessageHoverHighlightEnabled() const { return isMessageHoverHighlightEnabled_; }
|
||||
bool isTrayEnabled() const { return isTrayEnabled_; }
|
||||
bool isStartInTrayEnabled() const { return isStartInTrayEnabled_; }
|
||||
bool isGroupViewEnabled() const { return isGroupViewEnabled_; }
|
||||
bool isAvatarCirclesEnabled() const { return avatarCircles_; }
|
||||
bool isDecryptSidebarEnabled() const { return decryptSidebar_; }
|
||||
bool isMarkdownEnabled() const { return isMarkdownEnabled_; }
|
||||
bool isTypingNotificationsEnabled() const { return isTypingNotificationsEnabled_; }
|
||||
bool isSortByImportanceEnabled() const { return sortByImportance_; }
|
||||
bool isButtonsInTimelineEnabled() const { return isButtonsInTimelineEnabled_; }
|
||||
bool isReadReceiptsEnabled() const { return isReadReceiptsEnabled_; }
|
||||
bool messageHoverHighlight() const { return messageHoverHighlight_; }
|
||||
bool enlargeEmojiOnlyMessages() const { return enlargeEmojiOnlyMessages_; }
|
||||
bool tray() const { return tray_; }
|
||||
bool startInTray() const { return startInTray_; }
|
||||
bool groupView() const { return groupView_; }
|
||||
bool avatarCircles() const { return avatarCircles_; }
|
||||
bool decryptSidebar() const { return decryptSidebar_; }
|
||||
bool markdown() const { return markdown_; }
|
||||
bool typingNotifications() const { return typingNotifications_; }
|
||||
bool sortByImportance() const { return sortByImportance_; }
|
||||
bool buttonsInTimeline() const { return buttonsInTimeline_; }
|
||||
bool readReceipts() const { return readReceipts_; }
|
||||
bool hasDesktopNotifications() const { return hasDesktopNotifications_; }
|
||||
int timelineMaxWidth() const { return timelineMaxWidth_; }
|
||||
double fontSize() const { return baseFontSize_; }
|
||||
QString font() const { return font_; }
|
||||
QString emojiFont() const { return emojiFont_; }
|
||||
|
||||
signals:
|
||||
void groupViewStateChanged(bool state);
|
||||
void roomSortingChanged();
|
||||
void roomSortingChanged(bool state);
|
||||
void themeChanged(QString state);
|
||||
void messageHoverHighlightChanged(bool state);
|
||||
void enlargeEmojiOnlyMessagesChanged(bool state);
|
||||
void trayChanged(bool state);
|
||||
void startInTrayChanged(bool state);
|
||||
void markdownChanged(bool state);
|
||||
void typingNotificationsChanged(bool state);
|
||||
void buttonInTimelineChanged(bool state);
|
||||
void readReceiptsChanged(bool state);
|
||||
void desktopNotificationsChanged(bool state);
|
||||
void avatarCirclesChanged(bool state);
|
||||
void decryptSidebarChanged(bool state);
|
||||
void timelineMaxWidthChanged(int state);
|
||||
void fontSizeChanged(double state);
|
||||
void fontChanged(QString state);
|
||||
void emojiFontChanged(QString state);
|
||||
|
||||
private:
|
||||
// Default to system theme if QT_QPA_PLATFORMTHEME var is set.
|
||||
|
@ -150,18 +140,20 @@ private:
|
|||
? "light"
|
||||
: "system";
|
||||
QString theme_;
|
||||
bool isMessageHoverHighlightEnabled_;
|
||||
bool isTrayEnabled_;
|
||||
bool isStartInTrayEnabled_;
|
||||
bool isGroupViewEnabled_;
|
||||
bool isMarkdownEnabled_;
|
||||
bool isTypingNotificationsEnabled_;
|
||||
bool messageHoverHighlight_;
|
||||
bool enlargeEmojiOnlyMessages_;
|
||||
bool tray_;
|
||||
bool startInTray_;
|
||||
bool groupView_;
|
||||
bool markdown_;
|
||||
bool typingNotifications_;
|
||||
bool sortByImportance_;
|
||||
bool isButtonsInTimelineEnabled_;
|
||||
bool isReadReceiptsEnabled_;
|
||||
bool buttonsInTimeline_;
|
||||
bool readReceipts_;
|
||||
bool hasDesktopNotifications_;
|
||||
bool avatarCircles_;
|
||||
bool decryptSidebar_;
|
||||
int timelineMaxWidth_;
|
||||
double baseFontSize_;
|
||||
QString font_;
|
||||
QString emojiFont_;
|
||||
|
@ -211,9 +203,10 @@ private:
|
|||
Toggle *timelineButtonsToggle_;
|
||||
Toggle *typingNotifications_;
|
||||
Toggle *messageHoverHighlight_;
|
||||
Toggle *enlargeEmojiOnlyMessages_;
|
||||
Toggle *sortByImportance_;
|
||||
Toggle *readReceipts_;
|
||||
Toggle *markdownEnabled_;
|
||||
Toggle *markdown_;
|
||||
Toggle *desktopNotifications_;
|
||||
Toggle *avatarCircles_;
|
||||
Toggle *decryptSidebar_;
|
||||
|
@ -226,5 +219,7 @@ private:
|
|||
QComboBox *fontSelectionCombo_;
|
||||
QComboBox *emojiFontSelectionCombo_;
|
||||
|
||||
QSpinBox *timelineMaxWidthSpin_;
|
||||
|
||||
int sideMargin_ = 0;
|
||||
};
|
||||
|
|
|
@ -51,6 +51,14 @@ utils::localUser()
|
|||
return QString::fromStdString(http::client()->user_id().to_string());
|
||||
}
|
||||
|
||||
bool
|
||||
utils::codepointIsEmoji(uint code)
|
||||
{
|
||||
// TODO: Be more precise here.
|
||||
return (code >= 0x2600 && code <= 0x27bf) || (code >= 0x1f300 && code <= 0x1f3ff) ||
|
||||
(code >= 0x1f000 && code <= 0x1faff);
|
||||
}
|
||||
|
||||
QString
|
||||
utils::replaceEmoji(const QString &body)
|
||||
{
|
||||
|
@ -63,9 +71,7 @@ utils::replaceEmoji(const QString &body)
|
|||
|
||||
bool insideFontBlock = false;
|
||||
for (auto &code : utf32_string) {
|
||||
// TODO: Be more precise here.
|
||||
if ((code >= 0x2600 && code <= 0x27bf) || (code >= 0x1f300 && code <= 0x1f3ff) ||
|
||||
(code >= 0x1f000 && code <= 0x1faff)) {
|
||||
if (utils::codepointIsEmoji(code)) {
|
||||
if (!insideFontBlock) {
|
||||
fmtBody += QString("<font face=\"" + userFontFamily + "\">");
|
||||
insideFontBlock = true;
|
||||
|
@ -136,13 +142,13 @@ utils::descriptiveTime(const QDateTime &then)
|
|||
const auto days = then.daysTo(now);
|
||||
|
||||
if (days == 0)
|
||||
return then.time().toString(Qt::DefaultLocaleShortDate);
|
||||
return QLocale::system().toString(then.time(), QLocale::ShortFormat);
|
||||
else if (days < 2)
|
||||
return QString(QCoreApplication::translate("descriptiveTime", "Yesterday"));
|
||||
else if (days < 7)
|
||||
return then.toString("dddd");
|
||||
|
||||
return then.date().toString(Qt::DefaultLocaleShortDate);
|
||||
return QLocale::system().toString(then.date(), QLocale::ShortFormat);
|
||||
}
|
||||
|
||||
DescInfo
|
||||
|
|
39
src/Utils.h
39
src/Utils.h
|
@ -36,6 +36,9 @@ namespace utils {
|
|||
|
||||
using TimelineEvent = mtx::events::collections::TimelineEvents;
|
||||
|
||||
bool
|
||||
codepointIsEmoji(uint code);
|
||||
|
||||
QString
|
||||
replaceEmoji(const QString &body);
|
||||
|
||||
|
@ -183,42 +186,6 @@ erase_if(ContainerT &items, const PredicateT &predicate)
|
|||
}
|
||||
}
|
||||
|
||||
inline uint64_t
|
||||
event_timestamp(const mtx::events::collections::TimelineEvents &event)
|
||||
{
|
||||
return std::visit([](auto msg) { return msg.origin_server_ts; }, event);
|
||||
}
|
||||
|
||||
inline nlohmann::json
|
||||
serialize_event(const mtx::events::collections::TimelineEvents &event)
|
||||
{
|
||||
return std::visit([](auto msg) { return json(msg); }, event);
|
||||
}
|
||||
|
||||
inline mtx::events::EventType
|
||||
event_type(const mtx::events::collections::TimelineEvents &event)
|
||||
{
|
||||
return std::visit([](auto msg) { return msg.type; }, event);
|
||||
}
|
||||
|
||||
inline std::string
|
||||
event_id(const mtx::events::collections::TimelineEvents &event)
|
||||
{
|
||||
return std::visit([](auto msg) { return msg.event_id; }, event);
|
||||
}
|
||||
|
||||
inline QString
|
||||
eventId(const mtx::events::collections::TimelineEvents &event)
|
||||
{
|
||||
return QString::fromStdString(event_id(event));
|
||||
}
|
||||
|
||||
inline QString
|
||||
event_sender(const mtx::events::collections::TimelineEvents &event)
|
||||
{
|
||||
return std::visit([](auto msg) { return QString::fromStdString(msg.sender); }, event);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
QString
|
||||
message_body(const mtx::events::collections::TimelineEvents &event)
|
||||
|
|
|
@ -112,7 +112,7 @@ CreateRoom::CreateRoom(QWidget *parent)
|
|||
});
|
||||
|
||||
connect(visibilityCombo_,
|
||||
static_cast<void (QComboBox::*)(const QString &)>(&QComboBox::activated),
|
||||
static_cast<void (QComboBox::*)(const QString &)>(&QComboBox::currentTextChanged),
|
||||
[this](const QString &text) {
|
||||
if (text == "Private") {
|
||||
request_.visibility = mtx::requests::Visibility::Private;
|
||||
|
@ -122,7 +122,7 @@ CreateRoom::CreateRoom(QWidget *parent)
|
|||
});
|
||||
|
||||
connect(presetCombo_,
|
||||
static_cast<void (QComboBox::*)(const QString &)>(&QComboBox::activated),
|
||||
static_cast<void (QComboBox::*)(const QString &)>(&QComboBox::currentTextChanged),
|
||||
[this](const QString &text) {
|
||||
if (text == "Private Chat") {
|
||||
request_.preset = mtx::requests::Preset::PrivateChat;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#include <QDebug>
|
||||
#include <QIcon>
|
||||
#include <QLabel>
|
||||
#include <QListWidget>
|
||||
#include <QListWidgetItem>
|
||||
#include <QPushButton>
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
#pragma once
|
||||
|
||||
#include <QFrame>
|
||||
#include <QLabel>
|
||||
#include <QListWidgetItem>
|
||||
#include <QStringList>
|
||||
|
||||
class QPushButton;
|
||||
class QLabel;
|
||||
class TextField;
|
||||
class QListWidget;
|
||||
class QListWidgetItem;
|
||||
|
||||
namespace dialogs {
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#include <QDebug>
|
||||
#include <QIcon>
|
||||
#include <QLabel>
|
||||
#include <QListWidgetItem>
|
||||
#include <QPainter>
|
||||
#include <QPushButton>
|
||||
|
@ -74,15 +75,17 @@ ReceiptItem::dateFormat(const QDateTime &then) const
|
|||
auto days = then.daysTo(now);
|
||||
|
||||
if (days == 0)
|
||||
return tr("Today %1").arg(then.time().toString(Qt::DefaultLocaleShortDate));
|
||||
return tr("Today %1")
|
||||
.arg(QLocale::system().toString(then.time(), QLocale::ShortFormat));
|
||||
else if (days < 2)
|
||||
return tr("Yesterday %1").arg(then.time().toString(Qt::DefaultLocaleShortDate));
|
||||
return tr("Yesterday %1")
|
||||
.arg(QLocale::system().toString(then.time(), QLocale::ShortFormat));
|
||||
else if (days < 7)
|
||||
return QString("%1 %2")
|
||||
.arg(then.toString("dddd"))
|
||||
.arg(then.time().toString(Qt::DefaultLocaleShortDate));
|
||||
.arg(QLocale::system().toString(then.time(), QLocale::ShortFormat));
|
||||
|
||||
return then.toString(Qt::DefaultLocaleShortDate);
|
||||
return QLocale::system().toString(then.time(), QLocale::ShortFormat);
|
||||
}
|
||||
|
||||
ReadReceipts::ReadReceipts(QWidget *parent)
|
||||
|
@ -163,3 +166,10 @@ ReadReceipts::paintEvent(QPaintEvent *)
|
|||
QPainter p(this);
|
||||
style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
|
||||
}
|
||||
|
||||
void
|
||||
ReadReceipts::hideEvent(QHideEvent *event)
|
||||
{
|
||||
userList_->clear();
|
||||
QFrame::hideEvent(event);
|
||||
}
|
||||
|
|
|
@ -2,12 +2,12 @@
|
|||
|
||||
#include <QDateTime>
|
||||
#include <QFrame>
|
||||
#include <QHBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QListWidget>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
class Avatar;
|
||||
class QLabel;
|
||||
class QListWidget;
|
||||
class QHBoxLayout;
|
||||
class QVBoxLayout;
|
||||
|
||||
namespace dialogs {
|
||||
|
||||
|
@ -47,11 +47,7 @@ public slots:
|
|||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *event) override;
|
||||
void hideEvent(QHideEvent *event) override
|
||||
{
|
||||
userList_->clear();
|
||||
QFrame::hideEvent(event);
|
||||
}
|
||||
void hideEvent(QHideEvent *event) override;
|
||||
|
||||
private:
|
||||
QLabel *topLabel_;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#include <QApplication>
|
||||
#include <QComboBox>
|
||||
#include <QEvent>
|
||||
#include <QFileDialog>
|
||||
#include <QFontDatabase>
|
||||
#include <QImageReader>
|
||||
|
@ -41,6 +42,17 @@ constexpr int WIDGET_SPACING = 15;
|
|||
constexpr int TEXT_SPACING = 4;
|
||||
constexpr int BUTTON_SPACING = 2 * TEXT_SPACING;
|
||||
|
||||
bool
|
||||
ClickableFilter::eventFilter(QObject *obj, QEvent *event)
|
||||
{
|
||||
if (event->type() == QEvent::MouseButtonRelease) {
|
||||
emit clicked();
|
||||
return true;
|
||||
}
|
||||
|
||||
return QObject::eventFilter(obj, event);
|
||||
}
|
||||
|
||||
EditModal::EditModal(const QString &roomId, QWidget *parent)
|
||||
: QWidget(parent)
|
||||
, roomId_{roomId}
|
||||
|
@ -93,6 +105,28 @@ EditModal::EditModal(const QString &roomId, QWidget *parent)
|
|||
move(center.x() - (width() * 0.5), center.y() - (height() * 0.5));
|
||||
}
|
||||
|
||||
void
|
||||
EditModal::topicEventSent()
|
||||
{
|
||||
errorField_->hide();
|
||||
close();
|
||||
}
|
||||
|
||||
void
|
||||
EditModal::nameEventSent(const QString &name)
|
||||
{
|
||||
errorField_->hide();
|
||||
emit nameChanged(name);
|
||||
close();
|
||||
}
|
||||
|
||||
void
|
||||
EditModal::error(const QString &msg)
|
||||
{
|
||||
errorField_->setText(msg);
|
||||
errorField_->show();
|
||||
}
|
||||
|
||||
void
|
||||
EditModal::applyClicked()
|
||||
{
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <QEvent>
|
||||
#include <QFrame>
|
||||
#include <QImage>
|
||||
#include <QLabel>
|
||||
|
||||
#include <mtx/events/guest_access.hpp>
|
||||
|
||||
|
@ -21,6 +19,8 @@ class QPixmap;
|
|||
class TextField;
|
||||
class TextField;
|
||||
class Toggle;
|
||||
class QLabel;
|
||||
class QEvent;
|
||||
|
||||
class ClickableFilter : public QObject
|
||||
{
|
||||
|
@ -35,15 +35,7 @@ signals:
|
|||
void clicked();
|
||||
|
||||
protected:
|
||||
bool eventFilter(QObject *obj, QEvent *event) override
|
||||
{
|
||||
if (event->type() == QEvent::MouseButtonRelease) {
|
||||
emit clicked();
|
||||
return true;
|
||||
}
|
||||
|
||||
return QObject::eventFilter(obj, event);
|
||||
}
|
||||
bool eventFilter(QObject *obj, QEvent *event) override;
|
||||
};
|
||||
|
||||
/// Convenience class which connects events emmited from threads
|
||||
|
@ -72,24 +64,9 @@ signals:
|
|||
void nameChanged(const QString &roomName);
|
||||
|
||||
private slots:
|
||||
void topicEventSent()
|
||||
{
|
||||
errorField_->hide();
|
||||
close();
|
||||
}
|
||||
|
||||
void nameEventSent(const QString &name)
|
||||
{
|
||||
errorField_->hide();
|
||||
emit nameChanged(name);
|
||||
close();
|
||||
}
|
||||
|
||||
void error(const QString &msg)
|
||||
{
|
||||
errorField_->setText(msg);
|
||||
errorField_->show();
|
||||
}
|
||||
void topicEventSent();
|
||||
void nameEventSent(const QString &name);
|
||||
void error(const QString &msg);
|
||||
|
||||
void applyClicked();
|
||||
|
||||
|
|
|
@ -15,9 +15,12 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <QLabel>
|
||||
#include <QListView>
|
||||
#include <QPainter>
|
||||
#include <QScrollBar>
|
||||
#include <QStyleOption>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#include "Config.h"
|
||||
|
||||
|
|
|
@ -18,13 +18,14 @@
|
|||
#pragma once
|
||||
|
||||
#include <QColor>
|
||||
#include <QLabel>
|
||||
#include <QLayout>
|
||||
#include <QListView>
|
||||
#include <QStandardItemModel>
|
||||
|
||||
#include "ItemDelegate.h"
|
||||
|
||||
class QLabel;
|
||||
class QListView;
|
||||
class QStandardItemModel;
|
||||
class QVBoxLayout;
|
||||
|
||||
namespace emoji {
|
||||
|
||||
class Category : public QWidget
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
#include <QLabel>
|
||||
#include <QPaintEvent>
|
||||
#include <QPainter>
|
||||
#include <QStyleOption>
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
#pragma once
|
||||
|
||||
#include <QHBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QPoint>
|
||||
#include <QWidget>
|
||||
|
||||
#include "../AvatarProvider.h"
|
||||
|
@ -10,6 +7,8 @@
|
|||
|
||||
class Avatar;
|
||||
struct SearchResult;
|
||||
class QLabel;
|
||||
class QHBoxLayout;
|
||||
|
||||
class PopupItem : public QWidget
|
||||
{
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
#pragma once
|
||||
|
||||
#include <QHBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QPoint>
|
||||
#include <QWidget>
|
||||
|
||||
#include "CacheStructs.h"
|
||||
|
|
|
@ -8,9 +8,9 @@
|
|||
|
||||
#include "Cache.h"
|
||||
#include "ChatPage.h"
|
||||
#include "EventAccessors.h"
|
||||
#include "Logging.h"
|
||||
#include "UserMentions.h"
|
||||
//#include "timeline/TimelineItem.h"
|
||||
|
||||
using namespace popups;
|
||||
|
||||
|
@ -75,12 +75,15 @@ UserMentions::initializeMentions(const QMap<QString, mtx::responses::Notificatio
|
|||
|
||||
for (const auto &item : notifs) {
|
||||
for (const auto ¬if : item.notifications) {
|
||||
const auto event_id = QString::fromStdString(utils::event_id(notif.event));
|
||||
const auto event_id =
|
||||
QString::fromStdString(mtx::accessors::event_id(notif.event));
|
||||
|
||||
try {
|
||||
const auto room_id = QString::fromStdString(notif.room_id);
|
||||
const auto user_id = utils::event_sender(notif.event);
|
||||
const auto body = utils::event_body(notif.event);
|
||||
const auto user_id =
|
||||
QString::fromStdString(mtx::accessors::sender(notif.event));
|
||||
const auto body =
|
||||
QString::fromStdString(mtx::accessors::body(notif.event));
|
||||
|
||||
pushItem(event_id,
|
||||
user_id,
|
||||
|
|
|
@ -219,6 +219,7 @@ TimelineModel::roleNames() const
|
|||
{Section, "section"},
|
||||
{Type, "type"},
|
||||
{TypeString, "typeString"},
|
||||
{IsOnlyEmoji, "isOnlyEmoji"},
|
||||
{Body, "body"},
|
||||
{FormattedBody, "formattedBody"},
|
||||
{UserId, "userId"},
|
||||
|
@ -284,6 +285,22 @@ TimelineModel::data(const QString &id, int role) const
|
|||
return QVariant(toRoomEventType(event));
|
||||
case TypeString:
|
||||
return QVariant(toRoomEventTypeString(event));
|
||||
case IsOnlyEmoji: {
|
||||
QString qBody = QString::fromStdString(body(event));
|
||||
|
||||
QVector<uint> utf32_string = qBody.toUcs4();
|
||||
int emojiCount = 0;
|
||||
|
||||
for (auto &code : utf32_string) {
|
||||
if (utils::codepointIsEmoji(code)) {
|
||||
emojiCount++;
|
||||
} else {
|
||||
return QVariant(0);
|
||||
}
|
||||
}
|
||||
|
||||
return QVariant(emojiCount);
|
||||
}
|
||||
case Body:
|
||||
return QVariant(utils::replaceEmoji(QString::fromStdString(body(event))));
|
||||
case FormattedBody: {
|
||||
|
@ -386,6 +403,7 @@ TimelineModel::data(const QString &id, int role) const
|
|||
// m.insert(names[Section], data(id, static_cast<int>(Section)));
|
||||
m.insert(names[Type], data(id, static_cast<int>(Type)));
|
||||
m.insert(names[TypeString], data(id, static_cast<int>(TypeString)));
|
||||
m.insert(names[IsOnlyEmoji], data(id, static_cast<int>(IsOnlyEmoji)));
|
||||
m.insert(names[Body], data(id, static_cast<int>(Body)));
|
||||
m.insert(names[FormattedBody], data(id, static_cast<int>(FormattedBody)));
|
||||
m.insert(names[UserId], data(id, static_cast<int>(UserId)));
|
||||
|
@ -810,7 +828,7 @@ TimelineModel::escapeEmoji(QString str) const
|
|||
void
|
||||
TimelineModel::viewRawMessage(QString id) const
|
||||
{
|
||||
std::string ev = utils::serialize_event(events.value(id)).dump(4);
|
||||
std::string ev = mtx::accessors::serialize_event(events.value(id)).dump(4);
|
||||
auto dialog = new dialogs::RawMessage(QString::fromStdString(ev));
|
||||
Q_UNUSED(dialog);
|
||||
}
|
||||
|
@ -824,7 +842,7 @@ TimelineModel::viewDecryptedRawMessage(QString id) const
|
|||
event = decryptEvent(*e).event;
|
||||
}
|
||||
|
||||
std::string ev = utils::serialize_event(event).dump(4);
|
||||
std::string ev = mtx::accessors::serialize_event(event).dump(4);
|
||||
auto dialog = new dialogs::RawMessage(QString::fromStdString(ev));
|
||||
Q_UNUSED(dialog);
|
||||
}
|
||||
|
@ -1849,6 +1867,8 @@ TimelineModel::formatMemberEvent(QString id)
|
|||
rendered = tr("%1 changed their display name.").arg(name);
|
||||
else if (avatarChanged)
|
||||
rendered = tr("%1 changed their avatar.").arg(name);
|
||||
else
|
||||
rendered = tr("%1 changed some profile info.").arg(name);
|
||||
// the case of nothing changed but join follows join shouldn't happen, so
|
||||
// just show it as join
|
||||
} else {
|
||||
|
|
|
@ -142,6 +142,7 @@ public:
|
|||
Section,
|
||||
Type,
|
||||
TypeString,
|
||||
IsOnlyEmoji,
|
||||
Body,
|
||||
FormattedBody,
|
||||
UserId,
|
||||
|
|
|
@ -21,7 +21,7 @@ Q_DECLARE_METATYPE(mtx::events::collections::TimelineEvents)
|
|||
void
|
||||
TimelineViewManager::updateEncryptedDescriptions()
|
||||
{
|
||||
auto decrypt = settings->isDecryptSidebarEnabled();
|
||||
auto decrypt = settings->decryptSidebar();
|
||||
QHash<QString, QSharedPointer<TimelineModel>>::iterator i;
|
||||
for (i = models.begin(); i != models.end(); ++i) {
|
||||
auto ptr = i.value();
|
||||
|
@ -96,6 +96,7 @@ TimelineViewManager::TimelineViewManager(QSharedPointer<UserSettings> userSettin
|
|||
#endif
|
||||
container->setMinimumSize(200, 200);
|
||||
view->rootContext()->setContextProperty("timelineManager", this);
|
||||
view->rootContext()->setContextProperty("settings", settings.data());
|
||||
updateColorPalette();
|
||||
view->engine()->addImageProvider("MxcImage", imgProvider);
|
||||
view->engine()->addImageProvider("colorimage", colorImgProvider);
|
||||
|
@ -121,7 +122,7 @@ TimelineViewManager::sync(const mtx::responses::Rooms &rooms)
|
|||
const auto &room_model = models.value(QString::fromStdString(room_id));
|
||||
room_model->addEvents(room.timeline);
|
||||
|
||||
if (ChatPage::instance()->userSettings()->isTypingNotificationsEnabled()) {
|
||||
if (ChatPage::instance()->userSettings()->typingNotifications()) {
|
||||
std::vector<QString> typing;
|
||||
typing.reserve(room.ephemeral.typing.size());
|
||||
for (const auto &user : room.ephemeral.typing) {
|
||||
|
@ -141,7 +142,7 @@ TimelineViewManager::addRoom(const QString &room_id)
|
|||
{
|
||||
if (!models.contains(room_id)) {
|
||||
QSharedPointer<TimelineModel> newRoom(new TimelineModel(this, room_id));
|
||||
newRoom->setDecryptDescription(settings->isDecryptSidebarEnabled());
|
||||
newRoom->setDecryptDescription(settings->decryptSidebar());
|
||||
|
||||
connect(newRoom.data(),
|
||||
&TimelineModel::newEncryptedImage,
|
||||
|
@ -225,7 +226,7 @@ TimelineViewManager::queueTextMessage(const QString &msg)
|
|||
mtx::events::msg::Text text = {};
|
||||
text.body = msg.trimmed().toStdString();
|
||||
|
||||
if (settings->isMarkdownEnabled()) {
|
||||
if (settings->markdown()) {
|
||||
text.formatted_body = utils::markdownToHtml(msg).toStdString();
|
||||
|
||||
// Don't send formatted_body, when we don't need to
|
||||
|
@ -253,7 +254,7 @@ TimelineViewManager::queueTextMessage(const QString &msg)
|
|||
|
||||
// NOTE(Nico): rich replies always need a formatted_body!
|
||||
text.format = "org.matrix.custom.html";
|
||||
if (settings->isMarkdownEnabled())
|
||||
if (settings->markdown())
|
||||
text.formatted_body =
|
||||
utils::getFormattedQuoteBody(related, utils::markdownToHtml(msg))
|
||||
.toStdString();
|
||||
|
@ -276,7 +277,7 @@ TimelineViewManager::queueEmoteMessage(const QString &msg)
|
|||
mtx::events::msg::Emote emote;
|
||||
emote.body = msg.trimmed().toStdString();
|
||||
|
||||
if (html != msg.trimmed().toHtmlEscaped() && settings->isMarkdownEnabled()) {
|
||||
if (html != msg.trimmed().toHtmlEscaped() && settings->markdown()) {
|
||||
emote.formatted_body = html.toStdString();
|
||||
emote.format = "org.matrix.custom.html";
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include <QPainter>
|
||||
#include <QPainterPath>
|
||||
#include <QSettings>
|
||||
|
||||
#include "AvatarProvider.h"
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include <QDateTime>
|
||||
#include <QLocale>
|
||||
#include <QPainter>
|
||||
#include <QPainterPath>
|
||||
#include <QPen>
|
||||
#include <QtGlobal>
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include <QFontMetrics>
|
||||
#include <QPaintDevice>
|
||||
#include <QPainter>
|
||||
#include <QPainterPath>
|
||||
#include <QtGlobal>
|
||||
|
||||
class Painter : public QPainter
|
||||
|
@ -163,5 +164,5 @@ public:
|
|||
|
||||
private:
|
||||
Painter &_painter;
|
||||
QPainter::RenderHints hints_ = 0;
|
||||
QPainter::RenderHints hints_ = {};
|
||||
};
|
||||
|
|
|
@ -6,4 +6,11 @@
|
|||
/examples/basic/basic
|
||||
/examples/calculator/calculator
|
||||
/examples/sending_arguments/sending_arguments
|
||||
CMakeLists.txt.user
|
||||
/**/CMakeLists.txt.user
|
||||
/**/CMakeCache.txt
|
||||
/**/CMakeCache/*
|
||||
/**/CMakeFiles/*
|
||||
/**/Makefile
|
||||
/**/cmake_install.cmake
|
||||
/**/*_autogen/
|
||||
libSingleApplication.a
|
|
@ -1,6 +1,40 @@
|
|||
Changelog
|
||||
=========
|
||||
|
||||
If by accident I have forgotten to credit someone in the CHANGELOG, email me and I will fix it.
|
||||
|
||||
__3.1.3.1__
|
||||
---------
|
||||
* CMake build system improvements
|
||||
* Fixed Clang Tidy warnings
|
||||
|
||||
_Hennadii Chernyshchyk_
|
||||
|
||||
__3.1.3__
|
||||
---------
|
||||
* Improved `CMakeLists.txt`
|
||||
|
||||
_Hennadii Chernyshchyk_
|
||||
|
||||
__3.1.2__
|
||||
---------
|
||||
|
||||
* Fix a crash when exiting an application on Android and iOS
|
||||
|
||||
_Emeric Grange_
|
||||
|
||||
__3.1.1a__
|
||||
----------
|
||||
|
||||
* Added currentUser() method that returns the user the current instance is running as.
|
||||
|
||||
_Leander Schulten_
|
||||
|
||||
__3.1.0a__
|
||||
----------
|
||||
|
||||
* Added primaryUser() method that returns the user the primary instance is running as.
|
||||
|
||||
__3.0.19__
|
||||
----------
|
||||
|
|
@ -1,45 +1,34 @@
|
|||
cmake_minimum_required(VERSION 3.1.0)
|
||||
cmake_minimum_required(VERSION 3.7.0)
|
||||
|
||||
project(SingleApplication)
|
||||
project(SingleApplication LANGUAGES CXX)
|
||||
|
||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
|
||||
# SingleApplication base class
|
||||
set(QAPPLICATION_CLASS QCoreApplication CACHE STRING "Inheritance class for SingleApplication")
|
||||
set_property(CACHE QAPPLICATION_CLASS PROPERTY STRINGS QApplication QGuiApplication QCoreApplication)
|
||||
|
||||
# Libary target
|
||||
add_library(${PROJECT_NAME} STATIC
|
||||
singleapplication.cpp
|
||||
singleapplication_p.cpp
|
||||
)
|
||||
)
|
||||
add_library(${PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME})
|
||||
|
||||
# Find dependencies
|
||||
find_package(Qt5Network)
|
||||
find_package(Qt5 COMPONENTS Network REQUIRED)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE Qt5::Network)
|
||||
|
||||
if(QAPPLICATION_CLASS STREQUAL QApplication)
|
||||
find_package(Qt5 COMPONENTS Widgets REQUIRED)
|
||||
target_link_libraries(${PROJECT_NAME} PUBLIC Qt5::Widgets)
|
||||
elseif(QAPPLICATION_CLASS STREQUAL QGuiApplication)
|
||||
find_package(Qt5 COMPONENTS Gui REQUIRED)
|
||||
target_link_libraries(${PROJECT_NAME} PUBLIC Qt5::Gui)
|
||||
else()
|
||||
set(QAPPLICATION_CLASS QCoreApplication)
|
||||
find_package(Qt5 COMPONENTS Core REQUIRED)
|
||||
endif()
|
||||
target_compile_definitions(${PROJECT_NAME} PUBLIC QAPPLICATION_CLASS=${QAPPLICATION_CLASS})
|
||||
|
||||
# Link dependencies
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE Qt5::Network)
|
||||
if(QAPPLICATION_CLASS STREQUAL QApplication)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE Qt5::Widgets)
|
||||
elseif(QAPPLICATION_CLASS STREQUAL QGuiApplication)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE Qt5::Gui)
|
||||
else()
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE Qt5::Core)
|
||||
target_link_libraries(${PROJECT_NAME} PUBLIC Qt5::Core)
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE advapi32)
|
||||
endif()
|
||||
|
||||
target_compile_definitions(${PROJECT_NAME} PUBLIC QAPPLICATION_CLASS=${QAPPLICATION_CLASS})
|
||||
target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
add_library(${PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME})
|
|
@ -1,6 +1,6 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) Itay Grudev 2015 - 2016
|
||||
Copyright (c) Itay Grudev 2015 - 2020
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
|
@ -1,5 +1,6 @@
|
|||
SingleApplication
|
||||
=================
|
||||
[![CI](https://github.com/itay-grudev/SingleApplication/workflows/CI:%20Build%20Test/badge.svg)](https://github.com/itay-grudev/SingleApplication/actions)
|
||||
|
||||
This is a replacement of the QtSingleApplication for `Qt5`.
|
||||
|
||||
|
@ -15,18 +16,6 @@ class you specify via the `QAPPLICATION_CLASS` macro (`QCoreApplication` is the
|
|||
default). Further usage is similar to the use of the `Q[Core|Gui]Application`
|
||||
classes.
|
||||
|
||||
The library sets up a `QLocalServer` and a `QSharedMemory` block. The first
|
||||
instance of your Application is your Primary Instance. It would check if the
|
||||
shared memory block exists and if not it will start a `QLocalServer` and listen
|
||||
for connections. Each subsequent instance of your application would check if the
|
||||
shared memory block exists and if it does, it will connect to the QLocalServer
|
||||
to notify the primary instance that a new instance had been started, after which
|
||||
it would terminate with status code `0`. In the Primary Instance
|
||||
`SingleApplication` would emit the `instanceStarted()` signal upon detecting
|
||||
that a new instance had been started.
|
||||
|
||||
The library uses `stdlib` to terminate the program with the `exit()` function.
|
||||
|
||||
You can use the library as if you use any other `QCoreApplication` derived
|
||||
class:
|
||||
|
||||
|
@ -43,8 +32,7 @@ int main( int argc, char* argv[] )
|
|||
```
|
||||
|
||||
To include the library files I would recommend that you add it as a git
|
||||
submodule to your project and include it's contents with a `.pri` file. Here is
|
||||
how:
|
||||
submodule to your project. Here is how:
|
||||
|
||||
```bash
|
||||
git submodule add git@github.com:itay-grudev/SingleApplication.git singleapplication
|
||||
|
@ -66,13 +54,27 @@ Then include the subdirectory in your `CMakeLists.txt` project file.
|
|||
```cmake
|
||||
set(QAPPLICATION_CLASS QApplication CACHE STRING "Inheritance class for SingleApplication")
|
||||
add_subdirectory(src/third-party/singleapplication)
|
||||
target_link_libraries(${PROJECT_NAME} SingleApplication::SingleApplication)
|
||||
```
|
||||
|
||||
|
||||
The library sets up a `QLocalServer` and a `QSharedMemory` block. The first
|
||||
instance of your Application is your Primary Instance. It would check if the
|
||||
shared memory block exists and if not it will start a `QLocalServer` and listen
|
||||
for connections. Each subsequent instance of your application would check if the
|
||||
shared memory block exists and if it does, it will connect to the QLocalServer
|
||||
to notify the primary instance that a new instance had been started, after which
|
||||
it would terminate with status code `0`. In the Primary Instance
|
||||
`SingleApplication` would emit the `instanceStarted()` signal upon detecting
|
||||
that a new instance had been started.
|
||||
|
||||
The library uses `stdlib` to terminate the program with the `exit()` function.
|
||||
|
||||
Also don't forget to specify which `QCoreApplication` class your app is using if it
|
||||
is not `QCoreApplication` as in examples above.
|
||||
|
||||
The `Instance Started` signal
|
||||
------------------------
|
||||
-----------------------------
|
||||
|
||||
The SingleApplication class implements a `instanceStarted()` signal. You can
|
||||
bind to that signal to raise your application's window when a new instance had
|
||||
|
@ -204,6 +206,22 @@ qint64 SingleApplication::primaryPid()
|
|||
|
||||
Returns the process ID (PID) of the primary instance.
|
||||
|
||||
---
|
||||
|
||||
```cpp
|
||||
QString SingleApplication::primaryUser()
|
||||
```
|
||||
|
||||
Returns the username the primary instance is running as.
|
||||
|
||||
---
|
||||
|
||||
```cpp
|
||||
QString SingleApplication::currentUser()
|
||||
```
|
||||
|
||||
Returns the username the current instance is running as.
|
||||
|
||||
### Signals
|
||||
|
||||
```cpp
|
1
third_party/SingleApplication-3.1.3.1/SingleApplication
vendored
Normal file
1
third_party/SingleApplication-3.1.3.1/SingleApplication
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
#include "singleapplication.h"
|
|
@ -1,6 +1,6 @@
|
|||
// The MIT License (MIT)
|
||||
//
|
||||
// Copyright (c) Itay Grudev 2015 - 2018
|
||||
// Copyright (c) Itay Grudev 2015 - 2020
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
|
@ -85,7 +85,7 @@ SingleApplication::SingleApplication( int &argc, char *argv[], bool allowSeconda
|
|||
}
|
||||
}
|
||||
|
||||
InstancesInfo* inst = static_cast<InstancesInfo*>( d->memory->data() );
|
||||
auto *inst = static_cast<InstancesInfo*>( d->memory->data() );
|
||||
QElapsedTimer time;
|
||||
time.start();
|
||||
|
||||
|
@ -172,7 +172,19 @@ qint64 SingleApplication::primaryPid()
|
|||
return d->primaryPid();
|
||||
}
|
||||
|
||||
bool SingleApplication::sendMessage( QByteArray message, int timeout )
|
||||
QString SingleApplication::primaryUser()
|
||||
{
|
||||
Q_D(SingleApplication);
|
||||
return d->primaryUser();
|
||||
}
|
||||
|
||||
QString SingleApplication::currentUser()
|
||||
{
|
||||
Q_D(SingleApplication);
|
||||
return d->getUsername();
|
||||
}
|
||||
|
||||
bool SingleApplication::sendMessage( const QByteArray &message, int timeout )
|
||||
{
|
||||
Q_D(SingleApplication);
|
||||
|
|
@ -43,7 +43,7 @@ class SingleApplication : public QAPPLICATION_CLASS
|
|||
{
|
||||
Q_OBJECT
|
||||
|
||||
typedef QAPPLICATION_CLASS app_t;
|
||||
using app_t = QAPPLICATION_CLASS;
|
||||
|
||||
public:
|
||||
/**
|
||||
|
@ -86,7 +86,7 @@ public:
|
|||
* @see See the corresponding QAPPLICATION_CLASS constructor for reference
|
||||
*/
|
||||
explicit SingleApplication( int &argc, char *argv[], bool allowSecondary = false, Options options = Mode::User, int timeout = 1000 );
|
||||
~SingleApplication();
|
||||
~SingleApplication() override;
|
||||
|
||||
/**
|
||||
* @brief Returns if the instance is the primary instance
|
||||
|
@ -112,6 +112,18 @@ public:
|
|||
*/
|
||||
qint64 primaryPid();
|
||||
|
||||
/**
|
||||
* @brief Returns the username of the user running the primary instance
|
||||
* @returns {QString}
|
||||
*/
|
||||
QString primaryUser();
|
||||
|
||||
/**
|
||||
* @brief Returns the username of the current user
|
||||
* @returns {QString}
|
||||
*/
|
||||
QString currentUser();
|
||||
|
||||
/**
|
||||
* @brief Sends a message to the primary instance. Returns true on success.
|
||||
* @param {int} timeout - Timeout for connecting
|
||||
|
@ -119,7 +131,7 @@ public:
|
|||
* @note sendMessage() will return false if invoked from the primary
|
||||
* instance.
|
||||
*/
|
||||
bool sendMessage( QByteArray message, int timeout = 100 );
|
||||
bool sendMessage( const QByteArray &message, int timeout = 100 );
|
||||
|
||||
Q_SIGNALS:
|
||||
void instanceStarted();
|
20
third_party/SingleApplication-3.1.3.1/singleapplication.pri
vendored
Normal file
20
third_party/SingleApplication-3.1.3.1/singleapplication.pri
vendored
Normal file
|
@ -0,0 +1,20 @@
|
|||
QT += core network
|
||||
CONFIG += c++11
|
||||
|
||||
HEADERS += $$PWD/SingleApplication \
|
||||
$$PWD/singleapplication.h \
|
||||
$$PWD/singleapplication_p.h
|
||||
SOURCES += $$PWD/singleapplication.cpp \
|
||||
$$PWD/singleapplication_p.cpp
|
||||
|
||||
INCLUDEPATH += $$PWD
|
||||
|
||||
win32 {
|
||||
msvc:LIBS += Advapi32.lib
|
||||
gcc:LIBS += -ladvapi32
|
||||
}
|
||||
|
||||
DISTFILES += \
|
||||
$$PWD/README.md \
|
||||
$$PWD/CHANGELOG.md \
|
||||
$$PWD/Windows.md
|
|
@ -1,6 +1,6 @@
|
|||
// The MIT License (MIT)
|
||||
//
|
||||
// Copyright (c) Itay Grudev 2015 - 2018
|
||||
// Copyright (c) Itay Grudev 2015 - 2020
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
|
@ -69,18 +69,52 @@ SingleApplicationPrivate::~SingleApplicationPrivate()
|
|||
delete socket;
|
||||
}
|
||||
|
||||
memory->lock();
|
||||
InstancesInfo* inst = static_cast<InstancesInfo*>(memory->data());
|
||||
if( server != nullptr ) {
|
||||
server->close();
|
||||
delete server;
|
||||
inst->primary = false;
|
||||
inst->primaryPid = -1;
|
||||
inst->checksum = blockChecksum();
|
||||
}
|
||||
memory->unlock();
|
||||
if( memory != nullptr ) {
|
||||
memory->lock();
|
||||
auto *inst = static_cast<InstancesInfo*>(memory->data());
|
||||
if( server != nullptr ) {
|
||||
server->close();
|
||||
delete server;
|
||||
inst->primary = false;
|
||||
inst->primaryPid = -1;
|
||||
inst->primaryUser[0] = '\0';
|
||||
inst->checksum = blockChecksum();
|
||||
}
|
||||
memory->unlock();
|
||||
|
||||
delete memory;
|
||||
delete memory;
|
||||
}
|
||||
}
|
||||
|
||||
QString SingleApplicationPrivate::getUsername()
|
||||
{
|
||||
#ifdef Q_OS_WIN
|
||||
wchar_t username[UNLEN + 1];
|
||||
// Specifies size of the buffer on input
|
||||
DWORD usernameLength = UNLEN + 1;
|
||||
if( GetUserNameW( username, &usernameLength ) )
|
||||
return QString::fromWCharArray( username );
|
||||
#if QT_VERSION < QT_VERSION_CHECK(5, 10, 0)
|
||||
return QString::fromLocal8Bit( qgetenv( "USERNAME" ) );
|
||||
#else
|
||||
return qEnvironmentVariable( "USERNAME" );
|
||||
#endif
|
||||
#endif
|
||||
#ifdef Q_OS_UNIX
|
||||
QString username;
|
||||
uid_t uid = geteuid();
|
||||
struct passwd *pw = getpwuid( uid );
|
||||
if( pw )
|
||||
username = QString::fromLocal8Bit( pw->pw_name );
|
||||
if ( username.isEmpty() ) {
|
||||
#if QT_VERSION < QT_VERSION_CHECK(5, 10, 0)
|
||||
username = QString::fromLocal8Bit( qgetenv( "USER" ) );
|
||||
#else
|
||||
username = qEnvironmentVariable( "USER" );
|
||||
#endif
|
||||
}
|
||||
return username;
|
||||
#endif
|
||||
}
|
||||
|
||||
void SingleApplicationPrivate::genBlockServerName()
|
||||
|
@ -105,28 +139,7 @@ void SingleApplicationPrivate::genBlockServerName()
|
|||
|
||||
// User level block requires a user specific data in the hash
|
||||
if( options & SingleApplication::Mode::User ) {
|
||||
#ifdef Q_OS_WIN
|
||||
wchar_t username [ UNLEN + 1 ];
|
||||
// Specifies size of the buffer on input
|
||||
DWORD usernameLength = UNLEN + 1;
|
||||
if( GetUserNameW( username, &usernameLength ) ) {
|
||||
appData.addData( QString::fromWCharArray(username).toUtf8() );
|
||||
} else {
|
||||
appData.addData( qgetenv("USERNAME") );
|
||||
}
|
||||
#endif
|
||||
#ifdef Q_OS_UNIX
|
||||
QByteArray username;
|
||||
uid_t uid = geteuid();
|
||||
struct passwd *pw = getpwuid(uid);
|
||||
if( pw ) {
|
||||
username = pw->pw_name;
|
||||
}
|
||||
if( username.isEmpty() ) {
|
||||
username = qgetenv("USER");
|
||||
}
|
||||
appData.addData(username);
|
||||
#endif
|
||||
appData.addData( getUsername().toUtf8() );
|
||||
}
|
||||
|
||||
// Replace the backslash in RFC 2045 Base64 [a-zA-Z0-9+/=] to comply with
|
||||
|
@ -136,10 +149,11 @@ void SingleApplicationPrivate::genBlockServerName()
|
|||
|
||||
void SingleApplicationPrivate::initializeMemoryBlock()
|
||||
{
|
||||
InstancesInfo* inst = static_cast<InstancesInfo*>( memory->data() );
|
||||
auto *inst = static_cast<InstancesInfo*>( memory->data() );
|
||||
inst->primary = false;
|
||||
inst->secondary = 0;
|
||||
inst->primaryPid = -1;
|
||||
inst->primaryUser[0] = '\0';
|
||||
inst->checksum = blockChecksum();
|
||||
}
|
||||
|
||||
|
@ -169,10 +183,12 @@ void SingleApplicationPrivate::startPrimary()
|
|||
);
|
||||
|
||||
// Reset the number of connections
|
||||
InstancesInfo* inst = static_cast <InstancesInfo*>( memory->data() );
|
||||
auto *inst = static_cast <InstancesInfo*>( memory->data() );
|
||||
|
||||
inst->primary = true;
|
||||
inst->primaryPid = q->applicationPid();
|
||||
strncpy( inst->primaryUser, getUsername().toUtf8().data(), 127 );
|
||||
inst->primaryUser[127] = '\0';
|
||||
inst->checksum = blockChecksum();
|
||||
|
||||
instanceNumber = 0;
|
||||
|
@ -250,13 +266,25 @@ qint64 SingleApplicationPrivate::primaryPid()
|
|||
qint64 pid;
|
||||
|
||||
memory->lock();
|
||||
InstancesInfo* inst = static_cast<InstancesInfo*>( memory->data() );
|
||||
auto *inst = static_cast<InstancesInfo*>( memory->data() );
|
||||
pid = inst->primaryPid;
|
||||
memory->unlock();
|
||||
|
||||
return pid;
|
||||
}
|
||||
|
||||
QString SingleApplicationPrivate::primaryUser()
|
||||
{
|
||||
QByteArray username;
|
||||
|
||||
memory->lock();
|
||||
auto *inst = static_cast<InstancesInfo*>( memory->data() );
|
||||
username = inst->primaryUser;
|
||||
memory->unlock();
|
||||
|
||||
return QString::fromUtf8( username );
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Executed when a connection has been made to the LocalServer
|
||||
*/
|
|
@ -1,6 +1,6 @@
|
|||
// The MIT License (MIT)
|
||||
//
|
||||
// Copyright (c) Itay Grudev 2015 - 2016
|
||||
// Copyright (c) Itay Grudev 2015 - 2020
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
|
@ -42,14 +42,13 @@ struct InstancesInfo {
|
|||
quint32 secondary;
|
||||
qint64 primaryPid;
|
||||
quint16 checksum;
|
||||
char primaryUser[128];
|
||||
};
|
||||
|
||||
struct ConnectionInfo {
|
||||
explicit ConnectionInfo() :
|
||||
msgLen(0), instanceId(0), stage(0) {}
|
||||
qint64 msgLen;
|
||||
quint32 instanceId;
|
||||
quint8 stage;
|
||||
qint64 msgLen = 0;
|
||||
quint32 instanceId = 0;
|
||||
quint8 stage = 0;
|
||||
};
|
||||
|
||||
class SingleApplicationPrivate : public QObject {
|
||||
|
@ -69,8 +68,9 @@ public:
|
|||
Q_DECLARE_PUBLIC(SingleApplication)
|
||||
|
||||
SingleApplicationPrivate( SingleApplication *q_ptr );
|
||||
~SingleApplicationPrivate();
|
||||
~SingleApplicationPrivate() override;
|
||||
|
||||
QString getUsername();
|
||||
void genBlockServerName();
|
||||
void initializeMemoryBlock();
|
||||
void startPrimary();
|
||||
|
@ -78,6 +78,7 @@ public:
|
|||
void connectToPrimary(int msecs, ConnectionType connectionType );
|
||||
quint16 blockChecksum();
|
||||
qint64 primaryPid();
|
||||
QString primaryUser();
|
||||
void readInitMessageHeader(QLocalSocket *socket);
|
||||
void readInitMessageBody(QLocalSocket *socket);
|
||||
|
Loading…
Reference in a new issue