Skip to content

Commit e38725d

Browse files
feat(capture): place primary actions near cursor
1 parent b501405 commit e38725d

5 files changed

Lines changed: 143 additions & 30 deletions

File tree

src/widgets/capture/buttonhandler.cpp

Lines changed: 121 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
#include <QPoint>
88
#include <QScreen>
99

10+
#include <algorithm>
11+
#include <numeric>
12+
1013
// ButtonHandler is a handler for every active button. It makes easier to
1114
// manipulate the buttons as a unit.
1215

@@ -67,7 +70,7 @@ size_t ButtonHandler::size() const
6770
// selection area. Ignores the sides blocked by the end of the screen.
6871
// When the selection is too small, it works on a virtual selection with
6972
// the original in the center.
70-
void ButtonHandler::updatePosition(const QRect& selection)
73+
void ButtonHandler::updatePosition(const QRect& selection, const QPoint& anchor)
7174
{
7275
resetRegionTrack();
7376
const int vecLength = m_vectorButtons.size();
@@ -80,13 +83,18 @@ void ButtonHandler::updatePosition(const QRect& selection)
8083
ensureSelectionMinimumSize();
8184
// Indicates the actual button to be moved
8285
int elemIndicator = 0;
86+
// Collect the available slots before assigning buttons. This keeps the
87+
// existing screen-edge layout while allowing primary actions to follow
88+
// the pointer.
89+
QVector<QPoint> buttonPositions;
90+
buttonPositions.reserve(vecLength);
8391

8492
while (elemIndicator < vecLength) {
8593

8694
// Add them inside the area when there is no more space
8795
if (m_allSidesBlocked) {
8896
m_selection = selection;
89-
positionButtonsInside(elemIndicator);
97+
positionButtonsInside(elemIndicator, buttonPositions);
9098
break; // the while
9199
}
92100
// Number of buttons per row column
@@ -119,9 +127,10 @@ void ButtonHandler::updatePosition(const QRect& selection)
119127
adjustHorizontalCenter(center);
120128
}
121129
// ElemIndicator, elemsAtCorners
122-
QVector<QPoint> positions =
130+
const QVector<QPoint> sidePositions =
123131
horizontalPoints(center, addCounter, true);
124-
moveButtonsToPoints(positions, elemIndicator);
132+
appendButtonPositions(
133+
sidePositions, buttonPositions, elemIndicator);
125134
}
126135
// Add buttons to the right side of the selection
127136
if (!m_blockedRight && elemIndicator < vecLength) {
@@ -130,9 +139,10 @@ void ButtonHandler::updatePosition(const QRect& selection)
130139

131140
QPoint center = QPoint(m_selection.right() + m_separator,
132141
m_selection.center().y());
133-
QVector<QPoint> positions =
142+
const QVector<QPoint> sidePositions =
134143
verticalPoints(center, addCounter, false);
135-
moveButtonsToPoints(positions, elemIndicator);
144+
appendButtonPositions(
145+
sidePositions, buttonPositions, elemIndicator);
136146
}
137147
// Add buttons at the top of the selection
138148
if (!m_blockedTop && elemIndicator < vecLength) {
@@ -143,9 +153,10 @@ void ButtonHandler::updatePosition(const QRect& selection)
143153
if (addCounter == 1 + buttonsPerRow) {
144154
adjustHorizontalCenter(center);
145155
}
146-
QVector<QPoint> positions =
156+
const QVector<QPoint> sidePositions =
147157
horizontalPoints(center, addCounter, false);
148-
moveButtonsToPoints(positions, elemIndicator);
158+
appendButtonPositions(
159+
sidePositions, buttonPositions, elemIndicator);
149160
}
150161
// Add buttons to the left side of the selection
151162
if (!m_blockedLeft && elemIndicator < vecLength) {
@@ -154,9 +165,10 @@ void ButtonHandler::updatePosition(const QRect& selection)
154165

155166
QPoint center = QPoint(m_selection.left() - m_buttonExtendedSize,
156167
m_selection.center().y());
157-
QVector<QPoint> positions =
168+
const QVector<QPoint> sidePositions =
158169
verticalPoints(center, addCounter, true);
159-
moveButtonsToPoints(positions, elemIndicator);
170+
appendButtonPositions(
171+
sidePositions, buttonPositions, elemIndicator);
160172
}
161173
// If there are elements for the next cycle, increase the size of the
162174
// base area
@@ -165,6 +177,8 @@ void ButtonHandler::updatePosition(const QRect& selection)
165177
}
166178
updateBlockedSides();
167179
}
180+
181+
assignButtonsToPositions(buttonPositions, anchor);
168182
}
169183

170184
int ButtonHandler::calculateShift(int elements, bool reverse) const
@@ -289,7 +303,7 @@ void ButtonHandler::expandSelection()
289303
m_selection = intersectWithAreas(m_selection);
290304
}
291305

