mirror-chatterino2/src/widgets/splits/SplitContainer.hpp

270 lines
6.9 KiB
C++
Raw Normal View History

#pragma once
2016-12-29 17:31:07 +01:00
#include "common/WindowDescriptors.hpp"
2018-06-26 14:09:39 +02:00
#include "widgets/BaseWidget.hpp"
2016-12-30 12:20:26 +01:00
2017-01-18 04:52:47 +01:00
#include <QDragEnterEvent>
#include <QHBoxLayout>
#include <QRect>
#include <QVBoxLayout>
#include <QVector>
#include <QWidget>
2018-05-10 19:50:31 +02:00
#include <algorithm>
#include <functional>
2018-05-10 23:58:07 +02:00
#include <pajlada/signals/signal.hpp>
2018-05-10 19:50:31 +02:00
#include <pajlada/signals/signalholder.hpp>
#include <unordered_map>
#include <vector>
2018-05-10 19:50:31 +02:00
2018-05-16 14:55:45 +02:00
class QJsonObject;
2018-05-10 23:58:07 +02:00
2017-01-18 21:30:23 +01:00
namespace chatterino {
class Split;
class NotebookTab;
class Notebook;
2018-05-16 14:55:45 +02:00
//
2018-08-06 21:17:03 +02:00
// Note: This class is a spaghetti container. There is a lot of spaghetti code
// inside but it doesn't expose any of it publicly.
2018-05-16 14:55:45 +02:00
//
class SplitContainer final : public BaseWidget
2016-12-29 17:31:07 +01:00
{
2017-01-11 18:52:09 +01:00
Q_OBJECT
2016-12-29 17:31:07 +01:00
2018-05-16 17:47:58 +02:00
public:
2018-05-16 14:55:45 +02:00
struct Node;
2018-05-10 19:50:31 +02:00
// fourtf: !!! preserve the order of left, up, right and down
enum Direction { Left, Above, Right, Below };
struct Position final {
2018-05-10 19:50:31 +02:00
private:
Position() = default;
2018-07-06 19:23:47 +02:00
Position(Node *relativeNode, Direction direcion)
: relativeNode_(relativeNode)
, direction_(direcion)
2018-05-10 19:50:31 +02:00
{
}
2018-07-06 19:23:47 +02:00
Node *relativeNode_;
Direction direction_;
2018-05-10 19:50:31 +02:00
friend struct Node;
friend class SplitContainer;
};
2018-05-16 14:55:45 +02:00
private:
struct DropRect final {
2018-05-10 19:50:31 +02:00
QRect rect;
Position position;
DropRect(const QRect &_rect, const Position &_position)
: rect(_rect)
, position(_position)
{
}
};
struct ResizeRect final {
2018-05-16 14:55:45 +02:00
QRect rect;
Node *node;
bool vertical;
2018-05-10 19:50:31 +02:00
2018-05-16 14:55:45 +02:00
ResizeRect(const QRect &_rect, Node *_node, bool _vertical)
: rect(_rect)
, node(_node)
, vertical(_vertical)
2018-05-10 19:50:31 +02:00
{
}
2018-05-16 14:55:45 +02:00
};
public:
struct Node final {
enum Type { EmptyRoot, _Split, VerticalContainer, HorizontalContainer };
Type getType();
Split *getSplit();
Node *getParent();
qreal getHorizontalFlex();
qreal getVerticalFlex();
const std::vector<std::unique_ptr<Node>> &getChildren();
2018-05-10 19:50:31 +02:00
private:
2018-05-16 14:55:45 +02:00
Node();
Node(Split *_split, Node *_parent);
2018-05-10 19:50:31 +02:00
2018-05-16 14:55:45 +02:00
bool isOrContainsNode(Node *_node);
Node *findNodeContainingSplit(Split *_split);
void insertSplitRelative(Split *_split, Direction _direction);
void nestSplitIntoCollection(Split *_split, Direction _direction);
2018-07-06 19:23:47 +02:00
void insertNextToThis(Split *_split, Direction _direction);
2018-05-16 14:55:45 +02:00
void setSplit(Split *_split);
Position releaseSplit();
qreal getFlex(bool isVertical);
qreal getSize(bool isVertical);
qreal getChildrensTotalFlex(bool isVertical);
2018-08-06 21:17:03 +02:00
void layout(bool addSpacing, float _scale,
std::vector<DropRect> &dropRects_,
2018-05-16 14:55:45 +02:00
std::vector<ResizeRect> &resizeRects);
2018-05-10 19:50:31 +02:00
2018-05-16 14:55:45 +02:00
static Type toContainerType(Direction _dir);
2018-05-10 19:50:31 +02:00
2018-07-06 19:23:47 +02:00
Type type_;
Split *split_;
Node *preferedFocusTarget_;
Node *parent_;
QRectF geometry_;
qreal flexH_ = 1;
qreal flexV_ = 1;
std::vector<std::unique_ptr<Node>> children_;
2018-05-10 19:50:31 +02:00
friend class SplitContainer;
};
2018-05-16 14:55:45 +02:00
private:
class DropOverlay final : public QWidget
2018-05-10 23:58:07 +02:00
{
public:
2018-05-16 14:55:45 +02:00
DropOverlay(SplitContainer *_parent = nullptr);
2018-05-10 23:58:07 +02:00
2018-05-16 14:55:45 +02:00
void setRects(std::vector<SplitContainer::DropRect> _rects);
2018-05-10 23:58:07 +02:00
pajlada::Signals::NoArgSignal dragEnded;
protected:
2018-05-16 14:55:45 +02:00
void paintEvent(QPaintEvent *event) override;
void dragEnterEvent(QDragEnterEvent *event) override;
void dragMoveEvent(QDragMoveEvent *event) override;
void dragLeaveEvent(QDragLeaveEvent *event) override;
void dropEvent(QDropEvent *event) override;
2018-05-10 23:58:07 +02:00
2018-05-16 14:55:45 +02:00
private:
2018-07-06 19:23:47 +02:00
std::vector<DropRect> rects_;
QPoint mouseOverPoint_;
SplitContainer *parent_;
2018-05-16 14:55:45 +02:00
};
2018-05-10 23:58:07 +02:00
class ResizeHandle final : public QWidget
2018-05-16 14:55:45 +02:00
{
public:
SplitContainer *parent;
Node *node;
2018-05-10 23:58:07 +02:00
2018-05-16 14:55:45 +02:00
void setVertical(bool isVertical);
ResizeHandle(SplitContainer *_parent = nullptr);
void paintEvent(QPaintEvent *event) override;
void mousePressEvent(QMouseEvent *event) override;
void mouseReleaseEvent(QMouseEvent *event) override;
void mouseMoveEvent(QMouseEvent *event) override;
2018-07-04 19:52:11 +02:00
void mouseDoubleClickEvent(QMouseEvent *event) override;
2018-05-10 23:58:07 +02:00
2018-05-16 14:55:45 +02:00
friend class SplitContainer;
2018-05-10 23:58:07 +02:00
private:
void resetFlex();
2018-07-06 19:23:47 +02:00
bool vertical_;
bool isMouseDown_ = false;
2018-05-10 23:58:07 +02:00
};
2018-05-16 14:55:45 +02:00
public:
2018-05-23 11:59:37 +02:00
SplitContainer(Notebook *parent);
2018-10-13 14:20:06 +02:00
Split *appendNewSplit(bool openChannelNameDialog);
2018-05-10 19:50:31 +02:00
void appendSplit(Split *split);
void insertSplit(Split *split, const Position &position);
void insertSplit(Split *split, Direction direction, Split *relativeTo);
2018-08-06 21:17:03 +02:00
void insertSplit(Split *split, Direction direction,
Node *relativeTo = nullptr);
2020-08-13 18:02:23 +02:00
Split *getSelectedSplit() const;
2018-05-10 19:50:31 +02:00
Position releaseSplit(Split *split);
Position deleteSplit(Split *split);
2018-05-25 14:57:17 +02:00
void selectNextSplit(Direction direction);
Discord-like Quick Switcher (#1588) * Proof of Concept for Quick Switcher * Fix crash when suggestions are empty * QuickSwitcher: Use tab name instead of a single channel * Rebase later * Add missing include for <functional> * Move QuickSwitcher related classes into own subfolder * Refactor switcher list items Now, items are responsible for taking the right action when selected in the switcher list. This should allow for more focused code and responsibilities. * Add note about memory management * Add option to open channel in a new tab * Add support for using the mouse * Spawn switcher popup in the middle of the window Works reliably on i3 at least. Might need some additional testing on other WMs (and especially on Windows!). * Add some icons for switcher items Note that the final design of the list is not final but I do plan to incorporate these in the future. * Set Qt::Dialog window flag on switcher popup Prevents tiling window managers like i3 from trying to tile the window. * Rename "SwitcherItem" to "AbstractSwitcherItem" * Add comments about what items are inserted * Use custom model and view Still missing: Currently selected item is not highlighted yet. You can move between selected items with tab and arrow keys though. * Add helper function to convert QVariant to AbstractSwitcherItem * * Remove useless constant * Highlight currently selected switcher item * Use a different method for centering QuickSwitcherPopup window * QuickSwitcherModel: Add documentation * Add default parameter to QuickSwitcherModel::rowCount * QuickSwitcherPopup: Add comments * Remove outdated TODO * QuickSwitcherModel: Init vector with default capacity * Remove outdated comment * Add comment about 0 ms timeout interval * NewTabItem: Simplify interface * Only fetch opened splits once This is better than the prior approach since opened splits cannot change anyways while the switcher is open. * Use SplitContainer to pass information instead of custom type * Allow searching for tab titles as well Before this commit, only channel names could be searched. * Refactor switcher item interface to be more flexible Also show tab name and channel name in the switcher list. * Add documentation for AbstractSwitcherItem * Add documentation for NewTabItem * Add comments about {begin,end}{Insert,Remove}Rows * Remove unused method * Replace magic size with named constant * Add change log entry Co-authored-by: fourtf <tf.four@gmail.com>
2020-08-13 19:25:51 +02:00
void setSelected(Split *selected_);
2018-05-10 19:50:31 +02:00
2018-05-25 14:57:17 +02:00
int getSplitCount();
const std::vector<Split *> getSplits() const;
2018-10-13 14:20:06 +02:00
void refreshTab();
2018-05-23 11:59:37 +02:00
NotebookTab *getTab() const;
2018-05-25 14:57:17 +02:00
Node *getBaseNode();
2018-07-06 19:23:47 +02:00
void setTab(NotebookTab *tab_);
void hideResizeHandles();
void resetMouseStatus();
2018-05-23 04:22:17 +02:00
2018-05-10 19:50:31 +02:00
static bool isDraggingSplit;
static Split *draggingSplit;
2017-11-12 17:21:50 +01:00
void applyFromDescriptor(const NodeDescriptor &rootNode);
void popup();
2016-12-30 18:00:25 +01:00
protected:
2018-04-07 12:53:10 +02:00
void paintEvent(QPaintEvent *event) override;
2017-01-01 02:30:42 +01:00
2018-05-31 16:02:20 +02:00
void focusInEvent(QFocusEvent *event) override;
2018-05-10 23:58:07 +02:00
void leaveEvent(QEvent *event) override;
void mouseMoveEvent(QMouseEvent *event) override;
2018-04-07 12:53:10 +02:00
void mouseReleaseEvent(QMouseEvent *event) override;
2018-04-07 12:53:10 +02:00
void dragEnterEvent(QDragEnterEvent *event) override;
2017-01-01 02:30:42 +01:00
2018-05-10 19:50:31 +02:00
void resizeEvent(QResizeEvent *event) override;
2017-04-12 17:46:44 +02:00
private:
void applyFromDescriptorRecursively(const NodeDescriptor &rootNode,
Node *node);
2018-07-06 19:23:47 +02:00
void layout();
void selectSplitRecursive(Node *node, Direction direction);
2020-08-23 17:03:47 +02:00
void focusSplitRecursive(Node *node);
2018-07-06 19:23:47 +02:00
void setPreferedTargetRecursive(Node *node);
void addSplit(Split *split);
2018-09-04 21:39:54 +02:00
Split *getTopRightSplit(Node &node);
2018-07-06 19:23:47 +02:00
2018-10-13 14:20:06 +02:00
void refreshTabTitle();
void refreshTabLiveStatus();
2017-01-11 18:52:09 +01:00
struct DropRegion {
2017-01-01 02:30:42 +01:00
QRect rect;
std::pair<int, int> position;
DropRegion(QRect rect, std::pair<int, int> position)
{
this->rect = rect;
this->position = position;
}
};
2018-07-06 19:23:47 +02:00
std::vector<DropRect> dropRects_;
std::vector<DropRegion> dropRegions_;
DropOverlay overlay_;
std::vector<std::unique_ptr<ResizeHandle>> resizeHandles_;
QPoint mouseOverPoint_;
2018-07-06 19:23:47 +02:00
Node baseNode_;
2020-08-13 18:02:23 +02:00
Split *selected_{};
2018-09-04 21:39:54 +02:00
Split *topRight_{};
bool disableLayouting_{};
2018-07-06 19:23:47 +02:00
NotebookTab *tab_;
std::vector<Split *> splits_;
2017-01-01 02:30:42 +01:00
std::unordered_map<Split *, pajlada::Signals::SignalHolder>
connectionsPerSplit_;
pajlada::Signals::SignalHolder signalHolder_;
2018-07-06 19:23:47 +02:00
bool isDragging_ = false;
2016-12-29 17:31:07 +01:00
};
2017-04-14 17:52:22 +02:00
} // namespace chatterino