Compare commits

...

3 commits

Author SHA1 Message Date
nerix
292f9b9734
fix: ignore save requests after closing all windows (#5081) 2024-01-14 12:37:03 +00:00
pajlada
13ff11ea75
refactor: SplitOverlay (#5082) 2024-01-14 13:09:07 +01:00
nerix
c4c62f2796
fix: restore focus of last split when restoring (#5080) 2024-01-14 12:06:52 +01:00
11 changed files with 318 additions and 227 deletions

View file

@ -65,6 +65,8 @@
- Bugfix: Fixed avatar in usercard and moderation button triggering when releasing the mouse outside their area. (#5052) - Bugfix: Fixed avatar in usercard and moderation button triggering when releasing the mouse outside their area. (#5052)
- Bugfix: Fixed moderator-only topics being subscribed to for non-moderators. (#5056) - Bugfix: Fixed moderator-only topics being subscribed to for non-moderators. (#5056)
- Bugfix: Fixed a bug where buttons would remain in a hovered state after leaving them. (#5077) - Bugfix: Fixed a bug where buttons would remain in a hovered state after leaving them. (#5077)
- Bugfix: Fixed popup windows not persisting between restarts. (#5081)
- Bugfix: Fixed splits not retaining their focus after minimizing. (#5080)
- Dev: Run miniaudio in a separate thread, and simplify it to not manage the device ourselves. There's a chance the simplification is a bad idea. (#4978) - Dev: Run miniaudio in a separate thread, and simplify it to not manage the device ourselves. There's a chance the simplification is a bad idea. (#4978)
- Dev: Change clang-format from v14 to v16. (#4929) - Dev: Change clang-format from v14 to v16. (#4929)
- Dev: Fixed UTF16 encoding of `modes` file for the installer. (#4791) - Dev: Fixed UTF16 encoding of `modes` file for the installer. (#4791)
@ -102,6 +104,7 @@
- Dev: Added Tests for Windows and MacOS in CI. (#4970, #5032) - Dev: Added Tests for Windows and MacOS in CI. (#4970, #5032)
- Dev: Move `clang-tidy` checker to its own CI job. (#4996) - Dev: Move `clang-tidy` checker to its own CI job. (#4996)
- Dev: Refactored the Image Uploader feature. (#4971) - Dev: Refactored the Image Uploader feature. (#4971)
- Dev: Refactored the SplitOverlay code. (#5082)
- Dev: Fixed deadlock and use-after-free in tests. (#4981) - Dev: Fixed deadlock and use-after-free in tests. (#4981)
- Dev: Moved all `.clang-format` files to the root directory. (#5037) - Dev: Moved all `.clang-format` files to the root directory. (#5037)
- Dev: Load less message history upon reconnects. (#5001, #5018) - Dev: Load less message history upon reconnects. (#5001, #5018)

View file

@ -696,6 +696,7 @@ set(SOURCE_FILES
widgets/splits/InputCompletionPopup.hpp widgets/splits/InputCompletionPopup.hpp
widgets/splits/Split.cpp widgets/splits/Split.cpp
widgets/splits/Split.hpp widgets/splits/Split.hpp
widgets/splits/SplitCommon.hpp
widgets/splits/SplitContainer.cpp widgets/splits/SplitContainer.cpp
widgets/splits/SplitContainer.hpp widgets/splits/SplitContainer.hpp
widgets/splits/SplitHeader.cpp widgets/splits/SplitHeader.cpp

View file

@ -50,7 +50,6 @@ const QString WindowManager::WINDOW_LAYOUT_FILENAME(
QStringLiteral("window-layout.json")); QStringLiteral("window-layout.json"));
using SplitNode = SplitContainer::Node; using SplitNode = SplitContainer::Node;
using SplitDirection = SplitContainer::Direction;
void WindowManager::showSettingsDialog(QWidget *parent, void WindowManager::showSettingsDialog(QWidget *parent,
SettingsDialogPreference preference) SettingsDialogPreference preference)
@ -422,7 +421,14 @@ void WindowManager::save()
{ {
return; return;
} }
qCDebug(chatterinoWindowmanager) << "[WindowManager] Saving";
if (this->shuttingDown_)
{
qCDebug(chatterinoWindowmanager) << "Skipping save (shutting down)";
return;
}
qCDebug(chatterinoWindowmanager) << "Saving";
assertInGuiThread(); assertInGuiThread();
QJsonDocument document; QJsonDocument document;
@ -701,6 +707,9 @@ void WindowManager::closeAll()
{ {
assertInGuiThread(); assertInGuiThread();
qCDebug(chatterinoWindowmanager) << "Shutting down (closing windows)";
this->shuttingDown_ = true;
for (Window *window : windows_) for (Window *window : windows_)
{ {
window->close(); window->close();

View file

@ -140,6 +140,7 @@ private:
const QString windowLayoutFilePath; const QString windowLayoutFilePath;
bool initialized_ = false; bool initialized_ = false;
bool shuttingDown_ = false;
QPoint emotePopupPos_; QPoint emotePopupPos_;

View file

@ -1332,13 +1332,19 @@ SplitNotebook::SplitNotebook(Window *parent)
}); });
} }
void SplitNotebook::showEvent(QShowEvent *) void SplitNotebook::showEvent(QShowEvent * /*event*/)
{ {
if (auto page = this->getSelectedPage()) if (auto *page = this->getSelectedPage())
{ {
if (auto split = page->findChild<Split *>()) auto *split = page->getSelectedSplit();
if (!split)
{ {
split->setFocus(Qt::FocusReason::OtherFocusReason); split = page->findChild<Split *>();
}
if (split)
{
split->setFocus(Qt::OtherFocusReason);
} }
} }
} }

View file

@ -4,6 +4,7 @@
#include "common/Channel.hpp" #include "common/Channel.hpp"
#include "common/NullablePtr.hpp" #include "common/NullablePtr.hpp"
#include "widgets/BaseWidget.hpp" #include "widgets/BaseWidget.hpp"
#include "widgets/splits/SplitCommon.hpp"
#include <boost/signals2.hpp> #include <boost/signals2.hpp>
#include <pajlada/signals/signalholder.hpp> #include <pajlada/signals/signalholder.hpp>
@ -95,8 +96,7 @@ public:
pajlada::Signals::Signal<Action> actionRequested; pajlada::Signals::Signal<Action> actionRequested;
pajlada::Signals::Signal<ChannelPtr> openSplitRequested; pajlada::Signals::Signal<ChannelPtr> openSplitRequested;
// args: (SplitContainer::Direction dir, Split* parent) pajlada::Signals::Signal<SplitDirection, Split *> insertSplitRequested;
pajlada::Signals::Signal<int, Split *> insertSplitRequested;
protected: protected:
void paintEvent(QPaintEvent *event) override; void paintEvent(QPaintEvent *event) override;

View file

@ -0,0 +1,12 @@
#pragma once
namespace chatterino {
enum class SplitDirection {
Left,
Above,
Right,
Below,
};
} // namespace chatterino

View file

@ -159,7 +159,7 @@ void SplitContainer::insertSplit(Split *split, InsertOptions &&options)
} }
auto *relativeTo = options.relativeNode; auto *relativeTo = options.relativeNode;
const auto direction = options.direction.value_or(Direction::Right); const auto direction = options.direction.value_or(SplitDirection::Right);
if (relativeTo == nullptr) if (relativeTo == nullptr)
{ {
@ -260,27 +260,26 @@ void SplitContainer::addSplit(Split *split)
break; break;
case Split::Action::SelectSplitLeft: case Split::Action::SelectSplitLeft:
this->selectNextSplit(SplitContainer::Left); this->selectNextSplit(SplitDirection::Left);
break; break;
case Split::Action::SelectSplitRight: case Split::Action::SelectSplitRight:
this->selectNextSplit(SplitContainer::Right); this->selectNextSplit(SplitDirection::Right);
break; break;
case Split::Action::SelectSplitAbove: case Split::Action::SelectSplitAbove:
this->selectNextSplit(SplitContainer::Above); this->selectNextSplit(SplitDirection::Above);
break; break;
case Split::Action::SelectSplitBelow: case Split::Action::SelectSplitBelow:
this->selectNextSplit(SplitContainer::Below); this->selectNextSplit(SplitDirection::Below);
break; break;
} }
}); });
conns.managedConnect( conns.managedConnect(
split->insertSplitRequested, [this](int dir, Split *parent) { split->insertSplitRequested, [this](SplitDirection dir, Split *parent) {
this->insertSplit(new Split(this), this->insertSplit(new Split(this), {
{ .relativeSplit = parent,
.relativeSplit = parent, .direction = dir,
.direction = static_cast<Direction>(dir), });
});
}); });
this->layout(); this->layout();
@ -355,7 +354,7 @@ SplitContainer::Position SplitContainer::deleteSplit(Split *split)
return releaseSplit(split); return releaseSplit(split);
} }
void SplitContainer::selectNextSplit(Direction direction) void SplitContainer::selectNextSplit(SplitDirection direction)
{ {
assertInGuiThread(); assertInGuiThread();
@ -365,7 +364,7 @@ void SplitContainer::selectNextSplit(Direction direction)
} }
} }
void SplitContainer::selectSplitRecursive(Node *node, Direction direction) void SplitContainer::selectSplitRecursive(Node *node, SplitDirection direction)
{ {
if (node->parent_ != nullptr) if (node->parent_ != nullptr)
{ {
@ -379,7 +378,8 @@ void SplitContainer::selectSplitRecursive(Node *node, Direction direction)
}); });
assert(it != siblings.end()); assert(it != siblings.end());
if (direction == Direction::Left || direction == Direction::Above) if (direction == SplitDirection::Left ||
direction == SplitDirection::Above)
{ {
if (it == siblings.begin()) if (it == siblings.begin())
{ {
@ -507,20 +507,20 @@ void SplitContainer::layout()
// left // left
dropRects.emplace_back( dropRects.emplace_back(
QRect(g.left(), g.top(), g.width() / 3, g.height()), QRect(g.left(), g.top(), g.width() / 3, g.height()),
Position(node, Direction::Left)); Position(node, SplitDirection::Left));
// right // right
dropRects.emplace_back(QRect(g.right() - g.width() / 3, g.top(), dropRects.emplace_back(QRect(g.right() - g.width() / 3, g.top(),
g.width() / 3, g.height()), g.width() / 3, g.height()),
Position(node, Direction::Right)); Position(node, SplitDirection::Right));
// top // top
dropRects.emplace_back( dropRects.emplace_back(
QRect(g.left(), g.top(), g.width(), g.height() / 2), QRect(g.left(), g.top(), g.width(), g.height() / 2),
Position(node, Direction::Above)); Position(node, SplitDirection::Above));
// bottom // bottom
dropRects.emplace_back(QRect(g.left(), g.bottom() - g.height() / 2, dropRects.emplace_back(QRect(g.left(), g.bottom() - g.height() / 2,
g.width(), g.height() / 2), g.width(), g.height() / 2),
Position(node, Direction::Below)); Position(node, SplitDirection::Below));
} }
if (this->splits_.empty()) if (this->splits_.empty())
@ -528,7 +528,7 @@ void SplitContainer::layout()
QRect g = this->rect(); QRect g = this->rect();
dropRects.emplace_back( dropRects.emplace_back(
QRect(g.left(), g.top(), g.width() - 1, g.height() - 1), QRect(g.left(), g.top(), g.width() - 1, g.height() - 1),
Position(nullptr, Direction::Below)); Position(nullptr, SplitDirection::Below));
} }
this->overlay_.setRects(std::move(dropRects)); this->overlay_.setRects(std::move(dropRects));
@ -1027,7 +1027,7 @@ SplitContainer::Node *SplitContainer::Node::findNodeContainingSplit(
} }
void SplitContainer::Node::insertSplitRelative(Split *_split, void SplitContainer::Node::insertSplitRelative(Split *_split,
Direction _direction) SplitDirection _direction)
{ {
if (this->parent_ == nullptr) if (this->parent_ == nullptr)
{ {
@ -1066,7 +1066,7 @@ void SplitContainer::Node::insertSplitRelative(Split *_split,
} }
void SplitContainer::Node::nestSplitIntoCollection(Split *_split, void SplitContainer::Node::nestSplitIntoCollection(Split *_split,
Direction _direction) SplitDirection _direction)
{ {
if (toContainerType(_direction) == this->type_) if (toContainerType(_direction) == this->type_)
{ {
@ -1095,7 +1095,8 @@ void SplitContainer::Node::nestSplitIntoCollection(Split *_split,
} }
} }
void SplitContainer::Node::insertNextToThis(Split *_split, Direction _direction) void SplitContainer::Node::insertNextToThis(Split *_split,
SplitDirection _direction)
{ {
auto &siblings = this->parent_->children_; auto &siblings = this->parent_->children_;
@ -1115,7 +1116,8 @@ void SplitContainer::Node::insertNextToThis(Split *_split, Direction _direction)
}); });
assert(it != siblings.end()); assert(it != siblings.end());
if (_direction == Direction::Right || _direction == Direction::Below) if (_direction == SplitDirection::Right ||
_direction == SplitDirection::Below)
{ {
it++; it++;
} }
@ -1145,7 +1147,7 @@ SplitContainer::Position SplitContainer::Node::releaseSplit()
Position pos; Position pos;
pos.relativeNode_ = nullptr; pos.relativeNode_ = nullptr;
pos.direction_ = Direction::Right; pos.direction_ = SplitDirection::Right;
return pos; return pos;
} }
@ -1163,13 +1165,15 @@ SplitContainer::Position SplitContainer::Node::releaseSplit()
position.relativeNode_ = this->parent_; position.relativeNode_ = this->parent_;
if (this->parent_->type_ == Type::VerticalContainer) if (this->parent_->type_ == Type::VerticalContainer)
{ {
position.direction_ = position.direction_ = siblings.begin() == it
siblings.begin() == it ? Direction::Above : Direction::Below; ? SplitDirection::Above
: SplitDirection::Below;
} }
else else
{ {
position.direction_ = position.direction_ = siblings.begin() == it
siblings.begin() == it ? Direction::Left : Direction::Right; ? SplitDirection::Left
: SplitDirection::Right;
} }
auto *parent = this->parent_; auto *parent = this->parent_;
@ -1191,8 +1195,8 @@ SplitContainer::Position SplitContainer::Node::releaseSplit()
{ {
position.direction_ = position.direction_ =
this->parent_->type_ == Type::VerticalContainer this->parent_->type_ == Type::VerticalContainer
? Direction::Below ? SplitDirection::Below
: Direction::Right; : SplitDirection::Right;
siblings.erase(it); siblings.erase(it);
position.relativeNode_ = siblings.back().get(); position.relativeNode_ = siblings.back().get();
} }
@ -1201,8 +1205,8 @@ SplitContainer::Position SplitContainer::Node::releaseSplit()
position.relativeNode_ = (it + 1)->get(); position.relativeNode_ = (it + 1)->get();
position.direction_ = position.direction_ =
this->parent_->type_ == Type::VerticalContainer this->parent_->type_ == Type::VerticalContainer
? Direction::Above ? SplitDirection::Above
: Direction::Left; : SplitDirection::Left;
siblings.erase(it); siblings.erase(it);
} }
} }
@ -1282,8 +1286,8 @@ void SplitContainer::Node::layout(bool addSpacing, float _scale,
isVertical ? offset : this->geometry_.width(), isVertical ? offset : this->geometry_.width(),
isVertical ? this->geometry_.height() : offset) isVertical ? this->geometry_.height() : offset)
.toRect(), .toRect(),
Position(this, Position(this, isVertical ? SplitDirection::Left
isVertical ? Direction::Left : Direction::Above)); : SplitDirection::Above));
// droprect right / below // droprect right / below
if (isVertical) if (isVertical)
@ -1293,7 +1297,7 @@ void SplitContainer::Node::layout(bool addSpacing, float _scale,
this->geometry_.top(), offset, this->geometry_.top(), offset,
this->geometry_.height()) this->geometry_.height())
.toRect(), .toRect(),
Position(this, Direction::Right)); Position(this, SplitDirection::Right));
} }
else else
{ {
@ -1302,7 +1306,7 @@ void SplitContainer::Node::layout(bool addSpacing, float _scale,
this->geometry_.bottom() - offset, this->geometry_.bottom() - offset,
this->geometry_.width(), offset) this->geometry_.width(), offset)
.toRect(), .toRect(),
Position(this, Direction::Below)); Position(this, SplitDirection::Below));
} }
// shrink childRect // shrink childRect
@ -1391,9 +1395,10 @@ void SplitContainer::Node::clamp()
this->flexV_ = std::max(0.0, this->flexV_); this->flexV_ = std::max(0.0, this->flexV_);
} }
SplitContainer::Node::Type SplitContainer::Node::toContainerType(Direction _dir) SplitContainer::Node::Type SplitContainer::Node::toContainerType(
SplitDirection _dir)
{ {
return _dir == Direction::Left || _dir == Direction::Right return _dir == SplitDirection::Left || _dir == SplitDirection::Right
? Type::HorizontalContainer ? Type::HorizontalContainer
: Type::VerticalContainer; : Type::VerticalContainer;
} }

