diff --git a/client.cpp b/client.cpp index 4e86f953af..9bd1c58c3f 100644 --- a/client.cpp +++ b/client.cpp @@ -318,12 +318,8 @@ void Client::updateDecoration( bool check_workspace_pos, bool force ) XReparentWindow( display(), decoration->widget()->winId(), frameId(), 0, 0 ); decoration->widget()->lower(); decoration->borders( border_left, border_right, border_top, border_bottom ); - int save_workarea_diff_x = workarea_diff_x; - int save_workarea_diff_y = workarea_diff_y; move( calculateGravitation( false )); plainResize( sizeForClientSize( clientSize()), ForceGeometrySet ); - workarea_diff_x = save_workarea_diff_x; - workarea_diff_y = save_workarea_diff_y; do_show = true; if( compositing() ) discardWindowPixmap(); @@ -352,12 +348,8 @@ void Client::destroyDecoration() QPoint grav = calculateGravitation( true ); border_left = border_right = border_top = border_bottom = 0; setMask( QRegion()); // Reset shape mask - int save_workarea_diff_x = workarea_diff_x; - int save_workarea_diff_y = workarea_diff_y; plainResize( sizeForClientSize( clientSize()), ForceGeometrySet ); move( grav ); - workarea_diff_x = save_workarea_diff_x; - workarea_diff_y = save_workarea_diff_y; if( compositing() ) discardWindowPixmap(); if( scene != NULL && !deleting ) diff --git a/client.h b/client.h index 6c99cfc47a..8c46942a50 100644 --- a/client.h +++ b/client.h @@ -280,6 +280,8 @@ class Client void cancelShadeHoverTimer(); void cancelAutoRaise(); void checkActiveModal(); + StrutRect strutRect( StrutArea area ) const; + StrutRects strutRects() const; bool hasStrut() const; /** * Whether or not the window has a strut that expands through the invisible area of @@ -378,9 +380,7 @@ class Client void finishWindowRules(); void setShortcutInternal( const KShortcut& cut ); - void updateWorkareaDiffs(); void checkDirection( int new_diff, int old_diff, QRect& rect, const QRect& area ); - static int computeWorkareaDiff( int left, int right, int a_left, int a_right ); void configureRequest( int value_mask, int rx, int ry, int rw, int rh, int gravity, bool from_tool ); NETExtendedStrut strut() const; int checkShadeGeometry( int w, int h ); @@ -510,7 +510,6 @@ class Client QRect geom_restore; QRect geom_fs_restore; MaximizeMode maxmode_restore; - int workarea_diff_x, workarea_diff_y; QTimer* autoRaiseTimer; QTimer* shadeHoverTimer; QTimer* delayedMoveResizeTimer; diff --git a/geometry.cpp b/geometry.cpp index c48e5f0928..298200c67e 100644 --- a/geometry.cpp +++ b/geometry.cpp @@ -4,6 +4,7 @@ Copyright (C) 1999, 2000 Matthias Ettrich Copyright (C) 2003 Lubos Lunak +Copyright (C) 2009 Lucas Murray This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -85,6 +86,7 @@ void Workspace::updateClientArea( bool force ) int nscreens = Kephal::ScreenUtils::numScreens(); kDebug(1212) << "screens: " << nscreens << "desktops: " << numberOfDesktops(); QVector< QRect > new_wareas( numberOfDesktops() + 1 ); + QVector< StrutRects > new_rmoveareas( numberOfDesktops() + 1 ); QVector< QVector< QRect > > new_sareas( numberOfDesktops() + 1 ); QVector< QRect > screens( nscreens ); QRect desktopArea = Kephal::ScreenUtils::desktopGeometry(); @@ -110,6 +112,7 @@ void Workspace::updateClientArea( bool force ) if( !(*it)->hasStrut()) continue; QRect r = (*it)->adjustedClientArea( desktopArea, desktopArea ); + StrutRects strutRegion = (*it)->strutRects(); // Ignore offscreen xinerama struts. These interfere with the larger monitors on the setup // and should be ignored so that applications that use the work area to work out where @@ -127,6 +130,7 @@ void Workspace::updateClientArea( bool force ) { if( !hasOffscreenXineramaStrut ) new_wareas[ i ] = new_wareas[ i ].intersected( r ); + new_rmoveareas[ i ] += strutRegion; for( int iS = 0; iS < nscreens; iS ++ ) @@ -140,6 +144,7 @@ void Workspace::updateClientArea( bool force ) { if( !hasOffscreenXineramaStrut ) new_wareas[ (*it)->desktop() ] = new_wareas[ (*it)->desktop() ].intersected( r ); + new_rmoveareas[ (*it)->desktop() ] += strutRegion; for( int iS = 0; iS < nscreens; iS ++ ) @@ -170,7 +175,10 @@ void Workspace::updateClientArea( bool force ) for( int i = 1; i <= numberOfDesktops(); ++i ) + { new_wareas[ i ] = new_wareas[ i ].intersected( topmenu_area ); + new_rmoveareas[ i ] += topmenu_area; + } } bool changed = force; @@ -184,6 +192,8 @@ void Workspace::updateClientArea( bool force ) { if( workarea[ i ] != new_wareas[ i ] ) changed = true; + if( restrictedmovearea[ i ] != new_rmoveareas[ i ] ) + changed = true; if( screenarea[ i ].size() != new_sareas[ i ].size()) changed = true; for( int iS = 0; @@ -196,6 +206,8 @@ void Workspace::updateClientArea( bool force ) if ( changed ) { workarea = new_wareas; + oldrestrictedmovearea = restrictedmovearea; + restrictedmovearea = new_rmoveareas; screenarea = new_sareas; NETRect r; for( int i = 1; i <= numberOfDesktops(); i++) @@ -296,6 +308,27 @@ QRect Workspace::clientArea( clientAreaOption opt, const Client* c ) const return clientArea( opt, c->geometry().center(), c->desktop()); } +QRegion Workspace::restrictedMoveArea( int desktop, StrutAreas areas ) const + { + if( desktop == NETWinInfo::OnAllDesktops || desktop == 0 ) + desktop = currentDesktop(); + QRegion region; + foreach( const StrutRect& rect, restrictedmovearea[desktop] ) + if( areas & rect.area() ) + region += rect; + return region; + } + +QRegion Workspace::previousRestrictedMoveArea( int desktop, StrutAreas areas ) const + { + if( desktop == NETWinInfo::OnAllDesktops || desktop == 0 ) + desktop = currentDesktop(); + QRegion region; + foreach( const StrutRect& rect, oldrestrictedmovearea[desktop] ) + if( areas & rect.area() ) + region += rect; + return region; + } /*! Client \a c is moved around to position \a pos. This gives the @@ -964,6 +997,56 @@ NETExtendedStrut Client::strut() const return ext; } +StrutRect Client::strutRect( StrutArea area ) const + { + assert( area != StrutAreaAll ); // Not valid + NETExtendedStrut strutArea = strut(); + switch( area ) + { + case StrutAreaTop: + if( strutArea.top_width != 0 ) + return StrutRect( QRect( + strutArea.top_start, 0, + strutArea.top_end - strutArea.top_start, strutArea.top_width + ), StrutAreaTop ); + break; + case StrutAreaRight: + if( strutArea.right_width != 0 ) + return StrutRect( QRect( + displayWidth() - strutArea.right_width, strutArea.right_start, + strutArea.right_width, strutArea.right_end - strutArea.right_start + ), StrutAreaRight ); + break; + case StrutAreaBottom: + if( strutArea.bottom_width != 0 ) + return StrutRect( QRect( + strutArea.bottom_start, displayHeight() - strutArea.bottom_width, + strutArea.bottom_end - strutArea.bottom_start, strutArea.bottom_width + ), StrutAreaBottom ); + break; + case StrutAreaLeft: + if( strutArea.left_width != 0 ) + return StrutRect( QRect( + 0, strutArea.left_start, + strutArea.left_width, strutArea.left_end - strutArea.left_start + ), StrutAreaLeft ); + break; + default: + abort(); // Not valid + } + return StrutRect(); // Null rect + } + +StrutRects Client::strutRects() const + { + StrutRects region; + region += strutRect( StrutAreaTop ); + region += strutRect( StrutAreaRight ); + region += strutRect( StrutAreaBottom ); + region += strutRect( StrutAreaLeft ); + return region; + } + bool Client::hasStrut() const { NETExtendedStrut ext = strut(); @@ -974,72 +1057,20 @@ bool Client::hasStrut() const bool Client::hasOffscreenXineramaStrut() const { - // Convert strut to a QRegion - NETExtendedStrut strutArea = strut(); - QRegion strutRegion; - if( strutArea.left_width != 0 ) - strutRegion += QRect( - 0, strutArea.left_start, - strutArea.left_width, strutArea.left_end - strutArea.left_start - ); - if( strutArea.right_width != 0 ) - strutRegion += QRect( - displayWidth() - strutArea.right_width, strutArea.right_start, - strutArea.right_width, strutArea.right_end - strutArea.right_start - ); - if( strutArea.top_width != 0 ) - strutRegion += QRect( - strutArea.top_start, 0, - strutArea.top_end - strutArea.top_start, strutArea.top_width - ); - if( strutArea.bottom_width != 0 ) - strutRegion += QRect( - strutArea.bottom_start, displayHeight() - strutArea.bottom_width, - strutArea.bottom_end - strutArea.bottom_start, strutArea.bottom_width - ); + // Get strut as a QRegion + QRegion region; + region += strutRect( StrutAreaTop ); + region += strutRect( StrutAreaRight ); + region += strutRect( StrutAreaBottom ); + region += strutRect( StrutAreaLeft ); // Remove all visible areas so that only the invisible remain int numScreens = Kephal::ScreenUtils::numScreens(); for( int i = 0; i < numScreens; i ++ ) - strutRegion -= Kephal::ScreenUtils::screenGeometry( i ); + region -= Kephal::ScreenUtils::screenGeometry( i ); // If there's anything left then we have an offscreen strut - return !strutRegion.isEmpty(); - } - -// updates differences to workarea edges for all directions -void Client::updateWorkareaDiffs() - { - QRect area = workspace()->clientArea( WorkArea, this ); - QRect geom = geometry(); - workarea_diff_x = computeWorkareaDiff( geom.left(), geom.right(), area.left(), area.right()); - workarea_diff_y = computeWorkareaDiff( geom.top(), geom.bottom(), area.top(), area.bottom()); - } - -// If the client was inside workarea in the x direction, and if it was close to the left/right -// edge, return the distance from the left/right edge (negative for left, positive for right) -// INT_MIN means 'not inside workarea', INT_MAX means 'not near edge'. -// In order to recognize 'at the left workarea edge' from 'at the right workarea edge' -// (i.e. negative vs positive zero), the distances are one larger in absolute value than they -// really are (i.e. 5 pixels from the left edge is -6, not -5). A bit hacky, but I'm lazy -// to rewrite it just to make it nicer. If this will ever get touched again, perhaps then. -// the y direction is done the same, just the values will be rotated: top->left, bottom->right -int Client::computeWorkareaDiff( int left, int right, int a_left, int a_right ) - { - int left_diff = left - a_left; - int right_diff = a_right - right; - if( left_diff < 0 || right_diff < 0 ) - return INT_MIN; - else // fully inside workarea in this direction direction - { - // max distance from edge where it's still considered to be close and is kept at that distance - int max_diff = ( a_right - a_left ) / 10; - if( left_diff < right_diff ) - return left_diff < max_diff ? -left_diff - 1 : INT_MAX; - else if( left_diff > right_diff ) - return right_diff < max_diff ? right_diff + 1 : INT_MAX; - return INT_MAX; // not close to workarea edge - } + return !region.isEmpty(); } void Client::checkWorkspacePosition() @@ -1078,10 +1109,6 @@ void Client::checkWorkspacePosition() if( !isShade()) // TODO { - int old_diff_x = workarea_diff_x; - int old_diff_y = workarea_diff_y; - updateWorkareaDiffs(); - // this can be true only if this window was mapped before KWin // was started - in such case, don't adjust position to workarea, // because the window already had its position, and if a window @@ -1090,27 +1117,125 @@ void Client::checkWorkspacePosition() if( workspace()->initializing()) return; - QRect area = workspace()->clientArea( WorkArea, this ); - QRect new_geom = geometry(); - QRect tmp_rect_x( new_geom.left(), 0, new_geom.width(), 0 ); - QRect tmp_area_x( area.left(), 0, area.width(), 0 ); - checkDirection( workarea_diff_x, old_diff_x, tmp_rect_x, tmp_area_x ); - // the x<->y swapping - QRect tmp_rect_y( new_geom.top(), 0, new_geom.height(), 0 ); - QRect tmp_area_y( area.top(), 0, area.height(), 0 ); - checkDirection( workarea_diff_y, old_diff_y, tmp_rect_y, tmp_area_y ); - new_geom = QRect( tmp_rect_x.left(), tmp_rect_y.left(), tmp_rect_x.width(), tmp_rect_y.width()); - QRect final_geom( new_geom.topLeft(), adjustedSize( new_geom.size())); - if( final_geom != new_geom ) // size increments, or size restrictions - { // adjusted size differing matters only for right and bottom edge - if( old_diff_x != INT_MAX && old_diff_x > 0 ) - final_geom.moveRight( area.right() - ( old_diff_x - 1 )); - if( old_diff_y != INT_MAX && old_diff_y > 0 ) - final_geom.moveBottom( area.bottom() - ( old_diff_y - 1 )); + // If the window was touching an edge before but not now move it so it is again. + // Old and new maximums have different starting values so windows on the screen + // edge will move when a new strut is placed on the edge. + const QRect& screenArea = workspace()->clientArea( ScreenArea, this ); + int oldTopMax = screenArea.y(); + int oldRightMax = screenArea.x() + screenArea.width(); + int oldBottomMax = screenArea.y() + screenArea.height(); + int oldLeftMax = screenArea.x(); + int topMax = INT_MIN, rightMax = INT_MAX, bottomMax = INT_MAX, leftMax = INT_MIN; + QRect newGeom = geometry(); + const QRect& newGeomTall = QRect( newGeom.x(), 0, newGeom.width(), displayHeight() ); // Full screen height + const QRect& newGeomWide = QRect( 0, newGeom.y(), displayWidth(), newGeom.height() ); // Full screen width + + // Get the max strut point for each side where the window is (E.g. Highest point for + // the bottom struts bounded by the window's left and right sides). + foreach( QRect rect, workspace()->previousRestrictedMoveArea( desktop(), StrutAreaTop ).rects() ) + { + rect &= newGeomTall; + if( !rect.isEmpty() ) + oldTopMax = qMax( oldTopMax, rect.y() + rect.height() ); } - if( final_geom != geometry() ) - setGeometry( final_geom ); - // updateWorkareaDiffs(); done already by setGeometry() + foreach( QRect rect, workspace()->previousRestrictedMoveArea( desktop(), StrutAreaRight ).rects() ) + { + rect &= newGeomWide; + if( !rect.isEmpty() ) + oldRightMax = qMin( oldRightMax, rect.x() ); + } + foreach( QRect rect, workspace()->previousRestrictedMoveArea( desktop(), StrutAreaBottom ).rects() ) + { + rect &= newGeomTall; + if( !rect.isEmpty() ) + oldBottomMax = qMin( oldBottomMax, rect.y() ); + } + foreach( QRect rect, workspace()->previousRestrictedMoveArea( desktop(), StrutAreaLeft ).rects() ) + { + rect &= newGeomWide; + if( !rect.isEmpty() ) + oldLeftMax = qMax( oldLeftMax, rect.x() + rect.width() ); + } + foreach( QRect rect, workspace()->restrictedMoveArea( desktop(), StrutAreaTop ).rects() ) + { + rect &= newGeomTall; + if( !rect.isEmpty() ) + topMax = qMax( topMax, rect.y() + rect.height() ); + } + foreach( QRect rect, workspace()->restrictedMoveArea( desktop(), StrutAreaRight ).rects() ) + { + rect &= newGeomWide; + if( !rect.isEmpty() ) + rightMax = qMin( rightMax, rect.x() ); + } + foreach( QRect rect, workspace()->restrictedMoveArea( desktop(), StrutAreaBottom ).rects() ) + { + rect &= newGeomTall; + if( !rect.isEmpty() ) + bottomMax = qMin( bottomMax, rect.y() ); + } + foreach( QRect rect, workspace()->restrictedMoveArea( desktop(), StrutAreaLeft ).rects() ) + { + rect &= newGeomWide; + if( !rect.isEmpty() ) + leftMax = qMax( leftMax, rect.x() + rect.width() ); + } + + // Check if the sides were touching before but are no longer + if( newGeom.y() == oldTopMax && + newGeom.y() != topMax ) + { // Top was touching before but isn't anymore + // If the other side was touching make sure it still is afterwards + if( newGeom.y() + newGeom.height() == oldBottomMax ) + newGeom.setTop( qMax( topMax, screenArea.y() )); + else + newGeom.moveTop( qMax( topMax, screenArea.y() )); + // Make sure it doesn't go off the other side of the screen/under an opposite strut + newGeom.setBottom( qMin( qMin( bottomMax - 1, screenArea.bottom() ), + newGeom.y() + newGeom.height() - 1 )); + } + if( newGeom.x() + newGeom.width() == oldRightMax && + newGeom.x() + newGeom.width() != rightMax ) + { // Right was touching before but isn't anymore + // If the other side was touching make sure it still is afterwards + if( newGeom.x() == oldLeftMax ) + newGeom.setRight( qMin( rightMax - 1, screenArea.right() )); + else + newGeom.moveRight( qMin( rightMax - 1, screenArea.right() )); + // Make sure it doesn't go off the other side of the screen/under an opposite strut + newGeom.setLeft( qMax( qMax( leftMax, screenArea.x() ), + newGeom.x() )); + } + if( newGeom.y() + newGeom.height() == oldBottomMax && + newGeom.y() + newGeom.height() != bottomMax ) + { // Bottom was touching before but isn't anymore + // If the other side was touching make sure it still is afterwards + if( newGeom.y() == oldTopMax ) + newGeom.setBottom( qMin( bottomMax - 1, screenArea.bottom() )); + else + newGeom.moveBottom( qMin( bottomMax - 1, screenArea.bottom() )); + // Make sure it doesn't go off the other side of the screen/under an opposite strut + newGeom.setTop( qMax( qMax( topMax, screenArea.y() ), + newGeom.y() )); + } + if( newGeom.x() == oldLeftMax && + newGeom.x() != leftMax ) + { // Left was touching before but isn't anymore + // If the other side was touching make sure it still is afterwards + if( newGeom.y() == newGeom.x() + newGeom.width() == oldRightMax ) + newGeom.setLeft( qMax( leftMax, screenArea.x() )); + else + newGeom.moveLeft( qMax( leftMax, screenArea.x() )); + // Make sure it doesn't go off the other side of the screen/under an opposite strut + newGeom.setRight( qMin( qMin( rightMax - 1, screenArea.right() ), + newGeom.x() + newGeom.width() - 1 )); + } + + // Obey size hints. TODO: We really should make sure it stays in the right place + newGeom.setSize( adjustedSize( newGeom.size() )); + + if( newGeom != geometry() ) + setGeometry( newGeom ); } } @@ -1710,24 +1835,6 @@ void Client::resizeWithChecks( int w, int h, ForceGeometry_t force ) newy = newy + height() - h; break; } - // if it would be moved outside of workarea, keep it inside, - // see also Client::computeWorkareaDiff() - if( workarea_diff_x != INT_MIN && w <= area.width()) // was inside and can still fit - { - if( newx < area.left()) - newx = area.left(); - if( newx + w > area.right() + 1 ) - newx = area.right() + 1 - w; - assert( newx >= area.left() && newx + w <= area.right() + 1 ); // width was checked above - } - if( workarea_diff_y != INT_MIN && h <= area.height()) // was inside and can still fit - { - if( newy < area.top()) - newy = area.top(); - if( newy + h > area.bottom() + 1 ) - newy = area.bottom() + 1 - h; - assert( newy >= area.top() && newy + h <= area.bottom() + 1 ); // height was checked above - } setGeometry( newx, newy, w, h, force ); } @@ -1869,7 +1976,6 @@ void Client::setGeometry( int x, int y, int w, int h, ForceGeometry_t force ) if( force == NormalGeometrySet && geom == g && pending_geometry_update == PendingGeometryNone ) return; geom = g; - updateWorkareaDiffs(); if( block_geometry_updates != 0 ) { if( pending_geometry_update == PendingGeometryForced ) @@ -1949,7 +2055,6 @@ void Client::plainResize( int w, int h, ForceGeometry_t force ) if( force == NormalGeometrySet && geom.size() == s ) return; geom.setSize( s ); - updateWorkareaDiffs(); if( block_geometry_updates != 0 ) { if( pending_geometry_update == PendingGeometryForced ) @@ -2003,7 +2108,6 @@ void Client::move( int x, int y, ForceGeometry_t force ) if( force == NormalGeometrySet && geom.topLeft() == p ) return; geom.moveTopLeft( p ); - updateWorkareaDiffs(); if( block_geometry_updates != 0 ) { if( pending_geometry_update == PendingGeometryForced ) @@ -2761,22 +2865,14 @@ void Client::handleMoveResize( int x, int y, int x_root, int y_root ) if( workspace()->screenNumber( globalPos ) == -1 ) return; - // compute bounds - // NOTE: This is duped in checkUnrestrictedMoveResize(). - QRect desktopArea = workspace()->clientArea( WorkArea, globalPos, desktop()); - int left_marge, right_marge, top_marge, bottom_marge, titlebar_marge; - if( unrestrictedMoveResize ) // unrestricted, just don't let it go out completely - left_marge = right_marge = top_marge = bottom_marge = titlebar_marge = 5; - else // restricted move/resize - keep at least part of the titlebar always visible - { - // how much must remain visible when moved away in that direction - left_marge = qMin( 100 + border_right, moveResizeGeom.width()); - right_marge = qMin( 100 + border_left, moveResizeGeom.width()); - // width/height change with opaque resizing, use the initial ones - titlebar_marge = initialMoveResizeGeom.height(); - top_marge = border_bottom; - bottom_marge = border_top; - } + // When doing a restricted move we must always keep 100px of the titlebar + // visible to allow the user to be able to move it again. + int frameLeft, frameRight, frameTop, frameBottom; + if( decoration ) + decoration->borders( frameLeft, frameRight, frameTop, frameBottom ); + else + frameTop = 10; + int titlebarArea = qMin( frameTop * 100, moveResizeGeom.width() * moveResizeGeom.height() ); bool update = false; if( isResize()) @@ -2823,17 +2919,63 @@ void Client::handleMoveResize( int x, int y, int x_root, int y_root ) // adjust new size to snap to other windows/borders moveResizeGeom = workspace()->adjustClientSize( this, moveResizeGeom, mode ); - // NOTE: This is duped in checkUnrestrictedMoveResize(). - if( moveResizeGeom.bottom() < desktopArea.top() + top_marge ) - moveResizeGeom.setBottom( desktopArea.top() + top_marge ); - if( moveResizeGeom.top() > desktopArea.bottom() - bottom_marge ) - moveResizeGeom.setTop( desktopArea.bottom() - bottom_marge ); - if( moveResizeGeom.right() < desktopArea.left() + left_marge ) - moveResizeGeom.setRight( desktopArea.left() + left_marge ); - if( moveResizeGeom.left() > desktopArea.right() - right_marge ) - moveResizeGeom.setLeft(desktopArea.right() - right_marge ); - if( !unrestrictedMoveResize && moveResizeGeom.top() < desktopArea.top() ) // titlebar mustn't go out - moveResizeGeom.setTop( desktopArea.top()); + // Make sure the titlebar isn't behind a restricted area. We don't need to restrict + // the other directions. If not visible enough, move the window to the closest valid + // point. We bruteforce this by slowly moving the window back to its previous position. + for(;;) + { + QRegion titlebarRegion( moveResizeGeom.left(), moveResizeGeom.top(), + moveResizeGeom.width(), frameTop ); + titlebarRegion &= workspace()->clientArea( FullArea, -1, 0 ); // On the screen + titlebarRegion -= workspace()->restrictedMoveArea( desktop() ); // Strut areas + // Now we have a region of all the visible areas of the titlebar + // Count the visible pixels and check to see if it's enough + int visiblePixels = 0; + foreach( const QRect& rect, titlebarRegion.rects() ) + if( rect.height() >= frameTop ) // Only the full height regions, prevents long slim areas + visiblePixels += rect.width() * rect.height(); + if( visiblePixels >= titlebarArea ) + break; // We have reached a valid position + + // Not visible enough, move the window to the closest valid point. We bruteforce + // this by slowly moving the window back to its previous position. + if( previousMoveResizeGeom.y() != moveResizeGeom.y() ) + { + if( previousMoveResizeGeom.y() > moveResizeGeom.y() ) + moveResizeGeom.setTop( moveResizeGeom.y() + 1 ); + else + moveResizeGeom.setTop( moveResizeGeom.y() - 1 ); + } + else // Our heights match but we still don't have a valid area, maybe + { // we are trying to resize in from the side? + bool breakLoop = false; + switch( mode ) + { + case PositionTopLeft: + case PositionLeft: + if( previousMoveResizeGeom.x() >= moveResizeGeom.x() ) + { + breakLoop = true; + break; + } + moveResizeGeom.setLeft( moveResizeGeom.x() - 1 ); + break; + case PositionTopRight: + case PositionRight: + if( previousMoveResizeGeom.right() <= moveResizeGeom.right() ) + { + breakLoop = true; + break; + } + moveResizeGeom.setRight( moveResizeGeom.x() + moveResizeGeom.width() ); + break; + default: + breakLoop = true; + } + if( breakLoop ) + break; + } + } QSize size = adjustedSize( moveResizeGeom.size(), sizemode ); // the new topleft and bottomright corners (after checking size constrains), if they'll be needed @@ -2891,16 +3033,33 @@ void Client::handleMoveResize( int x, int y, int x_root, int y_root ) moveResizeGeom.moveTopLeft( topleft ); moveResizeGeom.moveTopLeft( workspace()->adjustClientPosition( this, moveResizeGeom.topLeft(), unrestrictedMoveResize )); - // NOTE: This is duped in checkUnrestrictedMoveResize(). - if( moveResizeGeom.bottom() < desktopArea.top() + titlebar_marge - 1 ) // titlebar mustn't go out - moveResizeGeom.moveBottom( desktopArea.top() + titlebar_marge - 1 ); - // no need to check top_marge, titlebar_marge already handles it - if( moveResizeGeom.top() > desktopArea.bottom() - bottom_marge ) - moveResizeGeom.moveTop( desktopArea.bottom() - bottom_marge ); - if( moveResizeGeom.right() < desktopArea.left() + left_marge ) - moveResizeGeom.moveRight( desktopArea.left() + left_marge ); - if( moveResizeGeom.left() > desktopArea.right() - right_marge ) - moveResizeGeom.moveLeft(desktopArea.right() - right_marge ); + + // Make sure the titlebar isn't behind a restricted area. + for(;;) + { + QRegion titlebarRegion( moveResizeGeom.left(), moveResizeGeom.top(), + moveResizeGeom.width(), frameTop ); + titlebarRegion &= workspace()->clientArea( FullArea, -1, 0 ); // On the screen + titlebarRegion -= workspace()->restrictedMoveArea( desktop() ); // Strut areas + // Now we have a region of all the visible areas of the titlebar + // Count the visible pixels and check to see if it's enough + int visiblePixels = 0; + foreach( const QRect& rect, titlebarRegion.rects() ) + if( rect.height() >= frameTop ) // Only the full height regions, prevents long slim areas + visiblePixels += rect.width() * rect.height(); + if( visiblePixels >= titlebarArea ) + break; // We have reached a valid position + + // Move it (Favour vertically) + if( previousMoveResizeGeom.y() != moveResizeGeom.y() ) + moveResizeGeom.translate( 0, + previousMoveResizeGeom.y() > moveResizeGeom.y() ? 1 : -1 ); + else + moveResizeGeom.translate( previousMoveResizeGeom.x() > moveResizeGeom.x() ? 1 : -1, + 0 ); + if( moveResizeGeom == previousMoveResizeGeom ) + break; // Prevent lockup + } } if( moveResizeGeom.topLeft() != previousMoveResizeGeom.topLeft()) update = true; diff --git a/manage.cpp b/manage.cpp index fe126f75f2..e2d1265b35 100644 --- a/manage.cpp +++ b/manage.cpp @@ -524,8 +524,6 @@ bool Client::manage( Window w, bool isMapped ) user_time = xTime() - 1000000 + 10; } - updateWorkareaDiffs(); - //sendSyntheticConfigureNotify(); // Done when setting mapping state delete session; diff --git a/utils.cpp b/utils.cpp index a1d0ca80c9..6a99d86e41 100644 --- a/utils.cpp +++ b/utils.cpp @@ -57,6 +57,26 @@ namespace KWin #ifndef KCMRULES +//************************************ +// StrutRect +//************************************ + +StrutRect::StrutRect( QRect rect, StrutArea area ) + : QRect( rect ) + , m_area( area ) + { + } + +StrutRect::StrutRect( const StrutRect& other ) + : QRect( other ) + , m_area( other.area() ) + { + } + +//************************************ +// Motif +//************************************ + void Motif::readFlags( WId w, bool& noborder, bool& resize, bool& move, bool& minimize, bool& maximize, bool& close ) { diff --git a/utils.h b/utils.h index 880af40093..89981687e9 100644 --- a/utils.h +++ b/utils.h @@ -127,6 +127,29 @@ enum ActivityFlags ActivityRaise = 1 << 2 // raise the window }; +enum StrutArea + { + StrutAreaInvalid = 0, // Null + StrutAreaTop = 1 << 0, + StrutAreaRight = 1 << 1, + StrutAreaBottom = 1 << 2, + StrutAreaLeft = 1 << 3, + StrutAreaAll = StrutAreaTop | StrutAreaRight | StrutAreaBottom | StrutAreaLeft + }; +Q_DECLARE_FLAGS( StrutAreas, StrutArea ) + +class StrutRect : public QRect + { + public: + StrutRect( QRect rect = QRect(), StrutArea area = StrutAreaInvalid ); + StrutRect( const StrutRect& other ); + inline StrutArea area() const + { return m_area; }; + private: + StrutArea m_area; + }; +typedef QVector StrutRects; + // Some KWin classes, mainly Client and Workspace, are very tighly coupled, // and some of the methods of one class may be called only from speficic places. // Those methods have additional allowed_t argument. If you pass Allowed @@ -342,4 +365,7 @@ class ShortcutDialog } // namespace +// Must be outside namespace +Q_DECLARE_OPERATORS_FOR_FLAGS( KWin::StrutAreas ) + #endif diff --git a/workspace.cpp b/workspace.cpp index 92953f55eb..87ec0fa43b 100644 --- a/workspace.cpp +++ b/workspace.cpp @@ -1132,6 +1132,10 @@ void Workspace::loadDesktopSettings() desktopCount_ = n; workarea.clear(); workarea.resize( n + 1 ); + restrictedmovearea.clear(); + restrictedmovearea.resize( n + 1 ); + oldrestrictedmovearea.clear(); + oldrestrictedmovearea.resize( n + 1 ); screenarea.clear(); rootInfo->setNumberOfDesktops( n ); desktop_focus_chain.resize( n ); diff --git a/workspace.h b/workspace.h index baf291fded..77e2cb6fe6 100644 --- a/workspace.h +++ b/workspace.h @@ -90,6 +90,9 @@ class Workspace : public QObject, public KDecorationDefines QRect clientArea( clientAreaOption, const Client* c ) const; QRect clientArea( clientAreaOption, int screen, int desktop ) const; + QRegion restrictedMoveArea( int desktop, StrutAreas areas = StrutAreaAll ) const; + QRegion previousRestrictedMoveArea( int desktop, StrutAreas areas = StrutAreaAll ) const; + /** * @internal */ @@ -853,6 +856,10 @@ class Workspace : public QObject, public KDecorationDefines Placement* initPositioning; QVector workarea; // Array of workareas for virtual desktops + // Array of restricted areas that window cannot be moved into + QVector restrictedmovearea; + // Array of the previous restricted areas that window cannot be moved into + QVector oldrestrictedmovearea; QVector< QVector > screenarea; // Array of workareas per xinerama screen for all virtual desktops bool managing_topmenus;