<commit_before>#include "widgets/splitcontainer.hpp"
#include "common.hpp"
#include "singletons/thememanager.hpp"
#include "util/helpers.hpp"
#include "util/layoutcreator.hpp"
#include "widgets/helper/notebooktab.hpp"
#include "widgets/notebook.hpp"
#include "widgets/split.hpp"

#include <QApplication>
#include <QDebug>
#include <QHBoxLayout>
#include <QMimeData>
#include <QObject>
#include <QPainter>
#include <QVBoxLayout>
#include <QWidget>
#include <boost/foreach.hpp>

#include <algorithm>

namespace chatterino {
namespace widgets {

bool SplitContainer::isDraggingSplit = false;
Split *SplitContainer::draggingSplit = nullptr;
std::pair<int, int> SplitContainer::dropPosition = std::pair<int, int>(-1, -1);

SplitContainer::SplitContainer(Notebook *parent, NotebookTab *_tab)
    : BaseWidget(parent->themeManager, parent)
    , tab(_tab)
    , dropPreview(this)
{
    this->tab->page = this;

    this->setLayout(&this->ui.parentLayout);

    this->setHidden(true);
    this->setAcceptDrops(true);

    this->ui.parentLayout.addSpacing(1);
    this->ui.parentLayout.addLayout(&this->ui.hbox);
    this->ui.parentLayout.setMargin(0);

    this->ui.hbox.setSpacing(1);
    this->ui.hbox.setMargin(0);

    this->refreshTitle();
}

void SplitContainer::updateFlexValues()
{
    for (int i = 0; i < this->ui.hbox.count(); i++) {
        QVBoxLayout *vbox = (QVBoxLayout *)ui.hbox.itemAt(i)->layout();

        if (vbox->count() != 0) {
            ui.hbox.setStretch(i, (int)(1000 * ((Split *)vbox->itemAt(0))->getFlexSizeX()));
        }
    }
}

int SplitContainer::splitCount() const
{
    return this->splits.size();
}

std::pair<int, int> SplitContainer::removeFromLayout(Split *widget)
{
    // remove reference to chat widget from chatWidgets vector
    auto it = std::find(std::begin(this->splits), std::end(this->splits), widget);
    if (it != std::end(this->splits)) {
        this->splits.erase(it);

        this->refreshTitle();
    }

    // remove from box and return location
    for (int i = 0; i < this->ui.hbox.count(); ++i) {
        auto vbox = static_cast<QVBoxLayout *>(this->ui.hbox.itemAt(i));

        for (int j = 0; j < vbox->count(); ++j) {
            if (vbox->itemAt(j)->widget() != widget) {
                continue;
            }

            widget->setParent(nullptr);

            bool isLastItem = vbox->count() == 0;

            if (isLastItem) {
                this->ui.hbox.removeItem(vbox);

                delete vbox;
            }

            return std::pair<int, int>(i, isLastItem ? -1 : j);
        }
    }

    return std::pair<int, int>(-1, -1);
}

void SplitContainer::addToLayout(Split *widget, std::pair<int, int> position)
{
    this->splits.push_back(widget);

    this->refreshTitle();

    widget->giveFocus(Qt::MouseFocusReason);

    // add vbox at the end
    if (position.first < 0 || position.first >= this->ui.hbox.count()) {
        auto vbox = new QVBoxLayout();
        vbox->addWidget(widget);

        this->ui.hbox.addLayout(vbox, 1);

        this->refreshCurrentFocusCoordinates();
        return;
    }

    // insert vbox
    if (position.second == -1) {
        auto vbox = new QVBoxLayout();
        vbox->addWidget(widget);<commit_msg>Closing splits now tries to focus a neighbouring split<commit_after>#include "widgets/splitcontainer.hpp"
#include "common.hpp"
#include "singletons/thememanager.hpp"
#include "util/helpers.hpp"
#include "util/layoutcreator.hpp"
#include "widgets/helper/notebooktab.hpp"
#include "widgets/notebook.hpp"
#include "widgets/split.hpp"

#include <QApplication>
#include <QDebug>
#include <QHBoxLayout>
#include <QMimeData>
#include <QObject>
#include <QPainter>
#include <QVBoxLayout>
#include <QWidget>
#include <boost/foreach.hpp>

#include <algorithm>

namespace chatterino {
namespace widgets {

bool SplitContainer::isDraggingSplit = false;
Split *SplitContainer::draggingSplit = nullptr;
std::pair<int, int> SplitContainer::dropPosition = std::pair<int, int>(-1, -1);

SplitContainer::SplitContainer(Notebook *parent, NotebookTab *_tab)
    : BaseWidget(parent->themeManager, parent)
    , tab(_tab)
    , dropPreview(this)
{
    this->tab->page = this;

    this->setLayout(&this->ui.parentLayout);

    this->setHidden(true);
    this->setAcceptDrops(true);

    this->ui.parentLayout.addSpacing(1);
    this->ui.parentLayout.addLayout(&this->ui.hbox);
    this->ui.parentLayout.setMargin(0);

    this->ui.hbox.setSpacing(1);
    this->ui.hbox.setMargin(0);

    this->refreshTitle();
}

void SplitContainer::updateFlexValues()
{
    for (int i = 0; i < this->ui.hbox.count(); i++) {
        QVBoxLayout *vbox = (QVBoxLayout *)ui.hbox.itemAt(i)->layout();

        if (vbox->count() != 0) {
            ui.hbox.setStretch(i, (int)(1000 * ((Split *)vbox->itemAt(0))->getFlexSizeX()));
        }
    }
}

int SplitContainer::splitCount() const
{
    return this->splits.size();
}

std::pair<int, int> SplitContainer::removeFromLayout(Split *widget)
{
    // remove reference to chat widget from chatWidgets vector
    auto it = std::find(std::begin(this->splits), std::end(this->splits), widget);
    if (it != std::end(this->splits)) {
        this->splits.erase(it);

        this->refreshTitle();
    }

    Split *neighbouringSplit = nullptr;

    // Position the split was found at
    int positionX = -1, positionY = -1;

    bool removed = false;

    QVBoxLayout *layoutToRemove = nullptr;

    // Find widget in box, remove it, return its position
    for (int i = 0; i < this->ui.hbox.count(); ++i) {
        auto vbox = static_cast<QVBoxLayout *>(this->ui.hbox.itemAt(i));

        auto vboxCount = vbox->count();

        for (int j = 0; j < vboxCount; ++j) {
            if (vbox->itemAt(j)->widget() != widget) {
                neighbouringSplit = dynamic_cast<Split *>(vbox->itemAt(j)->widget());

                if (removed && neighbouringSplit != nullptr) {
                    // The widget we searched for has been found, and we have a split to switch
                    // focus to
                    break;
                }

                continue;
            }

            removed = true;
            positionX = i;

            // Remove split from box
            widget->setParent(nullptr);

            if (vbox->count() == 0) {
                // The split was the last item remaining in the vbox
                // Remove the vbox once all iteration is done
                layoutToRemove = vbox;
                positionY = -1;
                break;
            }

            // Don't break here yet, we want to keep iterating this vbox if possible to find the
            // closest still-alive neighbour that we can switch focus to
            positionY = j;

            --j;
            --vboxCount;
        }

        if (removed && neighbouringSplit != nullptr) {
            // The widget we searched for has been found, and we have a split to switch focus to
            break;
        }
    }

    if (removed) {
        if (layoutToRemove != nullptr) {
            // The split we removed was the last split in its box. Remove the box
            // We delay the removing of the box so we can keep iterating over hbox safely
            this->ui.hbox.removeItem(layoutToRemove);
            delete layoutToRemove;
        }

        if (neighbouringSplit != nullptr) {
            // We found a neighbour split we can switch focus to
            neighbouringSplit->giveFocus(Qt::MouseFocusReason);
        }
    }

    return std::make_pair(positionX, positionY);
}

void SplitContainer::addToLayout(Split *widget, std::pair<int, int> position)
{
    this->splits.push_back(widget);

    this->refreshTitle();

    widget->giveFocus(Qt::MouseFocusReason);

    // add vbox at the end
    if (position.first < 0 || position.first >= this->ui.hbox.count()) {
        auto vbox = new QVBoxLayout();
        vbox->addWidget(widget);

        this->ui.hbox.addLayout(vbox, 1);

        this->refreshCurrentFocusCoordinates();
        return;
    }

    // insert vbox
    if (position.second == -1) {
        auto vbox = new QVBoxLayout();
        vbox->addWidget(widget);
{'fraction_non_alphanumeric': 0.1432780847145488, 'fraction_numerical': 0.005402087170042971, 'mean_word_length': 2.6108156028368796, 'pattern_counts': {'":': 0, '<': 44, '<?xml version=': 0, '>': 123, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6863501', 'n_tokens_mistral': 2625, 'n_tokens_neox': 2510, 'n_words': 586}