KWin
Loading...
Searching...
No Matches
placement.cpp
Go to the documentation of this file.
1/*
2 KWin - the KDE window manager
3 This file is part of the KDE project.
4
5 SPDX-FileCopyrightText: 1999, 2000 Matthias Ettrich <ettrich@kde.org>
6 SPDX-FileCopyrightText: 1997-2002 Cristian Tibirna <tibirna@kde.org>
7 SPDX-FileCopyrightText: 2003 Lubos Lunak <l.lunak@kde.org>
8 SPDX-FileCopyrightText: 2022 Natalie Clarius <natalie_clarius@yahoo.de>
9
10 SPDX-License-Identifier: GPL-2.0-or-later
11*/
12
13#include "placement.h"
14
15#ifndef KCMRULES
16#include "cursor.h"
17#include "options.h"
18#include "rules.h"
19#include "virtualdesktops.h"
20#include "workspace.h"
21#include "x11window.h"
22#endif
23
24#include <QTextStream>
25#include <QTimer>
26
27namespace KWin
28{
29
30#ifndef KCMRULES
31
36
40void Placement::place(Window *c, const QRectF &area)
41{
43 if (policy != PlacementDefault) {
44 place(c, area, policy);
45 return;
46 }
47
48 if (c->isUtility()) {
49 placeUtility(c, area.toRect(), options->placement());
50 } else if (c->isDialog()) {
51 placeDialog(c, area.toRect(), options->placement());
52 } else if (c->isSplash()) {
53 placeOnMainWindow(c, area.toRect()); // on mainwindow, if any, otherwise centered
54 } else if (c->isOnScreenDisplay() || c->isNotification() || c->isCriticalNotification()) {
55 placeOnScreenDisplay(c, area.toRect());
56 } else if (c->isTransient() && c->hasTransientPlacementHint()) {
57 placeTransient(c);
58 } else if (c->isTransient() && c->surface()) {
59 placeDialog(c, area.toRect(), options->placement());
60 } else {
61 place(c, area, options->placement());
62 }
63}
64
65void Placement::place(Window *c, const QRectF &area, PlacementPolicy policy, PlacementPolicy nextPlacement)
66{
67 if (policy == PlacementUnknown || policy == PlacementDefault) {
68 policy = options->placement();
69 }
70
71 switch (policy) {
72 case PlacementNone:
73 return;
74 case PlacementRandom:
75 placeAtRandom(c, area.toRect(), nextPlacement);
76 break;
78 placeCentered(c, area, nextPlacement);
79 break;
81 placeZeroCornered(c, area.toRect(), nextPlacement);
82 break;
84 placeUnderMouse(c, area.toRect(), nextPlacement);
85 break;
87 placeOnMainWindow(c, area.toRect(), nextPlacement);
88 break;
90 placeMaximizing(c, area.toRect(), nextPlacement);
91 break;
92 default:
93 placeSmart(c, area, nextPlacement);
94 }
95}
96
100void Placement::placeAtRandom(Window *c, const QRect &area, PlacementPolicy /*next*/)
101{
102 Q_ASSERT(area.isValid());
103
104 const int step = 24;
105 static int px = step;
106 static int py = 2 * step;
107 int tx, ty;
108
109 if (px < area.x()) {
110 px = area.x();
111 }
112 if (py < area.y()) {
113 py = area.y();
114 }
115
116 px += step;
117 py += 2 * step;
118
119 if (px > area.width() / 2) {
120 px = area.x() + step;
121 }
122 if (py > area.height() / 2) {
123 py = area.y() + step;
124 }
125 tx = px;
126 ty = py;
127 if (tx + c->width() > area.right()) {
128 tx = area.right() - c->width();
129 if (tx < 0) {
130 tx = 0;
131 }
132 px = area.x();
133 }
134 if (ty + c->height() > area.bottom()) {
135 ty = area.bottom() - c->height();
136 if (ty < 0) {
137 ty = 0;
138 }
139 py = area.y();
140 }
141 c->move(QPoint(tx, ty));
142 cascadeIfCovering(c, area);
143}
144
145static inline bool isIrrelevant(const Window *window, const Window *regarding, VirtualDesktop *desktop)
146{
147 return window == regarding
148 || !window->isClient()
149 || !window->isShown()
150 || window->isShade()
151 || !window->isOnDesktop(desktop)
152 || !window->isOnCurrentActivity()
153 || window->isDesktop();
154};
155
159void Placement::placeSmart(Window *window, const QRectF &area, PlacementPolicy /*next*/)
160{
161 Q_ASSERT(area.isValid());
162
163 /*
164 * SmartPlacement by Cristian Tibirna (tibirna@kde.org)
165 * adapted for kwm (16-19jan98) and for kwin (16Nov1999) using (with
166 * permission) ideas from fvwm, authored by
167 * Anthony Martin (amartin@engr.csulb.edu).
168 * Xinerama supported added by Balaji Ramani (balaji@yablibli.com)
169 * with ideas from xfce.
170 */
171
172 if (!window->frameGeometry().isValid()) {
173 return;
174 }
175
176 const int none = 0, h_wrong = -1, w_wrong = -2; // overlap types
177 long int overlap, min_overlap = 0;
178 int x_optimal, y_optimal;
179 int possible;
180 VirtualDesktop *const desktop = window->isOnCurrentDesktop() ? VirtualDesktopManager::self()->currentDesktop() : window->desktops().front();
181
182 int cxl, cxr, cyt, cyb; // temp coords
183 int xl, xr, yt, yb; // temp coords
184 int basket; // temp holder
185
186 // get the maximum allowed windows space
187 int x = area.left();
188 int y = area.top();
189 x_optimal = x;
190 y_optimal = y;
191
192 // client gabarit
193 int ch = std::ceil(window->height());
194 int cw = std::ceil(window->width());
195
196 // Explicitly converts those to int to avoid accidentally
197 // mixing ints and qreal in the calculations below.
198 int area_xr = std::floor(area.x() + area.width());
199 int area_yb = std::floor(area.y() + area.height());
200
201 bool first_pass = true; // CT lame flag. Don't like it. What else would do?
202
203 // loop over possible positions
204 do {
205 // test if enough room in x and y directions
206 if (y + ch > area_yb && ch < area.height()) {
207 overlap = h_wrong; // this throws the algorithm to an exit
208 } else if (x + cw > area_xr) {
209 overlap = w_wrong;
210 } else {
211 overlap = none; // initialize
212
213 cxl = x;
214 cxr = x + cw;
215 cyt = y;
216 cyb = y + ch;
217 for (auto l = workspace()->stackingOrder().constBegin(); l != workspace()->stackingOrder().constEnd(); ++l) {
218 auto client = *l;
219 if (isIrrelevant(client, window, desktop)) {
220 continue;
221 }
222 xl = client->x();
223 yt = client->y();
224 xr = xl + client->width();
225 yb = yt + client->height();
226
227 // if windows overlap, calc the overall overlapping
228 if ((cxl < xr) && (cxr > xl) && (cyt < yb) && (cyb > yt)) {
229 xl = std::max(cxl, xl);
230 xr = std::min(cxr, xr);
231 yt = std::max(cyt, yt);
232 yb = std::min(cyb, yb);
233 if (client->keepAbove()) {
234 overlap += 16 * (xr - xl) * (yb - yt);
235 } else if (client->keepBelow() && !client->isDock()) { // ignore KeepBelow windows
236 overlap += 0; // for placement (see X11Window::belongsToLayer() for Dock)
237 } else {
238 overlap += (xr - xl) * (yb - yt);
239 }
240 }
241 }
242 }
243
244 // CT first time we get no overlap we stop.
245 if (overlap == none) {
246 x_optimal = x;
247 y_optimal = y;
248 break;
249 }
250
251 if (first_pass) {
252 first_pass = false;
253 min_overlap = overlap;
254 }
255 // CT save the best position and the minimum overlap up to now
256 else if (overlap >= none && overlap < min_overlap) {
257 min_overlap = overlap;
258 x_optimal = x;
259 y_optimal = y;
260 }
261
262 // really need to loop? test if there's any overlap
263 if (overlap > none) {
264
265 possible = area_xr;
266 if (possible - cw > x) {
267 possible -= cw;
268 }
269
270 // compare to the position of each client on the same desk
271 for (auto l = workspace()->stackingOrder().constBegin(); l != workspace()->stackingOrder().constEnd(); ++l) {
272 auto client = *l;
273 if (isIrrelevant(client, window, desktop)) {
274 continue;
275 }
276
277 xl = client->x();
278 yt = client->y();
279 xr = xl + client->width();
280 yb = yt + client->height();
281
282 // if not enough room above or under the current tested client
283 // determine the first non-overlapped x position
284 if ((y < yb) && (yt < ch + y)) {
285
286 if ((xr > x) && (possible > xr)) {
287 possible = xr;
288 }
289
290 basket = xl - cw;
291 if ((basket > x) && (possible > basket)) {
292 possible = basket;
293 }
294 }
295 }
296 x = possible;
297 }
298
299 // ... else ==> not enough x dimension (overlap was wrong on horizontal)
300 else if (overlap == w_wrong) {
301 x = area.left();
302 possible = area_yb;
303
304 if (possible - ch > y) {
305 possible -= ch;
306 }
307
308 // test the position of each window on the desk
309 for (auto l = workspace()->stackingOrder().constBegin(); l != workspace()->stackingOrder().constEnd(); ++l) {
310 auto client = *l;
311 if (isIrrelevant(client, window, desktop)) {
312 continue;
313 }
314
315 xl = client->x();
316 yt = client->y();
317 xr = xl + client->width();
318 yb = yt + client->height();
319
320 // if not enough room to the left or right of the current tested client
321 // determine the first non-overlapped y position
322 if ((yb > y) && (possible > yb)) {
323 possible = yb;
324 }
325
326 basket = yt - ch;
327 if ((basket > y) && (possible > basket)) {
328 possible = basket;
329 }
330 }
331 y = possible;
332 }
333 } while ((overlap != none) && (overlap != h_wrong) && (y < area_yb));
334
335 if (ch >= area.height()) {
336 y_optimal = area.top();
337 }
338
339 // place the window
340 window->move(QPoint(x_optimal, y_optimal));
341}
342
344{
345 cci.clear();
346 const auto desktops = VirtualDesktopManager::self()->desktops();
347 for (VirtualDesktop *desktop : desktops) {
348 reinitCascading(desktop);
349 }
350}
351
353{
354 cci[desktop] = DesktopCascadingInfo{
355 .pos = QPoint(-1, -1),
356 .col = 0,
357 .row = 0,
358 };
359}
360
361QPoint Workspace::cascadeOffset(const Window *c) const
362{
363 QRect area = clientArea(PlacementArea, c, c->frameGeometry().center()).toRect();
364 return QPoint(area.width() / 48, area.height() / 48);
365}
366
370void Placement::placeCascaded(Window *c, const QRect &area, PlacementPolicy nextPlacement)
371{
372 Q_ASSERT(area.isValid());
373
374 if (!c->frameGeometry().isValid()) {
375 return;
376 }
377
378 // CT how do I get from the 'Client' class the size that NW squarish "handle"
379 const QPoint delta = workspace()->cascadeOffset(c);
380
381 VirtualDesktop *dn = c->isOnCurrentDesktop() ? VirtualDesktopManager::self()->currentDesktop() : c->desktops().constLast();
382
383 if (nextPlacement == PlacementUnknown) {
384 nextPlacement = PlacementSmart;
385 }
386
387 // initialize if needed
388 if (cci[dn].pos.x() < 0 || cci[dn].pos.x() < area.left() || cci[dn].pos.y() < area.top()) {
389 cci[dn].pos = QPoint(area.left(), area.top());
390 cci[dn].col = cci[dn].row = 0;
391 }
392
393 int xp = cci[dn].pos.x();
394 int yp = cci[dn].pos.y();
395
396 // here to touch in case people vote for resize on placement
397 if ((yp + c->height()) > area.height()) {
398 yp = area.top();
399 }
400
401 if ((xp + c->width()) > area.width()) {
402 if (!yp) {
403 place(c, area, nextPlacement);
404 return;
405 } else {
406 xp = area.left();
407 }
408 }
409
410 // if this isn't the first window
411 if (cci[dn].pos.x() != area.left() && cci[dn].pos.y() != area.top()) {
412 if (xp != area.left() && yp == area.top()) {
413 cci[dn].col++;
414 xp = delta.x() * cci[dn].col;
415 }
416 if (yp != area.top() && xp == area.left()) {
417 cci[dn].row++;
418 yp = delta.y() * cci[dn].row;
419 }
420
421 // last resort: if still doesn't fit, smart place it
422 if (((xp + c->width()) > area.width() - area.left()) || ((yp + c->height()) > area.height() - area.top())) {
423 place(c, area, nextPlacement);
424 return;
425 }
426 }
427
428 // place the window
429 c->move(QPoint(xp, yp));
430
431 // new position
432 cci[dn].pos = QPoint(xp + delta.x(), yp + delta.y());
433}
434
438void Placement::placeCentered(Window *c, const QRectF &area, PlacementPolicy /*next*/)
439{
440 Q_ASSERT(area.isValid());
441
442 if (!c->frameGeometry().isValid()) {
443 return;
444 }
445
446 const int xp = area.left() + (area.width() - c->width()) / 2;
447 const int yp = area.top() + (area.height() - c->height()) / 2;
448
449 // place the window
450 c->move(QPoint(xp, yp));
451 cascadeIfCovering(c, area);
452}
453
457void Placement::placeZeroCornered(Window *c, const QRect &area, PlacementPolicy /*next*/)
458{
459 Q_ASSERT(area.isValid());
460
461 // get the maximum allowed windows space and desk's origin
462 c->move(area.topLeft());
463 cascadeIfCovering(c, area);
464}
465
466void Placement::placeUtility(Window *c, const QRect &area, PlacementPolicy /*next*/)
467{
468 // TODO kwin should try to place utility windows next to their mainwindow,
469 // preferably at the right edge, and going down if there are more of them
470 // if there's not enough place outside the mainwindow, it should prefer
471 // top-right corner
472 // use the default placement for now
473 place(c, area, PlacementDefault);
474}
475
476void Placement::placeOnScreenDisplay(Window *c, const QRect &area)
477{
478 Q_ASSERT(area.isValid());
479
480 // place at lower area of the screen
481 const int x = area.left() + (area.width() - c->width()) / 2;
482 const int y = area.top() + 2 * area.height() / 3 - c->height() / 2;
483
484 c->move(QPoint(x, y));
485}
486
487void Placement::placeTransient(Window *c)
488{
489 c->moveResize(c->transientPlacement());
490}
491
492void Placement::placeDialog(Window *c, const QRect &area, PlacementPolicy nextPlacement)
493{
494 placeOnMainWindow(c, area, nextPlacement);
495}
496
497void Placement::placeUnderMouse(Window *c, const QRect &area, PlacementPolicy /*next*/)
498{
499 Q_ASSERT(area.isValid());
500
501 QRectF geom = c->frameGeometry();
502 geom.moveCenter(Cursors::self()->mouse()->pos());
503 c->move(geom.topLeft().toPoint());
504 c->keepInArea(area); // make sure it's kept inside workarea
505 cascadeIfCovering(c, area);
506}
507
508void Placement::placeOnMainWindow(Window *c, const QRect &area, PlacementPolicy nextPlacement)
509{
510 Q_ASSERT(area.isValid());
511
512 if (nextPlacement == PlacementUnknown) {
513 nextPlacement = PlacementCentered;
514 }
515 if (nextPlacement == PlacementMaximizing) { // maximize if needed
516 placeMaximizing(c, area, PlacementNone);
517 }
518 auto mainwindows = c->mainWindows();
519 Window *place_on = nullptr;
520 Window *place_on2 = nullptr;
521 int mains_count = 0;
522 for (auto it = mainwindows.constBegin(); it != mainwindows.constEnd(); ++it) {
523 if (mainwindows.count() > 1 && (*it)->isSpecialWindow()) {
524 continue; // don't consider toolbars etc when placing
525 }
526 ++mains_count;
527 place_on2 = *it;
528 if ((*it)->isOnCurrentDesktop()) {
529 if (place_on == nullptr) {
530 place_on = *it;
531 } else {
532 // two or more on current desktop -> center
533 // That's the default at least. However, with maximizing placement
534 // policy as the default, the dialog should be either maximized or
535 // made as large as its maximum size and then placed centered.
536 // So the nextPlacement argument allows chaining. In this case, nextPlacement
537 // is Maximizing and it will call placeCentered().
538 place(c, area, PlacementCentered);
539 return;
540 }
541 }
542 }
543 if (place_on == nullptr) {
544 // 'mains_count' is used because it doesn't include ignored mainwindows
545 if (mains_count != 1) {
546 place(c, area, PlacementCentered);
547 return;
548 }
549 place_on = place_on2; // use the only window filtered together with 'mains_count'
550 }
551 if (place_on->isDesktop()) {
552 place(c, area, PlacementCentered);
553 return;
554 }
555 QRect geom = c->frameGeometry().toRect();
556 geom.moveCenter(place_on->frameGeometry().center().toPoint());
557 c->move(geom.topLeft());
558 // get area again, because the mainwindow may be on different xinerama screen
559 const QRect placementArea = workspace()->clientArea(PlacementArea, c).toRect();
560 c->keepInArea(placementArea); // make sure it's kept inside workarea
561}
562
563void Placement::placeMaximizing(Window *c, const QRect &area, PlacementPolicy nextPlacement)
564{
565 Q_ASSERT(area.isValid());
566
567 if (nextPlacement == PlacementUnknown) {
568 nextPlacement = PlacementSmart;
569 }
570 if (c->isMaximizable() && c->maxSize().width() >= area.width() && c->maxSize().height() >= area.height()) {
571 if (workspace()->clientArea(MaximizeArea, c) == area) {
572 c->maximize(MaximizeFull);
573 } else { // if the geometry doesn't match default maximize area (xinerama case?),
574 // it's probably better to use the given area
575 c->moveResize(area);
576 }
577 } else {
578 c->moveResize(c->resizeWithChecks(c->moveResizeGeometry(), c->maxSize().boundedTo(area.size())));
579 place(c, area, nextPlacement);
580 }
581}
582
586void Placement::cascadeIfCovering(Window *window, const QRectF &area)
587{
588 const QPoint offset = workspace()->cascadeOffset(window);
589
590 VirtualDesktop *const desktop = window->isOnCurrentDesktop() ? VirtualDesktopManager::self()->currentDesktop() : window->desktops().front();
591
592 QRectF possibleGeo = window->frameGeometry();
593 bool noOverlap = false;
594
595 // cascade until confirmed no total overlap or not enough space to cascade
596 while (!noOverlap) {
597 noOverlap = true;
598 QRectF coveredArea;
599 // check current position candidate for overlaps with other windows
600 for (auto l = workspace()->stackingOrder().crbegin(); l != workspace()->stackingOrder().crend(); ++l) {
601 auto other = *l;
602 if (isIrrelevant(other, window, desktop) || !other->frameGeometry().intersects(possibleGeo)) {
603 continue;
604 }
605
606 if (possibleGeo.contains(other->frameGeometry()) && !coveredArea.contains(other->frameGeometry())) {
607 // placed window would completely overlap another window which is not already
608 // covered by other windows: try to cascade it from the topleft of that other
609 // window
610 noOverlap = false;
611 possibleGeo.moveTopLeft(other->pos() + offset);
612 if (possibleGeo.right() > area.right() || possibleGeo.bottom() > area.bottom()) {
613 // new cascaded geometry would be out of the bounds of the placement area:
614 // abort the cascading and keep the window in the original position
615 return;
616 }
617 break;
618 }
619
620 // keep track of the area occupied by other windows as we go from top to bottom
621 // in the stacking order, so we don't need to bother trying to avoid overlap with
622 // windows which are already covered up by other windows anyway
623 coveredArea |= other->frameGeometry();
624 if (coveredArea.contains(area)) {
625 break;
626 }
627 }
628 }
629
630 window->move(possibleGeo.topLeft());
631}
632
634{
636 reinitCascading(VirtualDesktopManager::self()->currentDesktop());
637 const auto stackingOrder = ws->stackingOrder();
638 for (Window *window : stackingOrder) {
639 if (!window->isClient() || (!window->isOnCurrentDesktop()) || (window->isMinimized()) || (window->isOnAllDesktops()) || (!window->isMovable())) {
640 continue;
641 }
642 const QRect placementArea = workspace()->clientArea(PlacementArea, window).toRect();
643 placeCascaded(window, placementArea);
644 }
645}
646
648{
649 const auto &windows = Workspace::self()->windows();
650 for (int i = windows.size() - 1; i >= 0; i--) {
651 auto window = windows.at(i);
652 if (!window->isClient()) {
653 continue;
654 }
655 if ((!window->isOnCurrentDesktop()) || (window->isMinimized()) || (window->isOnAllDesktops()) || (!window->isMovable())) {
656 continue;
657 }
658 const QRect placementArea = workspace()->clientArea(PlacementArea, window).toRect();
659 placeSmart(window, placementArea);
660 }
661}
662
663#endif
664
666{
667 const char *const policies[] = {
668 "NoPlacement", "Default", "XXX should never see", "Random", "Smart", "Centered",
669 "ZeroCornered", "UnderMouse", "OnMainWindow", "Maximizing"};
670 Q_ASSERT(policy < int(sizeof(policies) / sizeof(policies[0])));
671 return policies[policy];
672}
673
674#ifndef KCMRULES
675
676// ********************
677// Workspace
678// ********************
679
680void Window::packTo(qreal left, qreal top)
681{
682 workspace()->updateFocusMousePosition(Cursors::self()->mouse()->pos()); // may cause leave event;
683
684 const Output *oldOutput = moveResizeOutput();
685 move(QPoint(left, top));
686 if (moveResizeOutput() != oldOutput) {
687 workspace()->sendWindowToOutput(this, moveResizeOutput()); // checks rule validity
690 }
691 }
692}
693
698{
699 if (m_activeWindow && m_activeWindow->isMovable()) {
700 const QRectF geometry = m_activeWindow->moveResizeGeometry();
701 m_activeWindow->packTo(packPositionLeft(m_activeWindow, geometry.left(), true),
702 geometry.y());
703 }
704}
705
707{
708 if (m_activeWindow && m_activeWindow->isMovable()) {
709 const QRectF geometry = m_activeWindow->moveResizeGeometry();
710 m_activeWindow->packTo(packPositionRight(m_activeWindow, geometry.right(), true) - geometry.width(),
711 geometry.y());
712 }
713}
714
716{
717 if (m_activeWindow && m_activeWindow->isMovable()) {
718 const QRectF geometry = m_activeWindow->moveResizeGeometry();
719 m_activeWindow->packTo(geometry.x(),
720 packPositionUp(m_activeWindow, geometry.top(), true));
721 }
722}
723
725{
726 if (m_activeWindow && m_activeWindow->isMovable()) {
727 const QRectF geometry = m_activeWindow->moveResizeGeometry();
728 m_activeWindow->packTo(geometry.x(),
729 packPositionDown(m_activeWindow, geometry.bottom(), true) - geometry.height());
730 }
731}
732
735{
736 if (m_activeWindow && m_activeWindow->isMovable()) {
737 const QRectF geometry = m_activeWindow->moveResizeGeometry();
738 QPointF center = clientArea(MaximizeArea, m_activeWindow).center();
739 m_activeWindow->packTo(center.x() - (geometry.width() / 2),
740 center.y() - (geometry.height() / 2));
741 }
742}
743
745{
746 if (m_activeWindow) {
747 m_activeWindow->growHorizontal();
748 }
749}
750
752{
753 if (!isResizable() || isShade()) {
754 return;
755 }
756 QRectF geom = moveResizeGeometry();
757 geom.setRight(workspace()->packPositionRight(this, geom.right(), true));
758 QSizeF adjsize = constrainFrameSize(geom.size(), SizeModeFixedW);
759 if (moveResizeGeometry().size() == adjsize && geom.size() != adjsize && resizeIncrements().width() > 1) { // take care of size increments
760 qreal newright = workspace()->packPositionRight(this, geom.right() + resizeIncrements().width() - 1, true);
761 // check that it hasn't grown outside of the area, due to size increments
762 // TODO this may be wrong?
763 if (workspace()->clientArea(MovementArea,
764 this,
765 QPoint((x() + newright) / 2, moveResizeGeometry().center().y()))
766 .right()
767 >= newright) {
768 geom.setRight(newright);
769 }
770 }
771 geom.setSize(constrainFrameSize(geom.size(), SizeModeFixedW));
772 geom.setSize(constrainFrameSize(geom.size(), SizeModeFixedH));
773 workspace()->updateFocusMousePosition(Cursors::self()->mouse()->pos()); // may cause leave event;
774 moveResize(geom);
775}
776
778{
779 if (m_activeWindow) {
780 m_activeWindow->shrinkHorizontal();
781 }
782}
783
785{
786 if (!isResizable() || isShade()) {
787 return;
788 }
789 QRectF geom = moveResizeGeometry();
790 geom.setRight(workspace()->packPositionLeft(this, geom.right(), false) + 1);
791 if (geom.width() <= 1) {
792 return;
793 }
794 geom.setSize(constrainFrameSize(geom.size(), SizeModeFixedW));
795 if (geom.width() > 20) {
796 workspace()->updateFocusMousePosition(Cursors::self()->mouse()->pos()); // may cause leave event;
797 moveResize(geom);
798 }
799}
800
802{
803 if (m_activeWindow) {
804 m_activeWindow->growVertical();
805 }
806}
807
809{
810 if (!isResizable() || isShade()) {
811 return;
812 }
813 QRectF geom = moveResizeGeometry();
814 geom.setBottom(workspace()->packPositionDown(this, geom.bottom(), true));
815 QSizeF adjsize = constrainFrameSize(geom.size(), SizeModeFixedH);
816 if (moveResizeGeometry().size() == adjsize && geom.size() != adjsize && resizeIncrements().height() > 1) { // take care of size increments
817 qreal newbottom = workspace()->packPositionDown(this, geom.bottom() + resizeIncrements().height(), true);
818 // check that it hasn't grown outside of the area, due to size increments
819 if (workspace()->clientArea(MovementArea,
820 this,
821 QPoint(moveResizeGeometry().center().x(), (y() + newbottom) / 2))
822 .bottom()
823 >= newbottom) {
824 geom.setBottom(newbottom);
825 }
826 }
827 geom.setSize(constrainFrameSize(geom.size(), SizeModeFixedH));
828 workspace()->updateFocusMousePosition(Cursors::self()->mouse()->pos()); // may cause leave event;
829 moveResize(geom);
830}
831
833{
834 if (m_activeWindow) {
835 m_activeWindow->shrinkVertical();
836 }
837}
838
840{
841 if (!isResizable() || isShade()) {
842 return;
843 }
844 QRectF geom = moveResizeGeometry();
845 geom.setBottom(workspace()->packPositionUp(this, geom.bottom(), false) + 1);
846 if (geom.height() <= 1) {
847 return;
848 }
849 geom.setSize(constrainFrameSize(geom.size(), SizeModeFixedH));
850 if (geom.height() > 20) {
851 workspace()->updateFocusMousePosition(Cursors::self()->mouse()->pos()); // may cause leave event;
852 moveResize(geom);
853 }
854}
855
856void Workspace::quickTileWindow(QuickTileMode mode)
857{
858 if (!m_activeWindow) {
859 return;
860 }
861
862 // If the user invokes two of these commands in a one second period, try to
863 // combine them together to enable easy and intuitive corner tiling
864#define FLAG(name) QuickTileMode(QuickTileFlag::name)
865 if (!m_quickTileCombineTimer->isActive()) {
866 m_quickTileCombineTimer->start(1000);
867 m_lastTilingMode = mode;
868 } else {
869 if (
870 ((m_lastTilingMode == FLAG(Left) || m_lastTilingMode == FLAG(Right)) && (mode == FLAG(Top) || mode == FLAG(Bottom)))
871 || ((m_lastTilingMode == FLAG(Top) || m_lastTilingMode == FLAG(Bottom)) && (mode == FLAG(Left) || mode == FLAG(Right)))
872#undef FLAG
873 ) {
874 mode |= m_lastTilingMode;
875 }
876 m_quickTileCombineTimer->stop();
877 }
878
879 m_activeWindow->setQuickTileMode(mode, true);
880}
881
882qreal Workspace::packPositionLeft(const Window *window, qreal oldX, bool leftEdge) const
883{
884 qreal newX = clientArea(MaximizeArea, window).left();
885 if (oldX <= newX) { // try another Xinerama screen
887 window,
888 QPointF(window->frameGeometry().left() - 1, window->frameGeometry().center().y()))
889 .left();
890 }
891 if (oldX <= newX) {
892 return oldX;
893 }
894 VirtualDesktop *const desktop = window->isOnCurrentDesktop() ? VirtualDesktopManager::self()->currentDesktop() : window->desktops().front();
895 for (auto it = m_windows.constBegin(), end = m_windows.constEnd(); it != end; ++it) {
896 if (isIrrelevant(*it, window, desktop)) {
897 continue;
898 }
899 const qreal x = leftEdge ? (*it)->frameGeometry().right() : (*it)->frameGeometry().left() - 1;
900 if (x > newX && x < oldX
901 && !(window->frameGeometry().top() > (*it)->frameGeometry().bottom() - 1 // they overlap in Y direction
902 || window->frameGeometry().bottom() - 1 < (*it)->frameGeometry().top())) {
903 newX = x;
904 }
905 }
906 return newX;
907}
908
909qreal Workspace::packPositionRight(const Window *window, qreal oldX, bool rightEdge) const
910{
911 qreal newX = clientArea(MaximizeArea, window).right();
912 if (oldX >= newX) { // try another Xinerama screen
914 window,
915 QPointF(window->frameGeometry().right(), window->frameGeometry().center().y()))
916 .right();
917 }
918 if (oldX >= newX) {
919 return oldX;
920 }
921 VirtualDesktop *const desktop = window->isOnCurrentDesktop() ? VirtualDesktopManager::self()->currentDesktop() : window->desktops().front();
922 for (auto it = m_windows.constBegin(), end = m_windows.constEnd(); it != end; ++it) {
923 if (isIrrelevant(*it, window, desktop)) {
924 continue;
925 }
926 const qreal x = rightEdge ? (*it)->frameGeometry().left() : (*it)->frameGeometry().right() + 1;
927
928 if (x < newX && x > oldX
929 && !(window->frameGeometry().top() > (*it)->frameGeometry().bottom() - 1
930 || window->frameGeometry().bottom() - 1 < (*it)->frameGeometry().top())) {
931 newX = x;
932 }
933 }
934 return newX;
935}
936
937qreal Workspace::packPositionUp(const Window *window, qreal oldY, bool topEdge) const
938{
939 qreal newY = clientArea(MaximizeArea, window).top();
940 if (oldY <= newY) { // try another Xinerama screen
942 window,
943 QPointF(window->frameGeometry().center().x(), window->frameGeometry().top() - 1))
944 .top();
945 }
946 if (oldY <= newY) {
947 return oldY;
948 }
949 VirtualDesktop *const desktop = window->isOnCurrentDesktop() ? VirtualDesktopManager::self()->currentDesktop() : window->desktops().front();
950 for (auto it = m_windows.constBegin(), end = m_windows.constEnd(); it != end; ++it) {
951 if (isIrrelevant(*it, window, desktop)) {
952 continue;
953 }
954 const qreal y = topEdge ? (*it)->frameGeometry().bottom() : (*it)->frameGeometry().top() - 1;
955 if (y > newY && y < oldY
956 && !(window->frameGeometry().left() > (*it)->frameGeometry().right() - 1 // they overlap in X direction
957 || window->frameGeometry().right() - 1 < (*it)->frameGeometry().left())) {
958 newY = y;
959 }
960 }
961 return newY;
962}
963
964qreal Workspace::packPositionDown(const Window *window, qreal oldY, bool bottomEdge) const
965{
966 qreal newY = clientArea(MaximizeArea, window).bottom();
967 if (oldY >= newY) { // try another Xinerama screen
969 window,
970 QPointF(window->frameGeometry().center().x(), window->frameGeometry().bottom()))
971 .bottom();
972 }
973 if (oldY >= newY) {
974 return oldY;
975 }
976 VirtualDesktop *const desktop = window->isOnCurrentDesktop() ? VirtualDesktopManager::self()->currentDesktop() : window->desktops().front();
977 for (auto it = m_windows.constBegin(), end = m_windows.constEnd(); it != end; ++it) {
978 if (isIrrelevant(*it, window, desktop)) {
979 continue;
980 }
981 const qreal y = bottomEdge ? (*it)->frameGeometry().top() : (*it)->frameGeometry().bottom() + 1;
982 if (y < newY && y > oldY
983 && !(window->frameGeometry().left() > (*it)->frameGeometry().right() - 1
984 || window->frameGeometry().right() - 1 < (*it)->frameGeometry().left())) {
985 newY = y;
986 }
987 }
988 return newY;
989}
990
991#endif
992
993} // namespace
static Cursors * self()
Definition cursor.cpp:35
PlacementPolicy placement
Definition options.h:111
void reinitCascading()
void placeCentered(Window *c, const QRectF &area, PlacementPolicy next=PlacementUnknown)
void cascadeDesktop()
void unclutterDesktop()
static const char * policyToString(PlacementPolicy policy)
void placeSmart(Window *c, const QRectF &area, PlacementPolicy next=PlacementUnknown)
void place(Window *c, const QRectF &area)
Definition placement.cpp:40
void cascadeIfCovering(Window *c, const QRectF &area)
QPointF pos
Definition window.h:79
virtual bool hasTransientPlacementHint() const
Definition window.cpp:2240
QRectF frameGeometry
Definition window.h:431
void shrinkHorizontal()
void moveResize(const QRectF &rect)
Definition window.cpp:3323
virtual bool isTransient() const
Definition window.cpp:2290
qreal width
Definition window.h:99
bool isNotification() const
Definition window.h:1977
void growVertical()
bool isDialog() const
Definition window.h:1952
QSizeF size
Definition window.h:84
bool isShade() const
Definition window.h:1034
SurfaceInterface * surface() const
Definition window.cpp:342
bool isOnScreenDisplay() const
Definition window.h:1992
bool isCriticalNotification() const
Definition window.h:1982
void packTo(qreal left, qreal top)
virtual QSizeF resizeIncrements() const
Definition window.cpp:2573
void setQuickTileMode(QuickTileMode mode, bool keyboard=false)
Definition window.cpp:3411
bool move
Definition window.h:437
bool isOnCurrentDesktop() const
Definition window.cpp:848
virtual MaximizeMode requestedMaximizeMode() const
Definition window.cpp:3999
bool isUtility() const
Definition window.h:1947
qreal y
Definition window.h:94
qreal x
Definition window.h:89
QList< KWin::VirtualDesktop * > desktops
Definition window.h:295
virtual bool isMovable() const =0
@ SizeModeFixedW
Try not to affect width.
Definition window.h:670
@ SizeModeFixedH
Try not to affect height.
Definition window.h:671
Output * moveResizeOutput() const
Definition window.cpp:3297
void shrinkVertical()
QRectF moveResizeGeometry() const
Definition window.cpp:3286
const WindowRules * rules() const
Definition window.h:1048
bool isSplash() const
Definition window.h:1942
QSizeF constrainFrameSize(const QSizeF &size, SizeMode mode=SizeModeAny) const
Definition window.cpp:3901
qreal height
Definition window.h:104
void checkWorkspacePosition(QRectF oldGeometry=QRectF(), const VirtualDesktop *oldDesktop=nullptr)
Definition window.cpp:3661
virtual bool isResizable() const =0
void growHorizontal()
PlacementPolicy checkPlacement(PlacementPolicy placement) const
void quickTileWindow(QuickTileMode mode)
QRectF clientArea(clientAreaOption, const Output *output, const VirtualDesktop *desktop) const
const QList< Window * > & stackingOrder() const
Definition workspace.h:788
void sendWindowToOutput(Window *window, Output *output)
qreal packPositionDown(const Window *window, qreal oldY, bool bottomEdge) const
void slotWindowShrinkHorizontal()
void slotWindowExpandHorizontal()
void slotWindowMoveUp()
void slotWindowCenter()
static Workspace * self()
Definition workspace.h:91
void slotWindowMoveDown()
void updateFocusMousePosition(const QPointF &pos)
Definition workspace.h:820
QPoint cascadeOffset(const Window *c) const
void slotWindowMoveRight()
void slotWindowShrinkVertical()
const QList< Window * > windows() const
Definition workspace.h:248
qreal packPositionRight(const Window *window, qreal oldX, bool rightEdge) const
void slotWindowExpandVertical()
QRect geometry() const
qreal packPositionUp(const Window *window, qreal oldY, bool topEdge) const
qreal packPositionLeft(const Window *window, qreal oldX, bool leftEdge) const
void slotWindowMoveLeft()
@ MovementArea
Definition globals.h:50
@ MaximizeArea
Definition globals.h:51
@ PlacementArea
Definition globals.h:49
@ MaximizeRestore
The window is not maximized in any direction.
Definition common.h:75
@ MaximizeFull
Equal to MaximizeVertical | MaximizeHorizontal.
Definition common.h:79
PlacementPolicy
Definition options.h:57
@ PlacementZeroCornered
Definition options.h:64
@ PlacementUnderMouse
Definition options.h:65
@ PlacementMaximizing
Definition options.h:67
@ PlacementRandom
Definition options.h:61
@ PlacementOnMainWindow
Definition options.h:66
@ PlacementCentered
Definition options.h:63
@ PlacementUnknown
Definition options.h:60
@ PlacementSmart
Definition options.h:62
@ PlacementDefault
Definition options.h:59
@ PlacementNone
Definition options.h:58
Workspace * workspace()
Definition workspace.h:830
Options * options
Definition main.cpp:73
#define FLAG(name)