mirror of
https://github.com/Chatterino/chatterino2.git
synced 2024-11-13 19:49:51 +01:00
added colon emote popup for ffz and bttv
This commit is contained in:
parent
6781482485
commit
f7237dccdd
|
@ -239,11 +239,9 @@ SOURCES += \
|
||||||
src/widgets/dialogs/QualityPopup.cpp \
|
src/widgets/dialogs/QualityPopup.cpp \
|
||||||
src/widgets/dialogs/SelectChannelDialog.cpp \
|
src/widgets/dialogs/SelectChannelDialog.cpp \
|
||||||
src/widgets/dialogs/SettingsDialog.cpp \
|
src/widgets/dialogs/SettingsDialog.cpp \
|
||||||
src/widgets/dialogs/switcher/AbstractSwitcherItem.cpp \
|
src/widgets/listview/GenericItemDelegate.cpp \
|
||||||
src/widgets/dialogs/switcher/NewTabItem.cpp \
|
src/widgets/dialogs/switcher/NewTabItem.cpp \
|
||||||
src/widgets/dialogs/switcher/QuickSwitcherModel.cpp \
|
|
||||||
src/widgets/dialogs/switcher/QuickSwitcherPopup.cpp \
|
src/widgets/dialogs/switcher/QuickSwitcherPopup.cpp \
|
||||||
src/widgets/dialogs/switcher/SwitcherItemDelegate.cpp \
|
|
||||||
src/widgets/dialogs/switcher/SwitchSplitItem.cpp \
|
src/widgets/dialogs/switcher/SwitchSplitItem.cpp \
|
||||||
src/widgets/dialogs/TextInputDialog.cpp \
|
src/widgets/dialogs/TextInputDialog.cpp \
|
||||||
src/widgets/dialogs/UpdateDialog.cpp \
|
src/widgets/dialogs/UpdateDialog.cpp \
|
||||||
|
@ -268,6 +266,9 @@ SOURCES += \
|
||||||
src/widgets/Label.cpp \
|
src/widgets/Label.cpp \
|
||||||
src/widgets/Notebook.cpp \
|
src/widgets/Notebook.cpp \
|
||||||
src/widgets/Scrollbar.cpp \
|
src/widgets/Scrollbar.cpp \
|
||||||
|
src/widgets/listview/GenericListItem.cpp \
|
||||||
|
src/widgets/listview/GenericListModel.cpp \
|
||||||
|
src/widgets/listview/GenericListView.cpp \
|
||||||
src/widgets/settingspages/AboutPage.cpp \
|
src/widgets/settingspages/AboutPage.cpp \
|
||||||
src/widgets/settingspages/AccountsPage.cpp \
|
src/widgets/settingspages/AccountsPage.cpp \
|
||||||
src/widgets/settingspages/CommandPage.cpp \
|
src/widgets/settingspages/CommandPage.cpp \
|
||||||
|
@ -280,6 +281,8 @@ SOURCES += \
|
||||||
src/widgets/settingspages/NotificationPage.cpp \
|
src/widgets/settingspages/NotificationPage.cpp \
|
||||||
src/widgets/settingspages/SettingsPage.cpp \
|
src/widgets/settingspages/SettingsPage.cpp \
|
||||||
src/widgets/splits/ClosedSplits.cpp \
|
src/widgets/splits/ClosedSplits.cpp \
|
||||||
|
src/widgets/splits/EmoteInputItem.cpp \
|
||||||
|
src/widgets/splits/EmoteInputPopup.cpp \
|
||||||
src/widgets/splits/Split.cpp \
|
src/widgets/splits/Split.cpp \
|
||||||
src/widgets/splits/SplitContainer.cpp \
|
src/widgets/splits/SplitContainer.cpp \
|
||||||
src/widgets/splits/SplitHeader.cpp \
|
src/widgets/splits/SplitHeader.cpp \
|
||||||
|
@ -474,10 +477,10 @@ HEADERS += \
|
||||||
src/widgets/dialogs/SelectChannelDialog.hpp \
|
src/widgets/dialogs/SelectChannelDialog.hpp \
|
||||||
src/widgets/dialogs/SettingsDialog.hpp \
|
src/widgets/dialogs/SettingsDialog.hpp \
|
||||||
src/widgets/dialogs/switcher/AbstractSwitcherItem.hpp \
|
src/widgets/dialogs/switcher/AbstractSwitcherItem.hpp \
|
||||||
|
src/widgets/listview/GenericItemDelegate.hpp \
|
||||||
src/widgets/dialogs/switcher/NewTabItem.hpp \
|
src/widgets/dialogs/switcher/NewTabItem.hpp \
|
||||||
src/widgets/dialogs/switcher/QuickSwitcherModel.hpp \
|
src/widgets/dialogs/switcher/QuickSwitcherModel.hpp \
|
||||||
src/widgets/dialogs/switcher/QuickSwitcherPopup.hpp \
|
src/widgets/dialogs/switcher/QuickSwitcherPopup.hpp \
|
||||||
src/widgets/dialogs/switcher/SwitcherItemDelegate.hpp \
|
|
||||||
src/widgets/dialogs/switcher/SwitchSplitItem.hpp \
|
src/widgets/dialogs/switcher/SwitchSplitItem.hpp \
|
||||||
src/widgets/dialogs/TextInputDialog.hpp \
|
src/widgets/dialogs/TextInputDialog.hpp \
|
||||||
src/widgets/dialogs/UpdateDialog.hpp \
|
src/widgets/dialogs/UpdateDialog.hpp \
|
||||||
|
@ -504,6 +507,9 @@ HEADERS += \
|
||||||
src/widgets/Label.hpp \
|
src/widgets/Label.hpp \
|
||||||
src/widgets/Notebook.hpp \
|
src/widgets/Notebook.hpp \
|
||||||
src/widgets/Scrollbar.hpp \
|
src/widgets/Scrollbar.hpp \
|
||||||
|
src/widgets/listview/GenericListItem.hpp \
|
||||||
|
src/widgets/listview/GenericListModel.hpp \
|
||||||
|
src/widgets/listview/GenericListView.hpp \
|
||||||
src/widgets/settingspages/AboutPage.hpp \
|
src/widgets/settingspages/AboutPage.hpp \
|
||||||
src/widgets/settingspages/AccountsPage.hpp \
|
src/widgets/settingspages/AccountsPage.hpp \
|
||||||
src/widgets/settingspages/CommandPage.hpp \
|
src/widgets/settingspages/CommandPage.hpp \
|
||||||
|
@ -516,6 +522,8 @@ HEADERS += \
|
||||||
src/widgets/settingspages/NotificationPage.hpp \
|
src/widgets/settingspages/NotificationPage.hpp \
|
||||||
src/widgets/settingspages/SettingsPage.hpp \
|
src/widgets/settingspages/SettingsPage.hpp \
|
||||||
src/widgets/splits/ClosedSplits.hpp \
|
src/widgets/splits/ClosedSplits.hpp \
|
||||||
|
src/widgets/splits/EmoteInputItem.hpp \
|
||||||
|
src/widgets/splits/EmoteInputPopup.hpp \
|
||||||
src/widgets/splits/Split.hpp \
|
src/widgets/splits/Split.hpp \
|
||||||
src/widgets/splits/SplitContainer.hpp \
|
src/widgets/splits/SplitContainer.hpp \
|
||||||
src/widgets/splits/SplitHeader.hpp \
|
src/widgets/splits/SplitHeader.hpp \
|
||||||
|
|
|
@ -22,6 +22,11 @@ public:
|
||||||
this->set(t);
|
this->set(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QObjectRef(const QObjectRef &other)
|
||||||
|
{
|
||||||
|
this->set(other.t_);
|
||||||
|
}
|
||||||
|
|
||||||
~QObjectRef()
|
~QObjectRef()
|
||||||
{
|
{
|
||||||
this->set(nullptr);
|
this->set(nullptr);
|
||||||
|
|
|
@ -11,8 +11,6 @@ public:
|
||||||
explicit BasePopup(FlagsEnum<BaseWindow::Flags> flags_ = None,
|
explicit BasePopup(FlagsEnum<BaseWindow::Flags> flags_ = None,
|
||||||
QWidget *parent = nullptr);
|
QWidget *parent = nullptr);
|
||||||
|
|
||||||
virtual ~BasePopup() = default;
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void keyPressEvent(QKeyEvent *e) override;
|
void keyPressEvent(QKeyEvent *e) override;
|
||||||
};
|
};
|
||||||
|
|
|
@ -57,6 +57,18 @@ BaseWindow::BaseWindow(FlagsEnum<Flags> _flags, QWidget *parent)
|
||||||
this->setWindowFlag(Qt::FramelessWindowHint);
|
this->setWindowFlag(Qt::FramelessWindowHint);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_flags.has(DontFocus))
|
||||||
|
{
|
||||||
|
this->setAttribute(Qt::WA_ShowWithoutActivating);
|
||||||
|
#ifdef Q_OS_LINUX
|
||||||
|
this->setWindowFlags(Qt::ToolTip);
|
||||||
|
#else
|
||||||
|
this->setWindowFlags(Qt::Tool | Qt::FramelessWindowHint |
|
||||||
|
Qt::X11BypassWindowManagerHint |
|
||||||
|
Qt::BypassWindowManagerHint);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
this->init();
|
this->init();
|
||||||
|
|
||||||
getSettings()->uiScale.connect(
|
getSettings()->uiScale.connect(
|
||||||
|
|
|
@ -29,6 +29,7 @@ public:
|
||||||
TopMost = 4,
|
TopMost = 4,
|
||||||
DisableCustomScaling = 8,
|
DisableCustomScaling = 8,
|
||||||
FramelessDraggable = 16,
|
FramelessDraggable = 16,
|
||||||
|
DontFocus = 32,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum ActionOnFocusLoss { Nothing, Delete, Close, Hide };
|
enum ActionOnFocusLoss { Nothing, Delete, Close, Hide };
|
||||||
|
|
|
@ -21,7 +21,7 @@ TooltipWidget *TooltipWidget::instance()
|
||||||
}
|
}
|
||||||
|
|
||||||
TooltipWidget::TooltipWidget(BaseWidget *parent)
|
TooltipWidget::TooltipWidget(BaseWidget *parent)
|
||||||
: BaseWindow(BaseWindow::TopMost, parent)
|
: BaseWindow({BaseWindow::TopMost, BaseWindow::DontFocus}, parent)
|
||||||
, displayImage_(new QLabel())
|
, displayImage_(new QLabel())
|
||||||
, displayText_(new QLabel())
|
, displayText_(new QLabel())
|
||||||
{
|
{
|
||||||
|
@ -31,15 +31,6 @@ TooltipWidget::TooltipWidget(BaseWidget *parent)
|
||||||
this->updateFont();
|
this->updateFont();
|
||||||
this->setStayInScreenRect(true);
|
this->setStayInScreenRect(true);
|
||||||
|
|
||||||
this->setAttribute(Qt::WA_ShowWithoutActivating);
|
|
||||||
#ifdef Q_OS_LINUX
|
|
||||||
this->setWindowFlags(Qt::ToolTip);
|
|
||||||
#else
|
|
||||||
this->setWindowFlags(Qt::Tool | Qt::FramelessWindowHint |
|
|
||||||
Qt::X11BypassWindowManagerHint |
|
|
||||||
Qt::BypassWindowManagerHint);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
displayImage_->hide();
|
displayImage_->hide();
|
||||||
displayImage_->setAlignment(Qt::AlignHCenter);
|
displayImage_->setAlignment(Qt::AlignHCenter);
|
||||||
displayImage_->setStyleSheet("background: transparent");
|
displayImage_->setStyleSheet("background: transparent");
|
||||||
|
|
|
@ -1,20 +0,0 @@
|
||||||
#include "widgets/dialogs/switcher/AbstractSwitcherItem.hpp"
|
|
||||||
|
|
||||||
#include "Application.hpp"
|
|
||||||
|
|
||||||
namespace chatterino {
|
|
||||||
|
|
||||||
const QSize AbstractSwitcherItem::ICON_SIZE(32, 32);
|
|
||||||
|
|
||||||
AbstractSwitcherItem *AbstractSwitcherItem::fromVariant(const QVariant &variant)
|
|
||||||
{
|
|
||||||
// See https://stackoverflow.com/a/44503822 .
|
|
||||||
return static_cast<AbstractSwitcherItem *>(variant.value<void *>());
|
|
||||||
}
|
|
||||||
|
|
||||||
AbstractSwitcherItem::AbstractSwitcherItem(const QIcon &icon)
|
|
||||||
: icon_(icon)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace chatterino
|
|
|
@ -1,45 +1,9 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "widgets/listview/GenericListItem.hpp"
|
||||||
|
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
|
|
||||||
class AbstractSwitcherItem
|
using AbstractSwitcherItem = GenericListItem;
|
||||||
{
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* @brief Attempt to obtain an AbstractSwitcherItem * from the passed QVariant.
|
|
||||||
*
|
|
||||||
* @param variant variant to try to convert to AbstractSwitcherItem *
|
|
||||||
*
|
|
||||||
* @return an AbstractSwitcherItem * if the QVariant could be converted,
|
|
||||||
* or nullptr if the variant did not contain AbstractSwitcherItem *
|
|
||||||
*/
|
|
||||||
static AbstractSwitcherItem *fromVariant(const QVariant &variant);
|
|
||||||
|
|
||||||
virtual ~AbstractSwitcherItem() = default;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Since all switcher items are required to have an icon, we require it
|
|
||||||
* in the base class constructor.
|
|
||||||
*
|
|
||||||
* @param icon icon to be displayed in the switcher list
|
|
||||||
*/
|
|
||||||
AbstractSwitcherItem(const QIcon &icon);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Action to perform when this item is activated. Must be implemented in
|
|
||||||
* subclasses.
|
|
||||||
*/
|
|
||||||
virtual void action() = 0;
|
|
||||||
|
|
||||||
virtual void paint(QPainter *painter, const QRect &rect) const = 0;
|
|
||||||
virtual QSize sizeHint(const QRect &rect) const = 0;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
QIcon icon_;
|
|
||||||
static const QSize ICON_SIZE;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace chatterino
|
} // namespace chatterino
|
||||||
|
|
||||||
// This allows us to store AbstractSwitcherItem * as a QVariant
|
|
||||||
Q_DECLARE_METATYPE(chatterino::AbstractSwitcherItem *);
|
|
||||||
|
|
|
@ -2,51 +2,4 @@
|
||||||
|
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
|
|
||||||
QuickSwitcherModel::QuickSwitcherModel(QWidget *parent)
|
|
||||||
: QAbstractListModel(parent)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
int QuickSwitcherModel::rowCount(const QModelIndex &parent) const
|
|
||||||
{
|
|
||||||
return this->items_.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
QVariant QuickSwitcherModel::data(const QModelIndex &index,
|
|
||||||
int /* role */) const
|
|
||||||
{
|
|
||||||
if (!index.isValid())
|
|
||||||
return QVariant();
|
|
||||||
|
|
||||||
if (index.row() >= this->items_.size())
|
|
||||||
return QVariant();
|
|
||||||
|
|
||||||
auto item = this->items_[index.row()].get();
|
|
||||||
// See https://stackoverflow.com/a/44503822 .
|
|
||||||
return QVariant::fromValue(static_cast<void *>(item));
|
|
||||||
}
|
|
||||||
|
|
||||||
void QuickSwitcherModel::addItem(std::unique_ptr<AbstractSwitcherItem> item)
|
|
||||||
{
|
|
||||||
// {begin,end}InsertRows needs to be called to notify attached views
|
|
||||||
this->beginInsertRows(QModelIndex(), this->items_.size(),
|
|
||||||
this->items_.size());
|
|
||||||
this->items_.push_back(std::move(item));
|
|
||||||
this->endInsertRows();
|
|
||||||
}
|
|
||||||
|
|
||||||
void QuickSwitcherModel::clear()
|
|
||||||
{
|
|
||||||
if (this->items_.empty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
// {begin,end}RemoveRows needs to be called to notify attached views
|
|
||||||
this->beginRemoveRows(QModelIndex(), 0, this->items_.size() - 1);
|
|
||||||
|
|
||||||
// clear
|
|
||||||
this->items_.clear();
|
|
||||||
|
|
||||||
this->endRemoveRows();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace chatterino
|
} // namespace chatterino
|
||||||
|
|
|
@ -1,52 +1,10 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include "widgets/dialogs/switcher/AbstractSwitcherItem.hpp"
|
#include "widgets/listview/GenericListModel.hpp"
|
||||||
|
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
|
|
||||||
class QuickSwitcherModel : public QAbstractListModel
|
using QuickSwitcherModel = GenericListModel;
|
||||||
{
|
|
||||||
public:
|
|
||||||
QuickSwitcherModel(QWidget *parent = nullptr);
|
|
||||||
|
|
||||||
/**
|
}
|
||||||
* @brief Reimplements QAbstractItemModel::rowCount.
|
|
||||||
*
|
|
||||||
* @return number of items currrently present in this model
|
|
||||||
*/
|
|
||||||
int rowCount(const QModelIndex &parent = QModelIndex()) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Reimplements QAbstractItemModel::data. Currently, the role parameter
|
|
||||||
* is not used and an AbstractSwitcherItem * is always returned.
|
|
||||||
*
|
|
||||||
* @param index index of item to fetch data from
|
|
||||||
* @param role (not used)
|
|
||||||
*
|
|
||||||
* @return AbstractSwitcherItem * (wrapped as QVariant) at index
|
|
||||||
*/
|
|
||||||
QVariant data(const QModelIndex &index, int role) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Add an item to this QuickSwitcherModel. It will be displayed in
|
|
||||||
* attached views.
|
|
||||||
*
|
|
||||||
* NOTE: The model will take ownership of the pointer. In particular,
|
|
||||||
* the same item should not be passed to multiple QuickSwitcherModels.
|
|
||||||
*
|
|
||||||
* @param item item to add to the model
|
|
||||||
*/
|
|
||||||
void addItem(std::unique_ptr<AbstractSwitcherItem> item);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Clears this QuickSwitcherModel of all items. This will delete all
|
|
||||||
* AbstractSwitcherItems added after the last invokation of
|
|
||||||
* QuickSwitcherModel::clear (and invalidate their pointers).
|
|
||||||
*/
|
|
||||||
void clear();
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::vector<std::unique_ptr<AbstractSwitcherItem>> items_;
|
|
||||||
};
|
|
||||||
} // namespace chatterino
|
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include "widgets/dialogs/switcher/NewTabItem.hpp"
|
#include "widgets/dialogs/switcher/NewTabItem.hpp"
|
||||||
#include "widgets/dialogs/switcher/SwitchSplitItem.hpp"
|
#include "widgets/dialogs/switcher/SwitchSplitItem.hpp"
|
||||||
#include "widgets/helper/NotebookTab.hpp"
|
#include "widgets/helper/NotebookTab.hpp"
|
||||||
|
#include "widgets/listview/GenericListView.hpp"
|
||||||
|
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
|
|
||||||
|
@ -36,7 +37,6 @@ QuickSwitcherPopup::QuickSwitcherPopup(QWidget *parent)
|
||||||
BaseWindow::Flags::TopMost},
|
BaseWindow::Flags::TopMost},
|
||||||
parent)
|
parent)
|
||||||
, switcherModel_(this)
|
, switcherModel_(this)
|
||||||
, switcherItemDelegate_(this)
|
|
||||||
{
|
{
|
||||||
this->setWindowFlag(Qt::Dialog);
|
this->setWindowFlag(Qt::Dialog);
|
||||||
this->setActionOnFocusLoss(BaseWindow::ActionOnFocusLoss::Delete);
|
this->setActionOnFocusLoss(BaseWindow::ActionOnFocusLoss::Delete);
|
||||||
|
@ -51,10 +51,8 @@ QuickSwitcherPopup::QuickSwitcherPopup(QWidget *parent)
|
||||||
this->size(), geom));
|
this->size(), geom));
|
||||||
|
|
||||||
this->themeChangedEvent();
|
this->themeChangedEvent();
|
||||||
}
|
|
||||||
|
|
||||||
QuickSwitcherPopup::~QuickSwitcherPopup()
|
this->installEventFilter(this->ui_.list);
|
||||||
{
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void QuickSwitcherPopup::initWidgets()
|
void QuickSwitcherPopup::initWidgets()
|
||||||
|
@ -72,24 +70,12 @@ void QuickSwitcherPopup::initWidgets()
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
vbox.emplace<QListView>().assign(&this->ui_.list);
|
auto listView = vbox.emplace<GenericListView>().assign(&this->ui_.list);
|
||||||
this->ui_.list->setSelectionMode(QAbstractItemView::SingleSelection);
|
listView->setModel(&this->switcherModel_);
|
||||||
this->ui_.list->setSelectionBehavior(QAbstractItemView::SelectItems);
|
|
||||||
this->ui_.list->setModel(&this->switcherModel_);
|
|
||||||
this->ui_.list->setItemDelegate(&this->switcherItemDelegate_);
|
|
||||||
|
|
||||||
/*
|
QObject::connect(listView.getElement(),
|
||||||
* I also tried handling key events using the according slots but
|
&GenericListView::closeRequested, this,
|
||||||
* it lead to all kind of problems that did not occur with the
|
[this] { this->close(); });
|
||||||
* eventFilter approach.
|
|
||||||
*/
|
|
||||||
QObject::connect(
|
|
||||||
this->ui_.list, &QListView::clicked, this,
|
|
||||||
[this](const QModelIndex &index) {
|
|
||||||
auto *item = AbstractSwitcherItem::fromVariant(index.data());
|
|
||||||
item->action();
|
|
||||||
this->close();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,61 +131,6 @@ void QuickSwitcherPopup::updateSuggestions(const QString &text)
|
||||||
QTimer::singleShot(0, [this] { this->adjustSize(); });
|
QTimer::singleShot(0, [this] { this->adjustSize(); });
|
||||||
}
|
}
|
||||||
|
|
||||||
bool QuickSwitcherPopup::eventFilter(QObject *watched, QEvent *event)
|
|
||||||
{
|
|
||||||
if (event->type() == QEvent::KeyPress)
|
|
||||||
{
|
|
||||||
auto *keyEvent = static_cast<QKeyEvent *>(event);
|
|
||||||
int key = keyEvent->key();
|
|
||||||
|
|
||||||
const QModelIndex &curIdx = this->ui_.list->currentIndex();
|
|
||||||
const int curRow = curIdx.row();
|
|
||||||
const int count = this->switcherModel_.rowCount(curIdx);
|
|
||||||
|
|
||||||
if (key == Qt::Key_Down || key == Qt::Key_Tab)
|
|
||||||
{
|
|
||||||
if (count <= 0)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
const int newRow = (curRow + 1) % count;
|
|
||||||
|
|
||||||
this->ui_.list->setCurrentIndex(curIdx.siblingAtRow(newRow));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else if (key == Qt::Key_Up || key == Qt::Key_Backtab)
|
|
||||||
{
|
|
||||||
if (count <= 0)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
int newRow = curRow - 1;
|
|
||||||
if (newRow < 0)
|
|
||||||
newRow += count;
|
|
||||||
|
|
||||||
this->ui_.list->setCurrentIndex(curIdx.siblingAtRow(newRow));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else if (key == Qt::Key_Enter || key == Qt::Key_Return)
|
|
||||||
{
|
|
||||||
if (count <= 0)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
const auto index = this->ui_.list->currentIndex();
|
|
||||||
auto *item = AbstractSwitcherItem::fromVariant(index.data());
|
|
||||||
|
|
||||||
item->action();
|
|
||||||
|
|
||||||
this->close();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void QuickSwitcherPopup::themeChangedEvent()
|
void QuickSwitcherPopup::themeChangedEvent()
|
||||||
{
|
{
|
||||||
BasePopup::themeChangedEvent();
|
BasePopup::themeChangedEvent();
|
||||||
|
@ -220,7 +151,7 @@ void QuickSwitcherPopup::themeChangedEvent()
|
||||||
.arg(selCol);
|
.arg(selCol);
|
||||||
|
|
||||||
this->ui_.searchEdit->setStyleSheet(this->theme->splits.input.styleSheet);
|
this->ui_.searchEdit->setStyleSheet(this->theme->splits.input.styleSheet);
|
||||||
this->ui_.list->setStyleSheet(listStyle);
|
this->ui_.list->refreshTheme(*this->theme);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace chatterino
|
} // namespace chatterino
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
#include "common/Channel.hpp"
|
#include "common/Channel.hpp"
|
||||||
#include "widgets/BasePopup.hpp"
|
#include "widgets/BasePopup.hpp"
|
||||||
#include "widgets/dialogs/switcher/QuickSwitcherModel.hpp"
|
#include "widgets/dialogs/switcher/QuickSwitcherModel.hpp"
|
||||||
#include "widgets/dialogs/switcher/SwitcherItemDelegate.hpp"
|
|
||||||
#include "widgets/splits/Split.hpp"
|
#include "widgets/splits/Split.hpp"
|
||||||
#include "widgets/splits/SplitContainer.hpp"
|
#include "widgets/splits/SplitContainer.hpp"
|
||||||
|
|
||||||
|
@ -11,6 +10,8 @@
|
||||||
|
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
|
|
||||||
|
class GenericListView;
|
||||||
|
|
||||||
class QuickSwitcherPopup : public BasePopup
|
class QuickSwitcherPopup : public BasePopup
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -22,10 +23,7 @@ public:
|
||||||
*/
|
*/
|
||||||
explicit QuickSwitcherPopup(QWidget *parent = nullptr);
|
explicit QuickSwitcherPopup(QWidget *parent = nullptr);
|
||||||
|
|
||||||
~QuickSwitcherPopup();
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual bool eventFilter(QObject *watched, QEvent *event) override;
|
|
||||||
virtual void themeChangedEvent() override;
|
virtual void themeChangedEvent() override;
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
|
@ -36,11 +34,10 @@ private:
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
QLineEdit *searchEdit{};
|
QLineEdit *searchEdit{};
|
||||||
QListView *list{};
|
GenericListView *list{};
|
||||||
} ui_;
|
} ui_;
|
||||||
|
|
||||||
QuickSwitcherModel switcherModel_;
|
QuickSwitcherModel switcherModel_;
|
||||||
SwitcherItemDelegate switcherItemDelegate_;
|
|
||||||
|
|
||||||
void initWidgets();
|
void initWidgets();
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#include "widgets/dialogs/switcher/SwitcherItemDelegate.hpp"
|
#include "widgets/dialogs/switcher/GenericItemDelegate.hpp"
|
||||||
|
|
||||||
#include "widgets/dialogs/switcher/AbstractSwitcherItem.hpp"
|
#include "widgets/dialogs/switcher/AbstractSwitcherItem.hpp"
|
||||||
|
|
22
src/widgets/listview/GenericListItem.cpp
Normal file
22
src/widgets/listview/GenericListItem.cpp
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
#include "GenericListItem.hpp"
|
||||||
|
|
||||||
|
namespace chatterino {
|
||||||
|
|
||||||
|
const QSize GenericListItem::ICON_SIZE(32, 32);
|
||||||
|
|
||||||
|
GenericListItem *GenericListItem::fromVariant(const QVariant &variant)
|
||||||
|
{
|
||||||
|
// See https://stackoverflow.com/a/44503822 .
|
||||||
|
return static_cast<GenericListItem *>(variant.value<void *>());
|
||||||
|
}
|
||||||
|
|
||||||
|
GenericListItem::GenericListItem()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
GenericListItem::GenericListItem(const QIcon &icon)
|
||||||
|
: icon_(icon)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace chatterino
|
44
src/widgets/listview/GenericListItem.hpp
Normal file
44
src/widgets/listview/GenericListItem.hpp
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace chatterino {
|
||||||
|
|
||||||
|
class GenericListItem
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Attempt to obtain an GenericListItem * from the passed QVariant.
|
||||||
|
*
|
||||||
|
* @param variant variant to try to convert to GenericListItem *
|
||||||
|
*
|
||||||
|
* @return an GenericListItem * if the QVariant could be converted,
|
||||||
|
* or nullptr if the variant did not contain GenericListItem *
|
||||||
|
*/
|
||||||
|
static GenericListItem *fromVariant(const QVariant &variant);
|
||||||
|
|
||||||
|
virtual ~GenericListItem() = default;
|
||||||
|
|
||||||
|
GenericListItem();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param icon icon to be displayed in the switcher list
|
||||||
|
*/
|
||||||
|
GenericListItem(const QIcon &icon);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Action to perform when this item is activated. Must be implemented in
|
||||||
|
* subclasses.
|
||||||
|
*/
|
||||||
|
virtual void action() = 0;
|
||||||
|
|
||||||
|
virtual void paint(QPainter *painter, const QRect &rect) const = 0;
|
||||||
|
virtual QSize sizeHint(const QRect &rect) const = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
QIcon icon_;
|
||||||
|
static const QSize ICON_SIZE;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace chatterino
|
||||||
|
|
||||||
|
// This allows us to store GenericListItem * as a QVariant
|
||||||
|
Q_DECLARE_METATYPE(chatterino::GenericListItem *);
|
51
src/widgets/listview/GenericListModel.cpp
Normal file
51
src/widgets/listview/GenericListModel.cpp
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
#include "GenericListModel.hpp"
|
||||||
|
|
||||||
|
namespace chatterino {
|
||||||
|
|
||||||
|
GenericListModel::GenericListModel(QWidget *parent)
|
||||||
|
: QAbstractListModel(parent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
int GenericListModel::rowCount(const QModelIndex &parent) const
|
||||||
|
{
|
||||||
|
return this->items_.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant GenericListModel::data(const QModelIndex &index, int /* role */) const
|
||||||
|
{
|
||||||
|
if (!index.isValid())
|
||||||
|
return QVariant();
|
||||||
|
|
||||||
|
if (index.row() >= this->items_.size())
|
||||||
|
return QVariant();
|
||||||
|
|
||||||
|
auto item = this->items_[index.row()].get();
|
||||||
|
// See https://stackoverflow.com/a/44503822 .
|
||||||
|
return QVariant::fromValue(static_cast<void *>(item));
|
||||||
|
}
|
||||||
|
|
||||||
|
void GenericListModel::addItem(std::unique_ptr<GenericListItem> item)
|
||||||
|
{
|
||||||
|
// {begin,end}InsertRows needs to be called to notify attached views
|
||||||
|
this->beginInsertRows(QModelIndex(), this->items_.size(),
|
||||||
|
this->items_.size());
|
||||||
|
this->items_.push_back(std::move(item));
|
||||||
|
this->endInsertRows();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GenericListModel::clear()
|
||||||
|
{
|
||||||
|
if (this->items_.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// {begin,end}RemoveRows needs to be called to notify attached views
|
||||||
|
this->beginRemoveRows(QModelIndex(), 0, this->items_.size() - 1);
|
||||||
|
|
||||||
|
// clear
|
||||||
|
this->items_.clear();
|
||||||
|
|
||||||
|
this->endRemoveRows();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace chatterino
|
49
src/widgets/listview/GenericListModel.hpp
Normal file
49
src/widgets/listview/GenericListModel.hpp
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
#include "widgets/listview/GenericListItem.hpp"
|
||||||
|
|
||||||
|
namespace chatterino {
|
||||||
|
|
||||||
|
class GenericListModel : public QAbstractListModel
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
GenericListModel(QWidget *parent = nullptr);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Reimplements QAbstractItemModel::rowCount.
|
||||||
|
*
|
||||||
|
* @return number of items currrently present in this model
|
||||||
|
*/
|
||||||
|
int rowCount(const QModelIndex &parent = QModelIndex()) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Reimplements QAbstractItemModel::data. Currently, the role parameter
|
||||||
|
* is not used and an GenericListItem * is always returned.
|
||||||
|
*
|
||||||
|
* @param index index of item to fetch data from
|
||||||
|
* @param role (not used)
|
||||||
|
*
|
||||||
|
* @return GenericListItem * (wrapped as QVariant) at index
|
||||||
|
*/
|
||||||
|
QVariant data(const QModelIndex &index, int role) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Add an item to this QuickSwitcherModel. It will be displayed in
|
||||||
|
* attached views.
|
||||||
|
*
|
||||||
|
* NOTE: The model will take ownership of the pointer. In particular,
|
||||||
|
* the same item should not be passed to multiple QuickSwitcherModels.
|
||||||
|
*
|
||||||
|
* @param item item to add to the model
|
||||||
|
*/
|
||||||
|
void addItem(std::unique_ptr<GenericListItem> item);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Clears this QuickSwitcherModel of all items. This will delete all
|
||||||
|
* GenericListItems added after the last invokation of
|
||||||
|
* QuickSwitcherModel::clear (and invalidate their pointers).
|
||||||
|
*/
|
||||||
|
void clear();
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<std::unique_ptr<GenericListItem>> items_;
|
||||||
|
};
|
||||||
|
} // namespace chatterino
|
114
src/widgets/listview/GenericListView.cpp
Normal file
114
src/widgets/listview/GenericListView.cpp
Normal file
|
@ -0,0 +1,114 @@
|
||||||
|
#include "GenericListView.hpp"
|
||||||
|
#include "singletons/Theme.hpp"
|
||||||
|
#include "widgets/listview/GenericListModel.hpp"
|
||||||
|
|
||||||
|
namespace chatterino {
|
||||||
|
|
||||||
|
GenericListView::GenericListView()
|
||||||
|
: itemDelegate_(this)
|
||||||
|
{
|
||||||
|
this->setSelectionMode(QAbstractItemView::SingleSelection);
|
||||||
|
this->setSelectionBehavior(QAbstractItemView::SelectItems);
|
||||||
|
this->setItemDelegate(&this->itemDelegate_);
|
||||||
|
|
||||||
|
QObject::connect(
|
||||||
|
this, &QListView::clicked, this, [this](const QModelIndex &index) {
|
||||||
|
auto *item = GenericListItem::fromVariant(index.data());
|
||||||
|
item->action();
|
||||||
|
|
||||||
|
emit this->closeRequested();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void GenericListView::setModel(QAbstractItemModel *model)
|
||||||
|
{
|
||||||
|
auto casted = dynamic_cast<GenericListModel *>(model);
|
||||||
|
assert(casted);
|
||||||
|
this->setModel(casted);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GenericListView::setModel(GenericListModel *model)
|
||||||
|
{
|
||||||
|
this->model_ = model;
|
||||||
|
QListView::setModel(model);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GenericListView::eventFilter(QObject * /*watched*/, QEvent *event)
|
||||||
|
{
|
||||||
|
if (!this->model_)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (event->type() == QEvent::KeyPress)
|
||||||
|
{
|
||||||
|
auto *keyEvent = static_cast<QKeyEvent *>(event);
|
||||||
|
int key = keyEvent->key();
|
||||||
|
|
||||||
|
const QModelIndex &curIdx = this->currentIndex();
|
||||||
|
const int curRow = curIdx.row();
|
||||||
|
const int count = this->model_->rowCount(curIdx);
|
||||||
|
|
||||||
|
if (key == Qt::Key_Down || key == Qt::Key_Tab)
|
||||||
|
{
|
||||||
|
if (count <= 0)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
const int newRow = (curRow + 1) % count;
|
||||||
|
|
||||||
|
this->setCurrentIndex(curIdx.siblingAtRow(newRow));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (key == Qt::Key_Up || key == Qt::Key_Backtab)
|
||||||
|
{
|
||||||
|
if (count <= 0)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
int newRow = curRow - 1;
|
||||||
|
if (newRow < 0)
|
||||||
|
newRow += count;
|
||||||
|
|
||||||
|
this->setCurrentIndex(curIdx.siblingAtRow(newRow));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (key == Qt::Key_Enter || key == Qt::Key_Return)
|
||||||
|
{
|
||||||
|
if (count <= 0)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
const auto index = this->currentIndex();
|
||||||
|
auto *item = GenericListItem::fromVariant(index.data());
|
||||||
|
|
||||||
|
item->action();
|
||||||
|
|
||||||
|
emit this->closeRequested();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GenericListView::refreshTheme(const Theme &theme)
|
||||||
|
{
|
||||||
|
const QString textCol = theme.window.text.name();
|
||||||
|
const QString bgCol = theme.window.background.name();
|
||||||
|
|
||||||
|
const QString selCol =
|
||||||
|
(theme.isLightTheme()
|
||||||
|
? "#68B1FF" // Copied from Theme::splits.input.styleSheet
|
||||||
|
: theme.tabs.selected.backgrounds.regular.color().name());
|
||||||
|
|
||||||
|
const QString listStyle =
|
||||||
|
QString(
|
||||||
|
"color: %1; background-color: %2; selection-background-color: %3")
|
||||||
|
.arg(textCol)
|
||||||
|
.arg(bgCol)
|
||||||
|
.arg(selCol);
|
||||||
|
|
||||||
|
this->setStyleSheet(listStyle);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace chatterino
|
32
src/widgets/listview/GenericListView.hpp
Normal file
32
src/widgets/listview/GenericListView.hpp
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QListView>
|
||||||
|
#include "widgets/listview/GenericItemDelegate.hpp"
|
||||||
|
#include "widgets/listview/GenericListItem.hpp"
|
||||||
|
|
||||||
|
namespace chatterino {
|
||||||
|
|
||||||
|
class GenericListModel;
|
||||||
|
class Theme;
|
||||||
|
|
||||||
|
class GenericListView : public QListView
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
GenericListView();
|
||||||
|
|
||||||
|
virtual void setModel(QAbstractItemModel *model) override;
|
||||||
|
void setModel(GenericListModel *);
|
||||||
|
bool eventFilter(QObject *watched, QEvent *event) override;
|
||||||
|
|
||||||
|
GenericListModel *model_{};
|
||||||
|
SwitcherItemDelegate itemDelegate_;
|
||||||
|
|
||||||
|
void refreshTheme(const Theme &theme);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void closeRequested();
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace chatterino
|
46
src/widgets/splits/EmoteInputItem.cpp
Normal file
46
src/widgets/splits/EmoteInputItem.cpp
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
#include "EmoteInputItem.hpp"
|
||||||
|
|
||||||
|
namespace chatterino {
|
||||||
|
|
||||||
|
EmoteInputItem::EmoteInputItem(const EmotePtr &emote, const QString &text,
|
||||||
|
ActionCallback action)
|
||||||
|
: emote_(emote)
|
||||||
|
, text_(text)
|
||||||
|
, action_(action)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmoteInputItem::action()
|
||||||
|
{
|
||||||
|
if (this->action_ && this->emote_)
|
||||||
|
this->action_(this->emote_->name.string);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmoteInputItem::paint(QPainter *painter, const QRect &rect) const
|
||||||
|
{
|
||||||
|
if (this->emote_)
|
||||||
|
{
|
||||||
|
if (auto image = this->emote_->images.getImage(4))
|
||||||
|
{
|
||||||
|
if (auto pixmap = image->pixmapOrLoad())
|
||||||
|
{
|
||||||
|
painter->drawPixmap(QRect(rect.x(), rect.y(), ICON_SIZE.width(),
|
||||||
|
ICON_SIZE.height()),
|
||||||
|
*pixmap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QRect iconRect(rect.topLeft(), ICON_SIZE);
|
||||||
|
QRect textRect =
|
||||||
|
QRect(iconRect.topRight(),
|
||||||
|
QSize(rect.width() - iconRect.width(), iconRect.height()));
|
||||||
|
painter->drawText(textRect, Qt::AlignLeft | Qt::AlignVCenter, this->text_);
|
||||||
|
}
|
||||||
|
|
||||||
|
QSize EmoteInputItem::sizeHint(const QRect &rect) const
|
||||||
|
{
|
||||||
|
return QSize(rect.width(), ICON_SIZE.height());
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace chatterino
|
29
src/widgets/splits/EmoteInputItem.hpp
Normal file
29
src/widgets/splits/EmoteInputItem.hpp
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include "messages/Emote.hpp"
|
||||||
|
#include "widgets/listview/GenericListItem.hpp"
|
||||||
|
|
||||||
|
namespace chatterino {
|
||||||
|
|
||||||
|
class EmoteInputItem : public GenericListItem
|
||||||
|
{
|
||||||
|
using ActionCallback = std::function<void(const QString &)>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
EmoteInputItem(const EmotePtr &emote, const QString &text,
|
||||||
|
ActionCallback action);
|
||||||
|
|
||||||
|
// GenericListItem interface
|
||||||
|
public:
|
||||||
|
virtual void action() override;
|
||||||
|
virtual void paint(QPainter *painter, const QRect &rect) const override;
|
||||||
|
virtual QSize sizeHint(const QRect &rect) const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
EmotePtr emote_;
|
||||||
|
QString text_;
|
||||||
|
ActionCallback action_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace chatterino
|
106
src/widgets/splits/EmoteInputPopup.cpp
Normal file
106
src/widgets/splits/EmoteInputPopup.cpp
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
#include "EmoteInputPopup.hpp"
|
||||||
|
|
||||||
|
#include "Application.hpp"
|
||||||
|
#include "messages/Emote.hpp"
|
||||||
|
#include "providers/bttv/BttvEmotes.hpp"
|
||||||
|
#include "providers/ffz/FfzEmotes.hpp"
|
||||||
|
#include "providers/twitch/TwitchChannel.hpp"
|
||||||
|
#include "singletons/Emotes.hpp"
|
||||||
|
#include "util/LayoutCreator.hpp"
|
||||||
|
#include "widgets/listview/GenericListView.hpp"
|
||||||
|
#include "widgets/splits/EmoteInputItem.hpp"
|
||||||
|
|
||||||
|
namespace chatterino {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
struct _Emote {
|
||||||
|
EmotePtr emote;
|
||||||
|
QString providerName;
|
||||||
|
};
|
||||||
|
|
||||||
|
void addEmotes(std::vector<_Emote> &out, const EmoteMap &map,
|
||||||
|
const QString &text, const QString &providerName)
|
||||||
|
{
|
||||||
|
for (auto &&emote : map)
|
||||||
|
if (emote.first.string.contains(text, Qt::CaseInsensitive))
|
||||||
|
out.push_back({emote.second, providerName});
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
EmoteInputPopup::EmoteInputPopup(QWidget *parent)
|
||||||
|
: BasePopup({BasePopup::EnableCustomFrame, BasePopup::Frameless,
|
||||||
|
BasePopup::DontFocus},
|
||||||
|
parent)
|
||||||
|
, model_(this)
|
||||||
|
{
|
||||||
|
this->initLayout();
|
||||||
|
|
||||||
|
// this->connections_.addConnection(
|
||||||
|
// getApp()->emotes->gifTimer.signal.connect([this] {
|
||||||
|
// if (this->isVisible())
|
||||||
|
// {
|
||||||
|
// // redraw listview somehow
|
||||||
|
// }
|
||||||
|
// }));
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmoteInputPopup::initLayout()
|
||||||
|
{
|
||||||
|
LayoutCreator creator = {this};
|
||||||
|
|
||||||
|
auto listView =
|
||||||
|
creator.emplace<GenericListView>().assign(&this->ui_.listView);
|
||||||
|
|
||||||
|
listView->setModel(&this->model_);
|
||||||
|
QObject::connect(listView.getElement(), &GenericListView::closeRequested,
|
||||||
|
this, [this] { this->close(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmoteInputPopup::updateEmotes(const QString &text, ChannelPtr channel)
|
||||||
|
{
|
||||||
|
std::vector<_Emote> emotes;
|
||||||
|
|
||||||
|
if (auto tc = dynamic_cast<TwitchChannel *>(channel.get()))
|
||||||
|
{
|
||||||
|
// TODO extract "Channel BetterTTV" text into a #define.
|
||||||
|
if (auto bttv = tc->bttvEmotes())
|
||||||
|
addEmotes(emotes, *bttv, text, "Channel BetterTTV");
|
||||||
|
if (auto ffz = tc->ffzEmotes())
|
||||||
|
addEmotes(emotes, *ffz, text, "Channel FrankerFaceZ");
|
||||||
|
|
||||||
|
if (auto bttvG = tc->globalBttv().emotes())
|
||||||
|
addEmotes(emotes, *bttvG, text, "Global BetterTTV");
|
||||||
|
if (auto ffzG = tc->globalFfz().emotes())
|
||||||
|
addEmotes(emotes, *ffzG, text, "Global FrankerFaceZ");
|
||||||
|
}
|
||||||
|
|
||||||
|
this->model_.clear();
|
||||||
|
|
||||||
|
int count = 0;
|
||||||
|
for (auto &&emote : emotes)
|
||||||
|
{
|
||||||
|
this->model_.addItem(std::make_unique<EmoteInputItem>(
|
||||||
|
emote.emote, emote.emote->name.string + " - " + emote.providerName,
|
||||||
|
this->callback_));
|
||||||
|
|
||||||
|
if (count++ == maxLineCount)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!emotes.empty())
|
||||||
|
{
|
||||||
|
this->ui_.listView->setCurrentIndex(this->model_.index(0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EmoteInputPopup::eventFilter(QObject *watched, QEvent *event)
|
||||||
|
{
|
||||||
|
return this->ui_.listView->eventFilter(watched, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmoteInputPopup::setInputAction(ActionCallback callback)
|
||||||
|
{
|
||||||
|
this->callback_ = std::move(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace chatterino
|
38
src/widgets/splits/EmoteInputPopup.hpp
Normal file
38
src/widgets/splits/EmoteInputPopup.hpp
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include "common/Channel.hpp"
|
||||||
|
#include "widgets/BasePopup.hpp"
|
||||||
|
#include "widgets/listview/GenericListModel.hpp"
|
||||||
|
|
||||||
|
namespace chatterino {
|
||||||
|
|
||||||
|
class GenericListView;
|
||||||
|
|
||||||
|
class EmoteInputPopup : public BasePopup
|
||||||
|
{
|
||||||
|
using ActionCallback = std::function<void(const QString &)>;
|
||||||
|
|
||||||
|
constexpr static int maxLineCount = 10;
|
||||||
|
|
||||||
|
public:
|
||||||
|
EmoteInputPopup(QWidget *parent = nullptr);
|
||||||
|
|
||||||
|
void updateEmotes(const QString &text, ChannelPtr channel);
|
||||||
|
virtual bool eventFilter(QObject *, QEvent *event) override;
|
||||||
|
|
||||||
|
void setInputAction(ActionCallback callback);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void initLayout();
|
||||||
|
|
||||||
|
struct {
|
||||||
|
GenericListView *listView;
|
||||||
|
} ui_;
|
||||||
|
|
||||||
|
GenericListModel model_;
|
||||||
|
ActionCallback callback_;
|
||||||
|
// pajlada::Signals::SignalHolder connections_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace chatterino
|
|
@ -7,6 +7,7 @@
|
||||||
#include "providers/twitch/TwitchIrcServer.hpp"
|
#include "providers/twitch/TwitchIrcServer.hpp"
|
||||||
#include "singletons/Settings.hpp"
|
#include "singletons/Settings.hpp"
|
||||||
#include "singletons/Theme.hpp"
|
#include "singletons/Theme.hpp"
|
||||||
|
#include "util/Clamp.hpp"
|
||||||
#include "util/LayoutCreator.hpp"
|
#include "util/LayoutCreator.hpp"
|
||||||
#include "widgets/Notebook.hpp"
|
#include "widgets/Notebook.hpp"
|
||||||
#include "widgets/Scrollbar.hpp"
|
#include "widgets/Scrollbar.hpp"
|
||||||
|
@ -14,6 +15,7 @@
|
||||||
#include "widgets/helper/ChannelView.hpp"
|
#include "widgets/helper/ChannelView.hpp"
|
||||||
#include "widgets/helper/EffectLabel.hpp"
|
#include "widgets/helper/EffectLabel.hpp"
|
||||||
#include "widgets/helper/ResizingTextEdit.hpp"
|
#include "widgets/helper/ResizingTextEdit.hpp"
|
||||||
|
#include "widgets/splits/EmoteInputPopup.hpp"
|
||||||
#include "widgets/splits/Split.hpp"
|
#include "widgets/splits/Split.hpp"
|
||||||
#include "widgets/splits/SplitContainer.hpp"
|
#include "widgets/splits/SplitContainer.hpp"
|
||||||
#include "widgets/splits/SplitInput.hpp"
|
#include "widgets/splits/SplitInput.hpp"
|
||||||
|
@ -41,6 +43,7 @@ SplitInput::SplitInput(Split *_chatWidget)
|
||||||
|
|
||||||
// misc
|
// misc
|
||||||
this->installKeyPressedEvent();
|
this->installKeyPressedEvent();
|
||||||
|
this->ui_.textEdit->focusLost.connect([this] { this->hideColonMenu(); });
|
||||||
this->scaleChangedEvent(this->scale());
|
this->scaleChangedEvent(this->scale());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,6 +81,8 @@ void SplitInput::initLayout()
|
||||||
// set edit font
|
// set edit font
|
||||||
this->ui_.textEdit->setFont(
|
this->ui_.textEdit->setFont(
|
||||||
app->fonts->getFont(FontStyle::ChatMedium, this->scale()));
|
app->fonts->getFont(FontStyle::ChatMedium, this->scale()));
|
||||||
|
QObject::connect(this->ui_.textEdit, &QTextEdit::cursorPositionChanged,
|
||||||
|
this, &SplitInput::onCursorPositionChanged);
|
||||||
|
|
||||||
this->managedConnections_.push_back(app->fonts->fontChanged.connect([=]() {
|
this->managedConnections_.push_back(app->fonts->fontChanged.connect([=]() {
|
||||||
this->ui_.textEdit->setFont(
|
this->ui_.textEdit->setFont(
|
||||||
|
@ -187,6 +192,18 @@ void SplitInput::installKeyPressedEvent()
|
||||||
auto app = getApp();
|
auto app = getApp();
|
||||||
|
|
||||||
this->ui_.textEdit->keyPressed.connect([this, app](QKeyEvent *event) {
|
this->ui_.textEdit->keyPressed.connect([this, app](QKeyEvent *event) {
|
||||||
|
if (auto popup = this->emoteInputPopup_.get())
|
||||||
|
{
|
||||||
|
if (popup->isVisible())
|
||||||
|
{
|
||||||
|
if (popup->eventFilter(nullptr, event))
|
||||||
|
{
|
||||||
|
event->accept();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return)
|
if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return)
|
||||||
{
|
{
|
||||||
auto c = this->split_->getChannel();
|
auto c = this->split_->getChannel();
|
||||||
|
@ -435,6 +452,102 @@ void SplitInput::installKeyPressedEvent()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SplitInput::onCursorPositionChanged()
|
||||||
|
{
|
||||||
|
this->updateColonMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SplitInput::updateColonMenu()
|
||||||
|
{
|
||||||
|
if (!dynamic_cast<TwitchChannel *>(this->split_->getChannel().get()))
|
||||||
|
{
|
||||||
|
this->hideColonMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if in :
|
||||||
|
auto &edit = *this->ui_.textEdit;
|
||||||
|
|
||||||
|
auto text = edit.toPlainText();
|
||||||
|
auto position = edit.textCursor().position();
|
||||||
|
|
||||||
|
if (text.length() == 0)
|
||||||
|
{
|
||||||
|
this->hideColonMenu();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = clamp(position, 0, text.length() - 1); i >= 0; i--)
|
||||||
|
{
|
||||||
|
if (text[i] == ' ')
|
||||||
|
{
|
||||||
|
this->hideColonMenu();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (text[i] == ':')
|
||||||
|
{
|
||||||
|
this->showColonMenu(text.mid(i, position - i).mid(1));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this->hideColonMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SplitInput::showColonMenu(const QString &text)
|
||||||
|
{
|
||||||
|
if (!this->emoteInputPopup_.get())
|
||||||
|
{
|
||||||
|
this->emoteInputPopup_ = new EmoteInputPopup(this);
|
||||||
|
this->emoteInputPopup_->setInputAction(
|
||||||
|
[that = QObjectRef(this)](const QString &text) mutable {
|
||||||
|
if (auto this2 = that.get())
|
||||||
|
{
|
||||||
|
this2->insertColonText(text);
|
||||||
|
this2->hideColonMenu();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
auto popup = this->emoteInputPopup_.get();
|
||||||
|
assert(popup);
|
||||||
|
|
||||||
|
popup->updateEmotes(text, this->split_->getChannel());
|
||||||
|
|
||||||
|
auto pos = this->mapToGlobal({0, 0}) - QPoint(0, popup->height()) +
|
||||||
|
QPoint((this->width() - popup->width()) / 2, 0);
|
||||||
|
|
||||||
|
popup->move(pos);
|
||||||
|
popup->show();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SplitInput::hideColonMenu()
|
||||||
|
{
|
||||||
|
if (auto popup = this->emoteInputPopup_.get())
|
||||||
|
popup->hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SplitInput::insertColonText(const QString &input)
|
||||||
|
{
|
||||||
|
auto &edit = *this->ui_.textEdit;
|
||||||
|
|
||||||
|
auto text = edit.toPlainText();
|
||||||
|
auto position = edit.textCursor().position();
|
||||||
|
|
||||||
|
for (int i = clamp(position, 0, text.length() - 1); i >= 0; i--)
|
||||||
|
{
|
||||||
|
if (text[i] == ':')
|
||||||
|
{
|
||||||
|
auto cursor = edit.textCursor();
|
||||||
|
|
||||||
|
edit.setText(text.remove(i, position - i).insert(i, input));
|
||||||
|
|
||||||
|
cursor.setPosition(i + input.size());
|
||||||
|
edit.setTextCursor(cursor);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void SplitInput::clearSelection()
|
void SplitInput::clearSelection()
|
||||||
{
|
{
|
||||||
QTextCursor c = this->ui_.textEdit->textCursor();
|
QTextCursor c = this->ui_.textEdit->textCursor();
|
||||||
|
|
|
@ -16,6 +16,7 @@ namespace chatterino {
|
||||||
|
|
||||||
class Split;
|
class Split;
|
||||||
class EmotePopup;
|
class EmotePopup;
|
||||||
|
class EmoteInputPopup;
|
||||||
class EffectLabel;
|
class EffectLabel;
|
||||||
class ResizingTextEdit;
|
class ResizingTextEdit;
|
||||||
|
|
||||||
|
@ -44,11 +45,17 @@ protected:
|
||||||
private:
|
private:
|
||||||
void initLayout();
|
void initLayout();
|
||||||
void installKeyPressedEvent();
|
void installKeyPressedEvent();
|
||||||
|
void onCursorPositionChanged();
|
||||||
void updateEmoteButton();
|
void updateEmoteButton();
|
||||||
|
void updateColonMenu();
|
||||||
|
void showColonMenu(const QString &text);
|
||||||
|
void hideColonMenu();
|
||||||
|
void insertColonText(const QString &text);
|
||||||
void openEmotePopup();
|
void openEmotePopup();
|
||||||
|
|
||||||
Split *const split_;
|
Split *const split_;
|
||||||
QObjectRef<EmotePopup> emotePopup_;
|
QObjectRef<EmotePopup> emotePopup_;
|
||||||
|
QObjectRef<EmoteInputPopup> emoteInputPopup_;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
ResizingTextEdit *textEdit;
|
ResizingTextEdit *textEdit;
|
||||||
|
|
Loading…
Reference in a new issue