292-
void ButtonHandler::positionButtonsInside(int index)
306+
void ButtonHandler::positionButtonsInside(int index, QVector<QPoint>& positions)
293307
{
294308
// Position the buttons in the botton-center of the main but inside of the
295309
// selection.
@@ -305,8 +319,9 @@ void ButtonHandler::positionButtonsInside(int index)
305319
while (m_vectorButtons.size() > index) {
306320
int addCounter = buttonsPerRow;
307321
addCounter = qBound(0, addCounter, m_vectorButtons.size() - index);
308-
QVector<QPoint> positions = horizontalPoints(center, addCounter, true);
309-
moveButtonsToPoints(positions, index);
322+
const QVector<QPoint> rowPositions =
323+
horizontalPoints(center, addCounter, true);
324+
appendButtonPositions(rowPositions, positions, index);
310325
center.setY(center.y() - m_buttonExtendedSize);
311326
}
312327

@@ -334,16 +349,96 @@ void ButtonHandler::ensureSelectionMinimumSize()
334349
}
335350
}
336351

337-
void ButtonHandler::moveButtonsToPoints(const QVector<QPoint>& points,
338-
int& index)
352+
void ButtonHandler::appendButtonPositions(const QVector<QPoint>& points,
353+
QVector<QPoint>& positions,
354+
int& index)
339355
{
340356
for (const QPoint& p : points) {
341-
auto* button = m_vectorButtons[index];
342-
button->move(p);
357+
positions.append(p);
343358
++index;
344359
}
345360
}
346361

362+
void ButtonHandler::assignButtonsToPositions(const QVector<QPoint>& positions,
363+
const QPoint& anchor)
364+
{
365+
if (positions.isEmpty()) {
366+
return;
367+
}
368+
369+
QVector<CaptureToolButton*> primaryButtons;
370+
QVector<CaptureToolButton*> remainingButtons;
371+
primaryButtons.reserve(m_vectorButtons.size());
372+
remainingButtons.reserve(m_vectorButtons.size());
373+
374+
const auto primaryRank = [](const CaptureToolButton* button) {
375+
switch (button->tool()->type()) {
376+
case CaptureTool::TYPE_COPY:
377+
case CaptureTool::TYPE_ACCEPT:
378+
return 0;
379+
case CaptureTool::TYPE_SAVE:
380+
return 1;
381+
case CaptureTool::TYPE_PIN:
382+
return 2;
383+
case CaptureTool::TYPE_PLUGIN:
384+
return 3;
385+
default:
386+
return -1;
387+
}
388+
};
389+
390+
for (CaptureToolButton* button : m_vectorButtons) {
391+
if (primaryRank(button) >= 0) {
392+
primaryButtons.append(button);
393+
} else {
394+
remainingButtons.append(button);
395+
}
396+
}
397+
std::stable_sort(primaryButtons.begin(),
398+
primaryButtons.end(),
399+
[&primaryRank](const CaptureToolButton* left,
400+
const CaptureToolButton* right) {
401+
return primaryRank(left) < primaryRank(right);
402+
});
403+
404+
// The first primary action gets the slot whose center is closest to the
405+
// release point, followed by the other primary actions.
406+
QVector<int> positionsByDistance(positions.size());
407+
std::iota(positionsByDistance.begin(), positionsByDistance.end(), 0);
408+
const QPoint buttonCenterOffset(m_buttonBaseSize / 2, m_buttonBaseSize / 2);
409+
const auto distanceSquared = [&](int positionIndex) {
410+
const QPoint delta =
411+
positions[positionIndex] + buttonCenterOffset - anchor;
412+
return static_cast<qint64>(delta.x()) * delta.x() +
413+
static_cast<qint64>(delta.y()) * delta.y();
414+
};
415+
std::stable_sort(positionsByDistance.begin(),
416+
positionsByDistance.end(),
417+
[&distanceSquared](int left, int right) {
418+
return distanceSquared(left) < distanceSquared(right);
419+
});
420+
421+
QVector<bool> usedPositions(positions.size(), false);
422+
int primaryIndex = 0;
423+
for (; primaryIndex < primaryButtons.size() &&
424+
primaryIndex < positionsByDistance.size();
425+
++primaryIndex) {
426+
const int positionIndex = positionsByDistance[primaryIndex];
427+
primaryButtons[primaryIndex]->move(positions[positionIndex]);
428+
usedPositions[positionIndex] = true;
429+
}
430+
431+
int remainingIndex = 0;
432+
for (int positionIndex = 0; positionIndex < positions.size() &&
433+
remainingIndex < remainingButtons.size();
434+
++positionIndex) {
435+
if (!usedPositions[positionIndex]) {
436+
remainingButtons[remainingIndex]->move(positions[positionIndex]);
437+
++remainingIndex;
438+
}
439+
}
440+
}
441+
347442
void ButtonHandler::adjustHorizontalCenter(QPoint& center)
348443
{
349444
if (m_blockedLeft) {
@@ -373,15 +468,15 @@ bool ButtonHandler::contains(const QPoint& p) const
373468
if (m_vectorButtons.isEmpty()) {
374469
return false;
375470
}
376-
QPoint first(m_vectorButtons.first()->pos());
377-
QPoint last(m_vectorButtons.last()->pos());
378-
bool firstIsTopLeft = (first.x() <= last.x() && first.y() <= last.y());
379-
QPoint topLeft = firstIsTopLeft ? first : last;
380-
QPoint bottonRight = firstIsTopLeft ? last : first;
381-
topLeft += QPoint(-m_separator, -m_separator);
382-
bottonRight += QPoint(m_buttonExtendedSize, m_buttonExtendedSize);
383-
QRegion r(QRect(topLeft, bottonRight).normalized());
384-
return r.contains(p);
471+
472+
QRegion buttonRegion;
473+
const QSize hitArea(m_buttonExtendedSize, m_buttonExtendedSize);
474+
for (const CaptureToolButton* button : m_vectorButtons) {
475+
const QPoint topLeft =
476+
button->pos() - QPoint(m_separator, m_separator);
477+
buttonRegion += QRect(topLeft, hitArea);
478+
}
479+
return buttonRegion.contains(p);
385480
}
386481

387482
void ButtonHandler::updateScreenRegions(const QVector<QRect>& rects)

src/widgets/capture/buttonhandler.h

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ class ButtonHandler : public QObject
3232
void updateScreenRegions(const QRect& rect);
3333

3434
public slots:
35-
void updatePosition(const QRect& selection);
35+
void updatePosition(const QRect& selection, const QPoint& anchor);
3636
void hide();
3737
void show();
3838

@@ -72,8 +72,12 @@ public slots:
7272
void resetRegionTrack();
7373
void updateBlockedSides();
7474
void expandSelection();
75-
void positionButtonsInside(int index);
75+
void positionButtonsInside(int index, QVector<QPoint>& positions);
7676
void ensureSelectionMinimumSize();
77-
void moveButtonsToPoints(const QVector<QPoint>& points, int& index);
77+
void appendButtonPositions(const QVector<QPoint>& points,
78+
QVector<QPoint>& positions,
79+
int& index);
80+
void assignButtonsToPositions(const QVector<QPoint>& positions,
81+
const QPoint& anchor);
7882
void adjustHorizontalCenter(QPoint& center);
7983
};

