Quick maximize and tiling when moving a window to the left/right or top screen edge.

Top screen edge (un)maximizes. Left screen edge sets window to left half of screen geometry, right edge to other half.

svn path=/trunk/KDE/kdebase/workspace/; revision=1021305
icc-effect-5.14.5
Martin Gräßlin 2009-09-08 20:01:08 +00:00
parent 4fa4dbd740
commit 754f5d0829
10 changed files with 349 additions and 37 deletions

View File

@ -121,6 +121,7 @@ Client::Client( Workspace* ws )
, padding_right( 0 )
, padding_top( 0 )
, padding_bottom( 0 )
, electricMaximizing( false )
{ // TODO: Do all as initialization
// Set the initial mapping state

View File

@ -218,6 +218,11 @@ class Client
void resizeWithChecks( int w, int h, ForceGeometry_t force = NormalGeometrySet );
void resizeWithChecks( const QSize& s, ForceGeometry_t force = NormalGeometrySet );
void keepInArea( QRect area, bool partial = false );
void setElectricBorderMode( ElectricMaximizingMode mode );
ElectricMaximizingMode electricBorderMode() const;
void setElectricBorderMaximizing( bool maximizing );
bool isElectricBorderMaximizing() const;
QRect electricBorderMaximizeGeometry();
void growHorizontal();
void shrinkHorizontal();
@ -582,6 +587,9 @@ class Client
QPixmap decorationPixmapLeft, decorationPixmapRight, decorationPixmapTop, decorationPixmapBottom;
PaintRedirector* paintRedirector;
bool electricMaximizing;
ElectricMaximizingMode electricMode;
friend bool performTransiencyCheck();
};

View File

@ -2709,7 +2709,9 @@ bool Client::startMoveResize()
Notify::raise( isResize() ? Notify::ResizeStart : Notify::MoveStart );
if( effects )
static_cast<EffectsHandlerImpl*>(effects)->windowUserMovedResized( effectWindow(), true, false );
if( options->electricBorders() == Options::ElectricMoveOnly )
if( options->electricBorders() == Options::ElectricMoveOnly ||
options->electricBorderMaximize() ||
options->electricBorderTiling() )
workspace()->reserveElectricBorderSwitching( true );
return true;
}
@ -2717,10 +2719,40 @@ bool Client::startMoveResize()
void Client::finishMoveResize( bool cancel )
{
leaveMoveResize();
if( isElectricBorderMaximizing() )
{
cancel = true;
}
if( cancel )
setGeometry( initialMoveResizeGeom );
else
setGeometry( moveResizeGeom );
if( isElectricBorderMaximizing() )
{
electricMaximizing = false;
switch( electricMode )
{
case ElectricMaximizeMode:
if( maximizeMode() == MaximizeFull )
setMaximize( false, false );
else
setMaximize( true, true );
break;
case ElectricLeftMode:
{
QRect max = workspace()->clientArea( MaximizeArea, cursorPos() ,workspace()->currentDesktop() );
setGeometry( QRect( max.x(), max.y(), max.width()/2, max.height() ) );
break;
}
case ElectricRightMode:
{
QRect max = workspace()->clientArea( MaximizeArea, cursorPos() ,workspace()->currentDesktop() );
setGeometry( QRect( max.x() + max.width()/2, max.y(), max.width()/2, max.height() ) );
break;
}
}
workspace()->hideElectricBorderWindowOutline();
}
checkMaximizeGeometry();
// FRAME update();
Notify::raise( isResize() ? Notify::ResizeEnd : Notify::MoveEnd );
@ -2755,7 +2787,9 @@ void Client::leaveMoveResize()
eater = 0;
delete sync_timeout;
sync_timeout = NULL;
if( options->electricBorders() == Options::ElectricMoveOnly )
if( options->electricBorders() == Options::ElectricMoveOnly ||
options->electricBorderMaximize() ||
options->electricBorderTiling() )
workspace()->reserveElectricBorderSwitching( false );
}
@ -3094,6 +3128,8 @@ void Client::handleMoveResize( int x, int y, int x_root, int y_root )
void Client::performMoveResize()
{
if( isElectricBorderMaximizing() )
return;
#ifdef HAVE_XSYNC
if( isResize() && sync_counter != None )
{
@ -3130,4 +3166,57 @@ void Client::syncTimeout()
performMoveResize();
}
void Client::setElectricBorderMode( ElectricMaximizingMode mode )
{
electricMode = mode;
}
ElectricMaximizingMode Client::electricBorderMode() const
{
return electricMode;
}
bool Client::isElectricBorderMaximizing() const
{
return electricMaximizing;
}
void Client::setElectricBorderMaximizing( bool maximizing )
{
electricMaximizing = maximizing;
if( maximizing )
workspace()->showElectricBorderWindowOutline();
else
workspace()->hideElectricBorderWindowOutline();
}
QRect Client::electricBorderMaximizeGeometry()
{
QRect ret;
switch( electricMode )
{
case ElectricMaximizeMode:
{
if( maximizeMode() == MaximizeFull )
ret = geometryRestore();
else
ret = workspace()->clientArea( MaximizeArea, cursorPos() ,workspace()->currentDesktop() );
break;
}
case ElectricLeftMode:
{
QRect max = workspace()->clientArea( MaximizeArea, cursorPos() ,workspace()->currentDesktop() );
ret = QRect( max.x(), max.y(), max.width()/2, max.height() );
break;
}
case ElectricRightMode:
{
QRect max = workspace()->clientArea( MaximizeArea, cursorPos() ,workspace()->currentDesktop() );
ret = QRect( max.x() + max.width()/2, max.y(), max.width()/2, max.height() );
break;
}
}
return ret;
}
} // namespace

