Add feature to duplicate tabs (#5277)

Co-authored-by: Rasmus Karlsson <rasmus.karlsson@pajlada.com>
This commit is contained in:
KleberPF 2024-05-25 09:39:19 -03:00 committed by GitHub
parent c5802a0f49
commit d161036b18
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 158 additions and 3 deletions

View file

@ -22,6 +22,7 @@ Checks: "-*,
-readability-magic-numbers,
-performance-noexcept-move-constructor,
-misc-non-private-member-variables-in-classes,
-misc-no-recursion,
-cppcoreguidelines-non-private-member-variables-in-classes,
-modernize-use-nodiscard,
-modernize-use-trailing-return-type,

View file

@ -7,6 +7,7 @@
- Minor: Add option to customise Moderation buttons with images. (#5369)
- Minor: Colored usernames now update on the fly when changing the "Color @usernames" setting. (#5300)
- Minor: Added `flags.action` filter variable, allowing you to filter on `/me` messages. (#5397)
- Minor: Added the ability to duplicate tabs. (#5277)
- Bugfix: If a network request errors with 200 OK, Qt's error code is now reported instead of the HTTP status. (#5378)
- Bugfix: Fixed a crash that could occur when logging was enabled in IRC servers that were removed. (#5419)
- Dev: Use Qt's high DPI scaling. (#4868, #5400)

View file

@ -4,6 +4,7 @@
#include "controllers/completion/TabCompletionModel.hpp"
#include "messages/LimitedQueue.hpp"
#include <magic_enum/magic_enum.hpp>
#include <pajlada/signals/signal.hpp>
#include <QDate>
#include <QString>
@ -45,7 +46,7 @@ public:
TwitchAutomod,
TwitchEnd,
Irc,
Misc
Misc,
};
explicit Channel(const QString &name, Type type);
@ -151,3 +152,32 @@ private:
};
} // namespace chatterino
template <>
constexpr magic_enum::customize::customize_t
magic_enum::customize::enum_name<chatterino::Channel::Type>(
chatterino::Channel::Type value) noexcept
{
using Type = chatterino::Channel::Type;
switch (value)
{
case Type::Twitch:
return "twitch";
case Type::TwitchWhispers:
return "whispers";
case Type::TwitchWatching:
return "watching";
case Type::TwitchMentions:
return "mentions";
case Type::TwitchLive:
return "live";
case Type::TwitchAutomod:
return "automod";
case Type::Irc:
return "irc";
case Type::Misc:
return "misc";
default:
return default_tag;
}
}

View file

@ -29,6 +29,8 @@
#include <QUuid>
#include <QWidget>
#include <utility>
namespace chatterino {
Notebook::Notebook(QWidget *parent)
@ -87,6 +89,12 @@ Notebook::Notebook(QWidget *parent)
}
NotebookTab *Notebook::addPage(QWidget *page, QString title, bool select)
{
return this->addPageAt(page, -1, std::move(title), select);
}
NotebookTab *Notebook::addPageAt(QWidget *page, int position, QString title,
bool select)
{
// Queue up save because: Tab added
getIApp()->getWindows()->queueSave();
@ -101,7 +109,14 @@ NotebookTab *Notebook::addPage(QWidget *page, QString title, bool select)
item.page = page;
item.tab = tab;
this->items_.append(item);
if (position == -1)
{
this->items_.push_back(item);
}
else
{
this->items_.insert(position, item);
}
page->hide();
page->setParent(this);
@ -165,6 +180,48 @@ void Notebook::removePage(QWidget *page)
this->performLayout(true);
}
void Notebook::duplicatePage(QWidget *page)
{
auto *item = this->findItem(page);
assert(item != nullptr);
if (item == nullptr)
{
return;
}
auto *container = dynamic_cast<SplitContainer *>(item->page);
if (!container)
{
return;
}
auto *newContainer = new SplitContainer(this);
if (!container->getSplits().empty())
{
auto descriptor = container->buildDescriptor();
newContainer->applyFromDescriptor(descriptor);
}
const auto tabPosition = this->indexOf(page);
auto newTabPosition = -1;
if (tabPosition != -1)
{
newTabPosition = tabPosition + 1;
}
auto newTabHighlightState = item->tab->highlightState();
QString newTabTitle = "";
if (item->tab->hasCustomTitle())
{
newTabTitle = item->tab->getCustomTitle();
}
auto *tab =
this->addPageAt(newContainer, newTabPosition, newTabTitle, false);
tab->setHighlightState(newTabHighlightState);
newContainer->setTab(tab);
}
void Notebook::removeCurrentPage()
{
if (this->selectedPage_ != nullptr)

View file

@ -42,7 +42,16 @@ public:
NotebookTab *addPage(QWidget *page, QString title = QString(),
bool select = false);
/**
* @brief Adds a page to the Notebook at a given position.
*
* @param position if set to -1, adds the page to the end
**/
NotebookTab *addPageAt(QWidget *page, int position,
QString title = QString(), bool select = false);
void removePage(QWidget *page);
void duplicatePage(QWidget *page);
void removeCurrentPage();
/**

View file

@ -99,6 +99,10 @@ NotebookTab::NotebookTab(Notebook *notebook)
getIApp()->getHotkeys()->getDisplaySequence(HotkeyCategory::Window,
"popup", {{"window"}}));
this->menu_.addAction("Duplicate Tab", [this]() {
this->notebook_->duplicatePage(this->page);
});
highlightNewMessagesAction_ =
new QAction("Mark Tab as Unread on New Messages", &this->menu_);
highlightNewMessagesAction_->setCheckable(true);

View file

@ -5,9 +5,12 @@
#include "common/QLogging.hpp"
#include "common/WindowDescriptors.hpp"
#include "debug/AssertInGuiThread.hpp"
#include "providers/irc/IrcChannel2.hpp"
#include "providers/irc/IrcServer.hpp"
#include "singletons/Fonts.hpp"
#include "singletons/Theme.hpp"
#include "singletons/WindowManager.hpp"
#include "util/QMagicEnum.hpp"
#include "widgets/helper/ChannelView.hpp"
#include "widgets/helper/NotebookTab.hpp"
#include "widgets/Notebook.hpp"
@ -762,6 +765,11 @@ SplitContainer::Node *SplitContainer::getBaseNode()
return &this->baseNode_;
}
NodeDescriptor SplitContainer::buildDescriptor() const
{
return this->buildDescriptorRecursively(&this->baseNode_);
}
void SplitContainer::applyFromDescriptor(const NodeDescriptor &rootNode)
{
assert(this->baseNode_.type_ == Node::Type::EmptyRoot);
@ -799,6 +807,49 @@ void SplitContainer::popup()
window.show();
}
NodeDescriptor SplitContainer::buildDescriptorRecursively(
const Node *currentNode) const
{
if (currentNode->children_.empty())
{
const auto channelType =
currentNode->split_->getIndirectChannel().getType();
SplitNodeDescriptor result;
result.type_ = qmagicenum::enumNameString(channelType);
switch (channelType)
{
case Channel::Type::Irc: {
if (auto *ircChannel = dynamic_cast<IrcChannel *>(
currentNode->split_->getChannel().get()))
{
if (ircChannel->server())
{
result.server_ = ircChannel->server()->id();
}
}
}
break;
}
result.channelName_ = currentNode->split_->getChannel()->getName();
result.filters_ = currentNode->split_->getFilters();
return result;
}
ContainerNodeDescriptor descriptor;
for (const auto &child : currentNode->children_)
{
descriptor.vertical_ =
currentNode->type_ == Node::Type::VerticalContainer;
descriptor.items_.push_back(
this->buildDescriptorRecursively(child.get()));
}
return descriptor;
}
void SplitContainer::applyFromDescriptorRecursively(
const NodeDescriptor &rootNode, Node *baseNode)
{
@ -849,9 +900,9 @@ void SplitContainer::applyFromDescriptorRecursively(
}
const auto &splitNode = *inner;
auto *split = new Split(this);
split->setFilters(splitNode.filters_);
split->setChannel(WindowManager::decodeChannel(splitNode));
split->setModerationMode(splitNode.moderationMode_);
split->setFilters(splitNode.filters_);
auto *node = new Node();
node->parent_ = baseNode;

View file

@ -220,6 +220,7 @@ public:
void hideResizeHandles();
void resetMouseStatus();
NodeDescriptor buildDescriptor() const;
void applyFromDescriptor(const NodeDescriptor &rootNode);
void popup();
@ -237,6 +238,7 @@ protected:
void resizeEvent(QResizeEvent *event) override;
private:
NodeDescriptor buildDescriptorRecursively(const Node *currentNode) const;
void applyFromDescriptorRecursively(const NodeDescriptor &rootNode,
Node *baseNode);