Improved window movement around struts. Windows can be moved anywhere

where the titlebar is still clickable even if it is outside the normal
work area. When struts are added or removed only move the windows that
cover the same area, leave all others untouched. If a strut is removed
on a xinerama screen that is not on the edge of the full desktop area
prevent the windows from being moved offscreen. Prevent struts/panels
from interfering with the movement of windows on other xinerama screens.
BUG: 74559
BUG: 90833
BUG: 160068

svn path=/trunk/KDE/kdebase/workspace/; revision=927466
icc-effect-5.14.5
Lucas Murray 2009-02-17 15:50:00 +00:00
parent 7243d7132e
commit ca901dae30
8 changed files with 360 additions and 155 deletions

View File

@ -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 )

View File

@ -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;

View File

@ -4,6 +4,7 @@
Copyright (C) 1999, 2000 Matthias Ettrich <ettrich@kde.org>
Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org>
Copyright (C) 2009 Lucas Murray <lmurray@undefinedfire.com>
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;

View File

@ -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;

View File

@ -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 )
{

26
utils.h
View File

@ -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<StrutRect> 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

View File

@ -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 );

View File

@ -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<QRect> workarea; // Array of workareas for virtual desktops
// Array of restricted areas that window cannot be moved into
QVector<StrutRects> restrictedmovearea;
// Array of the previous restricted areas that window cannot be moved into
QVector<StrutRects> oldrestrictedmovearea;
QVector< QVector<QRect> > screenarea; // Array of workareas per xinerama screen for all virtual desktops
bool managing_topmenus;