View File

@ -56,6 +56,8 @@ KWinScreenEdgesConfig::KWinScreenEdgesConfig( QWidget* parent, const QVariantLis
connect( m_ui->desktopSwitchCombo, SIGNAL( currentIndexChanged(int) ), this, SLOT( changed() ));
connect( m_ui->activationDelaySpin, SIGNAL( valueChanged(int) ), this, SLOT( changed() ));
connect( m_ui->triggerCooldownSpin, SIGNAL( valueChanged(int) ), this, SLOT( changed() ));
connect( m_ui->quickMaximizeBox, SIGNAL( stateChanged(int) ), this, SLOT( changed() ));
connect( m_ui->quickTileBox, SIGNAL( stateChanged(int) ), this, SLOT( changed() ));
// Visual feedback of action group conflicts
connect( m_ui->desktopSwitchCombo, SIGNAL( currentIndexChanged(int) ), this, SLOT( groupChanged() ));
@ -84,6 +86,11 @@ void KWinScreenEdgesConfig::groupChanged()
// Desktop switch conflicts
m_ui->desktopSwitchLabel->setEnabled( true );
m_ui->desktopSwitchCombo->setEnabled( true );
bool enableMaximize = false;
if( m_ui->desktopSwitchCombo->currentIndex() == 0 )
enableMaximize = true;
m_ui->quickMaximizeBox->setEnabled( enableMaximize );
}
void KWinScreenEdgesConfig::load()
@ -97,6 +104,8 @@ void KWinScreenEdgesConfig::load()
m_ui->desktopSwitchCombo->setCurrentIndex( config.readEntry( "ElectricBorders", 0 ));
m_ui->activationDelaySpin->setValue( config.readEntry( "ElectricBorderDelay", 150 ));
m_ui->triggerCooldownSpin->setValue( config.readEntry( "ElectricBorderCooldown", 350 ));
m_ui->quickMaximizeBox->setChecked( config.readEntry( "ElectricBorderMaximize", false ));
m_ui->quickTileBox->setChecked( config.readEntry( "ElectricBorderTiling", false ));
emit changed( false );
}
@ -112,6 +121,8 @@ void KWinScreenEdgesConfig::save()
config.writeEntry( "ElectricBorders", m_ui->desktopSwitchCombo->currentIndex() );
config.writeEntry( "ElectricBorderDelay", m_ui->activationDelaySpin->value() );
config.writeEntry( "ElectricBorderCooldown", m_ui->triggerCooldownSpin->value() );
config.writeEntry( "ElectricBorderMaximize", m_ui->quickMaximizeBox->isChecked() );
config.writeEntry( "ElectricBorderTiling", m_ui->quickTileBox->isChecked() );
config.sync();
@ -129,6 +140,8 @@ void KWinScreenEdgesConfig::defaults()
m_ui->desktopSwitchCombo->setCurrentIndex( 0 );
m_ui->activationDelaySpin->setValue( 150 );
m_ui->triggerCooldownSpin->setValue( 350 );
m_ui->quickMaximizeBox->setChecked( false );
m_ui->quickTileBox->setChecked( false );
emit changed( true );
}