View file

@ -2,6 +2,7 @@
#include "common/WindowDescriptors.hpp" #include "common/WindowDescriptors.hpp"
#include "widgets/BaseWidget.hpp" #include "widgets/BaseWidget.hpp"
#include "widgets/splits/SplitCommon.hpp"
#include <pajlada/signals/signal.hpp> #include <pajlada/signals/signal.hpp>
#include <pajlada/signals/signalholder.hpp> #include <pajlada/signals/signalholder.hpp>
@ -35,21 +36,17 @@ class SplitContainer final : public BaseWidget
public: public:
struct Node; struct Node;
// fourtf: !!! preserve the order of left, up, right and down
// It's important to preserve since we cast from an int to this enum, so 0 = left, 1 = above etc
enum Direction { Left, Above, Right, Below };
struct Position final { struct Position final {
private: private:
Position() = default; Position() = default;
Position(Node *relativeNode, Direction direcion) Position(Node *relativeNode, SplitDirection direction)
: relativeNode_(relativeNode) : relativeNode_(relativeNode)
, direction_(direcion) , direction_(direction)
{ {
} }
Node *relativeNode_{nullptr}; Node *relativeNode_{nullptr};
Direction direction_{Direction::Right}; SplitDirection direction_{SplitDirection::Right};
friend struct Node; friend struct Node;
friend class SplitContainer; friend class SplitContainer;
@ -102,9 +99,9 @@ public:
bool isOrContainsNode(Node *_node); bool isOrContainsNode(Node *_node);
Node *findNodeContainingSplit(Split *_split); Node *findNodeContainingSplit(Split *_split);
void insertSplitRelative(Split *_split, Direction _direction); void insertSplitRelative(Split *_split, SplitDirection _direction);
void nestSplitIntoCollection(Split *_split, Direction _direction); void nestSplitIntoCollection(Split *_split, SplitDirection _direction);
void insertNextToThis(Split *_split, Direction _direction); void insertNextToThis(Split *_split, SplitDirection _direction);
void setSplit(Split *_split); void setSplit(Split *_split);
Position releaseSplit(); Position releaseSplit();
qreal getFlex(bool isVertical); qreal getFlex(bool isVertical);
@ -117,7 +114,7 @@ public:
// Clamps the flex values ensuring they're never below 0 // Clamps the flex values ensuring they're never below 0
void clamp(); void clamp();
static Type toContainerType(Direction _dir); static Type toContainerType(SplitDirection _dir);
Type type_; Type type_;
Split *split_; Split *split_;
@ -190,7 +187,7 @@ public:
Split *relativeSplit{nullptr}; Split *relativeSplit{nullptr};
Node *relativeNode{nullptr}; Node *relativeNode{nullptr};
std::optional<Direction> direction{}; std::optional<SplitDirection> direction{};
}; };
// Insert split into the base node of this container // Insert split into the base node of this container
@ -209,7 +206,7 @@ public:
Position releaseSplit(Split *split); Position releaseSplit(Split *split);
Position deleteSplit(Split *split); Position deleteSplit(Split *split);
void selectNextSplit(Direction direction); void selectNextSplit(SplitDirection direction);
void setSelected(Split *split); void setSelected(Split *split);
std::vector<Split *> getSplits() const; std::vector<Split *> getSplits() const;
@ -244,7 +241,7 @@ private:
Node *baseNode); Node *baseNode);
void layout(); void layout();
void selectSplitRecursive(Node *node, Direction direction); void selectSplitRecursive(Node *node, SplitDirection direction);
void focusSplitRecursive(Node *node); void focusSplitRecursive(Node *node);
void setPreferedTargetRecursive(Node *node); void setPreferedTargetRecursive(Node *node);

