Chatterino / chatterino2

Chat client for https://twitch.tv
MIT License
2.02k stars 445 forks source link

Add feature to duplicate tabs #5277

Closed KleberPF closed 3 months ago

KleberPF commented 5 months ago

Implements tab duplication by creating a descriptor of the tab and applying that descriptor to a new SplitContainer. The rightmost tab here has a filter to only show messages sent by me.

image

image

Partially implements #5221

pajlada commented 3 months ago

This currently crashes if you try to duplicate a previously duplicated tab - something with attempting to access the clicked item's highlight state - item->tab seems to be a nullptr/invalid ptr

gdb:

Thread 1 "chatterino" received signal SIGSEGV, Segmentation fault.
chatterino::NotebookTab::highlightState (this=0x0) at /home/pajlada/git/chatterino2/src/widgets/helper/NotebookTab.cpp:380
380         return this->highlightState_;
(gdb) bt
#0  chatterino::NotebookTab::highlightState (this=0x0) at /home/pajlada/git/chatterino2/src/widgets/helper/NotebookTab.cpp:380
#1  0x000055555634d29f in chatterino::Notebook::duplicatePage (this=0x5555569b13a0, page=0x555557bf3eb0) at /home/pajlada/git/chatterino2/src/widgets/Notebook.cpp:201
#2  0x000055555645bdd6 in chatterino::NotebookTab::NotebookTab(chatterino::Notebook*)::$_4::operator()() const (this=0x5555583c20a0)
    at /home/pajlada/git/chatterino2/src/widgets/helper/NotebookTab.cpp:103
#3  0x000055555645bd76 in QtPrivate::FunctorCall<QtPrivate::IndexesList<>, QtPrivate::List<>, void, chatterino::NotebookTab::NotebookTab(chatterino::Notebook*)::$_4>::call(chatterino::NotebookTab::NotebookTab(chatterino::Notebook*)::$_4&, void**) (f=..., arg=0x7fffffff3ff0) at /usr/include/qt6/QtCore/qobjectdefs_impl.h:137
#4  0x000055555645bd31 in QtPrivate::FunctorCallable<chatterino::NotebookTab::NotebookTab(chatterino::Notebook*)::$_4>::call<QtPrivate::List<>, void>(chatterino::NotebookTab::NotebookTab(chatterino::Notebook*)::$_4&, void*, void**) (f=..., arg=0x7fffffff3ff0) at /usr/include/qt6/QtCore/qobjectdefs_impl.h:345
#5  0x000055555645bcce in QtPrivate::QCallableObject<chatterino::NotebookTab::NotebookTab(chatterino::Notebook*)::$_4, QtPrivate::List<>, void>::impl(int, QtPrivate::QSlotObjectBase*, QObject*, void**, bool*) (which=1, this_=0x5555583c2090, r=0x5555583c1f30, a=0x7fffffff3ff0, ret=0x0) at /usr/include/qt6/QtCore/qobjectdefs_impl.h:555
#6  0x00007ffff679b57f in ??? () at /usr/lib/libQt6Core.so.6
#7  0x00007ffff7301eda in QAction::activate(QAction::ActionEvent) () at /usr/lib/libQt6Gui.so.6
#8  0x00007ffff7ac5029 in ??? () at /usr/lib/libQt6Widgets.so.6
#9  0x00007ffff7ac6942 in ??? () at /usr/lib/libQt6Widgets.so.6
#10 0x00007ffff7949a99 in QWidget::event(QEvent*) () at /usr/lib/libQt6Widgets.so.6
#11 0x00007ffff78fc44d in QApplicationPrivate::notify_helper(QObject*, QEvent*) () at /usr/lib/libQt6Widgets.so.6
#12 0x00007ffff7901548 in QApplication::notify(QObject*, QEvent*) () at /usr/lib/libQt6Widgets.so.6
#13 0x00007ffff673fe18 in QCoreApplication::notifyInternal2(QObject*, QEvent*) () at /usr/lib/libQt6Core.so.6
#14 0x00007ffff78f457b in QApplicationPrivate::sendMouseEvent(QWidget*, QMouseEvent*, QWidget*, QWidget*, QWidget**, QPointer<QWidget>&, bool, bool) () at /usr/lib/libQt6Widgets.so.6
#15 0x00007ffff795f484 in ??? () at /usr/lib/libQt6Widgets.so.6
#16 0x00007ffff7960370 in ??? () at /usr/lib/libQt6Widgets.so.6
#17 0x00007ffff78fc44d in QApplicationPrivate::notify_helper(QObject*, QEvent*) () at /usr/lib/libQt6Widgets.so.6
#18 0x00007ffff673fe18 in QCoreApplication::notifyInternal2(QObject*, QEvent*) () at /usr/lib/libQt6Core.so.6
#19 0x00007ffff6f75b20 in QGuiApplicationPrivate::processMouseEvent(QWindowSystemInterfacePrivate::MouseEvent*) () at /usr/lib/libQt6Gui.so.6
#20 0x00007ffff6fe8dcc in QWindowSystemInterface::sendWindowSystemEvents(QFlags<QEventLoop::ProcessEventsFlag>) () at /usr/lib/libQt6Gui.so.6
#21 0x00007ffff23e6cb7 in ??? () at /usr/lib/qt6/plugins/platforms/../../../libQt6XcbQpa.so.6
#22 0x00007ffff57e4a89 in ??? () at /usr/lib/libglib-2.0.so.0
#23 0x00007ffff58469b7 in ??? () at /usr/lib/libglib-2.0.so.0
#24 0x00007ffff57e3f95 in g_main_context_iteration () at /usr/lib/libglib-2.0.so.0
#25 0x00007ffff6993389 in QEventDispatcherGlib::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) () at /usr/lib/libQt6Core.so.6
#26 0x00007ffff6748350 in QEventLoop::exec(QFlags<QEventLoop::ProcessEventsFlag>) () at /usr/lib/libQt6Core.so.6
#27 0x00007ffff6743c1d in QCoreApplication::exec() () at /usr/lib/libQt6Core.so.6
#28 0x0000555555b8e103 in chatterino::Application::run (this=0x7fffffff5310, qtApp=...) at /home/pajlada/git/chatterino2/src/Application.cpp:326
#29 0x0000555555bff0a2 in chatterino::runGui (a=..., paths=..., settings=..., args=..., updates=...) at /home/pajlada/git/chatterino2/src/RunGui.cpp:281
#30 0x0000555555b6d31b in main (argc=1, argv=0x7fffffffe358) at /home/pajlada/git/chatterino2/src/main.cpp:102

