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:
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user