mirror of
https://github.com/Chatterino/chatterino2.git
synced 2024-11-21 22:24:07 +01:00
Merge branch 'master' into apa-notification-on-live
This commit is contained in:
commit
e2a7765964
53 changed files with 628 additions and 227 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1,3 +1,5 @@
|
|||
__pycache__/
|
||||
|
||||
# C++ objects and libs
|
||||
|
||||
*.slo
|
||||
|
|
|
@ -240,6 +240,7 @@ SOURCES += \
|
|||
src/messages/Emote.cpp \
|
||||
src/messages/ImageSet.cpp \
|
||||
src/providers/bttv/BttvEmotes.cpp \
|
||||
src/providers/LinkResolver.cpp \
|
||||
src/providers/ffz/FfzEmotes.cpp \
|
||||
src/autogenerated/ResourcesAutogen.cpp \
|
||||
src/singletons/Badges.cpp \
|
||||
|
@ -439,6 +440,7 @@ HEADERS += \
|
|||
src/messages/ImageSet.hpp \
|
||||
src/common/Outcome.hpp \
|
||||
src/providers/bttv/BttvEmotes.hpp \
|
||||
src/providers/LinkResolver.hpp \
|
||||
src/providers/ffz/FfzEmotes.hpp \
|
||||
src/autogenerated/ResourcesAutogen.hpp \
|
||||
src/singletons/Badges.hpp \
|
||||
|
|
Binary file not shown.
BIN
resources/buttons/addSplitDark.png
Normal file
BIN
resources/buttons/addSplitDark.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 231 B |
|
@ -5,10 +5,18 @@ from _generate_resources import *
|
|||
|
||||
ignored_files = ['qt.conf', 'resources.qrc', 'resources_autogenerated.qrc', 'windows.rc',
|
||||
'generate_resources.py', '_generate_resources.py']
|
||||
|
||||
# to ignore all files in a/b, add a/b to ignored_directories.
|
||||
# this will ignore a/b/c/d.txt and a/b/xd.txt
|
||||
ignored_directories = ['__pycache__']
|
||||
|
||||
def isNotIgnored(file):
|
||||
return str(file) not in ignored_files
|
||||
# check if file exists in an ignored direcory
|
||||
for ignored_directory in ignored_directories:
|
||||
if file.parent.as_posix().startswith(ignored_directory):
|
||||
return False
|
||||
|
||||
return file.as_posix() not in ignored_files
|
||||
|
||||
all_files = list(filter(isNotIgnored, \
|
||||
filter(Path.is_file, Path('.').glob('**/*'))))
|
||||
|
@ -18,15 +26,15 @@ image_files = list(filter(isNotIgnored, \
|
|||
with open('./resources_autogenerated.qrc', 'w') as out:
|
||||
out.write(resources_header)
|
||||
for file in all_files:
|
||||
out.write(f" <file>{str(file)}</file>\n")
|
||||
out.write(f" <file>{file.as_posix()}</file>\n")
|
||||
out.write(resources_footer)
|
||||
|
||||
with open('../src/autogenerated/ResourcesAutogen.cpp', 'w') as out:
|
||||
out.write(source_header)
|
||||
for file in sorted(image_files):
|
||||
var_name = str(file.with_suffix("")).replace("/",".")
|
||||
var_name = file.with_suffix("").as_posix().replace("/",".")
|
||||
out.write(f' this->{var_name}')
|
||||
out.write(f' = QPixmap(":/{file}");\n')
|
||||
out.write(f' = QPixmap(":/{file.as_posix()}");\n')
|
||||
out.write(source_footer)
|
||||
|
||||
def writeHeader(out, name, element, indent):
|
||||
|
@ -46,11 +54,10 @@ with open('../src/autogenerated/ResourcesAutogen.hpp', 'w') as out:
|
|||
elements = {}
|
||||
for file in sorted(image_files):
|
||||
elements_ref = elements
|
||||
directories = str(file).split('/')[:-1]
|
||||
directories = file.as_posix().split('/')[:-1]
|
||||
filename = file.stem
|
||||
for directory in directories:
|
||||
if directory not in elements_ref:
|
||||
if directory not in ignored_directories:
|
||||
elements_ref[directory] = {}
|
||||
elements_ref = elements_ref[directory]
|
||||
elements_ref[filename] = filename
|
||||
|
|
|
@ -1,64 +1,66 @@
|
|||
<RCC>
|
||||
<qresource prefix="/"> <file>pajaDank.png</file>
|
||||
<file>icon.png</file>
|
||||
<file>emojidata.txt</file>
|
||||
<qresource prefix="/"> <file>chatterino2.icns</file>
|
||||
<file>contributors.txt</file>
|
||||
<file>error.png</file>
|
||||
<file>emoji.json</file>
|
||||
<file>emojidata.txt</file>
|
||||
<file>error.png</file>
|
||||
<file>icon.ico</file>
|
||||
<file>icon.png</file>
|
||||
<file>pajaDank.png</file>
|
||||
<file>tlds.txt</file>
|
||||
<file>chatterino2.icns</file>
|
||||
<file>qss/settings.qss</file>
|
||||
<file>__pycache__/_generate_resources.cpython-36.pyc</file>
|
||||
<file>licenses/fmt_bsd2.txt</file>
|
||||
<file>licenses/openssl.txt</file>
|
||||
<file>licenses/pajlada_settings.txt</file>
|
||||
<file>licenses/qt_lgpl-3.0.txt</file>
|
||||
<file>licenses/pajlada_signals.txt</file>
|
||||
<file>licenses/rapidjson.txt</file>
|
||||
<file>licenses/websocketpp.txt</file>
|
||||
<file>licenses/boost_boost.txt</file>
|
||||
<file>licenses/libcommuni_BSD3.txt</file>
|
||||
<file>settings/aboutlogo.png</file>
|
||||
<file>settings/behave.svg</file>
|
||||
<file>settings/accounts.svg</file>
|
||||
<file>settings/about.svg</file>
|
||||
<file>settings/notifications.svg</file>
|
||||
<file>settings/commands.svg</file>
|
||||
<file>settings/theme.svg</file>
|
||||
<file>split/up.png</file>
|
||||
<file>split/left.png</file>
|
||||
<file>split/move.png</file>
|
||||
<file>split/right.png</file>
|
||||
<file>split/down.png</file>
|
||||
<file>buttons/unban.png</file>
|
||||
<file>buttons/menuDark.png</file>
|
||||
<file>buttons/mod.png</file>
|
||||
<file>buttons/emote.svg</file>
|
||||
<file>buttons/modModeEnabled2.png</file>
|
||||
<file>avatars/fourtf.png</file>
|
||||
<file>avatars/pajlada.png</file>
|
||||
<file>buttons/addSplitDark.png</file>
|
||||
<file>buttons/ban.png</file>
|
||||
<file>buttons/unmod.png</file>
|
||||
<file>buttons/banRed.png</file>
|
||||
<file>buttons/emote.svg</file>
|
||||
<file>buttons/emoteDark.svg</file>
|
||||
<file>buttons/updateError.png</file>
|
||||
<file>buttons/menuDark.png</file>
|
||||
<file>buttons/menuLight.png</file>
|
||||
<file>buttons/mod.png</file>
|
||||
<file>buttons/modModeDisabled.png</file>
|
||||
<file>buttons/modModeDisabled2.png</file>
|
||||
<file>buttons/modModeEnabled.png</file>
|
||||
<file>buttons/menuLight.png</file>
|
||||
<file>buttons/update.png</file>
|
||||
<file>buttons/modModeEnabled2.png</file>
|
||||
<file>buttons/timeout.png</file>
|
||||
<file>buttons/banRed.png</file>
|
||||
<file>buttons/unban.png</file>
|
||||
<file>buttons/unmod.png</file>
|
||||
<file>buttons/update.png</file>
|
||||
<file>buttons/updateError.png</file>
|
||||
<file>licenses/boost_boost.txt</file>
|
||||
<file>licenses/emoji-data-source.txt</file>
|
||||
<file>licenses/fmt_bsd2.txt</file>
|
||||
<file>licenses/libcommuni_BSD3.txt</file>
|
||||
<file>licenses/openssl.txt</file>
|
||||
<file>licenses/pajlada_settings.txt</file>
|
||||
<file>licenses/pajlada_signals.txt</file>
|
||||
<file>licenses/qt_lgpl-3.0.txt</file>
|
||||
<file>licenses/rapidjson.txt</file>
|
||||
<file>licenses/websocketpp.txt</file>
|
||||
<file>qss/settings.qss</file>
|
||||
<file>settings/about.svg</file>
|
||||
<file>settings/aboutlogo.png</file>
|
||||
<file>settings/accounts.svg</file>
|
||||
<file>settings/behave.svg</file>
|
||||
<file>settings/commands.svg</file>
|
||||
<file>settings/emote.svg</file>
|
||||
<file>settings/notifications.svg</file>
|
||||
<file>settings/theme.svg</file>
|
||||
<file>sounds/ping2.wav</file>
|
||||
<file>twitch/prime.png</file>
|
||||
<file>twitch/verified.png</file>
|
||||
<file>split/down.png</file>
|
||||
<file>split/left.png</file>
|
||||
<file>split/move.png</file>
|
||||
<file>split/right.png</file>
|
||||
<file>split/up.png</file>
|
||||
<file>twitch/admin.png</file>
|
||||
<file>twitch/broadcaster.png</file>
|
||||
<file>twitch/cheer1.png</file>
|
||||
<file>twitch/globalmod.png</file>
|
||||
<file>twitch/moderator.png</file>
|
||||
<file>twitch/prime.png</file>
|
||||
<file>twitch/staff.png</file>
|
||||
<file>twitch/subscriber.png</file>
|
||||
<file>twitch/turbo.png</file>
|
||||
<file>twitch/moderator.png</file>
|
||||
<file>twitch/globalmod.png</file>
|
||||
<file>twitch/cheer1.png</file>
|
||||
<file>twitch/broadcaster.png</file>
|
||||
<file>twitch/staff.png</file>
|
||||
<file>avatars/fourtf.png</file>
|
||||
<file>avatars/pajlada.png</file>
|
||||
<file>twitch/verified.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
|
@ -10,6 +10,7 @@
|
|||
#include "debug/Log.hpp"
|
||||
#include "messages/MessageBuilder.hpp"
|
||||
#include "providers/bttv/BttvEmotes.hpp"
|
||||
#include "providers/chatterino/ChatterinoBadges.hpp"
|
||||
#include "providers/ffz/FfzEmotes.hpp"
|
||||
#include "providers/twitch/PubsubClient.hpp"
|
||||
#include "providers/twitch/TwitchServer.hpp"
|
||||
|
@ -56,6 +57,7 @@ Application::Application(Settings &_settings, Paths &_paths)
|
|||
, taggedUsers(&this->emplace<TaggedUsersController>())
|
||||
, moderationActions(&this->emplace<ModerationActions>())
|
||||
, twitch2(&this->emplace<TwitchServer>())
|
||||
, chatterinoBadges(&this->emplace<ChatterinoBadges>())
|
||||
, logging(&this->emplace<Logging>())
|
||||
|
||||
{
|
||||
|
|
|
@ -28,7 +28,7 @@ class Settings;
|
|||
class Fonts;
|
||||
class Resources2;
|
||||
class Toasts;
|
||||
|
||||
class ChatterinoBadges;
|
||||
|
||||
class Application
|
||||
{
|
||||
|
@ -65,6 +65,7 @@ public:
|
|||
TaggedUsersController *const taggedUsers{};
|
||||
ModerationActions *const moderationActions{};
|
||||
TwitchServer *const twitch2{};
|
||||
ChatterinoBadges *const chatterinoBadges{};
|
||||
|
||||
/*[[deprecated]]*/ Logging *const logging{};
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ Resources2::Resources2()
|
|||
{
|
||||
this->avatars.fourtf = QPixmap(":/avatars/fourtf.png");
|
||||
this->avatars.pajlada = QPixmap(":/avatars/pajlada.png");
|
||||
this->buttons.addSplitDark = QPixmap(":/buttons/addSplitDark.png");
|
||||
this->buttons.ban = QPixmap(":/buttons/ban.png");
|
||||
this->buttons.banRed = QPixmap(":/buttons/banRed.png");
|
||||
this->buttons.menuDark = QPixmap(":/buttons/menuDark.png");
|
||||
|
|
|
@ -3,8 +3,7 @@
|
|||
|
||||
namespace chatterino {
|
||||
|
||||
class Resources2 : public Singleton
|
||||
{
|
||||
class Resources2 : public Singleton {
|
||||
public:
|
||||
Resources2();
|
||||
|
||||
|
@ -13,6 +12,7 @@ public:
|
|||
QPixmap pajlada;
|
||||
} avatars;
|
||||
struct {
|
||||
QPixmap addSplitDark;
|
||||
QPixmap ban;
|
||||
QPixmap banRed;
|
||||
QPixmap menuDark;
|
||||
|
|
|
@ -143,7 +143,8 @@ void Channel::addOrReplaceTimeout(MessagePtr message)
|
|||
if (s->flags.hasNone({MessageFlag::Timeout, MessageFlag::Untimeout}) &&
|
||||
s->loginName == message->timeoutUser) {
|
||||
// FOURTF: disabled for now
|
||||
// s->flags.EnableFlag(MessageFlag::Disabled);
|
||||
// PAJLADA: Shitty solution described in Message.hpp
|
||||
s->flags.set(MessageFlag::Disabled);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -124,7 +124,7 @@ public:
|
|||
rowItem.items[column]->setData(value, role);
|
||||
|
||||
if (rowItem.isCustomRow) {
|
||||
this->customRowSetData(rowItem.items, column, value, role);
|
||||
this->customRowSetData(rowItem.items, column, value, role, row);
|
||||
} else {
|
||||
int vecRow = this->getVectorIndexFromModelIndex(row);
|
||||
this->vector_->removeItem(vecRow, this);
|
||||
|
@ -230,7 +230,8 @@ protected:
|
|||
}
|
||||
|
||||
virtual void customRowSetData(const std::vector<QStandardItem *> &row,
|
||||
int column, const QVariant &value, int role)
|
||||
int column, const QVariant &value, int role,
|
||||
int rowIndex)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -36,36 +36,67 @@ void HighlightModel::getRowFromItem(const HighlightPhrase &item,
|
|||
|
||||
void HighlightModel::afterInit()
|
||||
{
|
||||
std::vector<QStandardItem *> row = this->createRow();
|
||||
setBoolItem(row[0], getSettings()->enableHighlightsSelf.getValue(), true,
|
||||
std::vector<QStandardItem *> usernameRow = this->createRow();
|
||||
setBoolItem(usernameRow[0], getSettings()->enableSelfHighlight.getValue(),
|
||||
true, false);
|
||||
usernameRow[0]->setData("Your username (automatic)", Qt::DisplayRole);
|
||||
setBoolItem(usernameRow[1],
|
||||
getSettings()->enableSelfHighlightTaskbar.getValue(), true,
|
||||
false);
|
||||
row[0]->setData("Your username (automatic)", Qt::DisplayRole);
|
||||
setBoolItem(row[1], getSettings()->enableHighlightTaskbar.getValue(), true,
|
||||
setBoolItem(usernameRow[2],
|
||||
getSettings()->enableSelfHighlightSound.getValue(), true,
|
||||
false);
|
||||
setBoolItem(row[2], getSettings()->enableHighlightSound.getValue(), true,
|
||||
usernameRow[3]->setFlags(0);
|
||||
this->insertCustomRow(usernameRow, 0);
|
||||
std::vector<QStandardItem *> whisperRow = this->createRow();
|
||||
setBoolItem(whisperRow[0], getSettings()->enableWhisperHighlight.getValue(),
|
||||
true, false);
|
||||
whisperRow[0]->setData("Whispers", Qt::DisplayRole);
|
||||
setBoolItem(whisperRow[1],
|
||||
getSettings()->enableWhisperHighlightTaskbar.getValue(), true,
|
||||
false);
|
||||
row[3]->setFlags(0);
|
||||
this->insertCustomRow(row, 0);
|
||||
setBoolItem(whisperRow[2],
|
||||
getSettings()->enableWhisperHighlightSound.getValue(), true,
|
||||
false);
|
||||
whisperRow[3]->setFlags(0);
|
||||
this->insertCustomRow(whisperRow, 1);
|
||||
}
|
||||
|
||||
void HighlightModel::customRowSetData(const std::vector<QStandardItem *> &row,
|
||||
int column, const QVariant &value,
|
||||
int role)
|
||||
int role, int rowIndex)
|
||||
{
|
||||
switch (column) {
|
||||
case 0: {
|
||||
if (role == Qt::CheckStateRole) {
|
||||
getSettings()->enableHighlightsSelf.setValue(value.toBool());
|
||||
if (rowIndex == 0) {
|
||||
getSettings()->enableSelfHighlight.setValue(value.toBool());
|
||||
} else if (rowIndex == 1) {
|
||||
getSettings()->enableWhisperHighlight.setValue(
|
||||
value.toBool());
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case 1: {
|
||||
if (role == Qt::CheckStateRole) {
|
||||
getSettings()->enableHighlightTaskbar.setValue(value.toBool());
|
||||
if (rowIndex == 0) {
|
||||
getSettings()->enableSelfHighlightTaskbar.setValue(
|
||||
value.toBool());
|
||||
} else if (rowIndex == 1) {
|
||||
getSettings()->enableWhisperHighlightTaskbar.setValue(
|
||||
value.toBool());
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case 2: {
|
||||
if (role == Qt::CheckStateRole) {
|
||||
getSettings()->enableHighlightSound.setValue(value.toBool());
|
||||
if (rowIndex == 0) {
|
||||
getSettings()->enableSelfHighlightSound.setValue(
|
||||
value.toBool());
|
||||
} else if (rowIndex == 1) {
|
||||
getSettings()->enableWhisperHighlightSound.setValue(
|
||||
value.toBool());
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case 3: {
|
||||
|
|
|
@ -26,8 +26,8 @@ protected:
|
|||
virtual void afterInit() override;
|
||||
|
||||
virtual void customRowSetData(const std::vector<QStandardItem *> &row,
|
||||
int column, const QVariant &value,
|
||||
int role) override;
|
||||
int column, const QVariant &value, int role,
|
||||
int rowIndex) override;
|
||||
|
||||
friend class HighlightController;
|
||||
};
|
||||
|
|
|
@ -310,6 +310,14 @@ void Image::load()
|
|||
|
||||
return Success;
|
||||
});
|
||||
req.onError([that = this, weak = weakOf(this)](auto result) -> bool {
|
||||
auto shared = weak.lock();
|
||||
if (!shared) return false;
|
||||
|
||||
shared->empty_ = true;
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
req.execute();
|
||||
}
|
||||
|
|
|
@ -34,7 +34,13 @@ struct Message : boost::noncopyable {
|
|||
Message();
|
||||
~Message();
|
||||
|
||||
MessageFlags flags;
|
||||
// Making this a mutable means that we can update a messages flags,
|
||||
// while still keeping Message constant. This means that a message's flag
|
||||
// can be updated without the renderer being made aware, which might be bad.
|
||||
// This is a temporary effort until we can figure out what the right
|
||||
// const-correct way to deal with this is.
|
||||
// This might bring race conditions with it
|
||||
mutable MessageFlags flags;
|
||||
QTime parseTime;
|
||||
QString id;
|
||||
QString searchText;
|
||||
|
|
|
@ -61,6 +61,12 @@ MessageElementFlags MessageElement::getFlags() const
|
|||
return this->flags_;
|
||||
}
|
||||
|
||||
MessageElement *MessageElement::updateLink()
|
||||
{
|
||||
this->linkChanged.invoke();
|
||||
return this;
|
||||
}
|
||||
|
||||
// IMAGE
|
||||
ImageElement::ImageElement(ImagePtr image, MessageElementFlags flags)
|
||||
: MessageElement(flags)
|
||||
|
@ -152,6 +158,15 @@ void TextElement::addToContainer(MessageLayoutContainer &container,
|
|||
color, this->style_, container.getScale()))
|
||||
->setLink(this->getLink());
|
||||
e->setTrailingSpace(trailingSpace);
|
||||
|
||||
// If URL link was changed,
|
||||
// Should update it in MessageLayoutElement too!
|
||||
if (this->getLink().type == Link::Url) {
|
||||
this->linkChanged.connect(
|
||||
[this, e]() {
|
||||
e->setLink(this->getLink());
|
||||
});
|
||||
}
|
||||
return e;
|
||||
};
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <pajlada/signals/signalholder.hpp>
|
||||
|
||||
namespace chatterino {
|
||||
class Channel;
|
||||
|
@ -128,6 +129,7 @@ public:
|
|||
const Link &getLink() const;
|
||||
bool hasTrailingSpace() const;
|
||||
MessageElementFlags getFlags() const;
|
||||
MessageElement *updateLink();
|
||||
|
||||
virtual void addToContainer(MessageLayoutContainer &container,
|
||||
MessageElementFlags flags) = 0;
|
||||
|
@ -135,6 +137,7 @@ public:
|
|||
protected:
|
||||
MessageElement(MessageElementFlags flags);
|
||||
bool trailingSpace = true;
|
||||
pajlada::Signals::NoArgSignal linkChanged;
|
||||
|
||||
private:
|
||||
Link link_;
|
||||
|
|
48
src/providers/LinkResolver.cpp
Normal file
48
src/providers/LinkResolver.cpp
Normal file
|
@ -0,0 +1,48 @@
|
|||
#include "providers/LinkResolver.hpp"
|
||||
|
||||
#include "common/Common.hpp"
|
||||
#include "common/NetworkRequest.hpp"
|
||||
#include "messages/Link.hpp"
|
||||
#include "singletons/Settings.hpp"
|
||||
|
||||
#include <QString>
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
void LinkResolver::getLinkInfo(const QString url,
|
||||
std::function<void(QString, Link)> successCallback)
|
||||
{
|
||||
QString requestUrl("https://braize.pajlada.com/chatterino/link_resolver/" +
|
||||
QUrl::toPercentEncoding(url, "", "/:"));
|
||||
|
||||
NetworkRequest request(requestUrl);
|
||||
request.setCaller(QThread::currentThread());
|
||||
request.setTimeout(30000);
|
||||
request.onSuccess([successCallback, url](auto result) mutable -> Outcome {
|
||||
auto root = result.parseJson();
|
||||
auto statusCode = root.value("status").toInt();
|
||||
QString response = QString();
|
||||
QString linkString = url;
|
||||
if (statusCode == 200) {
|
||||
response = root.value("tooltip").toString();
|
||||
if (getSettings()->enableUnshortLinks) {
|
||||
linkString = root.value("link").toString();
|
||||
}
|
||||
} else {
|
||||
response = root.value("message").toString();
|
||||
}
|
||||
successCallback(QUrl::fromPercentEncoding(response.toUtf8()), Link(Link::Url, linkString));
|
||||
|
||||
return Success;
|
||||
});
|
||||
|
||||
request.onError([successCallback, url](auto result) {
|
||||
successCallback("No link info found", Link(Link::Url, url));
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
request.execute();
|
||||
}
|
||||
|
||||
} // namespace chatterino
|
19
src/providers/LinkResolver.hpp
Normal file
19
src/providers/LinkResolver.hpp
Normal file
|
@ -0,0 +1,19 @@
|
|||
#pragma once
|
||||
|
||||
#include <QString>
|
||||
#include <functional>
|
||||
|
||||
#include "messages/Link.hpp"
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
class LinkResolver
|
||||
{
|
||||
public:
|
||||
static void getLinkInfo(const QString url,
|
||||
std::function<void(QString, Link)> callback);
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
} // namespace chatterino
|
|
@ -5,8 +5,18 @@
|
|||
#include <QJsonValue>
|
||||
#include <QThread>
|
||||
#include "common/NetworkRequest.hpp"
|
||||
#include "common/Outcome.hpp"
|
||||
#include "messages/Emote.hpp"
|
||||
|
||||
#include <QUrl>
|
||||
|
||||
#include <map>
|
||||
|
||||
namespace chatterino {
|
||||
void ChatterinoBadges::initialize(Settings &settings, Paths &paths)
|
||||
{
|
||||
this->loadChatterinoBadges();
|
||||
}
|
||||
|
||||
ChatterinoBadges::ChatterinoBadges()
|
||||
{
|
||||
|
@ -14,41 +24,41 @@ ChatterinoBadges::ChatterinoBadges()
|
|||
|
||||
boost::optional<EmotePtr> ChatterinoBadges::getBadge(const UserName &username)
|
||||
{
|
||||
auto it = badgeMap.find(username.string);
|
||||
if (it != badgeMap.end()) {
|
||||
return emotes[it->second];
|
||||
}
|
||||
return boost::none;
|
||||
// return this->badges.access()->get(username);
|
||||
}
|
||||
|
||||
void ChatterinoBadges::loadChatterinoBadges()
|
||||
{
|
||||
// static QString url("https://fourtf.com/chatterino/badges.json");
|
||||
static QUrl url("https://fourtf.com/chatterino/badges.json");
|
||||
|
||||
// NetworkRequest req(url);
|
||||
// req.setCaller(QThread::currentThread());
|
||||
NetworkRequest req(url);
|
||||
req.setCaller(QThread::currentThread());
|
||||
|
||||
// req.onSuccess([this](auto result) {
|
||||
// auto jsonRoot = result.parseJson();
|
||||
// auto badges = this->badges.access();
|
||||
// auto replacement = badges->makeReplacment();
|
||||
req.onSuccess([this](auto result) -> Outcome {
|
||||
auto jsonRoot = result.parseJson();
|
||||
int index = 0;
|
||||
for (const auto &jsonBadge_ : jsonRoot.value("badges").toArray()) {
|
||||
auto jsonBadge = jsonBadge_.toObject();
|
||||
auto emote = Emote{
|
||||
EmoteName{}, ImageSet{Url{jsonBadge.value("image").toString()}},
|
||||
Tooltip{jsonBadge.value("tooltip").toString()}, Url{}};
|
||||
|
||||
// for (auto jsonBadge_ : jsonRoot.value("badges").toArray()) {
|
||||
// auto jsonBadge = jsonBadge_.toObject();
|
||||
emotes.push_back(std::make_shared<const Emote>(std::move(emote)));
|
||||
|
||||
// auto emote = Emote{
|
||||
// EmoteName{},
|
||||
// ImageSet{Url{jsonBadge.value("image").toString()}},
|
||||
// Tooltip{jsonBadge.value("tooltip").toString()}, Url{}};
|
||||
for (const auto &user : jsonBadge.value("users").toArray()) {
|
||||
badgeMap[user.toString()] = index;
|
||||
}
|
||||
++index;
|
||||
}
|
||||
|
||||
// for (auto jsonUser : jsonBadge.value("users").toArray()) {
|
||||
// replacement.add(UserName{jsonUser.toString()},
|
||||
// std::move(emote));
|
||||
// }
|
||||
// }
|
||||
return Success;
|
||||
});
|
||||
|
||||
// replacement.apply();
|
||||
// return Success;
|
||||
//});
|
||||
|
||||
// req.execute();
|
||||
req.execute();
|
||||
}
|
||||
|
||||
} // namespace chatterino
|
||||
|
|
|
@ -1,25 +1,30 @@
|
|||
#pragma once
|
||||
|
||||
#include <boost/optional.hpp>
|
||||
#include <common/Singleton.hpp>
|
||||
|
||||
#include "common/Aliases.hpp"
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
struct Emote;
|
||||
using EmotePtr = std::shared_ptr<const Emote>;
|
||||
|
||||
class ChatterinoBadges
|
||||
class ChatterinoBadges : public Singleton
|
||||
{
|
||||
public:
|
||||
virtual void initialize(Settings &settings, Paths &paths) override;
|
||||
ChatterinoBadges();
|
||||
|
||||
boost::optional<EmotePtr> getBadge(const UserName &username);
|
||||
|
||||
private:
|
||||
void loadChatterinoBadges();
|
||||
|
||||
// UniqueAccess<EmoteCache<UserName>> badges;
|
||||
std::map<QString, int> badgeMap;
|
||||
std::vector<EmotePtr> emotes;
|
||||
};
|
||||
|
||||
} // namespace chatterino
|
||||
|
|
|
@ -66,8 +66,8 @@ void IrcMessageHandler::addMessage(Communi::IrcMessage *_message,
|
|||
builder->flags.unset(MessageFlag::Highlighted);
|
||||
}
|
||||
|
||||
auto highlighted = builder->flags.has(MessageFlag::Highlighted);
|
||||
auto msg = builder.build();
|
||||
auto highlighted = msg->flags.has(MessageFlag::Highlighted);
|
||||
|
||||
if (!isSub) {
|
||||
if (highlighted) {
|
||||
|
@ -221,10 +221,10 @@ void IrcMessageHandler::handleWhisperMessage(Communi::IrcMessage *message)
|
|||
false);
|
||||
|
||||
if (!builder.isIgnored()) {
|
||||
app->twitch.server->lastUserThatWhisperedMe.set(builder.userName);
|
||||
|
||||
MessagePtr _message = builder.build();
|
||||
|
||||
app->twitch.server->lastUserThatWhisperedMe.set(builder.userName);
|
||||
|
||||
if (_message->flags.has(MessageFlag::Highlighted)) {
|
||||
app->twitch.server->mentionsChannel->addMessage(_message);
|
||||
}
|
||||
|
|
|
@ -633,12 +633,13 @@ void TwitchChannel::refreshBadges()
|
|||
auto jsonVersion = jsonVersion_->toObject();
|
||||
auto emote = std::make_shared<Emote>(Emote{
|
||||
EmoteName{},
|
||||
ImageSet{Image::fromUrl(
|
||||
{jsonVersion["image_url_1x"].toString()}),
|
||||
Image::fromUrl(
|
||||
{jsonVersion["image_url_2x"].toString()}),
|
||||
Image::fromUrl(
|
||||
{jsonVersion["image_url_4x"].toString()})},
|
||||
ImageSet{
|
||||
Image::fromUrl({jsonVersion["image_url_1x"].toString()},
|
||||
1),
|
||||
Image::fromUrl({jsonVersion["image_url_2x"].toString()},
|
||||
.5),
|
||||
Image::fromUrl({jsonVersion["image_url_4x"].toString()},
|
||||
.25)},
|
||||
Tooltip{jsonRoot["description"].toString()},
|
||||
Url{jsonVersion["clickURL"].toString()}});
|
||||
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
#include "controllers/ignores/IgnoreController.hpp"
|
||||
#include "debug/Log.hpp"
|
||||
#include "messages/Message.hpp"
|
||||
#include "providers/LinkResolver.hpp"
|
||||
#include "providers/chatterino/ChatterinoBadges.hpp"
|
||||
#include "providers/twitch/TwitchBadges.hpp"
|
||||
#include "providers/twitch/TwitchChannel.hpp"
|
||||
#include "singletons/Emotes.hpp"
|
||||
|
@ -268,12 +270,29 @@ void TwitchMessageBuilder::addTextOrEmoji(const QString &string_)
|
|||
link = Link(Link::Url, linkString);
|
||||
|
||||
textColor = MessageColor(MessageColor::Link);
|
||||
auto linkMELowercase =
|
||||
this->emplace<TextElement>(lowercaseLinkString,
|
||||
MessageElementFlag::LowercaseLink, textColor)
|
||||
MessageElementFlag::LowercaseLink,
|
||||
textColor)
|
||||
->setLink(link);
|
||||
auto linkMEOriginal =
|
||||
this->emplace<TextElement>(string, MessageElementFlag::OriginalLink,
|
||||
textColor)
|
||||
->setLink(link);
|
||||
|
||||
LinkResolver::getLinkInfo(
|
||||
linkString, [linkMELowercase, linkMEOriginal, linkString]
|
||||
(QString tooltipText, Link originalLink) {
|
||||
if (!tooltipText.isEmpty()) {
|
||||
linkMELowercase->setTooltip(tooltipText);
|
||||
linkMEOriginal->setTooltip(tooltipText);
|
||||
}
|
||||
if (originalLink.value != linkString &&
|
||||
!originalLink.value.isEmpty()) {
|
||||
linkMELowercase->setLink(originalLink)->updateLink();
|
||||
linkMEOriginal->setLink(originalLink)->updateLink();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// if (!linkString.isEmpty()) {
|
||||
|
@ -507,10 +526,10 @@ void TwitchMessageBuilder::parseHighlights(bool isPastMsg)
|
|||
std::vector<HighlightPhrase> userHighlights =
|
||||
app->highlights->highlightedUsers.getVector();
|
||||
|
||||
if (getSettings()->enableHighlightsSelf && currentUsername.size() > 0) {
|
||||
if (getSettings()->enableSelfHighlight && currentUsername.size() > 0) {
|
||||
HighlightPhrase selfHighlight(
|
||||
currentUsername, getSettings()->enableHighlightTaskbar,
|
||||
getSettings()->enableHighlightSound, false);
|
||||
currentUsername, getSettings()->enableSelfHighlightTaskbar,
|
||||
getSettings()->enableSelfHighlightSound, false);
|
||||
activeHighlights.emplace_back(std::move(selfHighlight));
|
||||
}
|
||||
|
||||
|
@ -564,6 +583,15 @@ void TwitchMessageBuilder::parseHighlights(bool isPastMsg)
|
|||
}
|
||||
}
|
||||
}
|
||||
if (this->args.isReceivedWhisper &&
|
||||
getSettings()->enableWhisperHighlight) {
|
||||
if (getSettings()->enableWhisperHighlightTaskbar) {
|
||||
doAlert = true;
|
||||
}
|
||||
if (getSettings()->enableWhisperHighlightSound) {
|
||||
playSound = true;
|
||||
}
|
||||
}
|
||||
|
||||
this->message().flags.set(MessageFlag::Highlighted, doHighlight);
|
||||
|
||||
|
@ -769,20 +797,12 @@ void TwitchMessageBuilder::appendTwitchBadges()
|
|||
|
||||
void TwitchMessageBuilder::appendChatterinoBadges()
|
||||
{
|
||||
// auto app = getApp();
|
||||
|
||||
// auto &badges = app->resources->chatterinoBadges;
|
||||
// auto it = badges.find(this->userName.toStdString());
|
||||
|
||||
// if (it == badges.end()) {
|
||||
// return;
|
||||
// }
|
||||
|
||||
// const auto badge = it->second;
|
||||
|
||||
// this->emplace<ImageElement>(badge->image,
|
||||
// MessageElementFlag::BadgeChatterino)
|
||||
// ->setTooltip(QString::fromStdString(badge->tooltip));
|
||||
auto chatterinoBadgePtr =
|
||||
getApp()->chatterinoBadges->getBadge({this->userName});
|
||||
if (chatterinoBadgePtr) {
|
||||
this->emplace<EmoteElement>(*chatterinoBadgePtr,
|
||||
MessageElementFlag::BadgeChatterino);
|
||||
}
|
||||
}
|
||||
|
||||
Outcome TwitchMessageBuilder::tryParseCheermote(const QString &string)
|
||||
|
|
|
@ -29,7 +29,6 @@ public:
|
|||
"/appearance/enableAnimationsWhenFocused", false};
|
||||
QStringSetting timestampFormat = {"/appearance/messages/timestampFormat",
|
||||
"h:mm"};
|
||||
BoolSetting showBadges = {"/appearance/messages/showBadges", true};
|
||||
BoolSetting showLastMessageIndicator = {
|
||||
"/appearance/messages/showLastMessageIndicator", false};
|
||||
IntSetting lastMessagePattern = {"/appearance/messages/lastMessagePattern",
|
||||
|
@ -67,6 +66,16 @@ public:
|
|||
// BoolSetting useCustomWindowFrame = {"/appearance/useCustomWindowFrame",
|
||||
// false};
|
||||
|
||||
// Badges
|
||||
BoolSetting showBadgesGlobalAuthority = {
|
||||
"/appearance/badges/GlobalAuthority", true};
|
||||
BoolSetting showBadgesChannelAuthority = {
|
||||
"/appearance/badges/ChannelAuthority", true};
|
||||
BoolSetting showBadgesSubscription = {"/appearance/badges/subscription",
|
||||
true};
|
||||
BoolSetting showBadgesVanity = {"/appearance/badges/vanity", true};
|
||||
BoolSetting showBadgesChatterino = {"/appearance/badges/chatterino", true};
|
||||
|
||||
/// Behaviour
|
||||
BoolSetting allowDuplicateMessages = {"/behaviour/allowDuplicateMessages",
|
||||
true};
|
||||
|
@ -103,6 +112,8 @@ public:
|
|||
|
||||
/// Links
|
||||
BoolSetting linksDoubleClickOnly = {"/links/doubleClickToOpen", false};
|
||||
BoolSetting enableLinkInfoTooltip = {"/links/linkInfoTooltip", false};
|
||||
BoolSetting enableUnshortLinks = {"/links/unshortLinks", false};
|
||||
BoolSetting enableLowercaseLink = {"/links/linkLowercase", true};
|
||||
|
||||
/// Ingored Users
|
||||
|
@ -114,12 +125,19 @@ public:
|
|||
|
||||
/// Highlighting
|
||||
// BoolSetting enableHighlights = {"/highlighting/enabled", true};
|
||||
BoolSetting enableHighlightsSelf = {"/highlighting/nameIsHighlightKeyword",
|
||||
true};
|
||||
BoolSetting enableHighlightSound = {"/highlighting/enableSound", true};
|
||||
BoolSetting enableHighlightTaskbar = {"/highlighting/enableTaskbarFlashing",
|
||||
true};
|
||||
BoolSetting customHighlightSound = {"/highlighting/useCustomSound", false};
|
||||
BoolSetting enableSelfHighlight = {
|
||||
"/highlighting/selfHighlight/nameIsHighlightKeyword", true};
|
||||
BoolSetting enableSelfHighlightSound = {
|
||||
"/highlighting/selfHighlight/enableSound", true};
|
||||
BoolSetting enableSelfHighlightTaskbar = {
|
||||
"/highlighting/selfHighlight/enableTaskbarFlashing", true};
|
||||
BoolSetting enableWhisperHighlight = {
|
||||
"/highlighting/whisperHighlight/whispersHighlighted", true};
|
||||
BoolSetting enableWhisperHighlightSound = {
|
||||
"/highlighting/whisperHighlight/enableSound", false};
|
||||
BoolSetting enableWhisperHighlightTaskbar = {
|
||||
"/highlighting/whisperHighlight/enableTaskbarFlashing", false};
|
||||
|
||||
/// Logging
|
||||
BoolSetting enableLogging = {"/logging/enabled", false};
|
||||
|
|
|
@ -69,7 +69,11 @@ WindowManager::WindowManager()
|
|||
auto settings = getSettings();
|
||||
|
||||
this->wordFlagsListener_.addSetting(settings->showTimestamps);
|
||||
this->wordFlagsListener_.addSetting(settings->showBadges);
|
||||
this->wordFlagsListener_.addSetting(settings->showBadgesGlobalAuthority);
|
||||
this->wordFlagsListener_.addSetting(settings->showBadgesChannelAuthority);
|
||||
this->wordFlagsListener_.addSetting(settings->showBadgesSubscription);
|
||||
this->wordFlagsListener_.addSetting(settings->showBadgesVanity);
|
||||
this->wordFlagsListener_.addSetting(settings->showBadgesChatterino);
|
||||
this->wordFlagsListener_.addSetting(settings->enableBttvEmotes);
|
||||
this->wordFlagsListener_.addSetting(settings->enableEmojis);
|
||||
this->wordFlagsListener_.addSetting(settings->enableFfzEmotes);
|
||||
|
@ -114,7 +118,15 @@ void WindowManager::updateWordTypeMask()
|
|||
: MEF::BitsStatic);
|
||||
|
||||
// badges
|
||||
flags.set(settings->showBadges ? MEF::Badges : MEF::None);
|
||||
flags.set(settings->showBadgesGlobalAuthority ? MEF::BadgeGlobalAuthority
|
||||
: MEF::None);
|
||||
flags.set(settings->showBadgesChannelAuthority ? MEF::BadgeChannelAuthority
|
||||
: MEF::None);
|
||||
flags.set(settings->showBadgesSubscription ? MEF::BadgeSubscription
|
||||
: MEF::None);
|
||||
flags.set(settings->showBadgesVanity ? MEF::BadgeVanity : MEF::None);
|
||||
flags.set(settings->showBadgesChatterino ? MEF::BadgeChatterino
|
||||
: MEF::None);
|
||||
|
||||
// username
|
||||
flags.set(MEF::Username);
|
||||
|
|
|
@ -61,9 +61,9 @@ protected:
|
|||
|
||||
private:
|
||||
struct Item {
|
||||
NotebookTab *tab;
|
||||
QWidget *page;
|
||||
QWidget *selectedWidget = nullptr;
|
||||
NotebookTab *tab{};
|
||||
QWidget *page{};
|
||||
QWidget *selectedWidget{};
|
||||
};
|
||||
|
||||
bool containsPage(QWidget *page);
|
||||
|
|
|
@ -55,6 +55,11 @@ void Scrollbar::unpauseHighlights()
|
|||
this->highlightsPaused_ = false;
|
||||
}
|
||||
|
||||
void Scrollbar::clearHighlights()
|
||||
{
|
||||
this->highlights_.clear();
|
||||
}
|
||||
|
||||
LimitedQueueSnapshot<ScrollbarHighlight> Scrollbar::getHighlightSnapshot()
|
||||
{
|
||||
if (!this->highlightsPaused_) {
|
||||
|
|
|
@ -27,6 +27,7 @@ public:
|
|||
|
||||
void pauseHighlights();
|
||||
void unpauseHighlights();
|
||||
void clearHighlights();
|
||||
|
||||
void scrollToBottom(bool animate = false);
|
||||
bool isAtBottom() const;
|
||||
|
|
|
@ -87,6 +87,11 @@ void TooltipWidget::setText(QString text)
|
|||
this->displayText_->setText(text);
|
||||
}
|
||||
|
||||
void TooltipWidget::setWordWrap(bool wrap)
|
||||
{
|
||||
this->displayText_->setWordWrap(wrap);
|
||||
}
|
||||
|
||||
void TooltipWidget::changeEvent(QEvent *)
|
||||
{
|
||||
// clear parents event
|
||||
|
|
|
@ -19,6 +19,7 @@ public:
|
|||
virtual ~TooltipWidget() override;
|
||||
|
||||
void setText(QString text);
|
||||
void setWordWrap(bool wrap);
|
||||
|
||||
#ifdef USEWINSDK
|
||||
void raise();
|
||||
|
|
|
@ -41,7 +41,8 @@ Window::Window(WindowType type)
|
|||
this->addShortcuts();
|
||||
this->addLayout();
|
||||
|
||||
getApp()->accounts->twitch.currentUserChanged.connect(
|
||||
this->signalHolder_.managedConnect(
|
||||
getApp()->accounts->twitch.currentUserChanged,
|
||||
[this] { this->onAccountSelected(); });
|
||||
this->onAccountSelected();
|
||||
|
||||
|
|
|
@ -29,12 +29,18 @@ namespace {
|
|||
builder->flags.set(MessageFlag::Centered);
|
||||
builder->flags.set(MessageFlag::DisableCompactEmotes);
|
||||
|
||||
if (!map.empty()) {
|
||||
for (const auto &emote : map) {
|
||||
builder
|
||||
.emplace<EmoteElement>(emote.second,
|
||||
MessageElementFlag::AlwaysShow)
|
||||
->setLink(Link(Link::InsertText, emote.first.string));
|
||||
}
|
||||
} else {
|
||||
builder.emplace<TextElement>("no emotes available",
|
||||
MessageElementFlag::Text,
|
||||
MessageColor::System);
|
||||
}
|
||||
|
||||
return builder.release();
|
||||
}
|
||||
|
@ -106,7 +112,7 @@ void EmotePopup::loadChannel(ChannelPtr _channel)
|
|||
{
|
||||
BenchmarkGuard guard("loadChannel");
|
||||
|
||||
this->setWindowTitle("Emotes from " + _channel->getName());
|
||||
this->setWindowTitle("Emotes in #" + _channel->getName());
|
||||
|
||||
auto twitchChannel = dynamic_cast<TwitchChannel *>(_channel.get());
|
||||
if (twitchChannel == nullptr) return;
|
||||
|
@ -139,6 +145,16 @@ void EmotePopup::loadChannel(ChannelPtr _channel)
|
|||
this->globalEmotesView_->setChannel(globalChannel);
|
||||
this->subEmotesView_->setChannel(subChannel);
|
||||
this->channelEmotesView_->setChannel(channelChannel);
|
||||
|
||||
if (subChannel->getMessageSnapshot().getLength() == 0) {
|
||||
MessageBuilder builder;
|
||||
builder->flags.set(MessageFlag::Centered);
|
||||
builder->flags.set(MessageFlag::DisableCompactEmotes);
|
||||
builder.emplace<TextElement>("no subscription emotes available",
|
||||
MessageElementFlag::Text,
|
||||
MessageColor::System);
|
||||
subChannel->addMessage(builder.release());
|
||||
}
|
||||
}
|
||||
|
||||
void EmotePopup::loadEmojis()
|
||||
|
|
|
@ -35,6 +35,7 @@ SettingsDialog::SettingsDialog()
|
|||
this->scaleChangedEvent(this->getScale());
|
||||
|
||||
this->overrideBackgroundColor_ = QColor("#282828");
|
||||
this->themeChangedEvent();
|
||||
}
|
||||
|
||||
void SettingsDialog::initUi()
|
||||
|
@ -187,7 +188,7 @@ void SettingsDialog::themeChangedEvent()
|
|||
BaseWindow::themeChangedEvent();
|
||||
|
||||
QPalette palette;
|
||||
palette.setColor(QPalette::Background, QColor("#444"));
|
||||
palette.setColor(QPalette::Background, QColor("#222"));
|
||||
this->setPalette(palette);
|
||||
}
|
||||
|
||||
|
|
|
@ -62,6 +62,18 @@ bool Button::getEnable() const
|
|||
return this->enabled_;
|
||||
}
|
||||
|
||||
void Button::setEnableMargin(bool value)
|
||||
{
|
||||
this->enableMargin_ = value;
|
||||
|
||||
this->update();
|
||||
}
|
||||
|
||||
bool Button::getEnableMargin() const
|
||||
{
|
||||
return this->enableMargin_;
|
||||
}
|
||||
|
||||
qreal Button::getCurrentDimAmount() const
|
||||
{
|
||||
return this->dimPixmap_ && !this->mouseOver_ ? 0.7 : 1;
|
||||
|
@ -105,7 +117,7 @@ void Button::paintEvent(QPaintEvent *)
|
|||
}
|
||||
|
||||
QRect rect = this->rect();
|
||||
int s = int(6 * this->getScale());
|
||||
int s = this->enableMargin_ ? int(6 * this->getScale()) : 0;
|
||||
|
||||
rect.moveLeft(s);
|
||||
rect.setRight(rect.right() - s - s);
|
||||
|
|
|
@ -41,6 +41,9 @@ public:
|
|||
void setEnable(bool value);
|
||||
bool getEnable() const;
|
||||
|
||||
void setEnableMargin(bool value);
|
||||
bool getEnableMargin() const;
|
||||
|
||||
void setBorderColor(const QColor &color);
|
||||
const QColor &getBorderColor() const;
|
||||
|
||||
|
@ -73,6 +76,7 @@ private:
|
|||
QColor borderColor_{};
|
||||
QPixmap pixmap_{};
|
||||
bool dimPixmap_{true};
|
||||
bool enableMargin_{true};
|
||||
QPoint mousePos_{};
|
||||
double hoverMultiplier_{0.0};
|
||||
QTimer effectTimer_{};
|
||||
|
|
|
@ -305,6 +305,7 @@ void ChannelView::clearMessages()
|
|||
{
|
||||
// Clear all stored messages in this chat widget
|
||||
this->messages.clear();
|
||||
this->scrollBar_->clearHighlights();
|
||||
|
||||
// Layout chat widget messages, and force an update regardless if there are
|
||||
// no messages
|
||||
|
@ -870,11 +871,15 @@ void ChannelView::mouseMoveEvent(QMouseEvent *event)
|
|||
return;
|
||||
}
|
||||
const auto &tooltip = hoverLayoutElement->getCreator().getTooltip();
|
||||
bool isLinkValid = hoverLayoutElement->getLink().isValid();
|
||||
|
||||
if (tooltip.isEmpty()) {
|
||||
tooltipWidget->hide();
|
||||
} else if (isLinkValid && !getSettings()->enableLinkInfoTooltip) {
|
||||
tooltipWidget->hide();
|
||||
} else {
|
||||
tooltipWidget->moveTo(this, event->globalPos());
|
||||
tooltipWidget->setWordWrap(isLinkValid);
|
||||
tooltipWidget->setText(tooltip);
|
||||
tooltipWidget->adjustSize();
|
||||
tooltipWidget->show();
|
||||
|
@ -882,7 +887,7 @@ void ChannelView::mouseMoveEvent(QMouseEvent *event)
|
|||
}
|
||||
|
||||
// check if word has a link
|
||||
if (hoverLayoutElement->getLink().isValid()) {
|
||||
if (isLinkValid) {
|
||||
this->setCursor(Qt::PointingHandCursor);
|
||||
} else {
|
||||
this->setCursor(Qt::ArrowCursor);
|
||||
|
|
|
@ -313,6 +313,8 @@ void NotebookTab::paintEvent(QPaintEvent *)
|
|||
if (this->shouldDrawXButton()) {
|
||||
QRect xRect = this->getXRect();
|
||||
if (!xRect.isNull()) {
|
||||
if (this->selected_) xRect.moveTop(xRect.top() - 1);
|
||||
|
||||
painter.setBrush(QColor("#fff"));
|
||||
|
||||
if (this->mouseOverX_) {
|
||||
|
@ -474,7 +476,7 @@ QRect NotebookTab::getXRect()
|
|||
|
||||
float s = this->getScale();
|
||||
return QRect(this->width() - static_cast<int>(20 * s),
|
||||
static_cast<int>(6 * s), static_cast<int>(16 * s),
|
||||
static_cast<int>(9 * s), static_cast<int>(16 * s),
|
||||
static_cast<int>(16 * s));
|
||||
}
|
||||
|
||||
|
|
|
@ -50,6 +50,12 @@ FeelPage::FeelPage()
|
|||
form->addRow("Links:",
|
||||
this->createCheckBox("Open links only on double click",
|
||||
getSettings()->linksDoubleClickOnly));
|
||||
form->addRow("",
|
||||
this->createCheckBox("Show link info in tooltips",
|
||||
getSettings()->enableLinkInfoTooltip));
|
||||
form->addRow("",
|
||||
this->createCheckBox("Auto unshort links (requires restart)",
|
||||
getSettings()->enableUnshortLinks));
|
||||
}
|
||||
|
||||
layout->addSpacing(16);
|
||||
|
|
|
@ -41,6 +41,8 @@ KeyboardSettingsPage::KeyboardSettingsPage()
|
|||
form->addRow(new QLabel("Ctrl + R"), new QLabel("Change channel"));
|
||||
form->addRow(new QLabel("Ctrl + F"),
|
||||
new QLabel("Search in current channel"));
|
||||
form->addRow(new QLabel("Ctrl + E"),
|
||||
new QLabel("Open Emote menu"));
|
||||
}
|
||||
|
||||
} // namespace chatterino
|
||||
|
|
|
@ -59,6 +59,7 @@ void LookPage::initializeUi()
|
|||
this->addMessageTab(tabs.appendTab(new QVBoxLayout, "Messages"));
|
||||
this->addEmoteTab(tabs.appendTab(new QVBoxLayout, "Emotes"));
|
||||
this->addSplitHeaderTab(tabs.appendTab(new QVBoxLayout, "Split header"));
|
||||
this->addBadgesTab(tabs.appendTab(new QVBoxLayout, "Badges"));
|
||||
|
||||
layout->addStretch(1);
|
||||
|
||||
|
@ -145,10 +146,6 @@ void LookPage::addMessageTab(LayoutCreator<QVBoxLayout> layout)
|
|||
box->addStretch(1);
|
||||
}
|
||||
|
||||
// badges
|
||||
layout.append(
|
||||
this->createCheckBox("Show badges", getSettings()->showBadges));
|
||||
|
||||
// --
|
||||
layout.emplace<Line>(false);
|
||||
|
||||
|
@ -270,16 +267,57 @@ void LookPage::addEmoteTab(LayoutCreator<QVBoxLayout> layout)
|
|||
|
||||
void LookPage::addSplitHeaderTab(LayoutCreator<QVBoxLayout> layout)
|
||||
{
|
||||
layout.append(this->createCheckBox("Show viewer count",
|
||||
getSettings()->showViewerCount));
|
||||
layout.append(this->createCheckBox("Show title", getSettings()->showTitle));
|
||||
layout.append(this->createCheckBox("Show game", getSettings()->showGame));
|
||||
layout.append(
|
||||
this->createCheckBox("Show uptime", getSettings()->showUptime));
|
||||
layout.append(this->createCheckBox("Show viewer count",
|
||||
getSettings()->showViewerCount));
|
||||
layout.append(this->createCheckBox("Show game", getSettings()->showGame));
|
||||
layout.append(this->createCheckBox("Show title", getSettings()->showTitle));
|
||||
|
||||
layout->addStretch(1);
|
||||
}
|
||||
|
||||
void LookPage::addBadgesTab(LayoutCreator<QVBoxLayout> layout)
|
||||
{
|
||||
// layout.append(
|
||||
// this->createCheckBox(("Show all badges"), getSettings()->showBadges));
|
||||
auto fastSelection = layout.emplace<QHBoxLayout>();
|
||||
{
|
||||
auto addAll = fastSelection.emplace<QPushButton>("Enable all");
|
||||
QObject::connect(addAll.getElement(), &QPushButton::clicked, this, [] {
|
||||
getSettings()->showBadgesGlobalAuthority = true;
|
||||
getSettings()->showBadgesChannelAuthority = true;
|
||||
getSettings()->showBadgesSubscription = true;
|
||||
getSettings()->showBadgesVanity = true;
|
||||
getSettings()->showBadgesChatterino = true;
|
||||
});
|
||||
auto removeAll = fastSelection.emplace<QPushButton>("Disable all");
|
||||
QObject::connect(removeAll.getElement(), &QPushButton::clicked, this,
|
||||
[] {
|
||||
getSettings()->showBadgesGlobalAuthority = false;
|
||||
getSettings()->showBadgesChannelAuthority = false;
|
||||
getSettings()->showBadgesSubscription = false;
|
||||
getSettings()->showBadgesVanity = false;
|
||||
getSettings()->showBadgesChatterino = false;
|
||||
});
|
||||
}
|
||||
layout.emplace<Line>(false);
|
||||
layout.append(this->createCheckBox(
|
||||
("Show authorty badges (staff, admin, turbo, etc)"),
|
||||
getSettings()->showBadgesGlobalAuthority));
|
||||
layout.append(
|
||||
this->createCheckBox(("Show channel badges (broadcaster, moderator)"),
|
||||
getSettings()->showBadgesChannelAuthority));
|
||||
layout.append(this->createCheckBox(("Show subscriber badges "),
|
||||
getSettings()->showBadgesSubscription));
|
||||
layout.append(
|
||||
this->createCheckBox(("Show vanity badges (prime, bits, subgifter)"),
|
||||
getSettings()->showBadgesVanity));
|
||||
layout.append(this->createCheckBox(("Show chatterino badges"),
|
||||
getSettings()->showBadgesChatterino));
|
||||
layout->addStretch(1);
|
||||
}
|
||||
|
||||
void LookPage::addLastReadMessageIndicatorPatternSelector(
|
||||
LayoutCreator<QVBoxLayout> layout)
|
||||
{
|
||||
|
|
|
@ -23,6 +23,7 @@ private:
|
|||
void addMessageTab(LayoutCreator<QVBoxLayout> layout);
|
||||
void addEmoteTab(LayoutCreator<QVBoxLayout> layout);
|
||||
void addSplitHeaderTab(LayoutCreator<QVBoxLayout> layout);
|
||||
void addBadgesTab(LayoutCreator<QVBoxLayout> layout);
|
||||
|
||||
void addLastReadMessageIndicatorPatternSelector(
|
||||
LayoutCreator<QVBoxLayout> layout);
|
||||
|
|
|
@ -149,7 +149,7 @@ ModerationPage::ModerationPage()
|
|||
auto modMode = tabs.appendTab(new QVBoxLayout, "Moderation buttons");
|
||||
{
|
||||
// clang-format off
|
||||
auto label = modMode.emplace<QLabel>("Click the moderation mod button (<img width='18' height='18' src=':/images/moderatormode_disabled.png'>) in a channel that you moderate to enable moderator mode.<br>");
|
||||
auto label = modMode.emplace<QLabel>("Click the moderation mod button (<img width='18' height='18' src=':/buttons/modModeDisabled.png'>) in a channel that you moderate to enable moderator mode.<br>");
|
||||
label->setWordWrap(true);
|
||||
label->setStyleSheet("color: #bbb");
|
||||
// clang-format on
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include "SettingsPage.hpp"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QPainter>
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
|
|
|
@ -363,6 +363,12 @@ void Split::handleModifiers(Qt::KeyboardModifiers modifiers)
|
|||
}
|
||||
}
|
||||
|
||||
void Split::setIsTopRightSplit(bool value)
|
||||
{
|
||||
this->isTopRightSplit_ = value;
|
||||
this->header_->setAddButtonVisible(value);
|
||||
}
|
||||
|
||||
/// Slots
|
||||
void Split::addSibling()
|
||||
{
|
||||
|
|
|
@ -65,6 +65,7 @@ public:
|
|||
void layoutMessages();
|
||||
void updateGifEmotes();
|
||||
void updateLastReadMessage();
|
||||
void setIsTopRightSplit(bool value);
|
||||
|
||||
void drag();
|
||||
|
||||
|
@ -106,10 +107,11 @@ private:
|
|||
|
||||
NullablePtr<SelectChannelDialog> selectChannelDialog_;
|
||||
|
||||
bool moderationMode_ = false;
|
||||
bool moderationMode_{};
|
||||
bool isTopRightSplit_{};
|
||||
|
||||
bool isMouseOver_ = false;
|
||||
bool isDragging_ = false;
|
||||
bool isMouseOver_{};
|
||||
bool isDragging_{};
|
||||
|
||||
pajlada::Signals::Connection channelIDChangedConnection_;
|
||||
pajlada::Signals::Connection usermodeChangedConnection_;
|
||||
|
|
|
@ -310,8 +310,33 @@ void SplitContainer::focusSplitRecursive(Node *node, Direction direction)
|
|||
}
|
||||
}
|
||||
|
||||
Split *SplitContainer::getTopRightSplit(Node &node)
|
||||
{
|
||||
switch (node.getType()) {
|
||||
case Node::_Split:
|
||||
return node.getSplit();
|
||||
case Node::VerticalContainer:
|
||||
if (!node.getChildren().empty())
|
||||
return getTopRightSplit(*node.getChildren().front());
|
||||
break;
|
||||
case Node::HorizontalContainer:
|
||||
if (!node.getChildren().empty())
|
||||
return getTopRightSplit(*node.getChildren().back());
|
||||
break;
|
||||
default:;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void SplitContainer::layout()
|
||||
{
|
||||
// update top right split
|
||||
auto topRight = this->getTopRightSplit(this->baseNode_);
|
||||
if (this->topRight_) this->topRight_->setIsTopRightSplit(false);
|
||||
this->topRight_ = topRight;
|
||||
if (topRight) this->topRight_->setIsTopRightSplit(true);
|
||||
|
||||
// layout
|
||||
this->baseNode_.geometry_ = this->rect().adjusted(-1, -1, 0, 0);
|
||||
|
||||
std::vector<DropRect> _dropRects;
|
||||
|
@ -431,7 +456,8 @@ void SplitContainer::paintEvent(QPaintEvent *)
|
|||
|
||||
if (notebook != nullptr) {
|
||||
if (notebook->getPageCount() > 1) {
|
||||
text += "\n\nTip: After adding a split you can hold <Alt> to "
|
||||
text +=
|
||||
"\n\nTip: After adding a split you can hold <Ctrl+Alt> to "
|
||||
"move it or split it "
|
||||
"further.";
|
||||
}
|
||||
|
|
|
@ -219,6 +219,7 @@ private:
|
|||
void addSplit(Split *split);
|
||||
|
||||
void decodeNodeRecusively(QJsonObject &obj, Node *node);
|
||||
Split *getTopRightSplit(Node &node);
|
||||
|
||||
struct DropRegion {
|
||||
QRect rect;
|
||||
|
@ -239,6 +240,7 @@ private:
|
|||
|
||||
Node baseNode_;
|
||||
Split *selected_;
|
||||
Split *topRight_{};
|
||||
|
||||
NotebookTab *tab_;
|
||||
std::vector<Split *> splits_;
|
||||
|
|
|
@ -93,11 +93,11 @@ namespace {
|
|||
title += " (live)";
|
||||
|
||||
// description
|
||||
if (settings.showUptime) title += " - " + s.uptime;
|
||||
if (settings.showViewerCount)
|
||||
title += " - " + QString::number(s.viewerCount);
|
||||
if (settings.showTitle) title += " - " + s.title;
|
||||
if (settings.showGame) title += " - " + s.game;
|
||||
if (settings.showUptime) title += " - " + s.uptime;
|
||||
if (settings.showTitle) title += " - " + s.title;
|
||||
|
||||
return title;
|
||||
}
|
||||
|
@ -138,9 +138,9 @@ SplitHeader::SplitHeader(Split *_split)
|
|||
|
||||
void SplitHeader::initializeLayout()
|
||||
{
|
||||
auto layout = makeLayout<QHBoxLayout>(
|
||||
{// title
|
||||
this->titleLabel = makeWidget<Label>([](auto w) {
|
||||
auto layout = makeLayout<QHBoxLayout>({
|
||||
// title
|
||||
this->titleLabel_ = makeWidget<Label>([](auto w) {
|
||||
w->setSizePolicy(QSizePolicy::MinimumExpanding,
|
||||
QSizePolicy::Preferred);
|
||||
w->setCentered(true);
|
||||
|
@ -164,7 +164,16 @@ void SplitHeader::initializeLayout()
|
|||
}),
|
||||
// dropdown
|
||||
this->dropdownButton_ = makeWidget<Button>(
|
||||
[&](auto w) { w->setMenu(this->createMainMenu()); })});
|
||||
[&](auto w) { w->setMenu(this->createMainMenu()); }),
|
||||
// add split
|
||||
this->addButton_ = makeWidget<Button>([&](auto w) {
|
||||
w->setPixmap(getApp()->resources->buttons.addSplitDark);
|
||||
w->setEnableMargin(false);
|
||||
|
||||
QObject::connect(w, &Button::clicked, this,
|
||||
[this]() { this->split_->addSibling(); });
|
||||
}),
|
||||
});
|
||||
|
||||
layout->setMargin(0);
|
||||
layout->setSpacing(0);
|
||||
|
@ -174,18 +183,16 @@ void SplitHeader::initializeLayout()
|
|||
std::unique_ptr<QMenu> SplitHeader::createMainMenu()
|
||||
{
|
||||
auto menu = std::make_unique<QMenu>();
|
||||
menu->addAction("New split", this->split_, &Split::addSibling,
|
||||
QKeySequence("Ctrl+T"));
|
||||
menu->addAction("Close split", this->split_, &Split::deleteFromContainer,
|
||||
menu->addAction("Close channel", this->split_, &Split::deleteFromContainer,
|
||||
QKeySequence("Ctrl+W"));
|
||||
menu->addAction("Change channel", this->split_, &Split::changeChannel,
|
||||
QKeySequence("Ctrl+R"));
|
||||
menu->addSeparator();
|
||||
menu->addAction("Popup", this->split_, &Split::popup);
|
||||
menu->addAction("Viewer list", this->split_, &Split::showViewerList);
|
||||
menu->addAction("Search", this->split_, &Split::showSearch,
|
||||
QKeySequence("Ctrl+F"));
|
||||
menu->addSeparator();
|
||||
menu->addAction("Popup", this->split_, &Split::popup);
|
||||
#ifdef USEWEBENGINE
|
||||
this->dropdownMenu.addAction("Start watching", this, [this] {
|
||||
ChannelPtr _channel = this->split->getChannel();
|
||||
|
@ -224,7 +231,7 @@ std::unique_ptr<QMenu> SplitHeader::createMainMenu()
|
|||
menu->addSeparator();
|
||||
menu->addAction("Reload channel emotes", this, SLOT(reloadChannelEmotes()));
|
||||
menu->addAction("Reconnect", this, SLOT(reconnect()));
|
||||
// menu->addAction("Clear messages", this->split_, &Split::doClearChat);
|
||||
menu->addAction("Clear messages", this->split_, &Split::clear);
|
||||
// menu->addSeparator();
|
||||
// menu->addAction("Show changelog", this, SLOT(menuShowChangelog()));
|
||||
|
||||
|
@ -351,6 +358,12 @@ void SplitHeader::scaleChangedEvent(float scale)
|
|||
this->setFixedHeight(w);
|
||||
this->dropdownButton_->setFixedWidth(w);
|
||||
this->moderationButton_->setFixedWidth(w);
|
||||
this->addButton_->setFixedWidth(w * 5 / 8);
|
||||
}
|
||||
|
||||
void SplitHeader::setAddButtonVisible(bool value)
|
||||
{
|
||||
this->addButton_->setVisible(value);
|
||||
}
|
||||
|
||||
void SplitHeader::updateChannelText()
|
||||
|
@ -375,7 +388,7 @@ void SplitHeader::updateChannelText()
|
|||
}
|
||||
}
|
||||
|
||||
this->titleLabel->setText(title.isEmpty() ? "<empty>" : title);
|
||||
this->titleLabel_->setText(title.isEmpty() ? "<empty>" : title);
|
||||
}
|
||||
|
||||
void SplitHeader::updateModerationModeIcon()
|
||||
|
@ -475,6 +488,8 @@ void SplitHeader::enterEvent(QEvent *event)
|
|||
tooltip->moveTo(this, this->mapToGlobal(this->rect().bottomLeft()),
|
||||
false);
|
||||
tooltip->setText(this->tooltipText_);
|
||||
tooltip->setWordWrap(false);
|
||||
tooltip->adjustSize();
|
||||
tooltip->show();
|
||||
tooltip->raise();
|
||||
}
|
||||
|
@ -499,7 +514,7 @@ void SplitHeader::themeChangedEvent()
|
|||
} else {
|
||||
palette.setColor(QPalette::Foreground, this->theme->splits.header.text);
|
||||
}
|
||||
this->titleLabel->setPalette(palette);
|
||||
this->titleLabel_->setPalette(palette);
|
||||
|
||||
// --
|
||||
if (this->theme->isLightTheme()) {
|
||||
|
|
|
@ -24,6 +24,8 @@ class SplitHeader final : public BaseWidget, pajlada::Signals::SignalHolder
|
|||
public:
|
||||
explicit SplitHeader(Split *_chatWidget);
|
||||
|
||||
void setAddButtonVisible(bool value);
|
||||
|
||||
void updateChannelText();
|
||||
void updateModerationModeIcon();
|
||||
void updateRoomModes();
|
||||
|
@ -53,9 +55,10 @@ private:
|
|||
|
||||
// ui
|
||||
Button *dropdownButton_{};
|
||||
Label *titleLabel{};
|
||||
Label *titleLabel_{};
|
||||
EffectLabel *modeButton_{};
|
||||
Button *moderationButton_{};
|
||||
Button *addButton_{};
|
||||
|
||||
// states
|
||||
QPoint dragStart_{};
|
||||
|
|
|
@ -84,21 +84,8 @@ void SplitInput::initLayout()
|
|||
}));
|
||||
|
||||
// open emote popup
|
||||
QObject::connect(this->ui_.emoteButton, &EffectLabel::clicked, [this] {
|
||||
if (!this->emotePopup_) {
|
||||
this->emotePopup_ = std::make_unique<EmotePopup>();
|
||||
this->emotePopup_->linkClicked.connect([this](const Link &link) {
|
||||
if (link.type == Link::InsertText) {
|
||||
this->insertText(link.value + " ");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
this->emotePopup_->resize(int(300 * this->emotePopup_->getScale()),
|
||||
int(500 * this->emotePopup_->getScale()));
|
||||
this->emotePopup_->loadChannel(this->split_->getChannel());
|
||||
this->emotePopup_->show();
|
||||
});
|
||||
QObject::connect(this->ui_.emoteButton, &EffectLabel::clicked,
|
||||
[=] { this->openEmotePopup(); });
|
||||
|
||||
// clear channelview selection when selecting in the input
|
||||
QObject::connect(this->ui_.textEdit, &QTextEdit::copyAvailable,
|
||||
|
@ -159,6 +146,23 @@ void SplitInput::updateEmoteButton()
|
|||
this->ui_.emoteButton->setFixedHeight(int(18 * scale));
|
||||
}
|
||||
|
||||
void SplitInput::openEmotePopup()
|
||||
{
|
||||
if (!this->emotePopup_) {
|
||||
this->emotePopup_ = std::make_unique<EmotePopup>();
|
||||
this->emotePopup_->linkClicked.connect([this](const Link &link) {
|
||||
if (link.type == Link::InsertText) {
|
||||
this->insertText(link.value + " ");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
this->emotePopup_->resize(int(300 * this->emotePopup_->getScale()),
|
||||
int(500 * this->emotePopup_->getScale()));
|
||||
this->emotePopup_->loadChannel(this->split_->getChannel());
|
||||
this->emotePopup_->show();
|
||||
}
|
||||
|
||||
void SplitInput::installKeyPressedEvent()
|
||||
{
|
||||
auto app = getApp();
|
||||
|
@ -177,7 +181,8 @@ void SplitInput::installKeyPressedEvent()
|
|||
|
||||
c->sendMessage(sendMessage);
|
||||
// don't add duplicate messages and empty message to message history
|
||||
if ((this->prevMsg_.isEmpty() || !this->prevMsg_.endsWith(message)) &&
|
||||
if ((this->prevMsg_.isEmpty() ||
|
||||
!this->prevMsg_.endsWith(message)) &&
|
||||
!message.trimmed().isEmpty())
|
||||
this->prevMsg_.append(message);
|
||||
|
||||
|
@ -186,7 +191,7 @@ void SplitInput::installKeyPressedEvent()
|
|||
this->currMsg_ = QString();
|
||||
this->ui_.textEdit->setText(QString());
|
||||
this->prevIndex_ = 0;
|
||||
} else if (this->ui_.textEdit->toPlainText() ==
|
||||
} else if (message ==
|
||||
this->prevMsg_.at(this->prevMsg_.size() - 1)) {
|
||||
this->prevMsg_.removeLast();
|
||||
}
|
||||
|
@ -227,6 +232,13 @@ void SplitInput::installKeyPressedEvent()
|
|||
page->selectNextSplit(SplitContainer::Below);
|
||||
}
|
||||
} else {
|
||||
// If user did not write anything before then just do nothing.
|
||||
if (this->prevMsg_.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
bool cursorToEnd = true;
|
||||
QString message = ui_.textEdit->toPlainText();
|
||||
|
||||
if (this->prevIndex_ != (this->prevMsg_.size() - 1) &&
|
||||
this->prevIndex_ != this->prevMsg_.size()) {
|
||||
this->prevIndex_++;
|
||||
|
@ -234,13 +246,28 @@ void SplitInput::installKeyPressedEvent()
|
|||
this->prevMsg_.at(this->prevIndex_));
|
||||
} else {
|
||||
this->prevIndex_ = this->prevMsg_.size();
|
||||
if (message == this->prevMsg_.at(this->prevIndex_ - 1)) {
|
||||
// If user has just come from a message history
|
||||
// Then simply get currMsg_.
|
||||
this->ui_.textEdit->setText(this->currMsg_);
|
||||
} else if (message != this->currMsg_) {
|
||||
// If user are already in current message
|
||||
// And type something new
|
||||
// Then replace currMsg_ with new one.
|
||||
this->currMsg_ = message;
|
||||
}
|
||||
// If user is already in current message
|
||||
// Then don't touch cursos.
|
||||
cursorToEnd =
|
||||
(message == this->prevMsg_.at(this->prevIndex_ - 1));
|
||||
}
|
||||
|
||||
if (cursorToEnd) {
|
||||
QTextCursor cursor = this->ui_.textEdit->textCursor();
|
||||
cursor.movePosition(QTextCursor::End);
|
||||
this->ui_.textEdit->setTextCursor(cursor);
|
||||
}
|
||||
}
|
||||
} else if (event->key() == Qt::Key_Left) {
|
||||
if (event->modifiers() == Qt::AltModifier) {
|
||||
SplitContainer *page = this->split_->getContainer();
|
||||
|
@ -284,6 +311,9 @@ void SplitInput::installKeyPressedEvent()
|
|||
this->split_->copyToClipboard();
|
||||
event->accept();
|
||||
}
|
||||
} else if (event->key() == Qt::Key_E &&
|
||||
event->modifiers() == Qt::ControlModifier) {
|
||||
this->openEmotePopup();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -43,6 +43,7 @@ private:
|
|||
void initLayout();
|
||||
void installKeyPressedEvent();
|
||||
void updateEmoteButton();
|
||||
void openEmotePopup();
|
||||
|
||||
Split *const split_;
|
||||
std::shared_ptr<EmotePopup> emotePopup_;
|
||||
|
|
Loading…
Reference in a new issue