View file

@ -14,14 +14,117 @@
#include <QPainter> #include <QPainter>
#include <QPushButton> #include <QPushButton>
namespace {
using namespace chatterino;
class ButtonEventFilter : public QObject
{
SplitOverlay *parent;
SplitOverlayButton buttonType;
public:
ButtonEventFilter(SplitOverlay *_parent, SplitOverlayButton _buttonType);
protected:
bool eventFilter(QObject *watched, QEvent *event) override;
};
ButtonEventFilter::ButtonEventFilter(SplitOverlay *_parent,
SplitOverlayButton _buttonType)
: QObject(_parent)
, parent(_parent)
, buttonType(_buttonType)
{
}
bool ButtonEventFilter::eventFilter(QObject *watched, QEvent *event)
{
switch (event->type())
{
case QEvent::Enter: {
auto *effect = dynamic_cast<QGraphicsOpacityEffect *>(
((QWidget *)watched)->graphicsEffect());
if (effect != nullptr)
{
effect->setOpacity(0.99);
}
this->parent->setHoveredButton(this->buttonType);
}
break;
case QEvent::Leave: {
auto *effect = dynamic_cast<QGraphicsOpacityEffect *>(
((QWidget *)watched)->graphicsEffect());
if (effect != nullptr)
{
effect->setOpacity(0.7);
}
this->parent->setHoveredButton({});
}
break;
case QEvent::MouseButtonPress: {
if (this->buttonType == SplitOverlayButton::Move)
{
auto *mouseEvent = dynamic_cast<QMouseEvent *>(event);
assert(mouseEvent != nullptr);
if (mouseEvent->button() == Qt::LeftButton)
{
this->parent->dragPressed();
}
return true;
}
}
break;
case QEvent::MouseButtonRelease: {
switch (this->buttonType)
{
case SplitOverlayButton::Move:
break;
case SplitOverlayButton::Left: {
this->parent->createSplitPressed(SplitDirection::Left);
}
break;
case SplitOverlayButton::Up: {
this->parent->createSplitPressed(SplitDirection::Above);
}
break;
case SplitOverlayButton::Right: {
this->parent->createSplitPressed(SplitDirection::Right);
}
break;
case SplitOverlayButton::Down: {
this->parent->createSplitPressed(SplitDirection::Below);
}
break;
}
}
break;
default:;
}
return QObject::eventFilter(watched, event);
}
} // namespace
namespace chatterino { namespace chatterino {
SplitOverlay::SplitOverlay(Split *parent) SplitOverlay::SplitOverlay(Split *parent)
: BaseWidget(parent) : BaseWidget(parent)
, split_(parent) , split_(parent)
{ {
QGridLayout *layout = new QGridLayout(this); auto *layout = new QGridLayout(this);
this->layout_ = layout;
layout->setContentsMargins(1, 1, 1, 1); layout->setContentsMargins(1, 1, 1, 1);
layout->setSpacing(1); layout->setSpacing(1);
@ -30,69 +133,93 @@ SplitOverlay::SplitOverlay(Split *parent)
layout->setColumnStretch(1, 1); layout->setColumnStretch(1, 1);
layout->setColumnStretch(3, 1); layout->setColumnStretch(3, 1);
auto *move = new QPushButton(getResources().split.move, QString()); this->move_ = new QPushButton(getResources().split.move, {});
auto *left = this->left_ = this->left_ = new QPushButton(getResources().split.left, {});
new QPushButton(getResources().split.left, QString()); this->right_ = new QPushButton(getResources().split.right, {});
auto *right = this->right_ = this->up_ = new QPushButton(getResources().split.up, {});
new QPushButton(getResources().split.right, QString()); this->down_ = new QPushButton(getResources().split.down, {});
auto *up = this->up_ = new QPushButton(getResources().split.up, QString());
auto *down = this->down_ =
new QPushButton(getResources().split.down, QString());
move->setGraphicsEffect(new QGraphicsOpacityEffect(this)); this->move_->setGraphicsEffect(new QGraphicsOpacityEffect(this));
left->setGraphicsEffect(new QGraphicsOpacityEffect(this)); this->left_->setGraphicsEffect(new QGraphicsOpacityEffect(this));
right->setGraphicsEffect(new QGraphicsOpacityEffect(this)); this->right_->setGraphicsEffect(new QGraphicsOpacityEffect(this));
up->setGraphicsEffect(new QGraphicsOpacityEffect(this)); this->up_->setGraphicsEffect(new QGraphicsOpacityEffect(this));
down->setGraphicsEffect(new QGraphicsOpacityEffect(this)); this->down_->setGraphicsEffect(new QGraphicsOpacityEffect(this));
move->setFlat(true); this->move_->setFlat(true);
left->setFlat(true); this->left_->setFlat(true);
right->setFlat(true); this->right_->setFlat(true);
up->setFlat(true); this->up_->setFlat(true);
down->setFlat(true); this->down_->setFlat(true);
layout->addWidget(move, 2, 2); layout->addWidget(this->move_, 2, 2);
layout->addWidget(left, 2, 0); layout->addWidget(this->left_, 2, 0);
layout->addWidget(right, 2, 4); layout->addWidget(this->right_, 2, 4);
layout->addWidget(up, 0, 2); layout->addWidget(this->up_, 0, 2);
layout->addWidget(down, 4, 2); layout->addWidget(this->down_, 4, 2);
move->installEventFilter(new ButtonEventFilter(this, SplitMove)); this->move_->installEventFilter(
left->installEventFilter(new ButtonEventFilter(this, SplitLeft)); new ButtonEventFilter(this, SplitOverlayButton::Move));
right->installEventFilter(new ButtonEventFilter(this, SplitRight)); this->left_->installEventFilter(
up->installEventFilter(new ButtonEventFilter(this, SplitUp)); new ButtonEventFilter(this, SplitOverlayButton::Left));
down->installEventFilter(new ButtonEventFilter(this, SplitDown)); this->right_->installEventFilter(
new ButtonEventFilter(this, SplitOverlayButton::Right));
this->up_->installEventFilter(
new ButtonEventFilter(this, SplitOverlayButton::Up));
this->down_->installEventFilter(
new ButtonEventFilter(this, SplitOverlayButton::Down));
move->setFocusPolicy(Qt::NoFocus); this->move_->setFocusPolicy(Qt::NoFocus);
left->setFocusPolicy(Qt::NoFocus); this->left_->setFocusPolicy(Qt::NoFocus);
right->setFocusPolicy(Qt::NoFocus); this->right_->setFocusPolicy(Qt::NoFocus);
up->setFocusPolicy(Qt::NoFocus); this->up_->setFocusPolicy(Qt::NoFocus);
down->setFocusPolicy(Qt::NoFocus); this->down_->setFocusPolicy(Qt::NoFocus);
move->setCursor(Qt::SizeAllCursor); this->move_->setCursor(Qt::SizeAllCursor);
left->setCursor(Qt::PointingHandCursor); this->left_->setCursor(Qt::PointingHandCursor);
right->setCursor(Qt::PointingHandCursor); this->right_->setCursor(Qt::PointingHandCursor);
up->setCursor(Qt::PointingHandCursor); this->up_->setCursor(Qt::PointingHandCursor);
down->setCursor(Qt::PointingHandCursor); this->down_->setCursor(Qt::PointingHandCursor);
this->signalHolder_.managedConnect(this->scaleChanged, [=](float _scale) {
int a = int(_scale * 30);
QSize size(a, a);
move->setIconSize(size);
left->setIconSize(size);
right->setIconSize(size);
up->setIconSize(size);
down->setIconSize(size);
});
this->setMouseTracking(true); this->setMouseTracking(true);
this->setCursor(Qt::ArrowCursor); this->setCursor(Qt::ArrowCursor);
} }
void SplitOverlay::paintEvent(QPaintEvent *) void SplitOverlay::setHoveredButton(
std::optional<SplitOverlayButton> hoveredButton)
{ {
this->hoveredButton_ = hoveredButton;
this->update();
}
void SplitOverlay::dragPressed()
{
this->split_->drag();
}
void SplitOverlay::createSplitPressed(SplitDirection direction)
{
this->split_->insertSplitRequested.invoke(direction, this->split_);
this->hide();
}
void SplitOverlay::scaleChangedEvent(float newScale)
{
int a = int(newScale * 30);
QSize size(a, a);
this->move_->setIconSize(size);
this->left_->setIconSize(size);
this->right_->setIconSize(size);
this->up_->setIconSize(size);
this->down_->setIconSize(size);
BaseWidget::scaleChangedEvent(newScale);
}
void SplitOverlay::paintEvent(QPaintEvent *event)
{
(void)event;
QPainter painter(this); QPainter painter(this);
if (this->theme->isLightTheme()) if (this->theme->isLightTheme())
{ {
@ -103,26 +230,32 @@ void SplitOverlay::paintEvent(QPaintEvent *)
painter.fillRect(this->rect(), QColor(0, 0, 0, 150)); painter.fillRect(this->rect(), QColor(0, 0, 0, 150));
} }
QRect rect; if (!this->hoveredButton_.has_value())
switch (this->hoveredElement_)
{ {
case SplitLeft: { return;
}
QRect rect;
auto hoveredButton = this->hoveredButton_.value();
switch (hoveredButton)
{
case SplitOverlayButton::Left: {
rect = QRect(0, 0, this->width() / 2, this->height()); rect = QRect(0, 0, this->width() / 2, this->height());
} }
break; break;
case SplitRight: { case SplitOverlayButton::Right: {
rect = rect =
QRect(this->width() / 2, 0, this->width() / 2, this->height()); QRect(this->width() / 2, 0, this->width() / 2, this->height());
} }
break; break;
case SplitUp: { case SplitOverlayButton::Up: {
rect = QRect(0, 0, this->width(), this->height() / 2); rect = QRect(0, 0, this->width(), this->height() / 2);
} }
break; break;
case SplitDown: { case SplitOverlayButton::Down: {
rect = rect =
QRect(0, this->height() / 2, this->width(), this->height() / 2); QRect(0, this->height() / 2, this->width(), this->height() / 2);
} }
@ -131,11 +264,10 @@ void SplitOverlay::paintEvent(QPaintEvent *)
default:; default:;
} }
rect.setRight(rect.right() - 1);
rect.setBottom(rect.bottom() - 1);
if (!rect.isNull()) if (!rect.isNull())
{ {
rect.setRight(rect.right() - 1);
rect.setBottom(rect.bottom() - 1);
painter.setPen(getApp()->themes->splits.dropPreviewBorder); painter.setPen(getApp()->themes->splits.dropPreviewBorder);
painter.setBrush(getApp()->themes->splits.dropPreview); painter.setBrush(getApp()->themes->splits.dropPreview);
painter.drawRect(rect); painter.drawRect(rect);
@ -144,9 +276,12 @@ void SplitOverlay::paintEvent(QPaintEvent *)
void SplitOverlay::resizeEvent(QResizeEvent *event) void SplitOverlay::resizeEvent(QResizeEvent *event)
{ {
float _scale = this->scale(); const auto currentScale = this->scale();
bool wideEnough = event->size().width() > 150 * _scale; const auto minimumWidth = 150.F * currentScale;
bool highEnough = event->size().height() > 150 * _scale; const auto minimumHeight = 150.F * currentScale;
const auto wideEnough = event->size().width() > minimumWidth;
const auto highEnough = event->size().height() > minimumHeight;
this->left_->setVisible(wideEnough); this->left_->setVisible(wideEnough);
this->right_->setVisible(wideEnough); this->right_->setVisible(wideEnough);
@ -164,75 +299,4 @@ void SplitOverlay::mouseMoveEvent(QMouseEvent *event)
// } // }
} }
SplitOverlay::ButtonEventFilter::ButtonEventFilter(SplitOverlay *_parent,
HoveredElement _element)
: QObject(_parent)
, parent(_parent)
, hoveredElement(_element)
{
}
bool SplitOverlay::ButtonEventFilter::eventFilter(QObject *watched,
QEvent *event)
{
switch (event->type())
{
case QEvent::Enter: {
QGraphicsOpacityEffect *effect =
dynamic_cast<QGraphicsOpacityEffect *>(
((QWidget *)watched)->graphicsEffect());
if (effect != nullptr)
{
effect->setOpacity(0.99);
}
this->parent->hoveredElement_ = this->hoveredElement;
this->parent->update();
}
break;
case QEvent::Leave: {
QGraphicsOpacityEffect *effect =
dynamic_cast<QGraphicsOpacityEffect *>(
((QWidget *)watched)->graphicsEffect());
if (effect != nullptr)
{
effect->setOpacity(0.7);
}
this->parent->hoveredElement_ = HoveredElement::None;
this->parent->update();
}
break;
case QEvent::MouseButtonPress: {
if (this->hoveredElement == HoveredElement::SplitMove)
{
QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
if (mouseEvent->button() == Qt::LeftButton)
{
this->parent->split_->drag();
}
return true;
}
}
break;
case QEvent::MouseButtonRelease: {
if (this->hoveredElement != HoveredElement::SplitMove)
{
auto dir = SplitContainer::Direction(
this->hoveredElement + SplitContainer::Left - SplitLeft);
this->parent->split_->insertSplitRequested.invoke(
static_cast<int>(dir), this->parent->split_);
this->parent->hide();
}
}
break;
default:;
}
return QObject::eventFilter(watched, event);
}
} // namespace chatterino } // namespace chatterino

View file

@ -1,60 +1,53 @@
#pragma once #pragma once
#include "widgets/BaseWidget.hpp" #include "widgets/BaseWidget.hpp"
#include "widgets/splits/SplitCommon.hpp"
#include <pajlada/signals/signalholder.hpp>
#include <QGridLayout>
#include <QPushButton> #include <QPushButton>
#include <optional>
namespace chatterino { namespace chatterino {
class Split; class Split;
/// Type of button in the split overlay (the overlay that appears when holding down ctrl+alt)
enum class SplitOverlayButton {
Move,
Left,
Up,
Right,
Down,
};
class SplitOverlay : public BaseWidget class SplitOverlay : public BaseWidget
{ {
public: public:
explicit SplitOverlay(Split *parent = nullptr); explicit SplitOverlay(Split *parent);
// Called from the Split Overlay's button when it gets hovered over
void setHoveredButton(std::optional<SplitOverlayButton> hoveredButton);
// Called from the Split Overlay's button when the move button is pressed
void dragPressed();
// Called from the Split Overlay's button when one of the direction buttons are pressed
void createSplitPressed(SplitDirection direction);
protected: protected:
// bool event(QEvent *event) override; void scaleChangedEvent(float newScale) override;
void paintEvent(QPaintEvent *event) override; void paintEvent(QPaintEvent *event) override;
void resizeEvent(QResizeEvent *event) override; void resizeEvent(QResizeEvent *event) override;
void mouseMoveEvent(QMouseEvent *event) override; void mouseMoveEvent(QMouseEvent *event) override;
private: private:
// fourtf: !!! preserve the order of left, up, right and down std::optional<SplitOverlayButton> hoveredButton_{};
enum HoveredElement {
None,
SplitMove,
SplitLeft,
SplitUp,
SplitRight,
SplitDown
};
class ButtonEventFilter : public QObject
{
SplitOverlay *parent;
HoveredElement hoveredElement;
public:
ButtonEventFilter(SplitOverlay *parent, HoveredElement hoveredElement);
protected:
bool eventFilter(QObject *watched, QEvent *event) override;
};
HoveredElement hoveredElement_ = None;
Split *split_; Split *split_;
QGridLayout *layout_; QPushButton *move_;
QPushButton *left_; QPushButton *left_;
QPushButton *up_; QPushButton *up_;
QPushButton *right_; QPushButton *right_;
QPushButton *down_; QPushButton *down_;
pajlada::Signals::SignalHolder signalHolder_;
friend class ButtonEventFilter;
}; };
} // namespace chatterino } // namespace chatterino