From ad771450f3fe9c170d8cef42147e444f2f7aaea8 Mon Sep 17 00:00:00 2001 From: Mavridis Philippe Date: Wed, 5 Apr 2023 22:08:38 +0300 Subject: TWin: Active borders and snap tiling This commit is a squash of the commits of TDE/tdebase#331. In short, this backports some improvements to existing electric border functionality from KDE, adds the snap tiling (or aerosnap) feature and brings rudimentary support for active corners, which will be fully implemented in a later PR. The options dialog and the documentation has been updated to reflect these changes. Additionally, a new relevant option is introduced: an option for restoring the original size of maximized/tiled windows when the user starts dragging them. The option is set to be off by default, preserving the traditional behaviour of KDE 3.x/TDE. Last but not least, the term "electric" in relation to borders and corners is replaced by "active" for clarity to the users. Signed-off-by: Mavridis Philippe (cherry picked from commit 31335a04ed9bc01fd3ede33afde40d6f3359f2e9) --- twin/client.cpp | 4 +- twin/client.h | 10 + twin/events.cpp | 4 +- twin/geometry.cpp | 266 +++++++++++----- twin/kcmtwin/twinoptions/windows.cpp | 159 +++++++--- twin/kcmtwin/twinoptions/windows.h | 25 +- twin/layers.cpp | 56 ++-- twin/options.cpp | 30 +- twin/options.h | 26 +- twin/utils.h | 23 ++ twin/workspace.cpp | 577 +++++++++++++++++++++-------------- twin/workspace.h | 48 +-- 12 files changed, 807 insertions(+), 421 deletions(-) (limited to 'twin') diff --git a/twin/client.cpp b/twin/client.cpp index a58fd22f2..484290e45 100644 --- a/twin/client.cpp +++ b/twin/client.cpp @@ -120,7 +120,9 @@ Client::Client( Workspace *ws ) border_top( 0 ), border_bottom( 0 ), opacity_( 0 ), - demandAttentionKNotifyTimer( NULL ) + demandAttentionKNotifyTimer( NULL ), + activeMaximizing(false), + activeTiled(false) // SELI do all as initialization { autoRaiseTimer = 0; diff --git a/twin/client.h b/twin/client.h index d3c7e7b13..332501f88 100644 --- a/twin/client.h +++ b/twin/client.h @@ -232,6 +232,11 @@ class Client : public TQObject, public KDecorationDefines void resizeWithChecks( int w, int h, ForceGeometry_t force = NormalGeometrySet ); void resizeWithChecks( const TQSize& s, ForceGeometry_t force = NormalGeometrySet ); void keepInArea( TQRect area, bool partial = false ); + void setActiveBorderMode( ActiveMaximizingMode mode ); + ActiveMaximizingMode activeBorderMode() const; + void setActiveBorderMaximizing(bool maximizing); + bool isActiveBorderMaximizing() const; + TQRect activeBorderMaximizeGeometry(); void growHorizontal(); void shrinkHorizontal(); @@ -600,6 +605,11 @@ class Client : public TQObject, public KDecorationDefines bool isBMP_; TQTimer* demandAttentionKNotifyTimer; + bool activeMaximizing; + bool activeTiled; + TQRect activeTiledOrigGeom; + ActiveMaximizingMode activeMode; + friend bool performTransiencyCheck(); bool minimized_before_suspend; }; diff --git a/twin/events.cpp b/twin/events.cpp index e9f8fd484..8430ad93a 100644 --- a/twin/events.cpp +++ b/twin/events.cpp @@ -389,7 +389,7 @@ bool Workspace::workspaceEvent( XEvent * e ) if ( w ) TQWhatsThis::leaveWhatsThisMode(); } - if( electricBorder(e)) + if (activeBorderEvent(e)) return true; break; } @@ -454,7 +454,7 @@ bool Workspace::workspaceEvent( XEvent * e ) case FocusOut: return true; // always eat these, they would tell Qt that KWin is the active app case ClientMessage: - if( electricBorder( e )) + if (activeBorderEvent(e)) return true; break; default: diff --git a/twin/geometry.cpp b/twin/geometry.cpp index 5c71e7c1f..7dd38bee2 100644 --- a/twin/geometry.cpp +++ b/twin/geometry.cpp @@ -40,7 +40,7 @@ namespace KWinInternal Resizes the workspace after an XRANDR screen size change */ void Workspace::desktopResized() - { +{ //printf("Workspace::desktopResized()\n"); TQRect geom = TDEApplication::desktop()->geometry(); NETSize desktop_geometry; @@ -49,24 +49,17 @@ void Workspace::desktopResized() rootInfo->setDesktopGeometry( -1, desktop_geometry ); updateClientArea( true ); - checkElectricBorders( true ); - } + destroyActiveBorders(); + updateActiveBorders(); +} /*! Resizes the workspace after kdesktop signals a desktop resize */ void Workspace::kDestopResized() - { - //printf("Workspace::kDesktopResized()\n"); - TQRect geom = TDEApplication::desktop()->geometry(); - NETSize desktop_geometry; - desktop_geometry.width = geom.width(); - desktop_geometry.height = geom.height(); - rootInfo->setDesktopGeometry( -1, desktop_geometry ); - - updateClientArea( true ); - checkElectricBorders( true ); - } +{ + desktopResized(); +} /*! Updates the current client areas according to the current clients. @@ -2302,7 +2295,7 @@ class EatAllPaintEvents static EatAllPaintEvents* eater = 0; bool Client::startMoveResize() - { +{ assert( !moveResizeMode ); assert( TQWidget::keyboardGrabber() == NULL ); assert( TQWidget::mouseGrabber() == NULL ); @@ -2324,28 +2317,55 @@ bool Client::startMoveResize() if( XGrabKeyboard( tqt_xdisplay(), frameId(), False, GrabModeAsync, GrabModeAsync, GET_QT_X_TIME() ) == Success ) has_grab = true; if( !has_grab ) // at least one grab is necessary in order to be able to finish move/resize - { + { XDestroyWindow( tqt_xdisplay(), move_resize_grab_window ); move_resize_grab_window = None; return false; - } - if ( maximizeMode() != MaximizeRestore ) - resetMaximize(); + } + removeShadow(); moveResizeMode = true; + initialMoveResizeGeom = geometry(); + + if (activeTiled) + { + // Restore original geometry + activeTiled = false; + if (options->resetMaximizedWindowGeometry() && isMove()) { + setGeometry(activeTiledOrigGeom); + } + } + + if ( maximizeMode() != MaximizeRestore ) + { + if (options->resetMaximizedWindowGeometry() && isMove()) { + maximize(MaximizeRestore); + } + else { + resetMaximize(); + } + activeTiled = false; + } + + moveResizeGeom = geometry(); workspace()->setClientIsMoving(this); - initialMoveResizeGeom = moveResizeGeom = geometry(); checkUnrestrictedMoveResize(); + // rule out non opaque windows from useless translucency settings, maybe resizes? if ((isResize() && options->removeShadowsOnResize) || (isMove() && options->removeShadowsOnMove)) + { setShadowSize(0); - if (rules()->checkMoveResizeMode( options->moveMode ) == Options::Opaque){ + } + + if (rules()->checkMoveResizeMode( options->moveMode ) == Options::Opaque) + { savedOpacity_ = opacity_; setOpacity(options->translucentMovingWindows, options->movingWindowOpacity); } + if ( ( isMove() && rules()->checkMoveResizeMode( options->moveMode ) != Options::Opaque ) || ( isResize() && rules()->checkMoveResizeMode( options->resizeMode ) != Options::Opaque ) ) - { + { grabXServer(); kapp->sendPostedEvents(); // we have server grab -> nothing should cause paint events @@ -2355,25 +2375,57 @@ bool Client::startMoveResize() // paint events for the geometrytip need to be allowed, though eater = new EatAllPaintEvents; // not needed anymore? kapp->installEventFilter( eater ); - } + } Notify::raise( isResize() ? Notify::ResizeStart : Notify::MoveStart ); - return true; + + if (options->activeBorders() == Options::ActiveSwitchOnMove || + options->activeBorders() == Options::ActiveTileMaximize || + options->activeBorders() == Options::ActiveTileOnly) + + { + workspace()->reserveActiveBorderSwitching(true); } + return true; +} + void Client::finishMoveResize( bool cancel ) - { +{ leaveMoveResize(); - if( cancel ) - setGeometry( initialMoveResizeGeom ); + + if (!isActiveBorderMaximizing()) { + setGeometry(cancel ? initialMoveResizeGeom : moveResizeGeom); + } + else - setGeometry( moveResizeGeom ); + { + kdDebug() <<"finishing moveresize in active mode, cancel is " << cancel << endl; + activeMaximizing = false; + activeTiled = true; + activeTiledOrigGeom = initialMoveResizeGeom; + switch(activeMode) + { + case ActiveMaximizeMode: + { + if (!cancel) { + bool full = (maximizeMode() == MaximizeFull); + setMaximize(!full, !full); + } + break; + } + default: + setGeometry(cancel ? initialMoveResizeGeom + : activeBorderMaximizeGeometry()); + } + } + checkMaximizeGeometry(); // FRAME update(); Notify::raise( isResize() ? Notify::ResizeEnd : Notify::MoveEnd ); - } +} void Client::leaveMoveResize() - { +{ // rule out non opaque windows from useless translucency settings, maybe resizes? if (rules()->checkMoveResizeMode( options->moveMode ) == Options::Opaque) setOpacity(true, savedOpacity_); @@ -2401,12 +2453,19 @@ void Client::leaveMoveResize() delete eater; eater = 0; if (options->shadowEnabled(isActive())) - { + { drawIntersectingShadows(); updateOpacityCache(); - } } + if (options->activeBorders() == Options::ActiveSwitchOnMove || + options->activeBorders() == Options::ActiveTileMaximize || + options->activeBorders() == Options::ActiveTileOnly) + { + workspace()->reserveActiveBorderSwitching(false); + } +} + // This function checks if it actually makes sense to perform a restricted move/resize. // If e.g. the titlebar is already outside of the workarea, there's no point in performing // a restricted move resize, because then e.g. resize would also move the window (#74555). @@ -2454,25 +2513,25 @@ void Client::checkUnrestrictedMoveResize() void Client::handleMoveResize( int x, int y, int x_root, int y_root ) { - if(( mode == PositionCenter && !isMovable()) - || ( mode != PositionCenter && ( isShade() || !isResizable()))) + if ( (mode == PositionCenter && !isMovable()) + || (mode != PositionCenter && (isShade() || !isResizable())) ) return; - if ( !moveResizeMode ) - { - TQPoint p( TQPoint( x, y ) - moveOffset ); + if (!moveResizeMode) + { + TQPoint p(TQPoint( x, y ) - moveOffset); if (p.manhattanLength() >= 6) + { + if(!startMoveResize()) { - if( !startMoveResize()) - { buttonDown = false; setCursor( mode ); return; - } } + } else return; - } + } // ShadeHover or ShadeActive, ShadeNormal was already avoided above if ( mode != PositionCenter && shade_mode != ShadeNone ) @@ -2494,7 +2553,7 @@ void Client::handleMoveResize( int x, int y, int x_root, int y_root ) 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 = KMIN( 100 + border_right, moveResizeGeom.width()); right_marge = KMIN( 100 + border_left, moveResizeGeom.width()); @@ -2502,16 +2561,16 @@ void Client::handleMoveResize( int x, int y, int x_root, int y_root ) titlebar_marge = initialMoveResizeGeom.height(); top_marge = border_bottom; bottom_marge = border_top; - } + } bool update = false; - if( isResize()) - { + if (isResize()) + { // first resize (without checking constraints), then snap, then check bounds, then check constraints TQRect orig = initialMoveResizeGeom; Sizemode sizemode = SizemodeAny; switch ( mode ) - { + { case PositionTopLeft: moveResizeGeom = TQRect( topleft, orig.bottomRight() ) ; break; @@ -2544,7 +2603,7 @@ void Client::handleMoveResize( int x, int y, int x_root, int y_root ) default: assert( false ); break; - } + } // adjust new size to snap to other windows/borders moveResizeGeom = workspace()->adjustClientSize( this, moveResizeGeom, mode ); @@ -2567,7 +2626,7 @@ void Client::handleMoveResize( int x, int y, int x_root, int y_root ) bottomright = TQPoint( moveResizeGeom.left() + size.width() - 1, moveResizeGeom.top() + size.height() - 1 ); orig = moveResizeGeom; switch ( mode ) - { // these 4 corners ones are copied from above + { // these 4 corners ones are copied from above case PositionTopLeft: moveResizeGeom = TQRect( topleft, orig.bottomRight() ) ; break; @@ -2599,12 +2658,12 @@ void Client::handleMoveResize( int x, int y, int x_root, int y_root ) default: assert( false ); break; - } - if( moveResizeGeom.size() != previousMoveResizeGeom.size()) - update = true; } - else if( isMove()) - { + if (moveResizeGeom.size() != previousMoveResizeGeom.size()) + update = true; + } + else if (isMove()) + { assert( mode == PositionCenter ); // first move, then snap, then check bounds moveResizeGeom.moveTopLeft( topleft ); @@ -2621,29 +2680,98 @@ void Client::handleMoveResize( int x, int y, int x_root, int y_root ) moveResizeGeom.moveLeft(desktopArea.right() - right_marge ); if( moveResizeGeom.topLeft() != previousMoveResizeGeom.topLeft()) update = true; - } + } else - assert( false ); + assert(false); - if( update ) + if (update) + { + if (rules()->checkMoveResizeMode(isResize() ? options->resizeMode : options->moveMode) == Options::Opaque + && !isActiveBorderMaximizing()) { - if( rules()->checkMoveResizeMode - ( isResize() ? options->resizeMode : options->moveMode ) == Options::Opaque ) - { setGeometry( moveResizeGeom ); positionGeometryTip(); - } - else if( rules()->checkMoveResizeMode - ( isResize() ? options->resizeMode : options->moveMode ) == Options::Transparent ) - { - clearbound(); // it's necessary to move the geometry tip when there's no outline - positionGeometryTip(); // shown, otherwise it would cause repaint problems in case - drawbound( moveResizeGeom ); // they overlap; the paint event will come after this, - } // so the geometry tip will be painted above the outline } - if ( isMove() ) - workspace()->clientMoved(globalPos, GET_QT_X_TIME()); + else if (rules()->checkMoveResizeMode(isResize() ? options->resizeMode : options->moveMode) == Options::Transparent ) + { + /* It's necessary to move the geometry tip when there's no outline + * shown, otherwise it would cause repaint problems in case + * they overlap; the paint event will come after this, + * so the geometry tip will be painted above the outline + */ + clearbound(); + positionGeometryTip(); + drawbound(isActiveBorderMaximizing() ? activeBorderMaximizeGeometry() : moveResizeGeom); + } } + if (isMove()) + workspace()->checkActiveBorder(globalPos, GET_QT_X_TIME()); +} + +void Client::setActiveBorderMode( ActiveMaximizingMode mode ) +{ + activeMode = mode; +} + +ActiveMaximizingMode Client::activeBorderMode() const +{ + return activeMode; +} + +bool Client::isActiveBorderMaximizing() const +{ + return activeMaximizing; +} +void Client::setActiveBorderMaximizing( bool maximizing ) +{ + activeMaximizing = maximizing; + + if (maximizing || rules()->checkMoveResizeMode(isResize() ? options->resizeMode : options->moveMode) == Options::Opaque) { + clearbound(); + } + + if (maximizing) { + drawbound(activeBorderMaximizeGeometry()); + } +} + +TQRect Client::activeBorderMaximizeGeometry() +{ + TQRect ret; + TQRect max = workspace()->clientArea(MaximizeArea, TQCursor::pos(), workspace()->currentDesktop()); + switch (activeMode) + { + case ActiveMaximizeMode: + { + if (maximizeMode() == MaximizeFull) + ret = geometryRestore(); + else + ret = max; + break; + } + case ActiveLeftMode: + { + ret = TQRect( max.x(), max.y(), max.width()/2, max.height() ); + break; + } + case ActiveRightMode: + { + ret = TQRect( max.x() + max.width()/2, max.y(), max.width()/2, max.height() ); + break; + } + case ActiveTopMode: + { + ret = TQRect( max.x(), max.y(), max.width(), max.height()/2 ); + break; + } + case ActiveBottomMode: + { + ret = TQRect( max.x(), max.y() + max.height()/2, max.width(), max.height()/2 ); + break; + } + } + return ret; +} } // namespace diff --git a/twin/kcmtwin/twinoptions/windows.cpp b/twin/kcmtwin/twinoptions/windows.cpp index 66249d474..3610eb443 100644 --- a/twin/kcmtwin/twinoptions/windows.cpp +++ b/twin/kcmtwin/twinoptions/windows.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -69,6 +70,7 @@ #define KWIN_CLICKRAISE "ClickRaise" #define KWIN_ANIMSHADE "AnimateShade" #define KWIN_MOVE_RESIZE_MAXIMIZED "MoveResizeMaximizedWindows" +#define KWIN_RESET_MAX_WIN_GEOM "ResetMaximizedWindowGeometry" #define KWIN_ALTTABMODE "AltTabStyle" #define KWIN_TRAVERSE_ALL "TraverseAll" #define KWIN_SHOW_POPUP "ShowPopup" @@ -79,10 +81,12 @@ #define KWIN_HIDE_UTILITY "HideUtilityWindowsForInactive" #define KWIN_SEPARATE_SCREEN_FOCUS "SeparateScreenFocus" #define KWIN_ACTIVE_MOUSE_SCREEN "ActiveMouseScreen" +#define KWIN_ACTIVE_BORDERS "ActiveBorders" +#define KWIN_ACTIVE_BORDER_DELAY "ActiveBorderDelay" -// kwm config keywords -#define KWM_ELECTRIC_BORDER "ElectricBorders" -#define KWM_ELECTRIC_BORDER_DELAY "ElectricBorderDelay" +// legacy options +#define KWIN_OLD_ACTIVE_BORDERS "ElectricBorders" +#define KWIN_OLD_ACTIVE_BORDER_DELAY "ElectricBorderDelay" //CT 15mar 98 - magics #define KWM_BRDR_SNAP_ZONE "BorderSnapZone" @@ -662,31 +666,59 @@ KAdvancedConfig::KAdvancedConfig (bool _standAlone, TDEConfig *_config, TQWidget connect(shadeHoverOn, TQT_SIGNAL(toggled(bool)), TQT_SLOT(changed())); connect(shadeHover, TQT_SIGNAL(valueChanged(int)), TQT_SLOT(changed())); - electricBox = new TQVButtonGroup(i18n("Active Desktop Borders"), this); - electricBox->setMargin(15); - - TQWhatsThis::add( electricBox, i18n("If this option is enabled, moving the mouse to a screen border" - " will change your desktop. This is e.g. useful if you want to drag windows from one desktop" - " to the other.") ); - active_disable = new TQRadioButton(i18n("D&isabled"), electricBox); - active_move = new TQRadioButton(i18n("Only &when moving windows"), electricBox); - active_always = new TQRadioButton(i18n("A&lways enabled"), electricBox); - - delays = new KIntNumInput(10, electricBox); + active_box = new TQButtonGroup(i18n("Active Desktop Borders"), this); + TQVBoxLayout *active_vbox = new TQVBoxLayout(active_box); + active_vbox->setSpacing(5); + active_vbox->setMargin(15); + TQWhatsThis::add( active_box, i18n("If this option is enabled, moving the mouse to a screen border" + " will perform an action. It will either change your desktop or tile the window that is currently" + " dragged.") ); + + TQLabel *active_func_label = new TQLabel(i18n("Function:"), active_box); + + active_disable = new TQRadioButton(i18n("D&isabled"), active_box); + + active_desktop = new TQRadioButton(i18n("Switch &desktop"), active_box); + active_desktop_conf = new TQWidget(active_box); + TQHBoxLayout *active_desktop_conf_hbox = new TQHBoxLayout(active_desktop_conf); + active_desktop_conf_hbox->addSpacing(20); + active_desktop_conf_hbox->setAutoAdd(true); + active_move = new TQCheckBox(i18n("Switch desktop only when &moving a window"), active_desktop_conf); + + active_tile = new TQRadioButton(i18n("Tile &window"), active_box); + active_tile_conf = new TQWidget(active_box); + TQHBoxLayout *active_tile_conf_hbox = new TQHBoxLayout(active_tile_conf); + active_tile_conf_hbox->addSpacing(20); + active_tile_conf_hbox->setAutoAdd(true); + active_maximize = new TQCheckBox(i18n("Maximize windows by dragging them to the &top of the screen"), active_tile_conf); + + delays = new KIntNumInput(10, active_box); delays->setRange(0, MAX_EDGE_RES, 50, true); delays->setSuffix(i18n(" msec")); - delays->setLabel(i18n("Desktop &switch delay:")); - TQWhatsThis::add( delays, i18n("Here you can set a delay for switching desktops using the active" - " borders feature. Desktops will be switched after the mouse has been pushed against a screen border" - " for the specified number of milliseconds.") ); - - connect( electricBox, TQT_SIGNAL(clicked(int)), this, TQT_SLOT(setEBorders())); + delays->setLabel(i18n("Border &activation delay:")); + TQWhatsThis::add( delays, i18n("Here you can set a delay for the activation of" + " active borders feature. The selected action will be performed after the mouse " + " has been pushed against a screen border for the specified number of milliseconds.") ); + + active_vbox->addSpacing(10); + active_vbox->addWidget(active_func_label); + active_vbox->addWidget(active_disable); + active_vbox->addWidget(active_desktop); + active_vbox->addWidget(active_desktop_conf); + active_vbox->addWidget(active_tile); + active_vbox->addWidget(active_tile_conf); + active_vbox->addSpacing(15); + active_vbox->addWidget(delays); + + connect(active_box, TQT_SIGNAL(clicked(int)), this, TQT_SLOT(setEBorders())); // Any changes goes to slotChanged() - connect(electricBox, TQT_SIGNAL(clicked(int)), TQT_SLOT(changed())); - connect(delays, TQT_SIGNAL(valueChanged(int)), TQT_SLOT(changed())); + connect(active_box, TQT_SIGNAL(clicked(int)), this, TQT_SLOT(changed())); + connect(active_move, TQT_SIGNAL(clicked()), this, TQT_SLOT(changed())); + connect(active_maximize, TQT_SIGNAL(clicked()), this, TQT_SLOT(changed())); + connect(delays, TQT_SIGNAL(valueChanged(int)), this, TQT_SLOT(changed())); - lay->addWidget(electricBox); + lay->addWidget(active_box); hideUtilityWindowsForInactive = new TQCheckBox( i18n( "Hide utility windows for inactive applications" ), this ); TQWhatsThis::add( hideUtilityWindowsForInactive, @@ -736,8 +768,19 @@ void KAdvancedConfig::load( void ) setShadeHover(config->readBoolEntry(KWIN_SHADEHOVER, false)); setShadeHoverInterval(config->readNumEntry(KWIN_SHADEHOVER_INTERVAL, 250)); - setElectricBorders(config->readNumEntry(KWM_ELECTRIC_BORDER, 0)); - setElectricBorderDelay(config->readNumEntry(KWM_ELECTRIC_BORDER_DELAY, 150)); + // compatibility with old option names + int active_borders = config->readNumEntry(KWIN_ACTIVE_BORDERS, -1); + if (active_borders == -1) { + active_borders = config->readNumEntry(KWIN_OLD_ACTIVE_BORDERS, 0); + } + + int active_borders_delay = config->readNumEntry(KWIN_ACTIVE_BORDER_DELAY, -1); + if (active_borders_delay == -1) { + active_borders_delay = config->readNumEntry(KWIN_OLD_ACTIVE_BORDER_DELAY, 150); + } + + setActiveBorders(active_borders); + setActiveBorderDelay(active_borders_delay); setHideUtilityWindowsForInactive( config->readBoolEntry( KWIN_HIDE_UTILITY, true )); @@ -759,11 +802,15 @@ void KAdvancedConfig::save( void ) if (v<0) v = 0; config->writeEntry(KWIN_SHADEHOVER_INTERVAL, v); - config->writeEntry(KWM_ELECTRIC_BORDER, getElectricBorders()); - config->writeEntry(KWM_ELECTRIC_BORDER_DELAY,getElectricBorderDelay()); + config->writeEntry(KWIN_ACTIVE_BORDERS, getActiveBorders()); + config->writeEntry(KWIN_ACTIVE_BORDER_DELAY, getActiveBorderDelay()); config->writeEntry(KWIN_HIDE_UTILITY, hideUtilityWindowsForInactive->isChecked()); + // remove replaced legacy entries + config->deleteEntry(KWIN_OLD_ACTIVE_BORDERS); + config->deleteEntry(KWIN_OLD_ACTIVE_BORDER_DELAY); + if (standAlone) { config->sync(); @@ -779,42 +826,59 @@ void KAdvancedConfig::defaults() setAnimateShade(true); setShadeHover(false); setShadeHoverInterval(250); - setElectricBorders(0); - setElectricBorderDelay(150); + setActiveBorders(0); + setActiveBorderDelay(150); setHideUtilityWindowsForInactive( true ); emit TDECModule::changed(true); } void KAdvancedConfig::setEBorders() { - delays->setEnabled(!active_disable->isChecked()); + active_desktop_conf->setEnabled(active_desktop->isChecked()); + active_tile_conf->setEnabled(active_tile->isChecked()); } -int KAdvancedConfig::getElectricBorders() +int KAdvancedConfig::getActiveBorders() { - if (active_move->isChecked()) - return 1; - if (active_always->isChecked()) - return 2; + if (active_desktop->isChecked()) + { + return active_move->isChecked() ? 1 : 2; + } + + if (active_tile->isChecked()) + { + return active_maximize->isChecked() ? 4 : 3; + } + return 0; } -int KAdvancedConfig::getElectricBorderDelay() +int KAdvancedConfig::getActiveBorderDelay() { return delays->value(); } -void KAdvancedConfig::setElectricBorders(int i){ +void KAdvancedConfig::setActiveBorders(int i){ switch(i) { - case 1: active_move->setChecked(true); break; - case 2: active_always->setChecked(true); break; - default: active_disable->setChecked(true); break; + case 1: + active_move->setChecked(true); + case 2: + active_desktop->setChecked(true); + break; + case 4: + active_maximize->setChecked(true); + case 3: + active_tile->setChecked(true); + break; + default: + active_disable->setChecked(true); + break; } setEBorders(); } -void KAdvancedConfig::setElectricBorderDelay(int delay) +void KAdvancedConfig::setActiveBorderDelay(int delay) { delays->setValue(delay); } @@ -900,6 +964,11 @@ KMovingConfig::KMovingConfig (bool _standAlone, TDEConfig *_config, TQWidget *pa " and allows you to move or resize them," " just like for normal windows")); + resetMaximizedWindowGeometry = new TQCheckBox( i18n("Restore size of maximized/tiled windows when moving"), windowsBox); + bLay->addWidget(resetMaximizedWindowGeometry); + TQWhatsThis::add(resetMaximizedWindowGeometry, i18n("If this feature is enabled, dragging a maximized or tiled window" + " will restore the window to its original size.")); + TQBoxLayout *vLay = new TQHBoxLayout(bLay); TQLabel *plcLabel = new TQLabel(i18n("&Placement:"),windowsBox); @@ -994,6 +1063,7 @@ KMovingConfig::KMovingConfig (bool _standAlone, TDEConfig *_config, TQWidget *pa connect( minimizeAnimOn, TQT_SIGNAL(clicked() ), TQT_SLOT(changed())); connect( minimizeAnimSlider, TQT_SIGNAL(valueChanged(int)), TQT_SLOT(changed())); connect( moveResizeMaximized, TQT_SIGNAL(toggled(bool)), TQT_SLOT(changed())); + connect( resetMaximizedWindowGeometry, TQT_SIGNAL(toggled(bool)), TQT_SLOT(changed())); connect( placementCombo, TQT_SIGNAL(activated(int)), TQT_SLOT(changed())); connect( BrdrSnap, TQT_SIGNAL(valueChanged(int)), TQT_SLOT(changed())); connect( BrdrSnap, TQT_SIGNAL(valueChanged(int)), TQT_SLOT(slotBrdrSnapChanged(int))); @@ -1074,6 +1144,10 @@ void KMovingConfig::setMoveResizeMaximized(bool a) { moveResizeMaximized->setChecked(a); } +void KMovingConfig::setResetMaximizedWindowGeometry(bool a) { + resetMaximizedWindowGeometry->setChecked(a); +} + void KMovingConfig::slotBrdrSnapChanged(int value) { BrdrSnap->setSuffix(i18n(" pixel", " pixels", value)); } @@ -1149,6 +1223,7 @@ void KMovingConfig::load( void ) // } setMoveResizeMaximized(config->readBoolEntry(KWIN_MOVE_RESIZE_MAXIMIZED, false)); + setResetMaximizedWindowGeometry(config->readBoolEntry(KWIN_RESET_MAX_WIN_GEOM, false)); int v; @@ -1212,6 +1287,7 @@ void KMovingConfig::save( void ) config->writeEntry(KWIN_RESIZE_OPAQUE, "Transparent"); config->writeEntry(KWIN_MOVE_RESIZE_MAXIMIZED, moveResizeMaximized->isChecked()); + config->writeEntry(KWIN_RESET_MAX_WIN_GEOM, resetMaximizedWindowGeometry->isChecked()); config->writeEntry(KWM_BRDR_SNAP_ZONE,getBorderSnapZone()); @@ -1235,6 +1311,7 @@ void KMovingConfig::defaults() setGeometryTip(false); setPlacement(SMART_PLACEMENT); setMoveResizeMaximized(false); + setResetMaximizedWindowGeometry(false); //copied from kcontrol/konq/twindesktop, aleXXX setWindowSnapZone(KWM_WNDW_SNAP_ZONE_DEFAULT); diff --git a/twin/kcmtwin/twinoptions/windows.h b/twin/kcmtwin/twinoptions/windows.h index d4d358e9a..435bd0521 100644 --- a/twin/kcmtwin/twinoptions/windows.h +++ b/twin/kcmtwin/twinoptions/windows.h @@ -68,6 +68,7 @@ class KIntNumInput; #define FOCUS_STRICTLY_UNDER_MOUSE 3 class TQSpinBox; +class TQHBox; class KFocusConfig : public TDECModule { @@ -88,7 +89,7 @@ private slots: void clickRaiseOnTog(bool); void updateAltTabMode(); void updateActiveMouseScreen(); - void changed() { emit TDECModule::changed(true); } + void changed() { emit TDECModule::changed(true); } private: @@ -163,6 +164,7 @@ private: void setGeometryTip(bool); //KS void setPlacement(int); //CT void setMoveResizeMaximized(bool); + void setResetMaximizedWindowGeometry(bool); TQButtonGroup *windowsBox; TQCheckBox *opaque; @@ -172,6 +174,7 @@ private: TQSlider *minimizeAnimSlider; TQLabel *minimizeAnimSlowLabel, *minimizeAnimFastLabel; TQCheckBox *moveResizeMaximized; + TQCheckBox *resetMaximizedWindowGeometry; TQComboBox *placementCombo; @@ -223,17 +226,21 @@ private: TDEConfig *config; bool standAlone; - int getElectricBorders( void ); - int getElectricBorderDelay(); - void setElectricBorders( int ); - void setElectricBorderDelay( int ); + int getActiveBorders( void ); + int getActiveBorderDelay(); + void setActiveBorders( int ); + void setActiveBorderDelay( int ); - TQVButtonGroup *electricBox; + TQButtonGroup *active_box; TQRadioButton *active_disable; - TQRadioButton *active_move; - TQRadioButton *active_always; + TQRadioButton *active_desktop; + TQCheckBox *active_move; + TQRadioButton *active_tile; + TQCheckBox *active_maximize; KIntNumInput *delays; - + TQWidget *active_desktop_conf; + TQWidget *active_tile_conf; + void setHideUtilityWindowsForInactive( bool ); TQCheckBox* hideUtilityWindowsForInactive; diff --git a/twin/layers.cpp b/twin/layers.cpp index 143d826d5..17068a675 100644 --- a/twin/layers.cpp +++ b/twin/layers.cpp @@ -129,43 +129,50 @@ void Workspace::updateStackingOrder( bool propagate_new_clients ) void Workspace::propagateClients( bool propagate_new_clients ) { Window *cl; // MW we should not assume WId and Window to be compatible - // when passig pointers around. + // when passing pointers around. // restack the windows according to the stacking order -#if 0 - Window* new_stack = new Window[ stacking_order.count() + 2 ]; - int pos = 0; -#endif NET::WindowType t; Window shadow; Window *dock_shadow_stack, *window_stack; int i, numDocks, pos, topmenu_space_pos; - + + // Dock Stack size magic number explanation: + // -> (count * 2) because we might need to also store the shadow window + // for each dock window (Chakra shadow patch, introduced in 9cc1e2c1aa) dock_shadow_stack = new Window[ stacking_order.count() * 2 ]; - window_stack = new Window[ stacking_order.count() * 2 + 2 ]; + + // Window Stack size magic number explanation: + // -> (count * 2) because we might need to store shadow windows (see above) + // -> + 1 for supportWindow + // -> + 1 for topmenu_space + // -> + 8 for active borders + window_stack = new Window[ stacking_order.count() * 2 + 1 + 1 + 8 ]; i = 0; pos = 0; topmenu_space_pos = 1; // not 0, that's supportWindow !!! - // Stack all windows under the support window. The support window is - // not used for anything (besides the NETWM property), and it's not shown, - // but it was lowered after twin startup. Stacking all clients below - // it ensures that no client will be ever shown above override-redirect - // windows (e.g. popups). -#if 0 - new_stack[ pos++ ] = supportWindow->winId(); - int topmenu_space_pos = 1; // not 0, that's supportWindow !!! -#endif + // Stack active windows under the support window. + /* The support window is not used for anything (besides the NETWM property), + * and it's not shown, but it was lowered after TWin startup. + * Stacking all clients below it ensures that no client will be ever shown + * above override-redirect windows (e.g. popups). + */ + for (int i = 0; i < ACTIVE_BORDER_COUNT; ++i) + { + if (active_windows[i] != None) + { + window_stack[pos++] = active_windows[i]; + } + } + + // Stack all windows under the support and active borders windows. window_stack[pos++] = supportWindow->winId(); for( ClientList::ConstIterator it = stacking_order.fromLast(); it != stacking_order.end(); --it ) { -#if 0 - new_stack[ pos++ ] = (*it)->frameId(); - if( (*it)->belongsToLayer() >= DockLayer ) - topmenu_space_pos = pos; -#endif + t = (*it)->windowType(); switch (t) { @@ -202,15 +209,14 @@ void Workspace::propagateClients( bool propagate_new_clients ) new_stack[ topmenu_space_pos ] = topmenu_space->winId(); #endif window_stack[ i ] = window_stack[ i - 1 ]; - window_stack[ topmenu_space_pos ] = topmenu_space->winId(); - ++pos; + window_stack[ topmenu_space_pos ] = topmenu_space->winId(); + ++pos; } #if 0 // TODO isn't it too inefficient to restart always all clients? // TODO don't restack not visible windows? assert( new_stack[ 0 ] = supportWindow->winId()); -#endif -#if 0 + XRestackWindows(tqt_xdisplay(), new_stack, pos); delete [] new_stack; #endif diff --git a/twin/options.cpp b/twin/options.cpp index ce35f6d06..3d237255b 100644 --- a/twin/options.cpp +++ b/twin/options.cpp @@ -31,8 +31,8 @@ namespace KWinInternal #ifndef KCMRULES Options::Options() - : electric_borders( 0 ), - electric_border_delay(0) + : active_borders( 0 ), + active_border_delay(0) { d = new KDecorationOptionsPrivate; d->defaultKWinSettings(); @@ -54,6 +54,7 @@ unsigned long Options::updateSettings() moveMode = stringToMoveResizeMode( config->readEntry("MoveMode", "Opaque" )); resizeMode = stringToMoveResizeMode( config->readEntry("ResizeMode", "Opaque" )); show_geometry_tip = config->readBoolEntry("GeometryTip", false); + reset_maximized_window_geometry = config->readBoolEntry("ResetMaximizedWindowGeometry", false); tabboxOutline = config->readBoolEntry("TabboxOutline", true); TQString val; @@ -125,8 +126,16 @@ unsigned long Options::updateSettings() borderSnapZone = config->readNumEntry("BorderSnapZone", 10); windowSnapZone = config->readNumEntry("WindowSnapZone", 10); snapOnlyWhenOverlapping=config->readBoolEntry("SnapOnlyWhenOverlapping",FALSE); - electric_borders = config->readNumEntry("ElectricBorders", 0); - electric_border_delay = config->readNumEntry("ElectricBorderDelay", 150); + + // active borders: compatibility with old option names (Electric*) + active_borders = config->readNumEntry("ActiveBorders", -1); + if (active_borders == -1) { + active_borders = config->readNumEntry("ElectricBorders", 0); + } + active_border_delay = config->readNumEntry("ActiveBorderDelay", -1); + if (active_border_delay == -1) { + active_border_delay = config->readNumEntry("ElectricBorderDelay", 150); + } OpTitlebarDblClick = windowOperation( config->readEntry("TitlebarDoubleClickCommand", "Shade"), true ); d->OpMaxButtonLeftClick = windowOperation( config->readEntry("MaximizeButtonLeftClickCommand", "Maximize"), true ); @@ -308,6 +317,11 @@ bool Options::showGeometryTip() return show_geometry_tip; } +bool Options::resetMaximizedWindowGeometry() + { + return reset_maximized_window_geometry; + } + TQColor &Options::shadowColour(bool active) { return active ? shadow_colour : shadow_inactive_colour; @@ -373,14 +387,14 @@ int Options::shadowYOffset(bool active) return active ? shadow_y_offset : shadow_inactive_y_offset; } -int Options::electricBorders() +int Options::activeBorders() { - return electric_borders; + return active_borders; } -int Options::electricBorderDelay() +int Options::activeBorderDelay() { - return electric_border_delay; + return active_border_delay; } bool Options::checkIgnoreFocusStealing( const Client* c ) diff --git a/twin/options.h b/twin/options.h index 113e6b9e6..55183e6bd 100644 --- a/twin/options.h +++ b/twin/options.h @@ -268,6 +268,13 @@ class Options : public KDecorationOptions */ bool showGeometryTip(); + /** + * @returns true if a maximized or tiled window should be reset to its original + * size when dragging it. + * @since R14.1.1 + */ + bool resetMaximizedWindowGeometry(); + /** * @returns A TQColor representing the colour that window drop shadows should * be. @@ -307,18 +314,22 @@ class Options : public KDecorationOptions */ int shadowYOffset(bool active=true); - enum { ElectricDisabled = 0, ElectricMoveOnly = 1, ElectricAlways = 2 }; + enum { ActiveDisabled = 0, + ActiveSwitchOnMove = 1, + ActiveSwitchAlways = 2, + ActiveTileOnly = 3, + ActiveTileMaximize = 4 }; /** - * @returns true if electric borders are enabled. With electric borders + * @returns true if active borders are enabled. With active borders * you can change desktop by moving the mouse pointer towards the edge * of the screen */ - int electricBorders(); + int activeBorders(); /** - * @returns the activation delay for electric borders in milliseconds. + * @returns the activation delay for active borders in milliseconds. */ - int electricBorderDelay(); + int activeBorderDelay(); bool topMenuEnabled() const { return topmenus; } bool desktopTopMenu() const { return desktop_topmenu; } @@ -373,9 +384,10 @@ class Options : public KDecorationOptions bool CmdAllRevWheel; uint CmdAllModKey; - int electric_borders; - int electric_border_delay; + int active_borders; + int active_border_delay; bool show_geometry_tip; + bool reset_maximized_window_geometry; bool topmenus; bool desktop_topmenu; TQColor shadow_colour; diff --git a/twin/utils.h b/twin/utils.h index e8bc09a46..65c766690 100644 --- a/twin/utils.h +++ b/twin/utils.h @@ -108,6 +108,29 @@ enum ShadeMode ShadeActivated // "shaded", but visible due to alt+tab to the window }; +enum ActiveBorder + { + ActiveTop, + ActiveTopRight, + ActiveRight, + ActiveBottomRight, + ActiveBottom, + ActiveBottomLeft, + ActiveLeft, + ActiveTopLeft, + ACTIVE_BORDER_COUNT, + ActiveNone + }; + +enum ActiveMaximizingMode +{ + ActiveMaximizeMode, + ActiveLeftMode, + ActiveRightMode, + ActiveTopMode, + ActiveBottomMode +}; + class Shape { public: diff --git a/twin/workspace.cpp b/twin/workspace.cpp index 8cddbb72e..03a2e4e50 100644 --- a/twin/workspace.cpp +++ b/twin/workspace.cpp @@ -167,12 +167,7 @@ Workspace::Workspace( bool restore ) global_shortcuts_disabled_for_client( false ), root (0), workspaceInit (true), - startup(0), electric_have_borders(false), - electric_current_border(0), - electric_top_border(None), - electric_bottom_border(None), - electric_left_border(None), - electric_right_border(None), + startup(0), layoutOrientation(Qt::Vertical), layoutX(-1), layoutY(2), @@ -194,6 +189,12 @@ Workspace::Workspace( bool restore ) installed_colormap = default_colormap; session.setAutoDelete( TRUE ); + for (int i = 0; i < ACTIVE_BORDER_COUNT; ++i) + { + active_reserved[i] = 0; + active_windows[i] = None; + } + connect( &temporaryRulesMessages, TQT_SIGNAL( gotMessage( const TQString& )), this, TQT_SLOT( gotTemporaryRulesMessage( const TQString& ))); connect( &rulesUpdatedTimer, TQT_SIGNAL( timeout()), this, TQT_SLOT( writeWindowRules())); @@ -202,8 +203,8 @@ Workspace::Workspace( bool restore ) delayFocusTimer = 0; - electric_time_first = GET_QT_X_TIME(); - electric_time_last = GET_QT_X_TIME(); + active_time_first = GET_QT_X_TIME(); + active_time_last = GET_QT_X_TIME(); if ( restore ) loadSessionInfo(); @@ -304,8 +305,12 @@ Workspace::Workspace( bool restore ) void Workspace::init() +{ + if (options->activeBorders() == Options::ActiveSwitchAlways) { - checkElectricBorders(); + reserveActiveBorderSwitching(true); + } + updateActiveBorders(); // not used yet // topDock = 0L; @@ -483,7 +488,6 @@ void Workspace::init() updateStackingOrder( true ); updateClientArea(); - raiseElectricBorders(); // NETWM spec says we have to set it to (0,0) if we don't support it NETPoint* viewports = new NETPoint[ number_of_desktops ]; @@ -514,12 +518,27 @@ void Workspace::init() } if( new_active_client != NULL ) activateClient( new_active_client ); + + // outline windows for active border maximize window mode + outline_left = XCreateWindow(tqt_xdisplay(), rootWin(), 0, 0, 1, 1, 0, + CopyFromParent, CopyFromParent, CopyFromParent, + CWOverrideRedirect, &attr); + outline_right = XCreateWindow(tqt_xdisplay(), rootWin(), 0, 0, 1, 1, 0, + CopyFromParent, CopyFromParent, CopyFromParent, + CWOverrideRedirect, &attr); + outline_top = XCreateWindow(tqt_xdisplay(), rootWin(), 0, 0, 1, 1, 0, + CopyFromParent, CopyFromParent, CopyFromParent, + CWOverrideRedirect, &attr); + outline_bottom = XCreateWindow(tqt_xdisplay(), rootWin(), 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 workspaceInit = false; // TODO ungrabXServer() - } +} Workspace::~Workspace() { @@ -550,6 +569,12 @@ Workspace::~Workspace() writeWindowRules(); TDEGlobal::config()->sync(); + // destroy outline windows for active border maximize window mode + XDestroyWindow(tqt_xdisplay(), outline_left); + XDestroyWindow(tqt_xdisplay(), outline_right); + XDestroyWindow(tqt_xdisplay(), outline_top); + XDestroyWindow(tqt_xdisplay(), outline_bottom); + delete rootInfo; delete supportWindow; delete mgr; @@ -1038,6 +1063,11 @@ void Workspace::slotReconfigure() kdDebug(1212) << "Workspace::slotReconfigure()" << endl; reconfigureTimer.stop(); + if (options->activeBorders() == Options::ActiveSwitchAlways) + { + reserveActiveBorderSwitching(false); + } + TDEGlobal::config()->reparseConfiguration(); unsigned long changed = options->updateSettings(); tab_box->reconfigure(); @@ -1068,7 +1098,10 @@ void Workspace::slotReconfigure() forEachClient( CheckBorderSizesProcedure()); } - checkElectricBorders(); + if (options->activeBorders() == Options::ActiveSwitchAlways) + { + reserveActiveBorderSwitching(true); + } if( options->topMenuEnabled() && !managingTopMenus()) { @@ -2357,7 +2390,7 @@ void Workspace::delayFocus() requestFocus( delayfocus_client ); cancelDelayFocus(); } - + void Workspace::requestDelayFocus( Client* c ) { delayfocus_client = c; @@ -2366,283 +2399,353 @@ void Workspace::requestDelayFocus( Client* c ) connect( delayFocusTimer, TQT_SIGNAL( timeout() ), this, TQT_SLOT( delayFocus() ) ); delayFocusTimer->start( options->delayFocusInterval, TRUE ); } - + void Workspace::cancelDelayFocus() { delete delayFocusTimer; delayFocusTimer = 0; } -// Electric Borders -//========================================================================// -// Electric Border Window management. Electric borders allow a user -// to change the virtual desktop by moving the mouse pointer to the -// borders. Technically this is done with input only windows. Since -// electric borders can be switched on and off, we have these two -// functions to create and destroy them. -void Workspace::checkElectricBorders( bool force ) +/* Active (Electric) Borders + * ======================================================================== + * Active Border Window management. Active borders allow a user to switch + * to another virtual desktop or activate other features by moving + * the mouse pointer to the borders or corners of the workspace. + * Technically this is done with input only windows. + */ +void Workspace::updateActiveBorders() { - if( force ) - destroyBorderWindows(); - - electric_current_border = 0; - + active_time_first = GET_QT_X_TIME(); + active_time_last = GET_QT_X_TIME(); + active_time_last_trigger = GET_QT_X_TIME(); + active_current_border = ActiveNone; TQRect r = TQApplication::desktop()->geometry(); - electricTop = r.top(); - electricBottom = r.bottom(); - electricLeft = r.left(); - electricRight = r.right(); + activeTop = r.top(); + activeBottom = r.bottom(); + activeLeft = r.left(); + activeRight = r.right(); - if (options->electricBorders() == Options::ElectricAlways) - createBorderWindows(); - else - destroyBorderWindows(); + for (int pos = 0; pos < ACTIVE_BORDER_COUNT; ++pos) + { + if (active_reserved[pos] == 0) + { + if (active_windows[pos] != None) + { + XDestroyWindow( tqt_xdisplay(), active_windows[pos] ); + } + active_windows[pos] = None; + continue; + } + + if (active_windows[pos] != None) + { + continue; + } + + XSetWindowAttributes attributes; + attributes.override_redirect = True; + attributes.event_mask = EnterWindowMask; + unsigned long valuemask = CWOverrideRedirect | CWEventMask; + int xywh[ ACTIVE_BORDER_COUNT ][ 4 ] = + { + { r.left() + 1, r.top(), r.width() - 2, 1 }, // top + { r.right(), r.top(), 1, 1 }, // topright + { r.right(), r.top() + 1, 1, r.height() - 2 }, // etc. + { r.right(), r.bottom(), 1, 1 }, + { r.left() + 1, r.bottom(), r.width() - 2, 1 }, + { r.left(), r.bottom(), 1, 1 }, + { r.left(), r.top() + 1, 1, r.height() - 2 }, + { r.left(), r.top(), 1, 1 } + }; + active_windows[pos] = XCreateWindow(tqt_xdisplay(), tqt_xrootwin(), + xywh[pos][0], xywh[pos][1], + xywh[pos][2], xywh[pos][3], + 0, CopyFromParent, InputOnly, + CopyFromParent, valuemask, + &attributes); + XMapWindow(tqt_xdisplay(), active_windows[pos]); + + // Set XdndAware on the windows, so that DND enter events are received (#86998) + Atom version = 4; // XDND version + XChangeProperty(tqt_xdisplay(), active_windows[pos], + atoms->xdnd_aware, XA_ATOM, 32, PropModeReplace, + (unsigned char*)&version, 1); } +} -void Workspace::createBorderWindows() +void Workspace::destroyActiveBorders() +{ + for (int pos = 0; pos < ACTIVE_BORDER_COUNT; ++pos) { - if ( electric_have_borders ) + if (active_windows[ pos ] != None) + { + XDestroyWindow( tqt_xdisplay(), active_windows[ pos ] ); + } + active_windows[ pos ] = None; + } +} + +void Workspace::reserveActiveBorderSwitching( bool reserve ) +{ + for (int pos = 0; pos < ACTIVE_BORDER_COUNT; ++pos) + { + if (reserve) + { + reserveActiveBorder(static_cast(pos)); + } + else + { + unreserveActiveBorder(static_cast(pos)); + } + } +} + +void Workspace::reserveActiveBorder( ActiveBorder border ) +{ + if (border == ActiveNone) return; - electric_have_borders = true; + if (active_reserved[border]++ == 0) + TQTimer::singleShot(0, this, TQT_SLOT(updateActiveBorders())); +} - TQRect r = TQApplication::desktop()->geometry(); - XSetWindowAttributes attributes; - unsigned long valuemask; - attributes.override_redirect = True; - attributes.event_mask = ( EnterWindowMask | LeaveWindowMask ); - valuemask= (CWOverrideRedirect | CWEventMask | CWCursor ); - attributes.cursor = XCreateFontCursor(tqt_xdisplay(), - XC_sb_up_arrow); - electric_top_border = XCreateWindow (tqt_xdisplay(), tqt_xrootwin(), - 0,0, - r.width(),1, - 0, - CopyFromParent, InputOnly, - CopyFromParent, - valuemask, &attributes); - XMapWindow(tqt_xdisplay(), electric_top_border); - - attributes.cursor = XCreateFontCursor(tqt_xdisplay(), - XC_sb_down_arrow); - electric_bottom_border = XCreateWindow (tqt_xdisplay(), tqt_xrootwin(), - 0,r.height()-1, - r.width(),1, - 0, - CopyFromParent, InputOnly, - CopyFromParent, - valuemask, &attributes); - XMapWindow(tqt_xdisplay(), electric_bottom_border); - - attributes.cursor = XCreateFontCursor(tqt_xdisplay(), - XC_sb_left_arrow); - electric_left_border = XCreateWindow (tqt_xdisplay(), tqt_xrootwin(), - 0,0, - 1,r.height(), - 0, - CopyFromParent, InputOnly, - CopyFromParent, - valuemask, &attributes); - XMapWindow(tqt_xdisplay(), electric_left_border); - - attributes.cursor = XCreateFontCursor(tqt_xdisplay(), - XC_sb_right_arrow); - electric_right_border = XCreateWindow (tqt_xdisplay(), tqt_xrootwin(), - r.width()-1,0, - 1,r.height(), - 0, - CopyFromParent, InputOnly, - CopyFromParent, - valuemask, &attributes); - XMapWindow(tqt_xdisplay(), electric_right_border); - // Set XdndAware on the windows, so that DND enter events are received (#86998) - Atom version = 4; // XDND version - XChangeProperty( tqt_xdisplay(), electric_top_border, atoms->xdnd_aware, XA_ATOM, - 32, PropModeReplace, ( unsigned char* )&version, 1 ); - XChangeProperty( tqt_xdisplay(), electric_bottom_border, atoms->xdnd_aware, XA_ATOM, - 32, PropModeReplace, ( unsigned char* )&version, 1 ); - XChangeProperty( tqt_xdisplay(), electric_left_border, atoms->xdnd_aware, XA_ATOM, - 32, PropModeReplace, ( unsigned char* )&version, 1 ); - XChangeProperty( tqt_xdisplay(), electric_right_border, atoms->xdnd_aware, XA_ATOM, - 32, PropModeReplace, ( unsigned char* )&version, 1 ); - } - - -// Electric Border Window management. Electric borders allow a user -// to change the virtual desktop by moving the mouse pointer to the -// borders. Technically this is done with input only windows. Since -// electric borders can be switched on and off, we have these two -// functions to create and destroy them. -void Workspace::destroyBorderWindows() - { - if( !electric_have_borders) - return; - - electric_have_borders = false; - - if(electric_top_border) - XDestroyWindow(tqt_xdisplay(),electric_top_border); - if(electric_bottom_border) - XDestroyWindow(tqt_xdisplay(),electric_bottom_border); - if(electric_left_border) - XDestroyWindow(tqt_xdisplay(),electric_left_border); - if(electric_right_border) - XDestroyWindow(tqt_xdisplay(),electric_right_border); - - electric_top_border = None; - electric_bottom_border = None; - electric_left_border = None; - electric_right_border = None; - } - -void Workspace::clientMoved(const TQPoint &pos, Time now) - { - if (options->electricBorders() == Options::ElectricDisabled) - return; +void Workspace::unreserveActiveBorder( ActiveBorder border ) +{ + if (border == ActiveNone) + return; - if ((pos.x() != electricLeft) && - (pos.x() != electricRight) && - (pos.y() != electricTop) && - (pos.y() != electricBottom)) - return; + assert(active_reserved[ border ] > 0); + if (--active_reserved[ border ] == 0) + TQTimer::singleShot(0, this, TQT_SLOT(updateActiveBorders())); +} - Time treshold_set = options->electricBorderDelay(); // set timeout +void Workspace::checkActiveBorder(const TQPoint &pos, Time now) +{ + Time treshold_set = options->activeBorderDelay(); // set timeout + Time treshold_trigger = 250; // Minimum time between triggers Time treshold_reset = 250; // reset timeout int distance_reset = 30; // Mouse should not move more than this many pixels - int border = 0; - if (pos.x() == electricLeft) - border = 1; - else if (pos.x() == electricRight) - border = 2; - else if (pos.y() == electricTop) - border = 3; - else if (pos.y() == electricBottom) - border = 4; + if ((pos.x() > activeLeft + distance_reset) && + (pos.x() < activeRight - distance_reset) && + (pos.y() > activeTop + distance_reset) && + (pos.y() < activeBottom - distance_reset)) + { + if (movingClient && + (options->activeBorders() == Options::ActiveTileMaximize || + options->activeBorders() == Options::ActiveTileOnly)) + { + movingClient->setActiveBorderMaximizing(false); + } + } - if ((electric_current_border == border) && - (timestampDiff(electric_time_last, now) < treshold_reset) && - ((pos-electric_push_point).manhattanLength() < distance_reset)) + if ((pos.x() != activeLeft) && + (pos.x() != activeRight) && + (pos.y() != activeTop) && + (pos.y() != activeBottom)) + return; + + bool have_borders = false; + for (int i = 0; i < ACTIVE_BORDER_COUNT; ++i) + { + if (active_windows[ i ] != None) { - electric_time_last = now; + have_borders = true; + } + } + if( !have_borders ) + return; + + ActiveBorder border; + if( pos.x() == activeLeft && pos.y() == activeTop ) + border = ActiveTopLeft; + else if( pos.x() == activeRight && pos.y() == activeTop ) + border = ActiveTopRight; + else if( pos.x() == activeLeft && pos.y() == activeBottom ) + border = ActiveBottomLeft; + else if( pos.x() == activeRight && pos.y() == activeBottom ) + border = ActiveBottomRight; + else if( pos.x() == activeLeft ) + border = ActiveLeft; + else if( pos.x() == activeRight ) + border = ActiveRight; + else if( pos.y() == activeTop ) + border = ActiveTop; + else if( pos.y() == activeBottom ) + border = ActiveBottom; + else + abort(); - if (timestampDiff(electric_time_first, now) > treshold_set) + if( active_windows[border] == None ) + return; + + if ((active_current_border == border) && + (timestampDiff(active_time_last, now) < treshold_reset) && + (timestampDiff(active_time_last_trigger, now) > treshold_trigger) && + ((pos-active_push_point).manhattanLength() < distance_reset)) + { + active_time_last = now; + + if (timestampDiff(active_time_first, now) > treshold_set) + { + active_time_last_trigger = now; + active_current_border = ActiveNone; + bool isSide = (border == ActiveTop || border == ActiveRight || + border == ActiveBottom || border == ActiveLeft); + + if (movingClient) { - electric_current_border = 0; + // Desktop switching + if (options->activeBorders() == Options::ActiveSwitchAlways || + options->activeBorders() == Options::ActiveSwitchOnMove) + { + activeBorderSwitchDesktop(border, pos); + return; // Don't reset cursor position + } - TQRect r = TQApplication::desktop()->geometry(); - int offset; + // Tiling maximize + else if (options->activeBorders() == Options::ActiveTileMaximize && + border == ActiveTop && movingClient->isMaximizable()) + { + if (!movingClient->isResizable()) return; + bool enable = !movingClient->isActiveBorderMaximizing(); + movingClient->setActiveBorderMode(ActiveMaximizeMode); + movingClient->setActiveBorderMaximizing(enable); + } - int desk_before = currentDesktop(); - switch(border) + // Tiling + else if ((options->activeBorders() == Options::ActiveTileMaximize || + options->activeBorders() == Options::ActiveTileOnly) && isSide) { - case 1: - slotSwitchDesktopLeft(); - if (currentDesktop() != desk_before) + if (!movingClient->isResizable()) return; + bool enable = !movingClient->isActiveBorderMaximizing(); + bool activate = false; + if (border == ActiveLeft) { - offset = r.width() / 5; - TQCursor::setPos(r.width() - offset, pos.y()); + movingClient->setActiveBorderMode( ActiveLeftMode ); + activate = true; } - break; - - case 2: - slotSwitchDesktopRight(); - if (currentDesktop() != desk_before) + else if (border == ActiveRight) { - offset = r.width() / 5; - TQCursor::setPos(offset, pos.y()); + movingClient->setActiveBorderMode( ActiveRightMode ); + activate = true; } - break; - - case 3: - slotSwitchDesktopUp(); - if (currentDesktop() != desk_before) + else if (border == ActiveTop) { - offset = r.height() / 5; - TQCursor::setPos(pos.x(), r.height() - offset); + movingClient->setActiveBorderMode( ActiveTopMode ); + activate = true; + } + else if (border == ActiveBottom) + { + movingClient->setActiveBorderMode( ActiveBottomMode ); + activate = true; } - break; - case 4: - slotSwitchDesktopDown(); - if (currentDesktop() != desk_before) + if (activate) { - offset = r.height() / 5; - TQCursor::setPos(pos.x(), offset); + movingClient->setActiveBorderMaximizing(enable); } - break; } - return; + + else + { + return; // Don't reset cursor position + } + } + else + { + // Desktop switching + if (options->activeBorders() == Options::ActiveSwitchAlways && isSide) + { + activeBorderSwitchDesktop(border, pos); + return; // Don't reset cursor position + } } } - else - { - electric_current_border = border; - electric_time_first = now; - electric_time_last = now; - electric_push_point = pos; - } + } + else + { + active_current_border = border; + active_time_first = now; + active_time_last = now; + active_push_point = pos; + } - int mouse_warp = 1; + // reset the pointer to find out wether the user is really pushing + // (the direction back from which it came, starting from top clockwise) + const int xdiff[ ACTIVE_BORDER_COUNT ] = { 0, -1, -1, -1, 0, 1, 1, 1 }; + const int ydiff[ ACTIVE_BORDER_COUNT ] = { 1, 1, 0, -1, -1, -1, 0, 1 }; + TQCursor::setPos(pos.x() + xdiff[border], pos.y() + ydiff[border]); - // reset the pointer to find out wether the user is really pushing - switch( border) - { - case 1: TQCursor::setPos(pos.x()+mouse_warp, pos.y()); break; - case 2: TQCursor::setPos(pos.x()-mouse_warp, pos.y()); break; - case 3: TQCursor::setPos(pos.x(), pos.y()+mouse_warp); break; - case 4: TQCursor::setPos(pos.x(), pos.y()-mouse_warp); break; - } } -// this function is called when the user entered an electric border +void Workspace::activeBorderSwitchDesktop(ActiveBorder border, const TQPoint& _pos) +{ + TQPoint pos = _pos; + TQRect r = TQApplication::desktop()->geometry(); + const int offset = 5; + + int desk_before = currentDesktop(); + if (border == ActiveLeft || border == ActiveTopLeft || border == ActiveBottomLeft) + { + slotSwitchDesktopLeft(); + pos.setX(r.width() - offset); + } + if (border == ActiveRight || border == ActiveTopRight || border == ActiveBottomRight) + { + slotSwitchDesktopRight(); + pos.setX(offset); + } + + if (border == ActiveTop || border == ActiveTopLeft || border == ActiveTopRight) + { + slotSwitchDesktopUp(); + pos.setY(r.height() - offset); + } + if (border == ActiveBottom || border == ActiveBottomLeft || border == ActiveBottomRight) + { + slotSwitchDesktopDown(); + pos.setY(offset); + } + + if (currentDesktop() != desk_before) + { + TQCursor::setPos(pos); + } +} + +// this function is called when the user entered an active border // with the mouse. It may switch to another virtual desktop -bool Workspace::electricBorder(XEvent *e) +bool Workspace::activeBorderEvent(XEvent *e) +{ + if (e->type == EnterNotify) { - if( !electric_have_borders ) - return false; - if( e->type == EnterNotify ) + for (int i = 0; i < ACTIVE_BORDER_COUNT; ++i) { - if( e->xcrossing.window == electric_top_border || - e->xcrossing.window == electric_left_border || - e->xcrossing.window == electric_bottom_border || - e->xcrossing.window == electric_right_border) - // the user entered an electric border - { - clientMoved( TQPoint( e->xcrossing.x_root, e->xcrossing.y_root ), e->xcrossing.time ); - return true; + if (active_windows[i] != None && e->xcrossing.window == active_windows[i]) + { // the user entered an active border + checkActiveBorder(TQPoint(e->xcrossing.x_root, e->xcrossing.y_root), e->xcrossing.time); + return true; } } - if( e->type == ClientMessage ) - { - if( e->xclient.message_type == atoms->xdnd_position - && ( e->xclient.window == electric_top_border - || e->xclient.window == electric_bottom_border - || e->xclient.window == electric_left_border - || e->xclient.window == electric_right_border )) - { - updateXTime(); - clientMoved( TQPoint( e->xclient.data.l[2]>>16, e->xclient.data.l[2]&0xffff), GET_QT_X_TIME() ); - return true; - } - } - return false; } - -// electric borders (input only windows) have to be always on the -// top. For that reason kwm calls this function always after some -// windows have been raised. -void Workspace::raiseElectricBorders() + if (e->type == ClientMessage) { - - if(electric_have_borders) + if (e->xclient.message_type == atoms->xdnd_position) { - XRaiseWindow(tqt_xdisplay(), electric_top_border); - XRaiseWindow(tqt_xdisplay(), electric_left_border); - XRaiseWindow(tqt_xdisplay(), electric_bottom_border); - XRaiseWindow(tqt_xdisplay(), electric_right_border); + for (int i = 0; i < ACTIVE_BORDER_COUNT; ++i) + { + if (active_windows[i] != None && e->xclient.window == active_windows[i]) + { + updateXTime(); + checkActiveBorder(TQPoint(e->xclient.data.l[2]>>16, e->xclient.data.l[2]&0xffff), GET_QT_X_TIME()); + return true; + } + } } } + return false; +} void Workspace::addTopMenu( Client* c ) { diff --git a/twin/workspace.h b/twin/workspace.h index e88e68c85..3a724a4a2 100644 --- a/twin/workspace.h +++ b/twin/workspace.h @@ -156,7 +156,10 @@ class Workspace : public TQObject, public KWinInterface, public KDecorationDefin void clientHidden( Client* ); void clientAttentionChanged( Client* c, bool set ); - void clientMoved(const TQPoint &pos, Time time); + void checkActiveBorder(const TQPoint &pos, Time time); + void reserveActiveBorder(ActiveBorder border); + void unreserveActiveBorder(ActiveBorder border); + void reserveActiveBorderSwitching(bool reserve); /** * Returns the current virtual desktop of this workspace @@ -302,7 +305,7 @@ class Workspace : public TQObject, public KWinInterface, public KDecorationDefin void requestDelayFocus( Client* ); void updateFocusMousePosition( const TQPoint& pos ); TQPoint focusMousePosition() const; - + void toggleTopDockShadows(bool on); public slots: @@ -406,6 +409,7 @@ class Workspace : public TQObject, public KWinInterface, public KDecorationDefin void cleanupTemporaryRules(); void writeWindowRules(); void kipcMessage( int id, int data ); + void updateActiveBorders(); // kompmgr void setPopupClientOpacity(int v); void resetClientOpacity(); @@ -486,12 +490,10 @@ class Workspace : public TQObject, public KWinInterface, public KDecorationDefin void tabBoxKeyPress( const KKeyNative& keyX ); void tabBoxKeyRelease( const XKeyEvent& ev ); - // electric borders - void checkElectricBorders( bool force = false ); - void createBorderWindows(); - void destroyBorderWindows(); - bool electricBorder(XEvent * e); - void raiseElectricBorders(); + // active borders + void destroyActiveBorders(); + bool activeBorderEvent(XEvent *e); + void activeBorderSwitchDesktop(ActiveBorder border, const TQPoint& pos); // ------------------ @@ -617,19 +619,17 @@ class Workspace : public TQObject, public KWinInterface, public KDecorationDefin TDEStartupInfo* startup; - bool electric_have_borders; - int electric_current_border; - WId electric_top_border; - WId electric_bottom_border; - WId electric_left_border; - WId electric_right_border; - int electricLeft; - int electricRight; - int electricTop; - int electricBottom; - Time electric_time_first; - Time electric_time_last; - TQPoint electric_push_point; + ActiveBorder active_current_border; + Window active_windows[ ACTIVE_BORDER_COUNT ]; + int activeLeft; + int activeRight; + int activeTop; + int activeBottom; + Time active_time_first; + Time active_time_last; + Time active_time_last_trigger; + TQPoint active_push_point; + int active_reserved[ ACTIVE_BORDER_COUNT ]; // corners/edges used by something Qt::Orientation layoutOrientation; int layoutX; @@ -663,7 +663,11 @@ class Workspace : public TQObject, public KWinInterface, public KDecorationDefin int maximizedWindowCounter; int topDockShadowSize;*/ //end - + + Window outline_left; + Window outline_right; + Window outline_top; + Window outline_bottom; signals: void kompmgrStarted(); void kompmgrStopped(); -- cgit v1.2.1