Capture GUI refactor (#1939)

* Merge CTB::ButtonType into CaptureTool::Type

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Remove configshortcuts.cpp which I forgot to do earlier

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Add activeButtonTool & activeButtonToolType in CaptureWidget

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Rename slots in CaptureTool for better mnemonics

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Fix move tool bug

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Refactor ShortcutsWidget::initButtons

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Move code from CaptureWidget to SelectionWidget: part 1

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Move code from CaptureWidget to SelectionWidget: part 2

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Move code from CaptureWidget to SelectionWidget: part 3

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Move code from CaptureWidget to SelectionWidget: part 4

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Add SelectionWidget::updateCursor

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Move code from CaptureWidget to SelectionWidget: part 5

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Refactor mouse events in CaptureWidget

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Overlay message update

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Replace connect/disconnect with blockSignals

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* updateIcon on button animation finished

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Remove CaptureWidget::selectAll

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Move moveLeft and similar to SelectionWidget

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Mark update calls for removal

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Specialize CaptureWidget update to affected rects

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Tune update of tool objects

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Remove redundant CaptureTool requests

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Improve performance of update in CaptureWidget

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Fix failing builds

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Fix failing builds again

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Fix undo/redo update

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Undo/redo update workaround

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Extend capture tool update rects

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Fix circle count tool update bug

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Add 'Left Double-Click' tooltip to copy button

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Improve ColorPicker performance

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>
This commit is contained in:
Haris Gušić
2021-10-08 18:04:06 +02:00
committed by GitHub
parent 9cdb8f9fb1
commit fef7787011
76 changed files with 1142 additions and 1159 deletions

View File

@@ -2,26 +2,39 @@
// SPDX-FileCopyrightText: 2017-2019 Alejandro Sirgo Rica & Contributors
#include "selectionwidget.h"
#include "capturetool.h"
#include "capturetoolbutton.h"
#include "src/utils/globalvalues.h"
#include <QApplication>
#include <QEvent>
#include <QMouseEvent>
#include <QPainter>
#include <QPropertyAnimation>
#include <QTimer>
#define MARGIN (m_THandle.width())
SelectionWidget::SelectionWidget(const QColor& c, QWidget* parent)
: QWidget(parent)
, m_color(c)
, m_activeSide(NO_SIDE)
, m_ignoreMouse(false)
{
// prevents this widget from consuming CaptureToolButton mouse events
setAttribute(Qt::WA_TransparentForMouseEvents);
parent->installEventFilter(this);
m_animation = new QPropertyAnimation(this, "geometry", this);
m_animation->setEasingCurve(QEasingCurve::InOutQuad);
m_animation->setDuration(200);
connect(m_animation,
&QPropertyAnimation::finished,
this,
&SelectionWidget::animationEnded);
connect(m_animation, &QPropertyAnimation::finished, this, [this]() {
emit geometrySettled();
});
setAttribute(Qt::WA_TransparentForMouseEvents);
int sideVal = GlobalValues::buttonBaseSize() * 0.6;
int handleSide = sideVal / 2;
const QRect areaRect(0, 0, sideVal, sideVal);
const QRect handleRect(0, 0, handleSide, handleSide);
m_TLHandle = m_TRHandle = m_BLHandle = m_BRHandle = m_LHandle = m_THandle =
m_RHandle = m_BHandle = handleRect;
@@ -31,25 +44,35 @@ SelectionWidget::SelectionWidget(const QColor& c, QWidget* parent)
m_handleOffset = QPoint(-handleSide / 2, -handleSide / 2);
}
/**
* @brief Get the side where the mouse cursor is.
* @param mousePos Mouse cursor position relative to the parent widget.
*/
SelectionWidget::SideType SelectionWidget::getMouseSide(
const QPoint& point) const
const QPoint& mousePos) const
{
if (m_TLArea.contains(point)) {
if (!isVisible()) {
return NO_SIDE;
}
QPoint localPos = mapFromParent(mousePos);
if (m_TLArea.contains(localPos)) {
return TOPLEFT_SIDE;
} else if (m_TRArea.contains(point)) {
} else if (m_TRArea.contains(localPos)) {
return TOPRIGHT_SIDE;
} else if (m_BLArea.contains(point)) {
} else if (m_BLArea.contains(localPos)) {
return BOTTOMLEFT_SIDE;
} else if (m_BRArea.contains(point)) {
} else if (m_BRArea.contains(localPos)) {
return BOTTOMRIGHT_SIDE;
} else if (m_LArea.contains(point)) {
} else if (m_LArea.contains(localPos)) {
return LEFT_SIDE;
} else if (m_TArea.contains(point)) {
} else if (m_TArea.contains(localPos)) {
return TOP_SIDE;
} else if (m_RArea.contains(point)) {
} else if (m_RArea.contains(localPos)) {
return RIGHT_SIDE;
} else if (m_BArea.contains(point)) {
} else if (m_BArea.contains(localPos)) {
return BOTTOM_SIDE;
} else if (rect().contains(localPos)) {
return CENTER;
} else {
return NO_SIDE;
}
@@ -63,6 +86,39 @@ QVector<QRect> SelectionWidget::handlerAreas()
return areas;
}
// helper function
SelectionWidget::SideType getProperSide(SelectionWidget::SideType side,
const QRect& r)
{
using SideType = SelectionWidget::SideType;
int intSide = side;
if (r.right() < r.left()) {
intSide ^= SideType::LEFT_SIDE;
intSide ^= SideType::RIGHT_SIDE;
}
if (r.bottom() < r.top()) {
intSide ^= SideType::TOP_SIDE;
intSide ^= SideType::BOTTOM_SIDE;
}
return (SideType)intSide;
}
void SelectionWidget::setIgnoreMouse(bool ignore)
{
m_ignoreMouse = ignore;
updateCursor();
}
/**
* Set the cursor that will be active when the mouse is inside the selection and
* the mouse is not clicked.
*/
void SelectionWidget::setIdleCentralCursor(const QCursor& cursor)
{
m_idleCentralCursor = cursor;
}
void SelectionWidget::setGeometryAnimated(const QRect& r)
{
if (isVisible()) {
@@ -72,14 +128,150 @@ void SelectionWidget::setGeometryAnimated(const QRect& r)
}
}
void SelectionWidget::saveGeometry()
void SelectionWidget::setGeometry(const QRect& r)
{
m_geometryBackup = geometry();
QWidget::setGeometry(r + QMargins(MARGIN, MARGIN, MARGIN, MARGIN));
updateCursor();
emit geometryChanged();
}
QRect SelectionWidget::savedGeometry()
QRect SelectionWidget::geometry() const
{
return m_geometryBackup;
return QWidget::geometry() - QMargins(MARGIN, MARGIN, MARGIN, MARGIN);
}
QRect SelectionWidget::fullGeometry() const
{
return QWidget::geometry();
}
QRect SelectionWidget::rect() const
{
return QWidget::rect() - QMargins(MARGIN, MARGIN, MARGIN, MARGIN);
}
bool SelectionWidget::eventFilter(QObject* obj, QEvent* event)
{
if (m_ignoreMouse && dynamic_cast<QMouseEvent*>(event)) {
m_activeSide = NO_SIDE;
unsetCursor();
} else if (event->type() == QEvent::MouseButtonRelease) {
parentMouseReleaseEvent(static_cast<QMouseEvent*>(event));
} else if (event->type() == QEvent::MouseButtonPress) {
parentMousePressEvent(static_cast<QMouseEvent*>(event));
} else if (event->type() == QEvent::MouseMove) {
parentMouseMoveEvent(static_cast<QMouseEvent*>(event));
}
return false;
}
void SelectionWidget::parentMousePressEvent(QMouseEvent* e)
{
if (e->button() != Qt::LeftButton) {
return;
}
m_dragStartPos = e->pos();
m_activeSide = getMouseSide(e->pos());
}
void SelectionWidget::parentMouseReleaseEvent(QMouseEvent* e)
{
// released outside of the selection area
if (!getMouseSide(e->pos())) {
hide();
}
m_activeSide = NO_SIDE;
updateCursor();
emit geometrySettled();
}
void SelectionWidget::parentMouseMoveEvent(QMouseEvent* e)
{
updateCursor();
if (e->buttons() != Qt::LeftButton) {
return;
}
SideType mouseSide = m_activeSide;
if (!m_activeSide) {
mouseSide = getMouseSide(e->pos());
}
if (!isVisible() || !mouseSide) {
show();
m_dragStartPos = e->pos();
m_activeSide = TOPLEFT_SIDE;
setGeometry({ e->pos(), e->pos() });
}
QPoint pos = e->pos();
auto geom = geometry();
bool symmetryMod = qApp->keyboardModifiers() & Qt::ShiftModifier;
QPoint newTopLeft = geom.topLeft(), newBottomRight = geom.bottomRight();
int &newLeft = newTopLeft.rx(), &newRight = newBottomRight.rx(),
&newTop = newTopLeft.ry(), &newBottom = newBottomRight.ry();
switch (mouseSide) {
case TOPLEFT_SIDE:
if (m_activeSide)
newTopLeft = pos;
break;
case BOTTOMRIGHT_SIDE:
if (m_activeSide)
newBottomRight = pos;
break;
case TOPRIGHT_SIDE:
if (m_activeSide) {
newTop = pos.y();
newRight = pos.x();
}
break;
case BOTTOMLEFT_SIDE:
if (m_activeSide) {
newBottom = pos.y();
newLeft = pos.x();
}
break;
case LEFT_SIDE:
if (m_activeSide)
newLeft = pos.x();
break;
case RIGHT_SIDE:
if (m_activeSide)
newRight = pos.x();
break;
case TOP_SIDE:
if (m_activeSide)
newTop = pos.y();
break;
case BOTTOM_SIDE:
if (m_activeSide)
newBottom = pos.y();
break;
default:
if (m_activeSide) {
move(this->pos() + pos - m_dragStartPos);
m_dragStartPos = pos;
}
return;
}
// finalize geometry change
if (m_activeSide) {
if (symmetryMod) {
QPoint deltaTopLeft = newTopLeft - geom.topLeft();
QPoint deltaBottomRight = newBottomRight - geom.bottomRight();
newTopLeft = geom.topLeft() + deltaTopLeft - deltaBottomRight;
newBottomRight =
geom.bottomRight() + deltaBottomRight - deltaTopLeft;
}
geom = { newTopLeft, newBottomRight };
setGeometry(geom.normalized());
m_activeSide = getProperSide(m_activeSide, geom);
}
m_dragStartPos = pos;
}
void SelectionWidget::paintEvent(QPaintEvent*)
@@ -87,17 +279,23 @@ void SelectionWidget::paintEvent(QPaintEvent*)
QPainter p(this);
p.setPen(m_color);
p.drawRect(rect() + QMargins(0, 0, -1, -1));
p.setRenderHint(QPainter::Antialiasing);
p.setBrush(m_color);
for (auto rect : handlerAreas()) {
p.drawEllipse(rect);
}
}
void SelectionWidget::resizeEvent(QResizeEvent*)
{
updateAreas();
emit geometryChanged();
}
void SelectionWidget::moveEvent(QMoveEvent*)
{
updateAreas();
emit resized();
emit geometryChanged();
}
void SelectionWidget::updateColor(const QColor& c)
@@ -105,13 +303,53 @@ void SelectionWidget::updateColor(const QColor& c)
m_color = c;
}
void SelectionWidget::moveLeft()
{
setGeometryByKeyboard(geometry().adjusted(-1, 0, -1, 0));
}
void SelectionWidget::moveRight()
{
setGeometryByKeyboard(geometry().adjusted(1, 0, 1, 0));
}
void SelectionWidget::moveUp()
{
setGeometryByKeyboard(geometry().adjusted(0, -1, 0, -1));
}
void SelectionWidget::moveDown()
{
setGeometryByKeyboard(geometry().adjusted(0, 1, 0, 1));
}
void SelectionWidget::resizeLeft()
{
setGeometryByKeyboard(geometry().adjusted(0, 0, -1, 0));
}
void SelectionWidget::resizeRight()
{
setGeometryByKeyboard(geometry().adjusted(0, 0, 1, 0));
}
void SelectionWidget::resizeUp()
{
setGeometryByKeyboard(geometry().adjusted(0, 0, 0, -1));
}
void SelectionWidget::resizeDown()
{
setGeometryByKeyboard(geometry().adjusted(0, 0, 0, 1));
}
void SelectionWidget::updateAreas()
{
QRect r = rect();
m_TLArea.moveTo(m_areaOffset + pos());
m_TRArea.moveTo(r.topRight() + m_areaOffset + pos());
m_BLArea.moveTo(r.bottomLeft() + m_areaOffset + pos());
m_BRArea.moveTo(r.bottomRight() + m_areaOffset + pos());
m_TLArea.moveTo(r.topLeft() + m_areaOffset);
m_TRArea.moveTo(r.topRight() + m_areaOffset);
m_BLArea.moveTo(r.bottomLeft() + m_areaOffset);
m_BRArea.moveTo(r.bottomRight() + m_areaOffset);
m_LArea = QRect(m_TLArea.bottomLeft(), m_BLArea.topRight());
m_TArea = QRect(m_TLArea.topRight(), m_TRArea.bottomLeft());
@@ -127,3 +365,64 @@ void SelectionWidget::updateAreas()
m_RHandle.moveTo(m_RArea.center() + m_handleOffset);
m_BHandle.moveTo(m_BArea.center() + m_handleOffset);
}
void SelectionWidget::updateCursor()
{
SideType mouseSide = m_activeSide;
if (!m_activeSide) {
mouseSide = getMouseSide(parentWidget()->mapFromGlobal(QCursor::pos()));
}
switch (mouseSide) {
case TOPLEFT_SIDE:
setCursor(Qt::SizeFDiagCursor);
break;
case BOTTOMRIGHT_SIDE:
setCursor(Qt::SizeFDiagCursor);
break;
case TOPRIGHT_SIDE:
setCursor(Qt::SizeBDiagCursor);
break;
case BOTTOMLEFT_SIDE:
setCursor(Qt::SizeBDiagCursor);
break;
case LEFT_SIDE:
setCursor(Qt::SizeHorCursor);
break;
case RIGHT_SIDE:
setCursor(Qt::SizeHorCursor);
break;
case TOP_SIDE:
setCursor(Qt::SizeVerCursor);
break;
case BOTTOM_SIDE:
setCursor(Qt::SizeVerCursor);
break;
default:
if (m_activeSide) {
setCursor(Qt::ClosedHandCursor);
} else {
setCursor(m_idleCentralCursor);
return;
}
break;
}
}
void SelectionWidget::setGeometryByKeyboard(const QRect& r)
{
static QTimer timer;
QRect rect = r.intersected(parentWidget()->rect());
if (rect.width() <= 0)
rect.setWidth(1);
if (rect.height() <= 0)
rect.setHeight(1);
setGeometry(rect);
connect(
&timer,
&QTimer::timeout,
this,
[this]() { emit geometrySettled(); },
Qt::UniqueConnection);
timer.start(400);
}