Window: Extract next interactive resize geometry code to the separate function
This will allow code reuse.
This commit is contained in:
parent
10e41fc948
commit
4449315e39
4 changed files with 180 additions and 158 deletions
333
src/window.cpp
333
src/window.cpp
|
@ -1448,6 +1448,182 @@ QRectF Window::titleBarRect(const QRectF &rect, bool &transposed, int &requiredP
|
|||
return titleRect;
|
||||
}
|
||||
|
||||
QRectF Window::nextInteractiveResizeGeometry(const QPointF &global) const
|
||||
{
|
||||
const QRectF currentMoveResizeGeom = moveResizeGeometry();
|
||||
QRectF nextMoveResizeGeom = moveResizeGeometry();
|
||||
|
||||
const Gravity gravity = interactiveMoveResizeGravity();
|
||||
if (gravity == Gravity::None || isShade() || !isResizable()) {
|
||||
return nextMoveResizeGeom;
|
||||
}
|
||||
|
||||
// these two points limit the geometry rectangle, i.e. if bottomleft resizing is done,
|
||||
// the bottomleft corner should be at is at (topleft.x(), bottomright().y())
|
||||
QPointF topleft = global - interactiveMoveOffset();
|
||||
QPointF bottomright = global + invertedInteractiveMoveOffset();
|
||||
|
||||
// TODO move whole group when moving its leader or when the leader is not mapped?
|
||||
|
||||
QRectF orig = initialInteractiveMoveResizeGeometry();
|
||||
SizeMode sizeMode = SizeModeAny;
|
||||
auto calculateMoveResizeGeom = [&topleft, &bottomright, &orig, &nextMoveResizeGeom, &sizeMode, &gravity]() {
|
||||
switch (gravity) {
|
||||
case Gravity::TopLeft:
|
||||
nextMoveResizeGeom = QRectF(topleft, orig.bottomRight());
|
||||
break;
|
||||
case Gravity::BottomRight:
|
||||
nextMoveResizeGeom = QRectF(orig.topLeft(), bottomright);
|
||||
break;
|
||||
case Gravity::BottomLeft:
|
||||
nextMoveResizeGeom = QRectF(QPointF(topleft.x(), orig.y()), QPointF(orig.right(), bottomright.y()));
|
||||
break;
|
||||
case Gravity::TopRight:
|
||||
nextMoveResizeGeom = QRectF(QPointF(orig.x(), topleft.y()), QPointF(bottomright.x(), orig.bottom()));
|
||||
break;
|
||||
case Gravity::Top:
|
||||
nextMoveResizeGeom = QRectF(QPointF(orig.left(), topleft.y()), orig.bottomRight());
|
||||
sizeMode = SizeModeFixedH; // try not to affect height
|
||||
break;
|
||||
case Gravity::Bottom:
|
||||
nextMoveResizeGeom = QRectF(orig.topLeft(), QPointF(orig.right(), bottomright.y()));
|
||||
sizeMode = SizeModeFixedH;
|
||||
break;
|
||||
case Gravity::Left:
|
||||
nextMoveResizeGeom = QRectF(QPointF(topleft.x(), orig.top()), orig.bottomRight());
|
||||
sizeMode = SizeModeFixedW;
|
||||
break;
|
||||
case Gravity::Right:
|
||||
nextMoveResizeGeom = QRectF(orig.topLeft(), QPointF(bottomright.x(), orig.bottom()));
|
||||
sizeMode = SizeModeFixedW;
|
||||
break;
|
||||
case Gravity::None:
|
||||
Q_UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
// first resize (without checking constrains), then snap, then check bounds, then check constrains
|
||||
calculateMoveResizeGeom();
|
||||
// adjust new size to snap to other windows/borders
|
||||
nextMoveResizeGeom = workspace()->adjustWindowSize(this, nextMoveResizeGeom, gravity);
|
||||
|
||||
if (!isUnrestrictedInteractiveMoveResize()) {
|
||||
// Make sure the titlebar isn't behind a restricted area. We don't need to restrict
|
||||
// the other directions. If not visible enough, move the window to the closest valid
|
||||
// point. We bruteforce this by slowly moving the window back to its previous position
|
||||
const StrutRects strut = workspace()->restrictedMoveArea(VirtualDesktopManager::self()->currentDesktop());
|
||||
QRegion availableArea(workspace()->clientArea(FullArea, this, workspace()->activeOutput()).toRect());
|
||||
for (const QRect &rect : strut) {
|
||||
availableArea -= rect;
|
||||
}
|
||||
bool transposed = false;
|
||||
int requiredPixels;
|
||||
QRectF bTitleRect = titleBarRect(nextMoveResizeGeom, transposed, requiredPixels);
|
||||
int lastVisiblePixels = -1;
|
||||
QRectF lastTry = nextMoveResizeGeom;
|
||||
bool titleFailed = false;
|
||||
for (;;) {
|
||||
const QRect titleRect = bTitleRect.translated(nextMoveResizeGeom.topLeft()).toRect();
|
||||
int visiblePixels = 0;
|
||||
int realVisiblePixels = 0;
|
||||
for (const QRect &rect : availableArea) {
|
||||
const QRect r = rect & titleRect;
|
||||
realVisiblePixels += r.width() * r.height();
|
||||
if ((transposed && r.width() == titleRect.width()) || // Only the full size regions...
|
||||
(!transposed && r.height() == titleRect.height())) { // ...prevents long slim areas
|
||||
visiblePixels += r.width() * r.height();
|
||||
}
|
||||
}
|
||||
|
||||
if (visiblePixels >= requiredPixels) {
|
||||
break; // We have reached a valid position
|
||||
}
|
||||
|
||||
if (realVisiblePixels <= lastVisiblePixels) {
|
||||
if (titleFailed && realVisiblePixels < lastVisiblePixels) {
|
||||
break; // we won't become better
|
||||
} else {
|
||||
if (!titleFailed) {
|
||||
nextMoveResizeGeom = lastTry;
|
||||
}
|
||||
titleFailed = true;
|
||||
}
|
||||
}
|
||||
lastVisiblePixels = realVisiblePixels;
|
||||
QRectF currentTry = nextMoveResizeGeom;
|
||||
lastTry = currentTry;
|
||||
|
||||
// Not visible enough, move the window to the closest valid point. We bruteforce
|
||||
// this by slowly moving the window back to its previous position.
|
||||
// The geometry changes at up to two edges, the one with the title (if) shall take
|
||||
// precedence. The opposing edge has no impact on visiblePixels and only one of
|
||||
// the adjacent can alter at a time, ie. it's enough to ignore adjacent edges
|
||||
// if the title edge altered
|
||||
bool leftChanged = !qFuzzyCompare(currentMoveResizeGeom.left(), currentTry.left());
|
||||
bool rightChanged = !qFuzzyCompare(currentMoveResizeGeom.right(), currentTry.right());
|
||||
bool topChanged = !qFuzzyCompare(currentMoveResizeGeom.top(), currentTry.top());
|
||||
bool btmChanged = !qFuzzyCompare(currentMoveResizeGeom.bottom(), currentTry.bottom());
|
||||
auto fixChangedState = [titleFailed](bool &major, bool &counter, bool &ad1, bool &ad2) {
|
||||
counter = false;
|
||||
if (titleFailed) {
|
||||
major = false;
|
||||
}
|
||||
if (major) {
|
||||
ad1 = ad2 = false;
|
||||
}
|
||||
};
|
||||
switch (titlebarPosition()) {
|
||||
default:
|
||||
case Qt::TopEdge:
|
||||
fixChangedState(topChanged, btmChanged, leftChanged, rightChanged);
|
||||
break;
|
||||
case Qt::LeftEdge:
|
||||
fixChangedState(leftChanged, rightChanged, topChanged, btmChanged);
|
||||
break;
|
||||
case Qt::BottomEdge:
|
||||
fixChangedState(btmChanged, topChanged, leftChanged, rightChanged);
|
||||
break;
|
||||
case Qt::RightEdge:
|
||||
fixChangedState(rightChanged, leftChanged, topChanged, btmChanged);
|
||||
break;
|
||||
}
|
||||
if (topChanged) {
|
||||
currentTry.setTop(currentTry.y() + qBound(-1.0, currentMoveResizeGeom.y() - currentTry.y(), 1.0));
|
||||
} else if (leftChanged) {
|
||||
currentTry.setLeft(currentTry.x() + qBound(-1.0, currentMoveResizeGeom.x() - currentTry.x(), 1.0));
|
||||
} else if (btmChanged) {
|
||||
currentTry.setBottom(currentTry.bottom() + qBound(-1.0, currentMoveResizeGeom.bottom() - currentTry.bottom(), 1.0));
|
||||
} else if (rightChanged) {
|
||||
currentTry.setRight(currentTry.right() + qBound(-1.0, currentMoveResizeGeom.right() - currentTry.right(), 1.0));
|
||||
} else {
|
||||
break; // no position changed - that's certainly not good
|
||||
}
|
||||
nextMoveResizeGeom = currentTry;
|
||||
}
|
||||
}
|
||||
|
||||
// Always obey size hints, even when in "unrestricted" mode
|
||||
QSizeF size = constrainFrameSize(nextMoveResizeGeom.size(), sizeMode);
|
||||
// the new topleft and bottomright corners (after checking size constrains), if they'll be needed
|
||||
topleft = QPointF(nextMoveResizeGeom.right() - size.width(), nextMoveResizeGeom.bottom() - size.height());
|
||||
bottomright = QPointF(nextMoveResizeGeom.left() + size.width(), nextMoveResizeGeom.top() + size.height());
|
||||
orig = nextMoveResizeGeom;
|
||||
|
||||
// if aspect ratios are specified, both dimensions may change.
|
||||
// Therefore grow to the right/bottom if needed.
|
||||
// TODO it should probably obey gravity rather than always using right/bottom ?
|
||||
if (sizeMode == SizeModeFixedH) {
|
||||
orig.setRight(bottomright.x());
|
||||
} else if (sizeMode == SizeModeFixedW) {
|
||||
orig.setBottom(bottomright.y());
|
||||
}
|
||||
|
||||
calculateMoveResizeGeom();
|
||||
|
||||
return nextMoveResizeGeom;
|
||||
}
|
||||
|
||||
void Window::handleInteractiveMoveResize(qreal x, qreal y, qreal x_root, qreal y_root)
|
||||
{
|
||||
if (isWaitingForInteractiveMoveResizeSync()) {
|
||||
|
@ -1483,7 +1659,6 @@ void Window::handleInteractiveMoveResize(qreal x, qreal y, qreal x_root, qreal y
|
|||
// these two points limit the geometry rectangle, i.e. if bottomleft resizing is done,
|
||||
// the bottomleft corner should be at is at (topleft.x(), bottomright().y())
|
||||
QPointF topleft = globalPos - interactiveMoveOffset();
|
||||
QPointF bottomright = globalPos + invertedInteractiveMoveOffset();
|
||||
const QRectF currentMoveResizeGeom = moveResizeGeometry();
|
||||
QRectF nextMoveResizeGeom = moveResizeGeometry();
|
||||
|
||||
|
@ -1495,161 +1670,7 @@ void Window::handleInteractiveMoveResize(qreal x, qreal y, qreal x_root, qreal y
|
|||
return;
|
||||
}
|
||||
|
||||
QRectF orig = initialInteractiveMoveResizeGeometry();
|
||||
SizeMode sizeMode = SizeModeAny;
|
||||
auto calculateMoveResizeGeom = [&topleft, &bottomright, &orig, &nextMoveResizeGeom, &sizeMode, &gravity]() {
|
||||
switch (gravity) {
|
||||
case Gravity::TopLeft:
|
||||
nextMoveResizeGeom = QRectF(topleft, orig.bottomRight());
|
||||
break;
|
||||
case Gravity::BottomRight:
|
||||
nextMoveResizeGeom = QRectF(orig.topLeft(), bottomright);
|
||||
break;
|
||||
case Gravity::BottomLeft:
|
||||
nextMoveResizeGeom = QRectF(QPointF(topleft.x(), orig.y()), QPointF(orig.right(), bottomright.y()));
|
||||
break;
|
||||
case Gravity::TopRight:
|
||||
nextMoveResizeGeom = QRectF(QPointF(orig.x(), topleft.y()), QPointF(bottomright.x(), orig.bottom()));
|
||||
break;
|
||||
case Gravity::Top:
|
||||
nextMoveResizeGeom = QRectF(QPointF(orig.left(), topleft.y()), orig.bottomRight());
|
||||
sizeMode = SizeModeFixedH; // try not to affect height
|
||||
break;
|
||||
case Gravity::Bottom:
|
||||
nextMoveResizeGeom = QRectF(orig.topLeft(), QPointF(orig.right(), bottomright.y()));
|
||||
sizeMode = SizeModeFixedH;
|
||||
break;
|
||||
case Gravity::Left:
|
||||
nextMoveResizeGeom = QRectF(QPointF(topleft.x(), orig.top()), orig.bottomRight());
|
||||
sizeMode = SizeModeFixedW;
|
||||
break;
|
||||
case Gravity::Right:
|
||||
nextMoveResizeGeom = QRectF(orig.topLeft(), QPointF(bottomright.x(), orig.bottom()));
|
||||
sizeMode = SizeModeFixedW;
|
||||
break;
|
||||
case Gravity::None:
|
||||
Q_UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
// first resize (without checking constrains), then snap, then check bounds, then check constrains
|
||||
calculateMoveResizeGeom();
|
||||
// adjust new size to snap to other windows/borders
|
||||
nextMoveResizeGeom = workspace()->adjustWindowSize(this, nextMoveResizeGeom, gravity);
|
||||
|
||||
if (!isUnrestrictedInteractiveMoveResize()) {
|
||||
// Make sure the titlebar isn't behind a restricted area. We don't need to restrict
|
||||
// the other directions. If not visible enough, move the window to the closest valid
|
||||
// point. We bruteforce this by slowly moving the window back to its previous position
|
||||
const StrutRects strut = workspace()->restrictedMoveArea(VirtualDesktopManager::self()->currentDesktop());
|
||||
QRegion availableArea(workspace()->clientArea(FullArea, this, workspace()->activeOutput()).toRect());
|
||||
for (const QRect &rect : strut) {
|
||||
availableArea -= rect;
|
||||
}
|
||||
bool transposed = false;
|
||||
int requiredPixels;
|
||||
QRectF bTitleRect = titleBarRect(nextMoveResizeGeom, transposed, requiredPixels);
|
||||
int lastVisiblePixels = -1;
|
||||
QRectF lastTry = nextMoveResizeGeom;
|
||||
bool titleFailed = false;
|
||||
for (;;) {
|
||||
const QRect titleRect = bTitleRect.translated(nextMoveResizeGeom.topLeft()).toRect();
|
||||
int visiblePixels = 0;
|
||||
int realVisiblePixels = 0;
|
||||
for (const QRect &rect : availableArea) {
|
||||
const QRect r = rect & titleRect;
|
||||
realVisiblePixels += r.width() * r.height();
|
||||
if ((transposed && r.width() == titleRect.width()) || // Only the full size regions...
|
||||
(!transposed && r.height() == titleRect.height())) { // ...prevents long slim areas
|
||||
visiblePixels += r.width() * r.height();
|
||||
}
|
||||
}
|
||||
|
||||
if (visiblePixels >= requiredPixels) {
|
||||
break; // We have reached a valid position
|
||||
}
|
||||
|
||||
if (realVisiblePixels <= lastVisiblePixels) {
|
||||
if (titleFailed && realVisiblePixels < lastVisiblePixels) {
|
||||
break; // we won't become better
|
||||
} else {
|
||||
if (!titleFailed) {
|
||||
nextMoveResizeGeom = lastTry;
|
||||
}
|
||||
titleFailed = true;
|
||||
}
|
||||
}
|
||||
lastVisiblePixels = realVisiblePixels;
|
||||
QRectF currentTry = nextMoveResizeGeom;
|
||||
lastTry = currentTry;
|
||||
|
||||
// Not visible enough, move the window to the closest valid point. We bruteforce
|
||||
// this by slowly moving the window back to its previous position.
|
||||
// The geometry changes at up to two edges, the one with the title (if) shall take
|
||||
// precedence. The opposing edge has no impact on visiblePixels and only one of
|
||||
// the adjacent can alter at a time, ie. it's enough to ignore adjacent edges
|
||||
// if the title edge altered
|
||||
bool leftChanged = !qFuzzyCompare(currentMoveResizeGeom.left(), currentTry.left());
|
||||
bool rightChanged = !qFuzzyCompare(currentMoveResizeGeom.right(), currentTry.right());
|
||||
bool topChanged = !qFuzzyCompare(currentMoveResizeGeom.top(), currentTry.top());
|
||||
bool btmChanged = !qFuzzyCompare(currentMoveResizeGeom.bottom(), currentTry.bottom());
|
||||
auto fixChangedState = [titleFailed](bool &major, bool &counter, bool &ad1, bool &ad2) {
|
||||
counter = false;
|
||||
if (titleFailed) {
|
||||
major = false;
|
||||
}
|
||||
if (major) {
|
||||
ad1 = ad2 = false;
|
||||
}
|
||||
};
|
||||
switch (titlebarPosition()) {
|
||||
default:
|
||||
case Qt::TopEdge:
|
||||
fixChangedState(topChanged, btmChanged, leftChanged, rightChanged);
|
||||
break;
|
||||
case Qt::LeftEdge:
|
||||
fixChangedState(leftChanged, rightChanged, topChanged, btmChanged);
|
||||
break;
|
||||
case Qt::BottomEdge:
|
||||
fixChangedState(btmChanged, topChanged, leftChanged, rightChanged);
|
||||
break;
|
||||
case Qt::RightEdge:
|
||||
fixChangedState(rightChanged, leftChanged, topChanged, btmChanged);
|
||||
break;
|
||||
}
|
||||
if (topChanged) {
|
||||
currentTry.setTop(currentTry.y() + qBound(-1.0, currentMoveResizeGeom.y() - currentTry.y(), 1.0));
|
||||
} else if (leftChanged) {
|
||||
currentTry.setLeft(currentTry.x() + qBound(-1.0, currentMoveResizeGeom.x() - currentTry.x(), 1.0));
|
||||
} else if (btmChanged) {
|
||||
currentTry.setBottom(currentTry.bottom() + qBound(-1.0, currentMoveResizeGeom.bottom() - currentTry.bottom(), 1.0));
|
||||
} else if (rightChanged) {
|
||||
currentTry.setRight(currentTry.right() + qBound(-1.0, currentMoveResizeGeom.right() - currentTry.right(), 1.0));
|
||||
} else {
|
||||
break; // no position changed - that's certainly not good
|
||||
}
|
||||
nextMoveResizeGeom = currentTry;
|
||||
}
|
||||
}
|
||||
|
||||
// Always obey size hints, even when in "unrestricted" mode
|
||||
QSizeF size = constrainFrameSize(nextMoveResizeGeom.size(), sizeMode);
|
||||
// the new topleft and bottomright corners (after checking size constrains), if they'll be needed
|
||||
topleft = QPointF(nextMoveResizeGeom.right() - size.width(), nextMoveResizeGeom.bottom() - size.height());
|
||||
bottomright = QPointF(nextMoveResizeGeom.left() + size.width(), nextMoveResizeGeom.top() + size.height());
|
||||
orig = nextMoveResizeGeom;
|
||||
|
||||
// if aspect ratios are specified, both dimensions may change.
|
||||
// Therefore grow to the right/bottom if needed.
|
||||
// TODO it should probably obey gravity rather than always using right/bottom ?
|
||||
if (sizeMode == SizeModeFixedH) {
|
||||
orig.setRight(bottomright.x());
|
||||
} else if (sizeMode == SizeModeFixedW) {
|
||||
orig.setBottom(bottomright.y());
|
||||
}
|
||||
|
||||
calculateMoveResizeGeom();
|
||||
nextMoveResizeGeom = nextInteractiveResizeGeometry(globalPos);
|
||||
} else if (isInteractiveMove()) {
|
||||
Q_ASSERT(gravity == Gravity::None);
|
||||
if (!isMovable()) { // isMovableAcrossScreens() must have been true to get here
|
||||
|
|
|
@ -1681,6 +1681,7 @@ protected:
|
|||
void handleInteractiveMoveResize(qreal x, qreal y, qreal x_root, qreal y_root);
|
||||
void handleInteractiveMoveResize(const QPointF &local, const QPointF &global);
|
||||
QRectF titleBarRect(const QRectF &rect, bool &transposed, int &requiredPixels) const;
|
||||
QRectF nextInteractiveResizeGeometry(const QPointF &global) const;
|
||||
void dontInteractiveMoveResize();
|
||||
|
||||
virtual QSizeF resizeIncrements() const;
|
||||
|
|
|
@ -2763,7 +2763,7 @@ QPointF Workspace::adjustWindowPosition(Window *window, QPointF pos, bool unrest
|
|||
return pos;
|
||||
}
|
||||
|
||||
QRectF Workspace::adjustWindowSize(Window *window, QRectF moveResizeGeom, Gravity gravity)
|
||||
QRectF Workspace::adjustWindowSize(const Window *window, QRectF moveResizeGeom, Gravity gravity) const
|
||||
{
|
||||
// adapted from adjustWindowPosition on 29May2004
|
||||
// this function is called when resizing a window and will modify
|
||||
|
|
|
@ -224,7 +224,7 @@ public:
|
|||
|
||||
QRectF adjustClientArea(Window *window, const QRectF &area) const;
|
||||
QPointF adjustWindowPosition(Window *window, QPointF pos, bool unrestricted, double snapAdjust = 1.0);
|
||||
QRectF adjustWindowSize(Window *window, QRectF moveResizeGeom, Gravity gravity);
|
||||
QRectF adjustWindowSize(const Window *window, QRectF moveResizeGeom, Gravity gravity) const;
|
||||
void raiseWindow(Window *window, bool nogroup = false);
|
||||
void lowerWindow(Window *window, bool nogroup = false);
|
||||
void raiseWindowRequest(Window *window, NET::RequestSource src = NET::FromApplication, xcb_timestamp_t timestamp = 0);
|
||||
|
|
Loading…
Reference in a new issue