View File

@ -58,7 +58,7 @@
</item>
</widget>
</item>
<item row="1" column="0">
<item row="3" column="0">
<widget class="QLabel" name="label">
<property name="toolTip">
<string>Amount of time required for the mouse cursor to be pushed against the edge of the screen before the action is triggered</string>
@ -71,7 +71,7 @@
</property>
</widget>
</item>
<item row="1" column="1">
<item row="3" column="1">
<widget class="KIntSpinBox" name="activationDelaySpin">
<property name="suffix">
<string> ms</string>
@ -87,7 +87,7 @@
</property>
</widget>
</item>
<item row="2" column="0">
<item row="4" column="0">
<widget class="QLabel" name="label_2">
<property name="enabled">
<bool>true</bool>
@ -103,7 +103,7 @@
</property>
</widget>
</item>
<item row="2" column="1">
<item row="4" column="1">
<widget class="KIntSpinBox" name="triggerCooldownSpin">
<property name="enabled">
<bool>true</bool>
@ -122,7 +122,7 @@
</property>
</widget>
</item>
<item row="3" column="0">
<item row="5" column="0">
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
@ -135,6 +135,20 @@
</property>
</spacer>
</item>
<item row="1" column="0" colspan="2">
<widget class="QCheckBox" name="quickMaximizeBox">
<property name="text">
<string>Maximize windows by dragging them to the top of the screen</string>
</property>
</widget>
</item>
<item row="2" column="0" colspan="2">
<widget class="QCheckBox" name="quickTileBox">
<property name="text">
<string>Tile windows by dragging them to the side of the screen</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>

View File

@ -72,6 +72,13 @@ enum ElectricBorder
ElectricNone
};
enum ElectricMaximizingMode
{
ElectricMaximizeMode,
ElectricLeftMode,
ElectricRightMode
};
// TODO: Hardcoding is bad, need to add some way of registering global actions to these.
// When designing the new system we must keep in mind that we have conditional actions
// such as "only when moving windows" desktop switching that the current global action

View File

@ -143,6 +143,8 @@ unsigned long Options::updateSettings()
electric_borders = config.readEntry("ElectricBorders", 0);
electric_border_delay = config.readEntry("ElectricBorderDelay", 150);
electric_border_cooldown = config.readEntry("ElectricBorderCooldown", 350);
electric_border_maximize = config.readEntry("ElectricBorderMaximize", false);
electric_border_tiling = config.readEntry("ElectricBorderTiling" , false );
OpTitlebarDblClick = windowOperation( config.readEntry("TitlebarDoubleClickCommand", "Maximize"), true );
setOpMaxButtonLeftClick( windowOperation( config.readEntry("MaximizeButtonLeftClickCommand", "Maximize"), true ));

View File

@ -289,6 +289,16 @@ class Options : public KDecorationOptions
* @returns the trigger cooldown for electric borders in milliseconds.
*/
int electricBorderCooldown();
/**
* @returns true if a window gets maximized when it reaches top screen edge
* while being moved.
*/
bool electricBorderMaximize() const { return electric_border_maximize; }
/**
* @returns true if window is tiled to half screen when reaching left or
* right screen edge while been moved
*/
bool electricBorderTiling() const { return electric_border_tiling; }
bool topMenuEnabled() const { return topmenus; }
bool desktopTopMenu() const { return desktop_topmenu; }
@ -353,6 +363,8 @@ class Options : public KDecorationOptions
int electric_borders;
int electric_border_delay;
int electric_border_cooldown;
bool electric_border_maximize;
bool electric_border_tiling;
bool show_geometry_tip;
bool topmenus;
bool desktop_topmenu;

