2020-08-02 22:22:19 +00:00
|
|
|
/*
|
|
|
|
KWin - the KDE window manager
|
|
|
|
This file is part of the KDE project.
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2020-08-02 22:22:19 +00:00
|
|
|
SPDX-FileCopyrightText: 1999, 2000 Matthias Ettrich <ettrich@kde.org>
|
|
|
|
SPDX-FileCopyrightText: 1997-2002 Cristian Tibirna <tibirna@kde.org>
|
|
|
|
SPDX-FileCopyrightText: 2003 Lubos Lunak <l.lunak@kde.org>
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2020-08-02 22:22:19 +00:00
|
|
|
SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
*/
|
2007-04-29 17:35:43 +00:00
|
|
|
|
|
|
|
#include "placement.h"
|
|
|
|
|
|
|
|
#ifndef KCMRULES
|
2014-03-19 13:45:27 +00:00
|
|
|
#include "cursor.h"
|
2007-04-29 17:35:43 +00:00
|
|
|
#include "options.h"
|
|
|
|
#include "rules.h"
|
2021-08-12 14:16:08 +00:00
|
|
|
#include "virtualdesktops.h"
|
2022-03-23 10:13:38 +00:00
|
|
|
#include "workspace.h"
|
2022-04-22 17:54:31 +00:00
|
|
|
#include "x11window.h"
|
2007-04-29 17:35:43 +00:00
|
|
|
#endif
|
|
|
|
|
2019-07-09 19:19:26 +00:00
|
|
|
#include <QTextStream>
|
2020-06-12 13:50:24 +00:00
|
|
|
#include <QTimer>
|
2019-07-09 19:19:26 +00:00
|
|
|
|
2007-04-29 17:35:43 +00:00
|
|
|
namespace KWin
|
|
|
|
{
|
|
|
|
|
|
|
|
#ifndef KCMRULES
|
|
|
|
|
2013-04-05 07:41:25 +00:00
|
|
|
KWIN_SINGLETON_FACTORY(Placement)
|
2012-11-22 12:06:42 +00:00
|
|
|
|
2022-03-23 10:13:38 +00:00
|
|
|
Placement::Placement(QObject *)
|
2012-11-22 12:06:42 +00:00
|
|
|
{
|
2011-01-30 14:34:42 +00:00
|
|
|
reinitCascading(0);
|
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2012-11-22 12:06:42 +00:00
|
|
|
Placement::~Placement()
|
|
|
|
{
|
Use nullptr everywhere
Summary:
Because KWin is a very old project, we use three kinds of null pointer
literals: 0, NULL, and nullptr. Since C++11, it's recommended to use
nullptr keyword.
This change converts all usages of 0 and NULL literal to nullptr. Even
though it breaks git history, we need to do it in order to have consistent
code as well to ease code reviews (it's very tempting for some people to
add unrelated changes to their patches, e.g. converting NULL to nullptr).
Test Plan: Compiles.
Reviewers: #kwin, davidedmundson, romangg
Reviewed By: #kwin, davidedmundson, romangg
Subscribers: romangg, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D23618
2019-09-19 14:46:54 +00:00
|
|
|
s_self = nullptr;
|
2012-11-22 12:06:42 +00:00
|
|
|
}
|
|
|
|
|
2019-02-02 18:17:44 +00:00
|
|
|
/**
|
|
|
|
* Places the client \a c according to the workspace's layout policy
|
2019-07-29 18:58:33 +00:00
|
|
|
*/
|
2022-05-16 20:13:39 +00:00
|
|
|
void Placement::place(Window *c, const QRectF &area)
|
2011-01-30 14:34:42 +00:00
|
|
|
{
|
|
|
|
Policy policy = c->rules()->checkPlacement(Default);
|
|
|
|
if (policy != Default) {
|
|
|
|
place(c, area, policy);
|
2007-04-29 17:35:43 +00:00
|
|
|
return;
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2022-03-25 12:20:32 +00:00
|
|
|
if (c->isUtility()) {
|
2022-05-16 20:13:39 +00:00
|
|
|
placeUtility(c, area.toRect(), options->placement());
|
2022-03-25 12:20:32 +00:00
|
|
|
} else if (c->isDialog()) {
|
2022-05-16 20:13:39 +00:00
|
|
|
placeDialog(c, area.toRect(), options->placement());
|
2022-03-25 12:20:32 +00:00
|
|
|
} else if (c->isSplash()) {
|
2022-05-16 20:13:39 +00:00
|
|
|
placeOnMainWindow(c, area.toRect()); // on mainwindow, if any, otherwise centered
|
2022-03-25 12:20:32 +00:00
|
|
|
} else if (c->isOnScreenDisplay() || c->isNotification() || c->isCriticalNotification()) {
|
2022-05-16 20:13:39 +00:00
|
|
|
placeOnScreenDisplay(c, area.toRect());
|
2022-03-25 12:20:32 +00:00
|
|
|
} else if (c->isTransient() && c->hasTransientPlacementHint()) {
|
2015-09-11 11:31:41 +00:00
|
|
|
placeTransient(c);
|
2022-03-25 12:20:32 +00:00
|
|
|
} else if (c->isTransient() && c->surface()) {
|
2022-05-16 20:13:39 +00:00
|
|
|
placeDialog(c, area.toRect(), options->placement());
|
2022-03-25 12:20:32 +00:00
|
|
|
} else {
|
2012-02-20 09:25:13 +00:00
|
|
|
place(c, area, options->placement());
|
2022-03-25 12:20:32 +00:00
|
|
|
}
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2022-05-16 20:13:39 +00:00
|
|
|
void Placement::place(Window *c, const QRectF &area, Policy policy, Policy nextPlacement)
|
2011-01-30 14:34:42 +00:00
|
|
|
{
|
2022-03-25 12:20:32 +00:00
|
|
|
if (policy == Unknown || policy == Default) {
|
2012-02-20 09:25:13 +00:00
|
|
|
policy = options->placement();
|
2022-03-25 12:20:32 +00:00
|
|
|
}
|
2021-09-19 16:22:56 +00:00
|
|
|
|
|
|
|
switch (policy) {
|
2021-09-18 17:12:25 +00:00
|
|
|
case NoPlacement:
|
2007-04-29 17:35:43 +00:00
|
|
|
return;
|
2021-09-18 17:12:25 +00:00
|
|
|
case Random:
|
2022-05-16 20:13:39 +00:00
|
|
|
placeAtRandom(c, area.toRect(), nextPlacement);
|
2021-09-18 17:12:25 +00:00
|
|
|
break;
|
|
|
|
case Cascade:
|
2022-05-16 20:13:39 +00:00
|
|
|
placeCascaded(c, area.toRect(), nextPlacement);
|
2021-09-18 17:12:25 +00:00
|
|
|
break;
|
|
|
|
case Centered:
|
2007-04-29 17:35:43 +00:00
|
|
|
placeCentered(c, area, nextPlacement);
|
2021-09-18 17:12:25 +00:00
|
|
|
break;
|
|
|
|
case ZeroCornered:
|
2022-05-16 20:13:39 +00:00
|
|
|
placeZeroCornered(c, area.toRect(), nextPlacement);
|
2021-09-18 17:12:25 +00:00
|
|
|
break;
|
|
|
|
case UnderMouse:
|
2022-05-16 20:13:39 +00:00
|
|
|
placeUnderMouse(c, area.toRect(), nextPlacement);
|
2021-09-18 17:12:25 +00:00
|
|
|
break;
|
|
|
|
case OnMainWindow:
|
2022-05-16 20:13:39 +00:00
|
|
|
placeOnMainWindow(c, area.toRect(), nextPlacement);
|
2021-09-18 17:12:25 +00:00
|
|
|
break;
|
|
|
|
case Maximizing:
|
2022-05-16 20:13:39 +00:00
|
|
|
placeMaximizing(c, area.toRect(), nextPlacement);
|
2021-09-18 17:12:25 +00:00
|
|
|
break;
|
|
|
|
default:
|
2007-04-29 17:35:43 +00:00
|
|
|
placeSmart(c, area, nextPlacement);
|
2021-09-18 17:12:25 +00:00
|
|
|
}
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2019-02-02 18:17:44 +00:00
|
|
|
/**
|
|
|
|
* Place the client \a c according to a simply "random" placement algorithm.
|
2019-07-29 18:58:33 +00:00
|
|
|
*/
|
2022-04-22 17:39:12 +00:00
|
|
|
void Placement::placeAtRandom(Window *c, const QRect &area, Policy /*next*/)
|
2011-01-30 14:34:42 +00:00
|
|
|
{
|
2019-09-13 09:19:32 +00:00
|
|
|
Q_ASSERT(area.isValid());
|
|
|
|
|
2022-03-23 10:13:38 +00:00
|
|
|
const int step = 24;
|
2007-04-29 17:35:43 +00:00
|
|
|
static int px = step;
|
|
|
|
static int py = 2 * step;
|
2011-01-30 14:34:42 +00:00
|
|
|
int tx, ty;
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2019-09-13 09:19:32 +00:00
|
|
|
if (px < area.x()) {
|
|
|
|
px = area.x();
|
|
|
|
}
|
|
|
|
if (py < area.y()) {
|
|
|
|
py = area.y();
|
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
|
|
|
px += step;
|
2011-01-30 14:34:42 +00:00
|
|
|
py += 2 * step;
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2019-09-13 09:19:32 +00:00
|
|
|
if (px > area.width() / 2) {
|
|
|
|
px = area.x() + step;
|
|
|
|
}
|
|
|
|
if (py > area.height() / 2) {
|
|
|
|
py = area.y() + step;
|
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
tx = px;
|
|
|
|
ty = py;
|
2019-09-13 09:19:32 +00:00
|
|
|
if (tx + c->width() > area.right()) {
|
|
|
|
tx = area.right() - c->width();
|
2022-03-25 12:20:32 +00:00
|
|
|
if (tx < 0) {
|
2007-04-29 17:35:43 +00:00
|
|
|
tx = 0;
|
2022-03-25 12:20:32 +00:00
|
|
|
}
|
2019-09-13 09:19:32 +00:00
|
|
|
px = area.x();
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2019-09-13 09:19:32 +00:00
|
|
|
if (ty + c->height() > area.bottom()) {
|
|
|
|
ty = area.bottom() - c->height();
|
2022-03-25 12:20:32 +00:00
|
|
|
if (ty < 0) {
|
2007-04-29 17:35:43 +00:00
|
|
|
ty = 0;
|
2022-03-25 12:20:32 +00:00
|
|
|
}
|
2019-09-13 09:19:32 +00:00
|
|
|
py = area.y();
|
2007-04-29 17:35:43 +00:00
|
|
|
}
|
Rework async geometry updates
Window management features were written with synchronous geometry
updates in mind. Currently, this poses a big problem on Wayland because
geometry updates are done in asynchronous fashion there.
At the moment, geometry is updated in a so called pseudo-asynchronous
fashion, meaning that the frame geometry will be reset to the old value
once geometry updates are unblocked. The main drawback of this approach
is that it is too error prone, the data flow is hard to comprehend, etc.
It is worth noting that there is already a machinery to perform async
geometry which is used during interactive move/resize operations.
This change extends the move/resize geometry usage beyond interactive
move/resize to make asynchronous geometry updates less error prone and
easier to comprehend.
With the proposed solution, all geometry updates must be done on the
move/resize geometry first. After that, the new geometry is passed on to
the Client-specific implementation of moveResizeInternal().
To be more specific, the frameGeometry() returns the current frame
geometry, it is primarily useful only to the scene. If you want to move
or resize a window, you need to use moveResizeGeometry() because it
corresponds to the last requested frame geometry.
It is worth noting that the moveResizeGeometry() returns the desired
bounding geometry. The client may commit the xdg_toplevel surface with a
slightly smaller window geometry, for example to enforce a specific
aspect ratio. The client is not allowed to resize beyond the size as
indicated in moveResizeGeometry().
The data flow is very simple: moveResize() updates the move/resize
geometry and calls the client-specific implementation of the
moveResizeInternal() method. Based on whether a configure event is
needed, moveResizeInternal() will update the frameGeometry() either
immediately or after the client commits a new buffer.
Unfortunately, both the compositor and xdg-shell clients try to update
the window geometry. It means that it's possible to have conflicts
between the two. With this change, the compositor's move resize geometry
will be synced only if there are no pending configure events, meaning
that the user doesn't try to resize the window.
2021-04-30 18:26:09 +00:00
|
|
|
c->move(QPoint(tx, ty));
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2013-01-22 19:09:42 +00:00
|
|
|
// TODO: one day, there'll be C++11 ...
|
2022-04-22 17:39:12 +00:00
|
|
|
static inline bool isIrrelevant(const Window *client, const Window *regarding, int desktop)
|
2013-01-22 19:09:42 +00:00
|
|
|
{
|
2022-04-18 08:01:51 +00:00
|
|
|
if (!client->isClient()) {
|
2013-01-22 19:09:42 +00:00
|
|
|
return true;
|
2022-03-25 12:20:32 +00:00
|
|
|
}
|
|
|
|
if (client == regarding) {
|
2013-01-22 19:09:42 +00:00
|
|
|
return true;
|
2022-03-25 12:20:32 +00:00
|
|
|
}
|
|
|
|
if (!client->isShown() || client->isShade()) {
|
2013-01-22 19:09:42 +00:00
|
|
|
return true;
|
2022-03-25 12:20:32 +00:00
|
|
|
}
|
|
|
|
if (!client->isOnDesktop(desktop)) {
|
2013-01-22 19:09:42 +00:00
|
|
|
return true;
|
2022-03-25 12:20:32 +00:00
|
|
|
}
|
|
|
|
if (!client->isOnCurrentActivity()) {
|
2013-01-22 19:09:42 +00:00
|
|
|
return true;
|
2022-03-25 12:20:32 +00:00
|
|
|
}
|
|
|
|
if (client->isDesktop()) {
|
2013-06-25 13:35:13 +00:00
|
|
|
return true;
|
2022-03-25 12:20:32 +00:00
|
|
|
}
|
2013-01-22 19:09:42 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-02-02 18:17:44 +00:00
|
|
|
/**
|
|
|
|
* Place the client \a c according to a really smart placement algorithm :-)
|
2019-07-29 18:58:33 +00:00
|
|
|
*/
|
2022-05-16 20:13:39 +00:00
|
|
|
void Placement::placeSmart(Window *c, const QRectF &area, Policy /*next*/)
|
2011-01-30 14:34:42 +00:00
|
|
|
{
|
2019-09-13 09:19:32 +00:00
|
|
|
Q_ASSERT(area.isValid());
|
|
|
|
|
2007-04-29 17:35:43 +00:00
|
|
|
/*
|
|
|
|
* SmartPlacement by Cristian Tibirna (tibirna@kde.org)
|
|
|
|
* adapted for kwm (16-19jan98) and for kwin (16Nov1999) using (with
|
|
|
|
* permission) ideas from fvwm, authored by
|
|
|
|
* Anthony Martin (amartin@engr.csulb.edu).
|
|
|
|
* Xinerama supported added by Balaji Ramani (balaji@yablibli.com)
|
|
|
|
* with ideas from xfce.
|
|
|
|
*/
|
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
if (!c->frameGeometry().isValid()) {
|
2019-06-24 20:59:21 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2007-04-29 17:35:43 +00:00
|
|
|
const int none = 0, h_wrong = -1, w_wrong = -2; // overlap types
|
|
|
|
long int overlap, min_overlap = 0;
|
|
|
|
int x_optimal, y_optimal;
|
|
|
|
int possible;
|
2012-11-16 07:23:47 +00:00
|
|
|
int desktop = c->desktop() == 0 || c->isOnAllDesktops() ? VirtualDesktopManager::self()->current() : c->desktop();
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2022-03-23 10:13:38 +00:00
|
|
|
int cxl, cxr, cyt, cyb; // temp coords
|
|
|
|
int xl, xr, yt, yb; // temp coords
|
|
|
|
int basket; // temp holder
|
2007-04-29 17:35:43 +00:00
|
|
|
|
|
|
|
// get the maximum allowed windows space
|
2019-09-13 09:19:32 +00:00
|
|
|
int x = area.left();
|
|
|
|
int y = area.top();
|
2022-03-23 10:13:38 +00:00
|
|
|
x_optimal = x;
|
|
|
|
y_optimal = y;
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2022-03-23 10:13:38 +00:00
|
|
|
// client gabarit
|
2007-04-29 17:35:43 +00:00
|
|
|
int ch = c->height() - 1;
|
2022-03-23 10:13:38 +00:00
|
|
|
int cw = c->width() - 1;
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2022-03-23 10:13:38 +00:00
|
|
|
bool first_pass = true; // CT lame flag. Don't like it. What else would do?
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2022-03-23 10:13:38 +00:00
|
|
|
// loop over possible positions
|
2011-01-30 14:34:42 +00:00
|
|
|
do {
|
2022-03-23 10:13:38 +00:00
|
|
|
// test if enough room in x and y directions
|
2019-09-13 09:19:32 +00:00
|
|
|
if (y + ch > area.bottom() && ch < area.height()) {
|
2007-04-29 17:35:43 +00:00
|
|
|
overlap = h_wrong; // this throws the algorithm to an exit
|
2019-09-13 09:19:32 +00:00
|
|
|
} else if (x + cw > area.right()) {
|
2007-04-29 17:35:43 +00:00
|
|
|
overlap = w_wrong;
|
2019-09-13 09:19:32 +00:00
|
|
|
} else {
|
2022-03-23 10:13:38 +00:00
|
|
|
overlap = none; // initialize
|
|
|
|
|
|
|
|
cxl = x;
|
|
|
|
cxr = x + cw;
|
|
|
|
cyt = y;
|
|
|
|
cyb = y + ch;
|
|
|
|
for (auto l = workspace()->stackingOrder().constBegin(); l != workspace()->stackingOrder().constEnd(); ++l) {
|
2022-04-18 08:01:51 +00:00
|
|
|
auto client = *l;
|
2013-01-22 19:09:42 +00:00
|
|
|
if (isIrrelevant(client, c, desktop)) {
|
2012-04-08 08:07:35 +00:00
|
|
|
continue;
|
|
|
|
}
|
2022-03-23 10:13:38 +00:00
|
|
|
xl = client->x();
|
|
|
|
yt = client->y();
|
|
|
|
xr = xl + client->width();
|
|
|
|
yb = yt + client->height();
|
|
|
|
|
|
|
|
// if windows overlap, calc the overall overlapping
|
|
|
|
if ((cxl < xr) && (cxr > xl) && (cyt < yb) && (cyb > yt)) {
|
|
|
|
xl = qMax(cxl, xl);
|
|
|
|
xr = qMin(cxr, xr);
|
|
|
|
yt = qMax(cyt, yt);
|
|
|
|
yb = qMin(cyb, yb);
|
2022-03-25 12:20:32 +00:00
|
|
|
if (client->keepAbove()) {
|
2013-01-22 19:09:42 +00:00
|
|
|
overlap += 16 * (xr - xl) * (yb - yt);
|
2022-03-25 12:20:32 +00:00
|
|
|
} else if (client->keepBelow() && !client->isDock()) { // ignore KeepBelow windows
|
2022-04-22 17:54:31 +00:00
|
|
|
overlap += 0; // for placement (see X11Window::belongsToLayer() for Dock)
|
2022-03-25 12:20:32 +00:00
|
|
|
} else {
|
2013-01-22 19:09:42 +00:00
|
|
|
overlap += (xr - xl) * (yb - yt);
|
2022-03-25 12:20:32 +00:00
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
}
|
|
|
|
}
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2022-03-23 10:13:38 +00:00
|
|
|
// CT first time we get no overlap we stop.
|
2011-01-30 14:34:42 +00:00
|
|
|
if (overlap == none) {
|
2007-04-29 17:35:43 +00:00
|
|
|
x_optimal = x;
|
|
|
|
y_optimal = y;
|
|
|
|
break;
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2011-01-30 14:34:42 +00:00
|
|
|
if (first_pass) {
|
2007-04-29 17:35:43 +00:00
|
|
|
first_pass = false;
|
|
|
|
min_overlap = overlap;
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2022-03-23 10:13:38 +00:00
|
|
|
// CT save the best position and the minimum overlap up to now
|
2011-01-30 14:34:42 +00:00
|
|
|
else if (overlap >= none && overlap < min_overlap) {
|
2007-04-29 17:35:43 +00:00
|
|
|
min_overlap = overlap;
|
|
|
|
x_optimal = x;
|
|
|
|
y_optimal = y;
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
|
|
|
// really need to loop? test if there's any overlap
|
2011-01-30 14:34:42 +00:00
|
|
|
if (overlap > none) {
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2019-09-13 09:19:32 +00:00
|
|
|
possible = area.right();
|
2022-03-25 12:20:32 +00:00
|
|
|
if (possible - cw > x) {
|
2022-03-23 10:13:38 +00:00
|
|
|
possible -= cw;
|
2022-03-25 12:20:32 +00:00
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
|
|
|
// compare to the position of each client on the same desk
|
2022-03-23 10:13:38 +00:00
|
|
|
for (auto l = workspace()->stackingOrder().constBegin(); l != workspace()->stackingOrder().constEnd(); ++l) {
|
2022-04-18 08:01:51 +00:00
|
|
|
auto client = *l;
|
2013-01-22 19:09:42 +00:00
|
|
|
if (isIrrelevant(client, c, desktop)) {
|
2012-04-08 08:07:35 +00:00
|
|
|
continue;
|
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2022-03-23 10:13:38 +00:00
|
|
|
xl = client->x();
|
|
|
|
yt = client->y();
|
|
|
|
xr = xl + client->width();
|
|
|
|
yb = yt + client->height();
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2013-01-22 19:09:42 +00:00
|
|
|
// if not enough room above or under the current tested client
|
|
|
|
// determine the first non-overlapped x position
|
|
|
|
if ((y < yb) && (yt < ch + y)) {
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2022-03-25 12:20:32 +00:00
|
|
|
if ((xr > x) && (possible > xr)) {
|
2022-03-23 10:13:38 +00:00
|
|
|
possible = xr;
|
2022-03-25 12:20:32 +00:00
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2013-01-22 19:09:42 +00:00
|
|
|
basket = xl - cw;
|
2022-03-25 12:20:32 +00:00
|
|
|
if ((basket > x) && (possible > basket)) {
|
2022-03-23 10:13:38 +00:00
|
|
|
possible = basket;
|
2022-03-25 12:20:32 +00:00
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
}
|
|
|
|
}
|
2011-01-30 14:34:42 +00:00
|
|
|
x = possible;
|
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
|
|
|
// ... else ==> not enough x dimension (overlap was wrong on horizontal)
|
2011-01-30 14:34:42 +00:00
|
|
|
else if (overlap == w_wrong) {
|
2019-09-13 09:19:32 +00:00
|
|
|
x = area.left();
|
|
|
|
possible = area.bottom();
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2022-03-25 12:20:32 +00:00
|
|
|
if (possible - ch > y) {
|
2022-03-23 10:13:38 +00:00
|
|
|
possible -= ch;
|
2022-03-25 12:20:32 +00:00
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2022-03-23 10:13:38 +00:00
|
|
|
// test the position of each window on the desk
|
|
|
|
for (auto l = workspace()->stackingOrder().constBegin(); l != workspace()->stackingOrder().constEnd(); ++l) {
|
2022-04-18 08:01:51 +00:00
|
|
|
auto client = *l;
|
2013-01-22 19:09:42 +00:00
|
|
|
if (isIrrelevant(client, c, desktop)) {
|
2012-04-08 08:07:35 +00:00
|
|
|
continue;
|
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2022-03-23 10:13:38 +00:00
|
|
|
xl = client->x();
|
|
|
|
yt = client->y();
|
|
|
|
xr = xl + client->width();
|
|
|
|
yb = yt + client->height();
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2013-01-22 19:09:42 +00:00
|
|
|
// if not enough room to the left or right of the current tested client
|
|
|
|
// determine the first non-overlapped y position
|
2022-03-25 12:20:32 +00:00
|
|
|
if ((yb > y) && (possible > yb)) {
|
2022-03-23 10:13:38 +00:00
|
|
|
possible = yb;
|
2022-03-25 12:20:32 +00:00
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2013-01-22 19:09:42 +00:00
|
|
|
basket = yt - ch;
|
2022-03-25 12:20:32 +00:00
|
|
|
if ((basket > y) && (possible > basket)) {
|
2022-03-23 10:13:38 +00:00
|
|
|
possible = basket;
|
2022-03-25 12:20:32 +00:00
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
}
|
2011-01-30 14:34:42 +00:00
|
|
|
y = possible;
|
2007-04-29 17:35:43 +00:00
|
|
|
}
|
2019-09-13 09:19:32 +00:00
|
|
|
} while ((overlap != none) && (overlap != h_wrong) && (y < area.bottom()));
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2019-09-13 09:19:32 +00:00
|
|
|
if (ch >= area.height()) {
|
|
|
|
y_optimal = area.top();
|
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
|
|
|
// place the window
|
Rework async geometry updates
Window management features were written with synchronous geometry
updates in mind. Currently, this poses a big problem on Wayland because
geometry updates are done in asynchronous fashion there.
At the moment, geometry is updated in a so called pseudo-asynchronous
fashion, meaning that the frame geometry will be reset to the old value
once geometry updates are unblocked. The main drawback of this approach
is that it is too error prone, the data flow is hard to comprehend, etc.
It is worth noting that there is already a machinery to perform async
geometry which is used during interactive move/resize operations.
This change extends the move/resize geometry usage beyond interactive
move/resize to make asynchronous geometry updates less error prone and
easier to comprehend.
With the proposed solution, all geometry updates must be done on the
move/resize geometry first. After that, the new geometry is passed on to
the Client-specific implementation of moveResizeInternal().
To be more specific, the frameGeometry() returns the current frame
geometry, it is primarily useful only to the scene. If you want to move
or resize a window, you need to use moveResizeGeometry() because it
corresponds to the last requested frame geometry.
It is worth noting that the moveResizeGeometry() returns the desired
bounding geometry. The client may commit the xdg_toplevel surface with a
slightly smaller window geometry, for example to enforce a specific
aspect ratio. The client is not allowed to resize beyond the size as
indicated in moveResizeGeometry().
The data flow is very simple: moveResize() updates the move/resize
geometry and calls the client-specific implementation of the
moveResizeInternal() method. Based on whether a configure event is
needed, moveResizeInternal() will update the frameGeometry() either
immediately or after the client commits a new buffer.
Unfortunately, both the compositor and xdg-shell clients try to update
the window geometry. It means that it's possible to have conflicts
between the two. With this change, the compositor's move resize geometry
will be synced only if there are no pending configure events, meaning
that the user doesn't try to resize the window.
2021-04-30 18:26:09 +00:00
|
|
|
c->move(QPoint(x_optimal, y_optimal));
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2011-01-30 14:34:42 +00:00
|
|
|
void Placement::reinitCascading(int desktop)
|
|
|
|
{
|
|
|
|
// desktop == 0 - reinit all
|
|
|
|
if (desktop == 0) {
|
2007-04-29 17:35:43 +00:00
|
|
|
cci.clear();
|
2012-11-16 07:23:47 +00:00
|
|
|
for (uint i = 0; i < VirtualDesktopManager::self()->count(); ++i) {
|
2007-04-29 17:35:43 +00:00
|
|
|
DesktopCascadingInfo inf;
|
2011-01-30 14:34:42 +00:00
|
|
|
inf.pos = QPoint(-1, -1);
|
2007-04-29 17:35:43 +00:00
|
|
|
inf.col = 0;
|
|
|
|
inf.row = 0;
|
|
|
|
cci.append(inf);
|
|
|
|
}
|
2011-01-30 14:34:42 +00:00
|
|
|
} else {
|
2007-04-29 17:35:43 +00:00
|
|
|
cci[desktop - 1].pos = QPoint(-1, -1);
|
|
|
|
cci[desktop - 1].col = cci[desktop - 1].row = 0;
|
|
|
|
}
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2022-04-22 17:39:12 +00:00
|
|
|
QPoint Workspace::cascadeOffset(const Window *c) const
|
2012-08-25 16:20:34 +00:00
|
|
|
{
|
2022-05-16 20:13:39 +00:00
|
|
|
QRect area = clientArea(PlacementArea, c, c->frameGeometry().center()).toRect();
|
2022-03-23 10:13:38 +00:00
|
|
|
return QPoint(area.width() / 48, area.height() / 48);
|
2012-08-25 16:20:34 +00:00
|
|
|
}
|
|
|
|
|
2019-02-02 18:17:44 +00:00
|
|
|
/**
|
|
|
|
* Place windows in a cascading order, remembering positions for each desktop
|
2019-07-29 18:58:33 +00:00
|
|
|
*/
|
2022-04-22 17:39:12 +00:00
|
|
|
void Placement::placeCascaded(Window *c, const QRect &area, Policy nextPlacement)
|
2011-01-30 14:34:42 +00:00
|
|
|
{
|
2019-09-13 09:19:32 +00:00
|
|
|
Q_ASSERT(area.isValid());
|
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
if (!c->frameGeometry().isValid()) {
|
2019-04-09 10:15:02 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2007-04-29 17:35:43 +00:00
|
|
|
/* cascadePlacement by Cristian Tibirna (tibirna@kde.org) (30Jan98)
|
|
|
|
*/
|
|
|
|
// work coords
|
|
|
|
int xp, yp;
|
|
|
|
|
2022-03-23 10:13:38 +00:00
|
|
|
// CT how do I get from the 'Client' class the size that NW squarish "handle"
|
2013-05-08 11:55:20 +00:00
|
|
|
const QPoint delta = workspace()->cascadeOffset(c);
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2012-11-16 07:23:47 +00:00
|
|
|
const int dn = c->desktop() == 0 || c->isOnAllDesktops() ? (VirtualDesktopManager::self()->current() - 1) : (c->desktop() - 1);
|
2007-04-29 17:35:43 +00:00
|
|
|
|
|
|
|
// initialize often used vars: width and height of c; we gain speed
|
|
|
|
const int ch = c->height();
|
|
|
|
const int cw = c->width();
|
2019-09-13 09:19:32 +00:00
|
|
|
const int X = area.left();
|
|
|
|
const int Y = area.top();
|
|
|
|
const int H = area.height();
|
|
|
|
const int W = area.width();
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2022-03-25 12:20:32 +00:00
|
|
|
if (nextPlacement == Unknown) {
|
2007-04-29 17:35:43 +00:00
|
|
|
nextPlacement = Smart;
|
2022-03-25 12:20:32 +00:00
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2022-03-23 10:13:38 +00:00
|
|
|
// initialize if needed
|
2011-01-30 14:34:42 +00:00
|
|
|
if (cci[dn].pos.x() < 0 || cci[dn].pos.x() < X || cci[dn].pos.y() < Y) {
|
2007-04-29 17:35:43 +00:00
|
|
|
cci[dn].pos = QPoint(X, Y);
|
|
|
|
cci[dn].col = cci[dn].row = 0;
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
|
|
|
xp = cci[dn].pos.x();
|
|
|
|
yp = cci[dn].pos.y();
|
|
|
|
|
2022-03-23 10:13:38 +00:00
|
|
|
// here to touch in case people vote for resize on placement
|
2022-03-25 12:20:32 +00:00
|
|
|
if ((yp + ch) > H) {
|
2022-03-23 10:13:38 +00:00
|
|
|
yp = Y;
|
2022-03-25 12:20:32 +00:00
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2011-01-30 14:34:42 +00:00
|
|
|
if ((xp + cw) > W) {
|
|
|
|
if (!yp) {
|
|
|
|
place(c, area, nextPlacement);
|
|
|
|
return;
|
2022-03-25 12:20:32 +00:00
|
|
|
} else {
|
2022-03-23 10:13:38 +00:00
|
|
|
xp = X;
|
2022-03-25 12:20:32 +00:00
|
|
|
}
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2022-03-23 10:13:38 +00:00
|
|
|
// if this isn't the first window
|
2011-01-30 14:34:42 +00:00
|
|
|
if (cci[dn].pos.x() != X && cci[dn].pos.y() != Y) {
|
2007-04-29 17:35:43 +00:00
|
|
|
/* The following statements cause an internal compiler error with
|
|
|
|
* egcs-2.91.66 on SuSE Linux 6.3. The equivalent forms compile fine.
|
|
|
|
* 22-Dec-1999 CS
|
|
|
|
*
|
2012-08-25 16:20:34 +00:00
|
|
|
* if (xp != X && yp == Y) xp = delta.x() * (++(cci[dn].col));
|
|
|
|
* if (yp != Y && xp == X) yp = delta.y() * (++(cci[dn].row));
|
2007-04-29 17:35:43 +00:00
|
|
|
*/
|
2011-01-30 14:34:42 +00:00
|
|
|
if (xp != X && yp == Y) {
|
2007-04-29 17:35:43 +00:00
|
|
|
++(cci[dn].col);
|
2012-08-25 16:20:34 +00:00
|
|
|
xp = delta.x() * cci[dn].col;
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
|
|
|
if (yp != Y && xp == X) {
|
2007-04-29 17:35:43 +00:00
|
|
|
++(cci[dn].row);
|
2012-08-25 16:20:34 +00:00
|
|
|
yp = delta.y() * cci[dn].row;
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
|
|
|
// last resort: if still doesn't fit, smart place it
|
2011-01-30 14:34:42 +00:00
|
|
|
if (((xp + cw) > W - X) || ((yp + ch) > H - Y)) {
|
|
|
|
place(c, area, nextPlacement);
|
2007-04-29 17:35:43 +00:00
|
|
|
return;
|
|
|
|
}
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
|
|
|
// place the window
|
|
|
|
c->move(QPoint(xp, yp));
|
|
|
|
|
|
|
|
// new position
|
2012-08-25 16:20:34 +00:00
|
|
|
cci[dn].pos = QPoint(xp + delta.x(), yp + delta.y());
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2019-02-02 18:17:44 +00:00
|
|
|
/**
|
|
|
|
* Place windows centered, on top of all others
|
2019-07-29 18:58:33 +00:00
|
|
|
*/
|
2022-05-16 20:13:39 +00:00
|
|
|
void Placement::placeCentered(Window *c, const QRectF &area, Policy /*next*/)
|
2011-01-30 14:34:42 +00:00
|
|
|
{
|
2019-09-13 09:19:32 +00:00
|
|
|
Q_ASSERT(area.isValid());
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2021-06-09 12:30:09 +00:00
|
|
|
if (!c->frameGeometry().isValid()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-09-13 09:19:32 +00:00
|
|
|
const int xp = area.left() + (area.width() - c->width()) / 2;
|
|
|
|
const int yp = area.top() + (area.height() - c->height()) / 2;
|
2007-04-29 17:35:43 +00:00
|
|
|
|
|
|
|
// place the window
|
|
|
|
c->move(QPoint(xp, yp));
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2019-02-02 18:17:44 +00:00
|
|
|
/**
|
|
|
|
* Place windows in the (0,0) corner, on top of all others
|
2019-07-29 18:58:33 +00:00
|
|
|
*/
|
2022-04-22 17:39:12 +00:00
|
|
|
void Placement::placeZeroCornered(Window *c, const QRect &area, Policy /*next*/)
|
2011-01-30 14:34:42 +00:00
|
|
|
{
|
2019-09-13 09:19:32 +00:00
|
|
|
Q_ASSERT(area.isValid());
|
|
|
|
|
2007-04-29 17:35:43 +00:00
|
|
|
// get the maximum allowed windows space and desk's origin
|
2019-09-13 09:19:32 +00:00
|
|
|
c->move(area.topLeft());
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2022-04-22 17:39:12 +00:00
|
|
|
void Placement::placeUtility(Window *c, const QRect &area, Policy /*next*/)
|
2011-01-30 14:34:42 +00:00
|
|
|
{
|
2022-03-23 10:13:38 +00:00
|
|
|
// TODO kwin should try to place utility windows next to their mainwindow,
|
|
|
|
// preferably at the right edge, and going down if there are more of them
|
|
|
|
// if there's not enough place outside the mainwindow, it should prefer
|
|
|
|
// top-right corner
|
2007-04-29 17:35:43 +00:00
|
|
|
// use the default placement for now
|
2011-01-30 14:34:42 +00:00
|
|
|
place(c, area, Default);
|
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2022-04-22 17:39:12 +00:00
|
|
|
void Placement::placeOnScreenDisplay(Window *c, const QRect &area)
|
2014-02-12 09:50:13 +00:00
|
|
|
{
|
2019-09-13 09:19:32 +00:00
|
|
|
Q_ASSERT(area.isValid());
|
|
|
|
|
Position OSD a bit farther down
Summary:
A common user complains is that our OSDs--particularly the volume OSD--are too intrusive
and get in the way of the screen content. For example when adjusting the volume while
watching a full-screen video, the volume change OSD will typically appear right in the
middle of an actor's face.
D20569 was an attempt to use a horizontal OSD to alleviate this issue. It mostly worked,
but IMO it was still positioned too high up.
This patch moved the placement down a little bit to make the OSD appear even farther from
the center of the screen to make it less likely to
I tried not to move it down too far or else it would interfere with subtitles in videos.
Test Plan:
With D20569:
{F8269172}
{F8269164}
{F8269163}
Reviewers: #kwin, #vdg, broulik, ndavis, zzag
Reviewed By: #kwin, #vdg, ndavis, zzag
Subscribers: meven, ndavis, niccolove, baberts, davidedmundson, filipf, zzag, kori, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D29263
2020-04-28 18:29:48 +00:00
|
|
|
// place at lower area of the screen
|
2022-03-23 10:13:38 +00:00
|
|
|
const int x = area.left() + (area.width() - c->width()) / 2;
|
Position OSD a bit farther down
Summary:
A common user complains is that our OSDs--particularly the volume OSD--are too intrusive
and get in the way of the screen content. For example when adjusting the volume while
watching a full-screen video, the volume change OSD will typically appear right in the
middle of an actor's face.
D20569 was an attempt to use a horizontal OSD to alleviate this issue. It mostly worked,
but IMO it was still positioned too high up.
This patch moved the placement down a little bit to make the OSD appear even farther from
the center of the screen to make it less likely to
I tried not to move it down too far or else it would interfere with subtitles in videos.
Test Plan:
With D20569:
{F8269172}
{F8269164}
{F8269163}
Reviewers: #kwin, #vdg, broulik, ndavis, zzag
Reviewed By: #kwin, #vdg, ndavis, zzag
Subscribers: meven, ndavis, niccolove, baberts, davidedmundson, filipf, zzag, kori, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D29263
2020-04-28 18:29:48 +00:00
|
|
|
const int y = area.top() + 2 * area.height() / 3 - c->height() / 2;
|
2014-02-12 09:50:13 +00:00
|
|
|
|
|
|
|
c->move(QPoint(x, y));
|
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2022-04-22 17:39:12 +00:00
|
|
|
void Placement::placeTransient(Window *c)
|
2015-09-11 11:31:41 +00:00
|
|
|
{
|
2018-12-27 19:22:53 +00:00
|
|
|
const auto parent = c->transientFor();
|
2022-05-16 20:13:39 +00:00
|
|
|
const QRectF screen = Workspace::self()->clientArea(parent->isFullScreen() ? FullScreenArea : PlacementArea, parent);
|
Rework async geometry updates
Window management features were written with synchronous geometry
updates in mind. Currently, this poses a big problem on Wayland because
geometry updates are done in asynchronous fashion there.
At the moment, geometry is updated in a so called pseudo-asynchronous
fashion, meaning that the frame geometry will be reset to the old value
once geometry updates are unblocked. The main drawback of this approach
is that it is too error prone, the data flow is hard to comprehend, etc.
It is worth noting that there is already a machinery to perform async
geometry which is used during interactive move/resize operations.
This change extends the move/resize geometry usage beyond interactive
move/resize to make asynchronous geometry updates less error prone and
easier to comprehend.
With the proposed solution, all geometry updates must be done on the
move/resize geometry first. After that, the new geometry is passed on to
the Client-specific implementation of moveResizeInternal().
To be more specific, the frameGeometry() returns the current frame
geometry, it is primarily useful only to the scene. If you want to move
or resize a window, you need to use moveResizeGeometry() because it
corresponds to the last requested frame geometry.
It is worth noting that the moveResizeGeometry() returns the desired
bounding geometry. The client may commit the xdg_toplevel surface with a
slightly smaller window geometry, for example to enforce a specific
aspect ratio. The client is not allowed to resize beyond the size as
indicated in moveResizeGeometry().
The data flow is very simple: moveResize() updates the move/resize
geometry and calls the client-specific implementation of the
moveResizeInternal() method. Based on whether a configure event is
needed, moveResizeInternal() will update the frameGeometry() either
immediately or after the client commits a new buffer.
Unfortunately, both the compositor and xdg-shell clients try to update
the window geometry. It means that it's possible to have conflicts
between the two. With this change, the compositor's move resize geometry
will be synced only if there are no pending configure events, meaning
that the user doesn't try to resize the window.
2021-04-30 18:26:09 +00:00
|
|
|
c->moveResize(c->transientPlacement(screen));
|
2018-10-19 22:21:54 +00:00
|
|
|
|
|
|
|
// Potentially a client could set no constraint adjustments
|
|
|
|
// and we'll be offscreen.
|
|
|
|
|
|
|
|
// The spec implies we should place window the offscreen. However,
|
|
|
|
// practically Qt doesn't set any constraint adjustments yet so we can't.
|
|
|
|
// Also kwin generally doesn't let clients do what they want
|
2022-05-16 20:13:39 +00:00
|
|
|
if (!screen.contains(c->moveResizeGeometry().toAlignedRect())) {
|
2018-10-19 22:21:54 +00:00
|
|
|
c->keepInArea(screen);
|
2016-03-03 14:38:18 +00:00
|
|
|
}
|
2015-09-11 11:31:41 +00:00
|
|
|
}
|
|
|
|
|
2022-04-22 17:39:12 +00:00
|
|
|
void Placement::placeDialog(Window *c, const QRect &area, Policy nextPlacement)
|
2011-01-30 14:34:42 +00:00
|
|
|
{
|
|
|
|
placeOnMainWindow(c, area, nextPlacement);
|
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2022-04-22 17:39:12 +00:00
|
|
|
void Placement::placeUnderMouse(Window *c, const QRect &area, Policy /*next*/)
|
2011-01-30 14:34:42 +00:00
|
|
|
{
|
2019-09-13 09:19:32 +00:00
|
|
|
Q_ASSERT(area.isValid());
|
|
|
|
|
2022-05-16 20:13:39 +00:00
|
|
|
QRectF geom = c->frameGeometry();
|
2020-04-02 16:18:01 +00:00
|
|
|
geom.moveCenter(Cursors::self()->mouse()->pos());
|
2011-01-30 14:34:42 +00:00
|
|
|
c->move(geom.topLeft());
|
2022-03-23 10:13:38 +00:00
|
|
|
c->keepInArea(area); // make sure it's kept inside workarea
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2022-04-22 17:39:12 +00:00
|
|
|
void Placement::placeOnMainWindow(Window *c, const QRect &area, Policy nextPlacement)
|
2011-01-30 14:34:42 +00:00
|
|
|
{
|
2019-09-13 09:19:32 +00:00
|
|
|
Q_ASSERT(area.isValid());
|
|
|
|
|
2022-03-25 12:20:32 +00:00
|
|
|
if (nextPlacement == Unknown) {
|
2007-04-29 17:35:43 +00:00
|
|
|
nextPlacement = Centered;
|
2022-03-25 12:20:32 +00:00
|
|
|
}
|
|
|
|
if (nextPlacement == Maximizing) { // maximize if needed
|
2011-01-30 14:34:42 +00:00
|
|
|
placeMaximizing(c, area, NoPlacement);
|
2022-03-25 12:20:32 +00:00
|
|
|
}
|
2022-04-23 08:33:23 +00:00
|
|
|
auto mainwindows = c->mainWindows();
|
2022-04-22 17:39:12 +00:00
|
|
|
Window *place_on = nullptr;
|
|
|
|
Window *place_on2 = nullptr;
|
2007-04-29 17:35:43 +00:00
|
|
|
int mains_count = 0;
|
2022-03-23 10:13:38 +00:00
|
|
|
for (auto it = mainwindows.constBegin(); it != mainwindows.constEnd(); ++it) {
|
2022-03-25 12:20:32 +00:00
|
|
|
if (mainwindows.count() > 1 && (*it)->isSpecialWindow()) {
|
2007-04-29 17:35:43 +00:00
|
|
|
continue; // don't consider toolbars etc when placing
|
2022-03-25 12:20:32 +00:00
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
++mains_count;
|
|
|
|
place_on2 = *it;
|
2011-01-30 14:34:42 +00:00
|
|
|
if ((*it)->isOnCurrentDesktop()) {
|
2022-03-25 12:20:32 +00:00
|
|
|
if (place_on == nullptr) {
|
2007-04-29 17:35:43 +00:00
|
|
|
place_on = *it;
|
2022-03-25 12:20:32 +00:00
|
|
|
} else {
|
2011-01-30 14:34:42 +00:00
|
|
|
// two or more on current desktop -> center
|
|
|
|
// That's the default at least. However, with maximizing placement
|
|
|
|
// policy as the default, the dialog should be either maximized or
|
|
|
|
// made as large as its maximum size and then placed centered.
|
|
|
|
// So the nextPlacement argument allows chaining. In this case, nextPlacement
|
|
|
|
// is Maximizing and it will call placeCentered().
|
|
|
|
place(c, area, Centered);
|
2007-04-29 17:35:43 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
Use nullptr everywhere
Summary:
Because KWin is a very old project, we use three kinds of null pointer
literals: 0, NULL, and nullptr. Since C++11, it's recommended to use
nullptr keyword.
This change converts all usages of 0 and NULL literal to nullptr. Even
though it breaks git history, we need to do it in order to have consistent
code as well to ease code reviews (it's very tempting for some people to
add unrelated changes to their patches, e.g. converting NULL to nullptr).
Test Plan: Compiles.
Reviewers: #kwin, davidedmundson, romangg
Reviewed By: #kwin, davidedmundson, romangg
Subscribers: romangg, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D23618
2019-09-19 14:46:54 +00:00
|
|
|
if (place_on == nullptr) {
|
2011-01-30 14:34:42 +00:00
|
|
|
// 'mains_count' is used because it doesn't include ignored mainwindows
|
|
|
|
if (mains_count != 1) {
|
|
|
|
place(c, area, Centered);
|
2007-04-29 17:35:43 +00:00
|
|
|
return;
|
|
|
|
}
|
2011-01-30 14:34:42 +00:00
|
|
|
place_on = place_on2; // use the only window filtered together with 'mains_count'
|
|
|
|
}
|
|
|
|
if (place_on->isDesktop()) {
|
|
|
|
place(c, area, Centered);
|
2007-05-07 12:37:12 +00:00
|
|
|
return;
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2022-05-16 20:13:39 +00:00
|
|
|
QRect geom = c->frameGeometry().toRect();
|
|
|
|
geom.moveCenter(place_on->frameGeometry().center().toPoint());
|
2011-01-30 14:34:42 +00:00
|
|
|
c->move(geom.topLeft());
|
2007-04-29 17:35:43 +00:00
|
|
|
// get area again, because the mainwindow may be on different xinerama screen
|
2022-05-16 20:13:39 +00:00
|
|
|
const QRect placementArea = workspace()->clientArea(PlacementArea, c).toRect();
|
2022-03-23 10:13:38 +00:00
|
|
|
c->keepInArea(placementArea); // make sure it's kept inside workarea
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2022-04-22 17:39:12 +00:00
|
|
|
void Placement::placeMaximizing(Window *c, const QRect &area, Policy nextPlacement)
|
2011-01-30 14:34:42 +00:00
|
|
|
{
|
2019-09-13 09:19:32 +00:00
|
|
|
Q_ASSERT(area.isValid());
|
|
|
|
|
2022-03-25 12:20:32 +00:00
|
|
|
if (nextPlacement == Unknown) {
|
2007-04-29 17:35:43 +00:00
|
|
|
nextPlacement = Smart;
|
2022-03-25 12:20:32 +00:00
|
|
|
}
|
2011-01-30 14:34:42 +00:00
|
|
|
if (c->isMaximizable() && c->maxSize().width() >= area.width() && c->maxSize().height() >= area.height()) {
|
2022-03-25 12:20:32 +00:00
|
|
|
if (workspace()->clientArea(MaximizeArea, c) == area) {
|
2014-12-02 12:49:08 +00:00
|
|
|
c->maximize(MaximizeFull);
|
2022-03-25 12:20:32 +00:00
|
|
|
} else { // if the geometry doesn't match default maximize area (xinerama case?),
|
2011-01-30 14:34:42 +00:00
|
|
|
// it's probably better to use the given area
|
Rework async geometry updates
Window management features were written with synchronous geometry
updates in mind. Currently, this poses a big problem on Wayland because
geometry updates are done in asynchronous fashion there.
At the moment, geometry is updated in a so called pseudo-asynchronous
fashion, meaning that the frame geometry will be reset to the old value
once geometry updates are unblocked. The main drawback of this approach
is that it is too error prone, the data flow is hard to comprehend, etc.
It is worth noting that there is already a machinery to perform async
geometry which is used during interactive move/resize operations.
This change extends the move/resize geometry usage beyond interactive
move/resize to make asynchronous geometry updates less error prone and
easier to comprehend.
With the proposed solution, all geometry updates must be done on the
move/resize geometry first. After that, the new geometry is passed on to
the Client-specific implementation of moveResizeInternal().
To be more specific, the frameGeometry() returns the current frame
geometry, it is primarily useful only to the scene. If you want to move
or resize a window, you need to use moveResizeGeometry() because it
corresponds to the last requested frame geometry.
It is worth noting that the moveResizeGeometry() returns the desired
bounding geometry. The client may commit the xdg_toplevel surface with a
slightly smaller window geometry, for example to enforce a specific
aspect ratio. The client is not allowed to resize beyond the size as
indicated in moveResizeGeometry().
The data flow is very simple: moveResize() updates the move/resize
geometry and calls the client-specific implementation of the
moveResizeInternal() method. Based on whether a configure event is
needed, moveResizeInternal() will update the frameGeometry() either
immediately or after the client commits a new buffer.
Unfortunately, both the compositor and xdg-shell clients try to update
the window geometry. It means that it's possible to have conflicts
between the two. With this change, the compositor's move resize geometry
will be synced only if there are no pending configure events, meaning
that the user doesn't try to resize the window.
2021-04-30 18:26:09 +00:00
|
|
|
c->moveResize(area);
|
2007-04-29 17:35:43 +00:00
|
|
|
}
|
2011-01-30 14:34:42 +00:00
|
|
|
} else {
|
|
|
|
c->resizeWithChecks(c->maxSize().boundedTo(area.size()));
|
|
|
|
place(c, area, nextPlacement);
|
2007-04-29 17:35:43 +00:00
|
|
|
}
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2012-11-22 10:07:33 +00:00
|
|
|
void Placement::cascadeDesktop()
|
|
|
|
{
|
|
|
|
Workspace *ws = Workspace::self();
|
2012-11-16 07:23:47 +00:00
|
|
|
const int desktop = VirtualDesktopManager::self()->current();
|
2012-11-22 10:07:33 +00:00
|
|
|
reinitCascading(desktop);
|
2021-11-03 11:55:31 +00:00
|
|
|
const auto stackingOrder = ws->stackingOrder();
|
2022-04-28 07:44:11 +00:00
|
|
|
for (Window *window : stackingOrder) {
|
|
|
|
if (!window->isClient() || (!window->isOnCurrentDesktop()) || (window->isMinimized()) || (window->isOnAllDesktops()) || (!window->isMovable())) {
|
2012-11-22 10:07:33 +00:00
|
|
|
continue;
|
2022-03-25 12:20:32 +00:00
|
|
|
}
|
2022-05-16 20:13:39 +00:00
|
|
|
const QRect placementArea = workspace()->clientArea(PlacementArea, window).toRect();
|
2022-04-28 07:44:11 +00:00
|
|
|
placeCascaded(window, placementArea);
|
2012-11-22 10:07:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Placement::unclutterDesktop()
|
|
|
|
{
|
2017-06-19 15:56:15 +00:00
|
|
|
const auto &clients = Workspace::self()->allClientList();
|
2012-11-22 10:07:33 +00:00
|
|
|
for (int i = clients.size() - 1; i >= 0; i--) {
|
2017-06-19 15:56:15 +00:00
|
|
|
auto client = clients.at(i);
|
2022-03-25 12:20:32 +00:00
|
|
|
if ((!client->isOnCurrentDesktop()) || (client->isMinimized()) || (client->isOnAllDesktops()) || (!client->isMovable())) {
|
2012-11-22 10:07:33 +00:00
|
|
|
continue;
|
2022-03-25 12:20:32 +00:00
|
|
|
}
|
2022-05-16 20:13:39 +00:00
|
|
|
const QRect placementArea = workspace()->clientArea(PlacementArea, client).toRect();
|
2019-09-13 09:19:32 +00:00
|
|
|
placeSmart(client, placementArea);
|
2012-11-22 10:07:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-04-29 17:35:43 +00:00
|
|
|
#endif
|
|
|
|
|
2022-03-23 10:13:38 +00:00
|
|
|
const char *Placement::policyToString(Policy policy)
|
2011-01-30 14:34:42 +00:00
|
|
|
{
|
2022-03-23 10:13:38 +00:00
|
|
|
const char *const policies[] = {
|
2011-01-30 14:34:42 +00:00
|
|
|
"NoPlacement", "Default", "XXX should never see", "Random", "Smart", "Cascade", "Centered",
|
2022-03-23 10:13:38 +00:00
|
|
|
"ZeroCornered", "UnderMouse", "OnMainWindow", "Maximizing"};
|
|
|
|
Q_ASSERT(policy < int(sizeof(policies) / sizeof(policies[0])));
|
|
|
|
return policies[policy];
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
|
|
|
#ifndef KCMRULES
|
|
|
|
|
|
|
|
// ********************
|
|
|
|
// Workspace
|
|
|
|
// ********************
|
|
|
|
|
2022-05-16 20:13:39 +00:00
|
|
|
void Window::packTo(qreal left, qreal top)
|
2013-04-15 18:43:50 +00:00
|
|
|
{
|
2020-04-02 16:18:01 +00:00
|
|
|
workspace()->updateFocusMousePosition(Cursors::self()->mouse()->pos()); // may cause leave event;
|
2015-01-28 23:14:47 +00:00
|
|
|
|
2022-04-14 12:33:28 +00:00
|
|
|
const Output *oldOutput = output();
|
2022-05-16 20:13:39 +00:00
|
|
|
move(QPointF(left, top));
|
2021-08-28 14:51:00 +00:00
|
|
|
if (output() != oldOutput) {
|
2022-04-23 08:33:23 +00:00
|
|
|
workspace()->sendWindowToOutput(this, output()); // checks rule validity
|
2022-03-25 12:20:32 +00:00
|
|
|
if (maximizeMode() != MaximizeRestore) {
|
2013-04-15 18:43:50 +00:00
|
|
|
checkWorkspacePosition();
|
2022-03-25 12:20:32 +00:00
|
|
|
}
|
2013-04-15 18:43:50 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-02 18:17:44 +00:00
|
|
|
/**
|
|
|
|
* Moves active window left until in bumps into another window or workarea edge.
|
2019-07-29 18:58:33 +00:00
|
|
|
*/
|
2021-10-04 16:20:10 +00:00
|
|
|
void Workspace::slotWindowMoveLeft()
|
2011-01-30 14:34:42 +00:00
|
|
|
{
|
2022-04-23 08:33:23 +00:00
|
|
|
if (m_activeWindow && m_activeWindow->isMovable()) {
|
2022-05-16 20:13:39 +00:00
|
|
|
const QRectF geometry = m_activeWindow->moveResizeGeometry();
|
2022-04-23 08:33:23 +00:00
|
|
|
m_activeWindow->packTo(packPositionLeft(m_activeWindow, geometry.left(), true),
|
|
|
|
geometry.y());
|
Rework async geometry updates
Window management features were written with synchronous geometry
updates in mind. Currently, this poses a big problem on Wayland because
geometry updates are done in asynchronous fashion there.
At the moment, geometry is updated in a so called pseudo-asynchronous
fashion, meaning that the frame geometry will be reset to the old value
once geometry updates are unblocked. The main drawback of this approach
is that it is too error prone, the data flow is hard to comprehend, etc.
It is worth noting that there is already a machinery to perform async
geometry which is used during interactive move/resize operations.
This change extends the move/resize geometry usage beyond interactive
move/resize to make asynchronous geometry updates less error prone and
easier to comprehend.
With the proposed solution, all geometry updates must be done on the
move/resize geometry first. After that, the new geometry is passed on to
the Client-specific implementation of moveResizeInternal().
To be more specific, the frameGeometry() returns the current frame
geometry, it is primarily useful only to the scene. If you want to move
or resize a window, you need to use moveResizeGeometry() because it
corresponds to the last requested frame geometry.
It is worth noting that the moveResizeGeometry() returns the desired
bounding geometry. The client may commit the xdg_toplevel surface with a
slightly smaller window geometry, for example to enforce a specific
aspect ratio. The client is not allowed to resize beyond the size as
indicated in moveResizeGeometry().
The data flow is very simple: moveResize() updates the move/resize
geometry and calls the client-specific implementation of the
moveResizeInternal() method. Based on whether a configure event is
needed, moveResizeInternal() will update the frameGeometry() either
immediately or after the client commits a new buffer.
Unfortunately, both the compositor and xdg-shell clients try to update
the window geometry. It means that it's possible to have conflicts
between the two. With this change, the compositor's move resize geometry
will be synced only if there are no pending configure events, meaning
that the user doesn't try to resize the window.
2021-04-30 18:26:09 +00:00
|
|
|
}
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2021-10-04 16:20:10 +00:00
|
|
|
void Workspace::slotWindowMoveRight()
|
2011-01-30 14:34:42 +00:00
|
|
|
{
|
2022-04-23 08:33:23 +00:00
|
|
|
if (m_activeWindow && m_activeWindow->isMovable()) {
|
2022-05-16 20:13:39 +00:00
|
|
|
const QRectF geometry = m_activeWindow->moveResizeGeometry();
|
|
|
|
qDebug() << geometry;
|
|
|
|
qDebug() << packPositionRight(m_activeWindow, geometry.right(), true);
|
|
|
|
m_activeWindow->packTo(packPositionRight(m_activeWindow, geometry.right(), true) - geometry.width(),
|
2022-04-23 08:33:23 +00:00
|
|
|
geometry.y());
|
Rework async geometry updates
Window management features were written with synchronous geometry
updates in mind. Currently, this poses a big problem on Wayland because
geometry updates are done in asynchronous fashion there.
At the moment, geometry is updated in a so called pseudo-asynchronous
fashion, meaning that the frame geometry will be reset to the old value
once geometry updates are unblocked. The main drawback of this approach
is that it is too error prone, the data flow is hard to comprehend, etc.
It is worth noting that there is already a machinery to perform async
geometry which is used during interactive move/resize operations.
This change extends the move/resize geometry usage beyond interactive
move/resize to make asynchronous geometry updates less error prone and
easier to comprehend.
With the proposed solution, all geometry updates must be done on the
move/resize geometry first. After that, the new geometry is passed on to
the Client-specific implementation of moveResizeInternal().
To be more specific, the frameGeometry() returns the current frame
geometry, it is primarily useful only to the scene. If you want to move
or resize a window, you need to use moveResizeGeometry() because it
corresponds to the last requested frame geometry.
It is worth noting that the moveResizeGeometry() returns the desired
bounding geometry. The client may commit the xdg_toplevel surface with a
slightly smaller window geometry, for example to enforce a specific
aspect ratio. The client is not allowed to resize beyond the size as
indicated in moveResizeGeometry().
The data flow is very simple: moveResize() updates the move/resize
geometry and calls the client-specific implementation of the
moveResizeInternal() method. Based on whether a configure event is
needed, moveResizeInternal() will update the frameGeometry() either
immediately or after the client commits a new buffer.
Unfortunately, both the compositor and xdg-shell clients try to update
the window geometry. It means that it's possible to have conflicts
between the two. With this change, the compositor's move resize geometry
will be synced only if there are no pending configure events, meaning
that the user doesn't try to resize the window.
2021-04-30 18:26:09 +00:00
|
|
|
}
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2021-10-04 16:20:10 +00:00
|
|
|
void Workspace::slotWindowMoveUp()
|
2011-01-30 14:34:42 +00:00
|
|
|
{
|
2022-04-23 08:33:23 +00:00
|
|
|
if (m_activeWindow && m_activeWindow->isMovable()) {
|
2022-05-16 20:13:39 +00:00
|
|
|
const QRectF geometry = m_activeWindow->moveResizeGeometry();
|
2022-04-23 08:33:23 +00:00
|
|
|
m_activeWindow->packTo(geometry.x(),
|
|
|
|
packPositionUp(m_activeWindow, geometry.top(), true));
|
Rework async geometry updates
Window management features were written with synchronous geometry
updates in mind. Currently, this poses a big problem on Wayland because
geometry updates are done in asynchronous fashion there.
At the moment, geometry is updated in a so called pseudo-asynchronous
fashion, meaning that the frame geometry will be reset to the old value
once geometry updates are unblocked. The main drawback of this approach
is that it is too error prone, the data flow is hard to comprehend, etc.
It is worth noting that there is already a machinery to perform async
geometry which is used during interactive move/resize operations.
This change extends the move/resize geometry usage beyond interactive
move/resize to make asynchronous geometry updates less error prone and
easier to comprehend.
With the proposed solution, all geometry updates must be done on the
move/resize geometry first. After that, the new geometry is passed on to
the Client-specific implementation of moveResizeInternal().
To be more specific, the frameGeometry() returns the current frame
geometry, it is primarily useful only to the scene. If you want to move
or resize a window, you need to use moveResizeGeometry() because it
corresponds to the last requested frame geometry.
It is worth noting that the moveResizeGeometry() returns the desired
bounding geometry. The client may commit the xdg_toplevel surface with a
slightly smaller window geometry, for example to enforce a specific
aspect ratio. The client is not allowed to resize beyond the size as
indicated in moveResizeGeometry().
The data flow is very simple: moveResize() updates the move/resize
geometry and calls the client-specific implementation of the
moveResizeInternal() method. Based on whether a configure event is
needed, moveResizeInternal() will update the frameGeometry() either
immediately or after the client commits a new buffer.
Unfortunately, both the compositor and xdg-shell clients try to update
the window geometry. It means that it's possible to have conflicts
between the two. With this change, the compositor's move resize geometry
will be synced only if there are no pending configure events, meaning
that the user doesn't try to resize the window.
2021-04-30 18:26:09 +00:00
|
|
|
}
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2021-10-04 16:20:10 +00:00
|
|
|
void Workspace::slotWindowMoveDown()
|
2011-01-30 14:34:42 +00:00
|
|
|
{
|
2022-04-23 08:33:23 +00:00
|
|
|
if (m_activeWindow && m_activeWindow->isMovable()) {
|
2022-05-16 20:13:39 +00:00
|
|
|
const QRectF geometry = m_activeWindow->moveResizeGeometry();
|
2022-04-23 08:33:23 +00:00
|
|
|
m_activeWindow->packTo(geometry.x(),
|
2022-05-16 20:13:39 +00:00
|
|
|
packPositionDown(m_activeWindow, geometry.bottom(), true) - geometry.height());
|
Rework async geometry updates
Window management features were written with synchronous geometry
updates in mind. Currently, this poses a big problem on Wayland because
geometry updates are done in asynchronous fashion there.
At the moment, geometry is updated in a so called pseudo-asynchronous
fashion, meaning that the frame geometry will be reset to the old value
once geometry updates are unblocked. The main drawback of this approach
is that it is too error prone, the data flow is hard to comprehend, etc.
It is worth noting that there is already a machinery to perform async
geometry which is used during interactive move/resize operations.
This change extends the move/resize geometry usage beyond interactive
move/resize to make asynchronous geometry updates less error prone and
easier to comprehend.
With the proposed solution, all geometry updates must be done on the
move/resize geometry first. After that, the new geometry is passed on to
the Client-specific implementation of moveResizeInternal().
To be more specific, the frameGeometry() returns the current frame
geometry, it is primarily useful only to the scene. If you want to move
or resize a window, you need to use moveResizeGeometry() because it
corresponds to the last requested frame geometry.
It is worth noting that the moveResizeGeometry() returns the desired
bounding geometry. The client may commit the xdg_toplevel surface with a
slightly smaller window geometry, for example to enforce a specific
aspect ratio. The client is not allowed to resize beyond the size as
indicated in moveResizeGeometry().
The data flow is very simple: moveResize() updates the move/resize
geometry and calls the client-specific implementation of the
moveResizeInternal() method. Based on whether a configure event is
needed, moveResizeInternal() will update the frameGeometry() either
immediately or after the client commits a new buffer.
Unfortunately, both the compositor and xdg-shell clients try to update
the window geometry. It means that it's possible to have conflicts
between the two. With this change, the compositor's move resize geometry
will be synced only if there are no pending configure events, meaning
that the user doesn't try to resize the window.
2021-04-30 18:26:09 +00:00
|
|
|
}
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2021-09-20 13:14:24 +00:00
|
|
|
/** Moves the active window to the center of the screen. */
|
|
|
|
void Workspace::slotWindowCenter()
|
|
|
|
{
|
2022-04-23 08:33:23 +00:00
|
|
|
if (m_activeWindow && m_activeWindow->isMovable()) {
|
2022-05-16 20:13:39 +00:00
|
|
|
const QRectF geometry = m_activeWindow->moveResizeGeometry();
|
|
|
|
QPointF center = clientArea(MaximizeArea, m_activeWindow).center();
|
2022-04-23 08:33:23 +00:00
|
|
|
m_activeWindow->packTo(center.x() - (geometry.width() / 2),
|
|
|
|
center.y() - (geometry.height() / 2));
|
2021-09-20 13:14:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-04 16:20:10 +00:00
|
|
|
void Workspace::slotWindowExpandHorizontal()
|
2011-01-30 14:34:42 +00:00
|
|
|
{
|
2022-04-23 08:33:23 +00:00
|
|
|
if (m_activeWindow) {
|
|
|
|
m_activeWindow->growHorizontal();
|
2022-03-25 12:20:32 +00:00
|
|
|
}
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2022-04-22 17:39:12 +00:00
|
|
|
void Window::growHorizontal()
|
2011-01-30 14:34:42 +00:00
|
|
|
{
|
2022-03-25 12:20:32 +00:00
|
|
|
if (!isResizable() || isShade()) {
|
2007-04-29 17:35:43 +00:00
|
|
|
return;
|
2022-03-25 12:20:32 +00:00
|
|
|
}
|
2022-05-16 20:13:39 +00:00
|
|
|
QRectF geom = moveResizeGeometry();
|
2011-01-30 14:34:42 +00:00
|
|
|
geom.setRight(workspace()->packPositionRight(this, geom.right(), true));
|
2022-05-16 20:13:39 +00:00
|
|
|
QSizeF adjsize = constrainFrameSize(geom.size(), SizeModeFixedW);
|
Rework async geometry updates
Window management features were written with synchronous geometry
updates in mind. Currently, this poses a big problem on Wayland because
geometry updates are done in asynchronous fashion there.
At the moment, geometry is updated in a so called pseudo-asynchronous
fashion, meaning that the frame geometry will be reset to the old value
once geometry updates are unblocked. The main drawback of this approach
is that it is too error prone, the data flow is hard to comprehend, etc.
It is worth noting that there is already a machinery to perform async
geometry which is used during interactive move/resize operations.
This change extends the move/resize geometry usage beyond interactive
move/resize to make asynchronous geometry updates less error prone and
easier to comprehend.
With the proposed solution, all geometry updates must be done on the
move/resize geometry first. After that, the new geometry is passed on to
the Client-specific implementation of moveResizeInternal().
To be more specific, the frameGeometry() returns the current frame
geometry, it is primarily useful only to the scene. If you want to move
or resize a window, you need to use moveResizeGeometry() because it
corresponds to the last requested frame geometry.
It is worth noting that the moveResizeGeometry() returns the desired
bounding geometry. The client may commit the xdg_toplevel surface with a
slightly smaller window geometry, for example to enforce a specific
aspect ratio. The client is not allowed to resize beyond the size as
indicated in moveResizeGeometry().
The data flow is very simple: moveResize() updates the move/resize
geometry and calls the client-specific implementation of the
moveResizeInternal() method. Based on whether a configure event is
needed, moveResizeInternal() will update the frameGeometry() either
immediately or after the client commits a new buffer.
Unfortunately, both the compositor and xdg-shell clients try to update
the window geometry. It means that it's possible to have conflicts
between the two. With this change, the compositor's move resize geometry
will be synced only if there are no pending configure events, meaning
that the user doesn't try to resize the window.
2021-04-30 18:26:09 +00:00
|
|
|
if (moveResizeGeometry().size() == adjsize && geom.size() != adjsize && resizeIncrements().width() > 1) { // take care of size increments
|
2022-05-16 20:13:39 +00:00
|
|
|
qreal newright = workspace()->packPositionRight(this, geom.right() + resizeIncrements().width() - 1, true);
|
2007-04-29 17:35:43 +00:00
|
|
|
// check that it hasn't grown outside of the area, due to size increments
|
|
|
|
// TODO this may be wrong?
|
2011-01-30 14:34:42 +00:00
|
|
|
if (workspace()->clientArea(MovementArea,
|
2021-08-17 11:55:34 +00:00
|
|
|
this,
|
2022-03-23 10:13:38 +00:00
|
|
|
QPoint((x() + newright) / 2, moveResizeGeometry().center().y()))
|
|
|
|
.right()
|
2022-03-25 12:20:32 +00:00
|
|
|
>= newright) {
|
2011-01-30 14:34:42 +00:00
|
|
|
geom.setRight(newright);
|
2022-03-25 12:20:32 +00:00
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
}
|
Refactor geometry constraints code
Summary:
Currently, there are a couple of issues with sizeForClientSize(). First
of all, we have a method called clientSizeToFrameSize() which does similar
thing except applying geometry constraints and checking window rules. The
other issue is that sizeForClientSize() is doing a bit too much, it checks
window rules, it applies a bunch of geometry constrains. Sometimes it
does not perform conversion between client sizes and frame sizes!
This change attempts to address those issues by replacing sizeForClientSize
with two similar methods and changing semantics of some methods of the
X11Client class.
The most significant difference between sizeForClientSize() and the new
methods is that neither constrainClientSize() nor constrainFrameSize()
check window rules. This is up to users of those methods. In many places,
we don't have to check window rules because we check isResizable(),
which returns false if the frame size is enforced by a window rule.
Reviewers: #kwin, davidedmundson
Reviewed By: #kwin, davidedmundson
Subscribers: davidedmundson, romangg, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D26828
2020-02-12 10:38:40 +00:00
|
|
|
geom.setSize(constrainFrameSize(geom.size(), SizeModeFixedW));
|
|
|
|
geom.setSize(constrainFrameSize(geom.size(), SizeModeFixedH));
|
2020-04-02 16:18:01 +00:00
|
|
|
workspace()->updateFocusMousePosition(Cursors::self()->mouse()->pos()); // may cause leave event;
|
Rework async geometry updates
Window management features were written with synchronous geometry
updates in mind. Currently, this poses a big problem on Wayland because
geometry updates are done in asynchronous fashion there.
At the moment, geometry is updated in a so called pseudo-asynchronous
fashion, meaning that the frame geometry will be reset to the old value
once geometry updates are unblocked. The main drawback of this approach
is that it is too error prone, the data flow is hard to comprehend, etc.
It is worth noting that there is already a machinery to perform async
geometry which is used during interactive move/resize operations.
This change extends the move/resize geometry usage beyond interactive
move/resize to make asynchronous geometry updates less error prone and
easier to comprehend.
With the proposed solution, all geometry updates must be done on the
move/resize geometry first. After that, the new geometry is passed on to
the Client-specific implementation of moveResizeInternal().
To be more specific, the frameGeometry() returns the current frame
geometry, it is primarily useful only to the scene. If you want to move
or resize a window, you need to use moveResizeGeometry() because it
corresponds to the last requested frame geometry.
It is worth noting that the moveResizeGeometry() returns the desired
bounding geometry. The client may commit the xdg_toplevel surface with a
slightly smaller window geometry, for example to enforce a specific
aspect ratio. The client is not allowed to resize beyond the size as
indicated in moveResizeGeometry().
The data flow is very simple: moveResize() updates the move/resize
geometry and calls the client-specific implementation of the
moveResizeInternal() method. Based on whether a configure event is
needed, moveResizeInternal() will update the frameGeometry() either
immediately or after the client commits a new buffer.
Unfortunately, both the compositor and xdg-shell clients try to update
the window geometry. It means that it's possible to have conflicts
between the two. With this change, the compositor's move resize geometry
will be synced only if there are no pending configure events, meaning
that the user doesn't try to resize the window.
2021-04-30 18:26:09 +00:00
|
|
|
moveResize(geom);
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
|
|
|
void Workspace::slotWindowShrinkHorizontal()
|
2011-01-30 14:34:42 +00:00
|
|
|
{
|
2022-04-23 08:33:23 +00:00
|
|
|
if (m_activeWindow) {
|
|
|
|
m_activeWindow->shrinkHorizontal();
|
2022-03-25 12:20:32 +00:00
|
|
|
}
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2022-04-22 17:39:12 +00:00
|
|
|
void Window::shrinkHorizontal()
|
2011-01-30 14:34:42 +00:00
|
|
|
{
|
2022-03-25 12:20:32 +00:00
|
|
|
if (!isResizable() || isShade()) {
|
2007-04-29 17:35:43 +00:00
|
|
|
return;
|
2022-03-25 12:20:32 +00:00
|
|
|
}
|
2022-05-16 20:13:39 +00:00
|
|
|
QRectF geom = moveResizeGeometry();
|
|
|
|
geom.setRight(workspace()->packPositionLeft(this, geom.right(), false) + 1);
|
2022-03-25 12:20:32 +00:00
|
|
|
if (geom.width() <= 1) {
|
2007-04-29 17:35:43 +00:00
|
|
|
return;
|
2022-03-25 12:20:32 +00:00
|
|
|
}
|
Refactor geometry constraints code
Summary:
Currently, there are a couple of issues with sizeForClientSize(). First
of all, we have a method called clientSizeToFrameSize() which does similar
thing except applying geometry constraints and checking window rules. The
other issue is that sizeForClientSize() is doing a bit too much, it checks
window rules, it applies a bunch of geometry constrains. Sometimes it
does not perform conversion between client sizes and frame sizes!
This change attempts to address those issues by replacing sizeForClientSize
with two similar methods and changing semantics of some methods of the
X11Client class.
The most significant difference between sizeForClientSize() and the new
methods is that neither constrainClientSize() nor constrainFrameSize()
check window rules. This is up to users of those methods. In many places,
we don't have to check window rules because we check isResizable(),
which returns false if the frame size is enforced by a window rule.
Reviewers: #kwin, davidedmundson
Reviewed By: #kwin, davidedmundson
Subscribers: davidedmundson, romangg, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D26828
2020-02-12 10:38:40 +00:00
|
|
|
geom.setSize(constrainFrameSize(geom.size(), SizeModeFixedW));
|
2015-01-28 23:14:47 +00:00
|
|
|
if (geom.width() > 20) {
|
2020-04-02 16:18:01 +00:00
|
|
|
workspace()->updateFocusMousePosition(Cursors::self()->mouse()->pos()); // may cause leave event;
|
Rework async geometry updates
Window management features were written with synchronous geometry
updates in mind. Currently, this poses a big problem on Wayland because
geometry updates are done in asynchronous fashion there.
At the moment, geometry is updated in a so called pseudo-asynchronous
fashion, meaning that the frame geometry will be reset to the old value
once geometry updates are unblocked. The main drawback of this approach
is that it is too error prone, the data flow is hard to comprehend, etc.
It is worth noting that there is already a machinery to perform async
geometry which is used during interactive move/resize operations.
This change extends the move/resize geometry usage beyond interactive
move/resize to make asynchronous geometry updates less error prone and
easier to comprehend.
With the proposed solution, all geometry updates must be done on the
move/resize geometry first. After that, the new geometry is passed on to
the Client-specific implementation of moveResizeInternal().
To be more specific, the frameGeometry() returns the current frame
geometry, it is primarily useful only to the scene. If you want to move
or resize a window, you need to use moveResizeGeometry() because it
corresponds to the last requested frame geometry.
It is worth noting that the moveResizeGeometry() returns the desired
bounding geometry. The client may commit the xdg_toplevel surface with a
slightly smaller window geometry, for example to enforce a specific
aspect ratio. The client is not allowed to resize beyond the size as
indicated in moveResizeGeometry().
The data flow is very simple: moveResize() updates the move/resize
geometry and calls the client-specific implementation of the
moveResizeInternal() method. Based on whether a configure event is
needed, moveResizeInternal() will update the frameGeometry() either
immediately or after the client commits a new buffer.
Unfortunately, both the compositor and xdg-shell clients try to update
the window geometry. It means that it's possible to have conflicts
between the two. With this change, the compositor's move resize geometry
will be synced only if there are no pending configure events, meaning
that the user doesn't try to resize the window.
2021-04-30 18:26:09 +00:00
|
|
|
moveResize(geom);
|
2015-01-28 23:14:47 +00:00
|
|
|
}
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2021-10-04 16:20:10 +00:00
|
|
|
void Workspace::slotWindowExpandVertical()
|
2011-01-30 14:34:42 +00:00
|
|
|
{
|
2022-04-23 08:33:23 +00:00
|
|
|
if (m_activeWindow) {
|
|
|
|
m_activeWindow->growVertical();
|
2022-03-25 12:20:32 +00:00
|
|
|
}
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2022-04-22 17:39:12 +00:00
|
|
|
void Window::growVertical()
|
2011-01-30 14:34:42 +00:00
|
|
|
{
|
2022-03-25 12:20:32 +00:00
|
|
|
if (!isResizable() || isShade()) {
|
2007-04-29 17:35:43 +00:00
|
|
|
return;
|
2022-03-25 12:20:32 +00:00
|
|
|
}
|
2022-05-16 20:13:39 +00:00
|
|
|
QRectF geom = moveResizeGeometry();
|
2011-01-30 14:34:42 +00:00
|
|
|
geom.setBottom(workspace()->packPositionDown(this, geom.bottom(), true));
|
2022-05-16 20:13:39 +00:00
|
|
|
QSizeF adjsize = constrainFrameSize(geom.size(), SizeModeFixedH);
|
Rework async geometry updates
Window management features were written with synchronous geometry
updates in mind. Currently, this poses a big problem on Wayland because
geometry updates are done in asynchronous fashion there.
At the moment, geometry is updated in a so called pseudo-asynchronous
fashion, meaning that the frame geometry will be reset to the old value
once geometry updates are unblocked. The main drawback of this approach
is that it is too error prone, the data flow is hard to comprehend, etc.
It is worth noting that there is already a machinery to perform async
geometry which is used during interactive move/resize operations.
This change extends the move/resize geometry usage beyond interactive
move/resize to make asynchronous geometry updates less error prone and
easier to comprehend.
With the proposed solution, all geometry updates must be done on the
move/resize geometry first. After that, the new geometry is passed on to
the Client-specific implementation of moveResizeInternal().
To be more specific, the frameGeometry() returns the current frame
geometry, it is primarily useful only to the scene. If you want to move
or resize a window, you need to use moveResizeGeometry() because it
corresponds to the last requested frame geometry.
It is worth noting that the moveResizeGeometry() returns the desired
bounding geometry. The client may commit the xdg_toplevel surface with a
slightly smaller window geometry, for example to enforce a specific
aspect ratio. The client is not allowed to resize beyond the size as
indicated in moveResizeGeometry().
The data flow is very simple: moveResize() updates the move/resize
geometry and calls the client-specific implementation of the
moveResizeInternal() method. Based on whether a configure event is
needed, moveResizeInternal() will update the frameGeometry() either
immediately or after the client commits a new buffer.
Unfortunately, both the compositor and xdg-shell clients try to update
the window geometry. It means that it's possible to have conflicts
between the two. With this change, the compositor's move resize geometry
will be synced only if there are no pending configure events, meaning
that the user doesn't try to resize the window.
2021-04-30 18:26:09 +00:00
|
|
|
if (moveResizeGeometry().size() == adjsize && geom.size() != adjsize && resizeIncrements().height() > 1) { // take care of size increments
|
2022-05-16 20:13:39 +00:00
|
|
|
qreal newbottom = workspace()->packPositionDown(this, geom.bottom() + resizeIncrements().height(), true);
|
2007-04-29 17:35:43 +00:00
|
|
|
// check that it hasn't grown outside of the area, due to size increments
|
2011-01-30 14:34:42 +00:00
|
|
|
if (workspace()->clientArea(MovementArea,
|
2021-08-17 11:55:34 +00:00
|
|
|
this,
|
2022-03-23 10:13:38 +00:00
|
|
|
QPoint(moveResizeGeometry().center().x(), (y() + newbottom) / 2))
|
|
|
|
.bottom()
|
2022-03-25 12:20:32 +00:00
|
|
|
>= newbottom) {
|
2011-01-30 14:34:42 +00:00
|
|
|
geom.setBottom(newbottom);
|
2022-03-25 12:20:32 +00:00
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
}
|
Refactor geometry constraints code
Summary:
Currently, there are a couple of issues with sizeForClientSize(). First
of all, we have a method called clientSizeToFrameSize() which does similar
thing except applying geometry constraints and checking window rules. The
other issue is that sizeForClientSize() is doing a bit too much, it checks
window rules, it applies a bunch of geometry constrains. Sometimes it
does not perform conversion between client sizes and frame sizes!
This change attempts to address those issues by replacing sizeForClientSize
with two similar methods and changing semantics of some methods of the
X11Client class.
The most significant difference between sizeForClientSize() and the new
methods is that neither constrainClientSize() nor constrainFrameSize()
check window rules. This is up to users of those methods. In many places,
we don't have to check window rules because we check isResizable(),
which returns false if the frame size is enforced by a window rule.
Reviewers: #kwin, davidedmundson
Reviewed By: #kwin, davidedmundson
Subscribers: davidedmundson, romangg, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D26828
2020-02-12 10:38:40 +00:00
|
|
|
geom.setSize(constrainFrameSize(geom.size(), SizeModeFixedH));
|
2020-04-02 16:18:01 +00:00
|
|
|
workspace()->updateFocusMousePosition(Cursors::self()->mouse()->pos()); // may cause leave event;
|
Rework async geometry updates
Window management features were written with synchronous geometry
updates in mind. Currently, this poses a big problem on Wayland because
geometry updates are done in asynchronous fashion there.
At the moment, geometry is updated in a so called pseudo-asynchronous
fashion, meaning that the frame geometry will be reset to the old value
once geometry updates are unblocked. The main drawback of this approach
is that it is too error prone, the data flow is hard to comprehend, etc.
It is worth noting that there is already a machinery to perform async
geometry which is used during interactive move/resize operations.
This change extends the move/resize geometry usage beyond interactive
move/resize to make asynchronous geometry updates less error prone and
easier to comprehend.
With the proposed solution, all geometry updates must be done on the
move/resize geometry first. After that, the new geometry is passed on to
the Client-specific implementation of moveResizeInternal().
To be more specific, the frameGeometry() returns the current frame
geometry, it is primarily useful only to the scene. If you want to move
or resize a window, you need to use moveResizeGeometry() because it
corresponds to the last requested frame geometry.
It is worth noting that the moveResizeGeometry() returns the desired
bounding geometry. The client may commit the xdg_toplevel surface with a
slightly smaller window geometry, for example to enforce a specific
aspect ratio. The client is not allowed to resize beyond the size as
indicated in moveResizeGeometry().
The data flow is very simple: moveResize() updates the move/resize
geometry and calls the client-specific implementation of the
moveResizeInternal() method. Based on whether a configure event is
needed, moveResizeInternal() will update the frameGeometry() either
immediately or after the client commits a new buffer.
Unfortunately, both the compositor and xdg-shell clients try to update
the window geometry. It means that it's possible to have conflicts
between the two. With this change, the compositor's move resize geometry
will be synced only if there are no pending configure events, meaning
that the user doesn't try to resize the window.
2021-04-30 18:26:09 +00:00
|
|
|
moveResize(geom);
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
|
|
|
void Workspace::slotWindowShrinkVertical()
|
2011-01-30 14:34:42 +00:00
|
|
|
{
|
2022-04-23 08:33:23 +00:00
|
|
|
if (m_activeWindow) {
|
|
|
|
m_activeWindow->shrinkVertical();
|
2022-03-25 12:20:32 +00:00
|
|
|
}
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2022-04-22 17:39:12 +00:00
|
|
|
void Window::shrinkVertical()
|
2011-01-30 14:34:42 +00:00
|
|
|
{
|
2022-03-25 12:20:32 +00:00
|
|
|
if (!isResizable() || isShade()) {
|
2007-04-29 17:35:43 +00:00
|
|
|
return;
|
2022-03-25 12:20:32 +00:00
|
|
|
}
|
2022-05-16 20:13:39 +00:00
|
|
|
QRectF geom = moveResizeGeometry();
|
|
|
|
geom.setBottom(workspace()->packPositionUp(this, geom.bottom(), false) + 1);
|
2022-03-25 12:20:32 +00:00
|
|
|
if (geom.height() <= 1) {
|
2007-04-29 17:35:43 +00:00
|
|
|
return;
|
2022-03-25 12:20:32 +00:00
|
|
|
}
|
Refactor geometry constraints code
Summary:
Currently, there are a couple of issues with sizeForClientSize(). First
of all, we have a method called clientSizeToFrameSize() which does similar
thing except applying geometry constraints and checking window rules. The
other issue is that sizeForClientSize() is doing a bit too much, it checks
window rules, it applies a bunch of geometry constrains. Sometimes it
does not perform conversion between client sizes and frame sizes!
This change attempts to address those issues by replacing sizeForClientSize
with two similar methods and changing semantics of some methods of the
X11Client class.
The most significant difference between sizeForClientSize() and the new
methods is that neither constrainClientSize() nor constrainFrameSize()
check window rules. This is up to users of those methods. In many places,
we don't have to check window rules because we check isResizable(),
which returns false if the frame size is enforced by a window rule.
Reviewers: #kwin, davidedmundson
Reviewed By: #kwin, davidedmundson
Subscribers: davidedmundson, romangg, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D26828
2020-02-12 10:38:40 +00:00
|
|
|
geom.setSize(constrainFrameSize(geom.size(), SizeModeFixedH));
|
2015-01-28 23:14:47 +00:00
|
|
|
if (geom.height() > 20) {
|
2020-04-02 16:18:01 +00:00
|
|
|
workspace()->updateFocusMousePosition(Cursors::self()->mouse()->pos()); // may cause leave event;
|
Rework async geometry updates
Window management features were written with synchronous geometry
updates in mind. Currently, this poses a big problem on Wayland because
geometry updates are done in asynchronous fashion there.
At the moment, geometry is updated in a so called pseudo-asynchronous
fashion, meaning that the frame geometry will be reset to the old value
once geometry updates are unblocked. The main drawback of this approach
is that it is too error prone, the data flow is hard to comprehend, etc.
It is worth noting that there is already a machinery to perform async
geometry which is used during interactive move/resize operations.
This change extends the move/resize geometry usage beyond interactive
move/resize to make asynchronous geometry updates less error prone and
easier to comprehend.
With the proposed solution, all geometry updates must be done on the
move/resize geometry first. After that, the new geometry is passed on to
the Client-specific implementation of moveResizeInternal().
To be more specific, the frameGeometry() returns the current frame
geometry, it is primarily useful only to the scene. If you want to move
or resize a window, you need to use moveResizeGeometry() because it
corresponds to the last requested frame geometry.
It is worth noting that the moveResizeGeometry() returns the desired
bounding geometry. The client may commit the xdg_toplevel surface with a
slightly smaller window geometry, for example to enforce a specific
aspect ratio. The client is not allowed to resize beyond the size as
indicated in moveResizeGeometry().
The data flow is very simple: moveResize() updates the move/resize
geometry and calls the client-specific implementation of the
moveResizeInternal() method. Based on whether a configure event is
needed, moveResizeInternal() will update the frameGeometry() either
immediately or after the client commits a new buffer.
Unfortunately, both the compositor and xdg-shell clients try to update
the window geometry. It means that it's possible to have conflicts
between the two. With this change, the compositor's move resize geometry
will be synced only if there are no pending configure events, meaning
that the user doesn't try to resize the window.
2021-04-30 18:26:09 +00:00
|
|
|
moveResize(geom);
|
2015-01-28 23:14:47 +00:00
|
|
|
}
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2017-07-18 19:12:37 +00:00
|
|
|
void Workspace::quickTileWindow(QuickTileMode mode)
|
2011-02-07 18:19:51 +00:00
|
|
|
{
|
2022-04-23 08:33:23 +00:00
|
|
|
if (!m_activeWindow) {
|
2011-02-07 18:19:51 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-06-12 13:50:24 +00:00
|
|
|
// If the user invokes two of these commands in a one second period, try to
|
|
|
|
// combine them together to enable easy and intuitive corner tiling
|
|
|
|
#define FLAG(name) QuickTileMode(QuickTileFlag::name)
|
|
|
|
if (!m_quickTileCombineTimer->isActive()) {
|
|
|
|
m_quickTileCombineTimer->start(1000);
|
|
|
|
m_lastTilingMode = mode;
|
|
|
|
} else {
|
|
|
|
if (
|
2022-03-23 10:13:38 +00:00
|
|
|
((m_lastTilingMode == FLAG(Left) || m_lastTilingMode == FLAG(Right)) && (mode == FLAG(Top) || mode == FLAG(Bottom)))
|
|
|
|
|| ((m_lastTilingMode == FLAG(Top) || m_lastTilingMode == FLAG(Bottom)) && (mode == FLAG(Left) || mode == FLAG(Right)))
|
2020-06-12 13:50:24 +00:00
|
|
|
#undef FLAG
|
|
|
|
) {
|
|
|
|
mode |= m_lastTilingMode;
|
|
|
|
}
|
|
|
|
m_quickTileCombineTimer->stop();
|
|
|
|
}
|
|
|
|
|
2022-04-23 08:33:23 +00:00
|
|
|
m_activeWindow->setQuickTileMode(mode, true);
|
2011-02-07 18:19:51 +00:00
|
|
|
}
|
|
|
|
|
2022-05-16 20:13:39 +00:00
|
|
|
qreal Workspace::packPositionLeft(const Window *client, qreal oldX, bool leftEdge) const
|
2019-09-28 15:36:15 +00:00
|
|
|
{
|
2022-05-16 20:13:39 +00:00
|
|
|
qreal newX = clientArea(MaximizeArea, client).left();
|
2019-09-28 15:36:15 +00:00
|
|
|
if (oldX <= newX) { // try another Xinerama screen
|
|
|
|
newX = clientArea(MaximizeArea,
|
2021-08-17 11:55:34 +00:00
|
|
|
client,
|
2022-05-16 20:13:39 +00:00
|
|
|
QPointF(client->frameGeometry().left() - 1, client->frameGeometry().center().y()))
|
2022-03-23 10:13:38 +00:00
|
|
|
.left();
|
2019-09-28 15:36:15 +00:00
|
|
|
}
|
|
|
|
if (oldX <= newX) {
|
|
|
|
return oldX;
|
|
|
|
}
|
|
|
|
const int desktop = client->desktop() == 0 || client->isOnAllDesktops() ? VirtualDesktopManager::self()->current() : client->desktop();
|
2015-10-26 12:25:47 +00:00
|
|
|
for (auto it = m_allClients.constBegin(), end = m_allClients.constEnd(); it != end; ++it) {
|
2019-09-28 15:36:15 +00:00
|
|
|
if (isIrrelevant(*it, client, desktop)) {
|
2007-04-29 17:35:43 +00:00
|
|
|
continue;
|
2019-09-28 15:36:15 +00:00
|
|
|
}
|
2022-05-16 20:13:39 +00:00
|
|
|
const qreal x = leftEdge ? (*it)->frameGeometry().right() : (*it)->frameGeometry().left() - 1;
|
2019-09-28 15:36:15 +00:00
|
|
|
if (x > newX && x < oldX
|
2022-05-16 20:13:39 +00:00
|
|
|
&& !(client->frameGeometry().top() > (*it)->frameGeometry().bottom() - 1 // they overlap in Y direction
|
|
|
|
|| client->frameGeometry().bottom() - 1 < (*it)->frameGeometry().top())) {
|
2019-09-28 15:36:15 +00:00
|
|
|
newX = x;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return newX;
|
|
|
|
}
|
|
|
|
|
2022-05-16 20:13:39 +00:00
|
|
|
qreal Workspace::packPositionRight(const Window *client, qreal oldX, bool rightEdge) const
|
2019-09-28 15:36:15 +00:00
|
|
|
{
|
2022-05-16 20:13:39 +00:00
|
|
|
qreal newX = clientArea(MaximizeArea, client).right();
|
2019-09-28 15:36:15 +00:00
|
|
|
if (oldX >= newX) { // try another Xinerama screen
|
|
|
|
newX = clientArea(MaximizeArea,
|
2021-08-17 11:55:34 +00:00
|
|
|
client,
|
2022-05-16 20:13:39 +00:00
|
|
|
QPointF(client->frameGeometry().right(), client->frameGeometry().center().y()))
|
2022-03-23 10:13:38 +00:00
|
|
|
.right();
|
2019-09-28 15:36:15 +00:00
|
|
|
}
|
|
|
|
if (oldX >= newX) {
|
|
|
|
return oldX;
|
|
|
|
}
|
|
|
|
const int desktop = client->desktop() == 0 || client->isOnAllDesktops() ? VirtualDesktopManager::self()->current() : client->desktop();
|
2015-10-26 12:25:47 +00:00
|
|
|
for (auto it = m_allClients.constBegin(), end = m_allClients.constEnd(); it != end; ++it) {
|
2019-09-28 15:36:15 +00:00
|
|
|
if (isIrrelevant(*it, client, desktop)) {
|
2007-04-29 17:35:43 +00:00
|
|
|
continue;
|
2019-09-28 15:36:15 +00:00
|
|
|
}
|
2022-05-16 20:13:39 +00:00
|
|
|
const qreal x = rightEdge ? (*it)->frameGeometry().left() : (*it)->frameGeometry().right() + 1;
|
|
|
|
|
2019-09-28 15:36:15 +00:00
|
|
|
if (x < newX && x > oldX
|
2022-05-16 20:13:39 +00:00
|
|
|
&& !(client->frameGeometry().top() > (*it)->frameGeometry().bottom() - 1
|
|
|
|
|| client->frameGeometry().bottom() - 1 < (*it)->frameGeometry().top())) {
|
2019-09-28 15:36:15 +00:00
|
|
|
newX = x;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return newX;
|
|
|
|
}
|
|
|
|
|
2022-05-16 20:13:39 +00:00
|
|
|
qreal Workspace::packPositionUp(const Window *client, qreal oldY, bool topEdge) const
|
2019-09-28 15:36:15 +00:00
|
|
|
{
|
2022-05-16 20:13:39 +00:00
|
|
|
qreal newY = clientArea(MaximizeArea, client).top();
|
2019-09-28 15:36:15 +00:00
|
|
|
if (oldY <= newY) { // try another Xinerama screen
|
|
|
|
newY = clientArea(MaximizeArea,
|
2021-08-17 11:55:34 +00:00
|
|
|
client,
|
2022-05-16 20:13:39 +00:00
|
|
|
QPointF(client->frameGeometry().center().x(), client->frameGeometry().top() - 1))
|
2022-03-23 10:13:38 +00:00
|
|
|
.top();
|
2019-09-28 15:36:15 +00:00
|
|
|
}
|
|
|
|
if (oldY <= newY) {
|
|
|
|
return oldY;
|
|
|
|
}
|
|
|
|
const int desktop = client->desktop() == 0 || client->isOnAllDesktops() ? VirtualDesktopManager::self()->current() : client->desktop();
|
2015-10-26 12:25:47 +00:00
|
|
|
for (auto it = m_allClients.constBegin(), end = m_allClients.constEnd(); it != end; ++it) {
|
2019-09-28 15:36:15 +00:00
|
|
|
if (isIrrelevant(*it, client, desktop)) {
|
2007-04-29 17:35:43 +00:00
|
|
|
continue;
|
2019-09-28 15:36:15 +00:00
|
|
|
}
|
2022-05-16 20:13:39 +00:00
|
|
|
const qreal y = topEdge ? (*it)->frameGeometry().bottom() : (*it)->frameGeometry().top() - 1;
|
2019-09-28 15:36:15 +00:00
|
|
|
if (y > newY && y < oldY
|
2022-05-16 20:13:39 +00:00
|
|
|
&& !(client->frameGeometry().left() > (*it)->frameGeometry().right() - 1 // they overlap in X direction
|
|
|
|
|| client->frameGeometry().right() - 1 < (*it)->frameGeometry().left())) {
|
2019-09-28 15:36:15 +00:00
|
|
|
newY = y;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return newY;
|
|
|
|
}
|
|
|
|
|
2022-05-16 20:13:39 +00:00
|
|
|
qreal Workspace::packPositionDown(const Window *client, qreal oldY, bool bottomEdge) const
|
2019-09-28 15:36:15 +00:00
|
|
|
{
|
2022-05-16 20:13:39 +00:00
|
|
|
qreal newY = clientArea(MaximizeArea, client).bottom();
|
2019-09-28 15:36:15 +00:00
|
|
|
if (oldY >= newY) { // try another Xinerama screen
|
|
|
|
newY = clientArea(MaximizeArea,
|
2021-08-17 11:55:34 +00:00
|
|
|
client,
|
2022-05-16 20:13:39 +00:00
|
|
|
QPointF(client->frameGeometry().center().x(), client->frameGeometry().bottom()))
|
2022-03-23 10:13:38 +00:00
|
|
|
.bottom();
|
2019-09-28 15:36:15 +00:00
|
|
|
}
|
|
|
|
if (oldY >= newY) {
|
|
|
|
return oldY;
|
|
|
|
}
|
|
|
|
const int desktop = client->desktop() == 0 || client->isOnAllDesktops() ? VirtualDesktopManager::self()->current() : client->desktop();
|
2015-10-26 12:25:47 +00:00
|
|
|
for (auto it = m_allClients.constBegin(), end = m_allClients.constEnd(); it != end; ++it) {
|
2019-09-28 15:36:15 +00:00
|
|
|
if (isIrrelevant(*it, client, desktop)) {
|
2007-04-29 17:35:43 +00:00
|
|
|
continue;
|
2019-09-28 15:36:15 +00:00
|
|
|
}
|
2022-05-16 20:13:39 +00:00
|
|
|
const qreal y = bottomEdge ? (*it)->frameGeometry().top() : (*it)->frameGeometry().bottom() + 1;
|
2019-09-28 15:36:15 +00:00
|
|
|
if (y < newY && y > oldY
|
2022-05-16 20:13:39 +00:00
|
|
|
&& !(client->frameGeometry().left() > (*it)->frameGeometry().right() - 1
|
|
|
|
|| client->frameGeometry().right() - 1 < (*it)->frameGeometry().left())) {
|
2019-09-28 15:36:15 +00:00
|
|
|
newY = y;
|
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
}
|
2019-09-28 15:36:15 +00:00
|
|
|
return newY;
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
} // namespace
|