valgrind:

==78015== Invalid read of size 8
==78015==    at 0xF01297: chatterino::Notebook::duplicatePage(QWidget*) (src/widgets/Notebook.cpp:201)
==78015==    by 0x100FDD5: chatterino::NotebookTab::NotebookTab(chatterino::Notebook*)::$_4::operator()() const (src/widgets/helper/NotebookTab.cpp:103)
==78015==    by 0x100FD75: QtPrivate::FunctorCall<QtPrivate::IndexesList<>, QtPrivate::List<>, void, chatterino::NotebookTab::NotebookTab(chatterino::Notebook*)::$_4>::call(chatterino::NotebookTab::NotebookTab(chatterino::Notebook*)::$_4&, void**) (qobjectdefs_impl.h:137)
==78015==    by 0x100FD30: void QtPrivate::FunctorCallable<chatterino::NotebookTab::NotebookTab(chatterino::Notebook*)::$_4>::call<QtPrivate::List<>, void>(chatterino::NotebookTab::NotebookTab(chatterino::Notebook*)::$_4&, void*, void**) (qobjectdefs_impl.h:345)
==78015==    by 0x100FCCD: QtPrivate::QCallableObject<chatterino::NotebookTab::NotebookTab(chatterino::Notebook*)::$_4, QtPrivate::List<>, void>::impl(int, QtPrivate::QSlotObjectBase*, QObject*, void**, bool*) (qobjectdefs_impl.h:555)
==78015==    by 0x5D4057E: UnknownInlinedFun (qobjectdefs_impl.h:469)
==78015==    by 0x5D4057E: void doActivate<false>(QObject*, int, void**) (qobject.cpp:4078)
==78015==    by 0x5506ED9: UnknownInlinedFun (moc_qaction.cpp:480)
==78015==    by 0x5506ED9: QAction::activate(QAction::ActionEvent) (qaction.cpp:1102)
==78015==    by 0x4B5D028: QMenuPrivate::activateCausedStack(QList<QPointer<QWidget> > const&, QAction*, QAction::ActionEvent, bool) (qmenu.cpp:1413)
==78015==    by 0x4B5E941: QMenuPrivate::activateAction(QAction*, QAction::ActionEvent, bool) (qmenu.cpp:1495)
==78015==    by 0x49E1A98: QWidget::event(QEvent*) (qwidget.cpp:9022)
==78015==    by 0x499444C: QApplicationPrivate::notify_helper(QObject*, QEvent*) (qapplication.cpp:3287)
==78015==    by 0x4999547: QApplication::notify(QObject*, QEvent*) (qapplication.cpp:2765)
==78015==  Address 0x14ea3f98 is 88 bytes inside a block of size 112 free'd
==78015==    at 0x4849E00: realloc (vg_replace_malloc.c:1800)
==78015==    by 0x5DEEFC1: QArrayData::reallocateUnaligned(QArrayData*, void*, long long, long long, QArrayData::AllocationOption) (qarraydata.cpp:244)
==78015==    by 0xF122BD: QTypedArrayData<chatterino::Notebook::Item>::reallocateUnaligned(QTypedArrayData<chatterino::Notebook::Item>*, chatterino::Notebook::Item*, long long, QArrayData::AllocationOption) (qarraydata.h:154)
==78015==    by 0xF11C1D: QtPrivate::QMovableArrayOps<chatterino::Notebook::Item>::reallocate(long long, QArrayData::AllocationOption) (qarraydataops.h:867)
==78015==    by 0xF11718: QArrayDataPointer<chatterino::Notebook::Item>::reallocateAndGrow(QArrayData::GrowthPosition, long long, QArrayDataPointer<chatterino::Notebook::Item>*) (qarraydatapointer.h:223)
==78015==    by 0xF112A1: QArrayDataPointer<chatterino::Notebook::Item>::detachAndGrow(QArrayData::GrowthPosition, long long, chatterino::Notebook::Item const**, QArrayDataPointer<chatterino::Notebook::Item>*) (qarraydatapointer.h:209)
==78015==    by 0xF10EC1: void QtPrivate::QMovableArrayOps<chatterino::Notebook::Item>::emplace<chatterino::Notebook::Item const&>(long long, chatterino::Notebook::Item const&) (qarraydataops.h:831)
==78015==    by 0xF12792: QList<chatterino::Notebook::Item>::iterator QList<chatterino::Notebook::Item>::emplace<chatterino::Notebook::Item const&>(long long, chatterino::Notebook::Item const&) (qlist.h:858)
==78015==    by 0xF0C614: QList<chatterino::Notebook::Item>::insert(long long, chatterino::Notebook::Item const&) (qlist.h:475)
==78015==    by 0xEFE59A: chatterino::Notebook::addPageAt(QWidget*, int, QString, bool) (src/widgets/Notebook.cpp:116)
==78015==    by 0xF0122B: chatterino::Notebook::duplicatePage(QWidget*) (src/widgets/Notebook.cpp:198)
==78015==    by 0x100FDD5: chatterino::NotebookTab::NotebookTab(chatterino::Notebook*)::$_4::operator()() const (src/widgets/helper/NotebookTab.cpp:103)
==78015==  Block was alloc'd at
==78015==    at 0x4849E00: realloc (vg_replace_malloc.c:1800)
==78015==    by 0x5DEEFC1: QArrayData::reallocateUnaligned(QArrayData*, void*, long long, long long, QArrayData::AllocationOption) (qarraydata.cpp:244)
==78015==    by 0xF122BD: QTypedArrayData<chatterino::Notebook::Item>::reallocateUnaligned(QTypedArrayData<chatterino::Notebook::Item>*, chatterino::Notebook::Item*, long long, QArrayData::AllocationOption) (qarraydata.h:154)
==78015==    by 0xF11C1D: QtPrivate::QMovableArrayOps<chatterino::Notebook::Item>::reallocate(long long, QArrayData::AllocationOption) (qarraydataops.h:867)
==78015==    by 0xF11718: QArrayDataPointer<chatterino::Notebook::Item>::reallocateAndGrow(QArrayData::GrowthPosition, long long, QArrayDataPointer<chatterino::Notebook::Item>*) (qarraydatapointer.h:223)
==78015==    by 0xF112A1: QArrayDataPointer<chatterino::Notebook::Item>::detachAndGrow(QArrayData::GrowthPosition, long long, chatterino::Notebook::Item const**, QArrayDataPointer<chatterino::Notebook::Item>*) (qarraydatapointer.h:209)
==78015==    by 0xF10EC1: void QtPrivate::QMovableArrayOps<chatterino::Notebook::Item>::emplace<chatterino::Notebook::Item const&>(long long, chatterino::Notebook::Item const&) (qarraydataops.h:831)
==78015==    by 0xF10CE7: chatterino::Notebook::Item& QList<chatterino::Notebook::Item>::emplaceBack<chatterino::Notebook::Item const&>(chatterino::Notebook::Item const&) (qlist.h:866)
==78015==    by 0xF10C8C: QList<chatterino::Notebook::Item>::append(chatterino::Notebook::Item const&) (qlist.h:444)
==78015==    by 0xF0C5DC: QList<chatterino::Notebook::Item>::push_back(chatterino::Notebook::Item const&) (qlist.h:661)
==78015==    by 0xEFE563: chatterino::Notebook::addPageAt(QWidget*, int, QString, bool) (src/widgets/Notebook.cpp:112)
==78015==    by 0xEFE401: chatterino::Notebook::addPage(QWidget*, QString, bool) (src/widgets/Notebook.cpp:91)
8thony commented 3 months ago

issue while duplicate a tab with a watching split watching: none -> empty split watching: pajlada -> pajlada

KleberPF commented 3 months ago

issue while duplicate a tab with a watching split watching: none -> empty split watching: pajlada -> pajlada

I'll look into it, thanks.

KleberPF commented 3 months ago

@8thony it should work now, could you test it again?

KleberPF commented 3 months ago

currentNode->split_ is a nullptr

I tested duplicating an empty tab, but not duplicating a duplicated empty tab. I added a check now to only try to build and apply from the descriptor if we have a split in the tab.