View File

@ -453,6 +453,17 @@ void Workspace::init()
}
if( new_active_client != NULL )
activateClient( new_active_client );
// outline windows for electric border maximize window mode
outline_left = XCreateWindow( QX11Info::display(), QX11Info::appRootWindow(), 0, 0, 1, 1, 0,
CopyFromParent, CopyFromParent, CopyFromParent, CWOverrideRedirect, &attr );
outline_right = XCreateWindow( QX11Info::display(), QX11Info::appRootWindow(), 0, 0, 1, 1, 0,
CopyFromParent, CopyFromParent, CopyFromParent, CWOverrideRedirect, &attr );
outline_top = XCreateWindow( QX11Info::display(), QX11Info::appRootWindow(), 0, 0, 1, 1, 0,
CopyFromParent, CopyFromParent, CopyFromParent, CWOverrideRedirect, &attr );
outline_bottom = XCreateWindow( QX11Info::display(), QX11Info::appRootWindow(), 0, 0, 1, 1, 0,
CopyFromParent, CopyFromParent, CopyFromParent, CWOverrideRedirect, &attr );
// SELI TODO: This won't work with unreasonable focus policies,
// and maybe in rare cases also if the selected client doesn't
// want focus
@ -493,6 +504,12 @@ Workspace::~Workspace()
writeWindowRules();
KGlobal::config()->sync();
// destroy outline windows for electric border maximize window mode
XDestroyWindow( QX11Info::display(), outline_left );
XDestroyWindow( QX11Info::display(), outline_right );
XDestroyWindow( QX11Info::display(), outline_top );
XDestroyWindow( QX11Info::display(), outline_bottom );
delete rootInfo;
delete supportWindow;
delete mgr;
@ -2117,6 +2134,7 @@ void Workspace::checkElectricBorder(const QPoint& pos, Time now)
Time treshold_reset = 250; // Reset timeout
Time treshold_trigger = options->electricBorderCooldown(); // Minimum time between triggers
int distance_reset = 30; // Mouse should not move more than this many pixels
int pushback_pixels = 1;
ElectricBorder border;
if( pos.x() == electricLeft && pos.y() == electricTop )
@ -2153,41 +2171,80 @@ void Workspace::checkElectricBorder(const QPoint& pos, Time now)
electric_current_border = ElectricNone;
electric_time_last_trigger = now;
if( movingClient )
{ // If moving a client or have force doing the desktop switch
if( options->electricBorders() != Options::ElectricDisabled )
electricBorderSwitchDesktop( border, pos );
return; // Don't reset cursor position
}
if( options->electricBorders() == Options::ElectricAlways &&
( border == ElectricTop || border == ElectricRight ||
border == ElectricBottom || border == ElectricLeft ))
{ // If desktop switching is always enabled don't apply it to the corners if
// an effect is applied to it (We will check that later).
electricBorderSwitchDesktop( border, pos );
return; // Don't reset cursor position
}
switch( options->electricBorderAction( border ))
{
case ElectricActionDashboard: // Display Plasma dashboard
// If moving a client or have force doing the desktop switch
if( options->electricBorders() != Options::ElectricDisabled )
{
QDBusInterface plasmaApp( "org.kde.plasma-desktop", "/App" );
plasmaApp.call( "toggleDashboard" );
electricBorderSwitchDesktop( border, pos );
return; // Don't reset cursor position
}
break;
case ElectricActionShowDesktop:
// maximize only when not using for switch
if( options->electricBorderMaximize() && border == ElectricTop &&
movingClient->isMaximizable() )
{
setShowingDesktop( !showingDesktop() );
break;
bool enable = !movingClient->isElectricBorderMaximizing();
movingClient->setElectricBorderMode( ElectricMaximizeMode );
movingClient->setElectricBorderMaximizing( enable );
// stronger push back
pushback_pixels = 10;
}
case ElectricActionNone: // Either desktop switching or an effect
default:
if( options->electricBorderTiling() )
{
if( effects && static_cast<EffectsHandlerImpl*>( effects )->borderActivated( border ))
{} // Handled by effects
else
bool enable = !movingClient->isElectricBorderMaximizing();
bool activate = false;
if( border == ElectricLeft )
{
electricBorderSwitchDesktop( border, pos );
return; // Don't reset cursor position
movingClient->setElectricBorderMode( ElectricLeftMode );
activate = true;
}
else if( border == ElectricRight )
{
movingClient->setElectricBorderMode( ElectricRightMode );
activate = true;
}
if( activate )
{
movingClient->setElectricBorderMaximizing( enable );
// stronger push back
pushback_pixels = 10;
}
}
else
return; // Don't reset cursor position
}
else
{
if( options->electricBorders() == Options::ElectricAlways &&
( border == ElectricTop || border == ElectricRight ||
border == ElectricBottom || border == ElectricLeft ))
{ // If desktop switching is always enabled don't apply it to the corners if
// an effect is applied to it (We will check that later).
electricBorderSwitchDesktop( border, pos );
return; // Don't reset cursor position
}
switch( options->electricBorderAction( border ))
{
case ElectricActionDashboard: // Display Plasma dashboard
{
QDBusInterface plasmaApp( "org.kde.plasma-desktop", "/App" );
plasmaApp.call( "toggleDashboard" );
}
break;
case ElectricActionShowDesktop:
{
setShowingDesktop( !showingDesktop() );
break;
}
case ElectricActionNone: // Either desktop switching or an effect
default:
{
if( effects && static_cast<EffectsHandlerImpl*>( effects )->borderActivated( border ))
{} // Handled by effects
else
{
electricBorderSwitchDesktop( border, pos );
return; // Don't reset cursor position
}
}
}
}
@ -2203,8 +2260,22 @@ void Workspace::checkElectricBorder(const QPoint& pos, Time now)
// Reset the pointer to find out whether the user is really pushing
// (the direction back from which it came, starting from top clockwise)
const int xdiff[ELECTRIC_COUNT] = { 0, -1, -1, -1, 0, 1, 1, 1 };
const int ydiff[ELECTRIC_COUNT] = { 1, 1, 0, -1, -1, -1, 0, 1 };
const int xdiff[ELECTRIC_COUNT] = { 0,
-pushback_pixels,
-pushback_pixels,
-pushback_pixels,
0,
pushback_pixels,
pushback_pixels,
pushback_pixels };
const int ydiff[ELECTRIC_COUNT] = { pushback_pixels,
pushback_pixels,
0,
-pushback_pixels,
-pushback_pixels,
-pushback_pixels,
0,
pushback_pixels };
QCursor::setPos( pos.x() + xdiff[border], pos.y() + ydiff[border] );
}
@ -2271,6 +2342,94 @@ bool Workspace::electricBorderEvent( XEvent* e )
return false;
}
void Workspace::showElectricBorderWindowOutline()
{
if( !movingClient )
return;
// code copied from TabBox::updateOutline() in tabbox.cpp
QRect c = movingClient->electricBorderMaximizeGeometry();
// left/right parts are between top/bottom, they don't reach as far as the corners
XMoveResizeWindow( QX11Info::display(), outline_left, c.x(), c.y() + 5, 5, c.height() - 10 );
XMoveResizeWindow( QX11Info::display(), outline_right, c.x() + c.width() - 5, c.y() + 5, 5, c.height() - 10 );
XMoveResizeWindow( QX11Info::display(), outline_top, c.x(), c.y(), c.width(), 5 );
XMoveResizeWindow( QX11Info::display(), outline_bottom, c.x(), c.y() + c.height() - 5, c.width(), 5 );
{
QPixmap pix( 5, c.height() - 10 );
QPainter p( &pix );
p.setPen( Qt::white );
p.drawLine( 0, 0, 0, pix.height() - 1 );
p.drawLine( 4, 0, 4, pix.height() - 1 );
p.setPen( Qt::gray );
p.drawLine( 1, 0, 1, pix.height() - 1 );
p.drawLine( 3, 0, 3, pix.height() - 1 );
p.setPen( Qt::black );
p.drawLine( 2, 0, 2, pix.height() - 1 );
p.end();
XSetWindowBackgroundPixmap( QX11Info::display(), outline_left, pix.handle());
XSetWindowBackgroundPixmap( QX11Info::display(), outline_right, pix.handle());
}
{
QPixmap pix( c.width(), 5 );
QPainter p( &pix );
p.setPen( Qt::white );
p.drawLine( 0, 0, pix.width() - 1 - 0, 0 );
p.drawLine( 4, 4, pix.width() - 1 - 4, 4 );
p.drawLine( 0, 0, 0, 4 );
p.drawLine( pix.width() - 1 - 0, 0, pix.width() - 1 - 0, 4 );
p.setPen( Qt::gray );
p.drawLine( 1, 1, pix.width() - 1 - 1, 1 );
p.drawLine( 3, 3, pix.width() - 1 - 3, 3 );
p.drawLine( 1, 1, 1, 4 );
p.drawLine( 3, 3, 3, 4 );
p.drawLine( pix.width() - 1 - 1, 1, pix.width() - 1 - 1, 4 );
p.drawLine( pix.width() - 1 - 3, 3, pix.width() - 1 - 3, 4 );
p.setPen( Qt::black );
p.drawLine( 2, 2, pix.width() - 1 - 2, 2 );
p.drawLine( 2, 2, 2, 4 );
p.drawLine( pix.width() - 1 - 2, 2, pix.width() - 1 - 2, 4 );
p.end();
XSetWindowBackgroundPixmap( QX11Info::display(), outline_top, pix.handle());
}
{
QPixmap pix( c.width(), 5 );
QPainter p( &pix );
p.setPen( Qt::white );
p.drawLine( 4, 0, pix.width() - 1 - 4, 0 );
p.drawLine( 0, 4, pix.width() - 1 - 0, 4 );
p.drawLine( 0, 4, 0, 0 );
p.drawLine( pix.width() - 1 - 0, 4, pix.width() - 1 - 0, 0 );
p.setPen( Qt::gray );
p.drawLine( 3, 1, pix.width() - 1 - 3, 1 );
p.drawLine( 1, 3, pix.width() - 1 - 1, 3 );
p.drawLine( 3, 1, 3, 0 );
p.drawLine( 1, 3, 1, 0 );
p.drawLine( pix.width() - 1 - 3, 1, pix.width() - 1 - 3, 0 );
p.drawLine( pix.width() - 1 - 1, 3, pix.width() - 1 - 1, 0 );
p.setPen( Qt::black );
p.drawLine( 2, 2, pix.width() - 1 - 2, 2 );
p.drawLine( 2, 0, 2, 2 );
p.drawLine( pix.width() - 1 - 2, 0, pix.width() - 1 - 2, 2 );
p.end();
XSetWindowBackgroundPixmap( QX11Info::display(), outline_bottom, pix.handle());
}
XClearWindow( QX11Info::display(), outline_left );
XClearWindow( QX11Info::display(), outline_right );
XClearWindow( QX11Info::display(), outline_top );
XClearWindow( QX11Info::display(), outline_bottom );
XMapWindow( QX11Info::display(), outline_left );
XMapWindow( QX11Info::display(), outline_right );
XMapWindow( QX11Info::display(), outline_top );
XMapWindow( QX11Info::display(), outline_bottom );
}
void Workspace::hideElectricBorderWindowOutline()
{
XUnmapWindow( QX11Info::display(), outline_left );
XUnmapWindow( QX11Info::display(), outline_right );
XUnmapWindow( QX11Info::display(), outline_top );
XUnmapWindow( QX11Info::display(), outline_bottom );
}
//-----------------------------------------------------------------------------
// Top menu

View File

@ -467,6 +467,8 @@ class Workspace : public QObject, public KDecorationDefines
void stopMousePolling();
void raiseElectricBorderWindows();
void showElectricBorderWindowOutline();
void hideElectricBorderWindowOutline();
public slots:
void addRepaintFull();
@ -903,6 +905,11 @@ class Workspace : public QObject, public KDecorationDefines
QList< int > composite_paint_times;
QTimer compositeResetTimer; // for compressing composite resets
Window outline_left;
Window outline_right;
Window outline_top;
Window outline_bottom;
private:
friend bool performTransiencyCheck();
};