src/widgets/capture/capturewidget.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1391,7 +1391,8 @@ void CaptureWidget::initSelection()
13911391
m_captureDone = true;
13921392
close();
13931393
}
1394-
m_buttonHandler->updatePosition(m_selection->geometry());
1394+
m_buttonHandler->updatePosition(m_selection->geometry(),
1395+
m_selection->toolbarAnchor());
13951396
m_buttonHandler->show();
13961397
} else {
13971398
m_buttonHandler->hide();

src/widgets/capture/selectionwidget.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,11 @@ QRect SelectionWidget::fullGeometry() const
149149
return QWidget::geometry();
150150
}
151151

152+
QPoint SelectionWidget::toolbarAnchor() const
153+
{
154+
return m_hasToolbarAnchor ? m_toolbarAnchor : geometry().bottomRight();
155+
}
156+
152157
QRect SelectionWidget::rect() const
153158
{
154159
return QWidget::rect() - QMargins(MARGIN, MARGIN, MARGIN, MARGIN);
@@ -184,6 +189,11 @@ void SelectionWidget::parentMousePressEvent(QMouseEvent* e)
184189

185190
void SelectionWidget::parentMouseReleaseEvent(QMouseEvent* e)
186191
{
192+
if (e->button() == Qt::LeftButton) {
193+
m_toolbarAnchor = e->pos();
194+
m_hasToolbarAnchor = true;
195+
}
196+
187197
// released outside of the selection area
188198
if (!getMouseSide(e->pos())) {
189199
hide();

src/widgets/capture/selectionwidget.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ class SelectionWidget : public QWidget
3737
void setGeometry(const QRect& r);
3838
QRect geometry() const;
3939
QRect fullGeometry() const;
40+
QPoint toolbarAnchor() const;
4041

4142
QRect rect() const;
4243

@@ -89,6 +90,8 @@ public slots:
8990
QPoint m_handleOffset;
9091

9192
QPoint m_dragStartPos;
93+
QPoint m_toolbarAnchor;
94+
bool m_hasToolbarAnchor = false;
9295
SideType m_activeSide;
9396
QCursor m_idleCentralCursor;
9497
bool m_ignoreMouse;

0 commit comments

Comments
 (0)