diff --git a/CMakeLists.txt b/CMakeLists.txt index 0dc61dfc5e..78ba4141cd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -105,8 +105,25 @@ set(kwin_KDEINIT_SRCS compositingprefs.cpp desktoplayout.cpp paintredirector.cpp + tile.cpp + tiling.cpp + tilinglayout.cpp + tilinglayoutfactory.cpp + + # tiling layouts + # spiral + #tilinglayouts/spiral/spiralfactory.cpp + tilinglayouts/spiral/spiral.cpp + + # columns + #tilinglayouts/columns/columnsfactory.cpp + tilinglayouts/columns/columns.cpp + + # floating + tilinglayouts/floating/floating.cpp ) +add_subdirectory( tilinglayouts ) qt4_add_dbus_adaptor( kwin_KDEINIT_SRCS org.kde.KWin.xml workspace.h KWin::Workspace ) diff --git a/activation.cpp b/activation.cpp index 5b5edb7392..bea853d570 100644 --- a/activation.cpp +++ b/activation.cpp @@ -259,6 +259,9 @@ void Workspace::setActiveClient( Client* c, allowed_t ) updateColormap(); if( effects ) static_cast(effects)->windowActivated( active_client ? active_client->effectWindow() : NULL ); + + if( tilingMode() ) + notifyWindowActivated( active_client ); --set_active_client_recursion; } diff --git a/client.cpp b/client.cpp index 982ac9b202..70f851d635 100644 --- a/client.cpp +++ b/client.cpp @@ -895,6 +895,9 @@ void Client::minimize( bool avoid_animation ) if( effects && !avoid_animation ) // TODO: Shouldn't it tell effects at least about the change? static_cast(effects)->windowMinimized( effectWindow()); + // when tiling, request a rearrangement + workspace()->notifyWindowMinimizeToggled( this ); + // Update states of all other windows in this group if( clientGroup() ) clientGroup()->updateStates( this ); @@ -911,9 +914,13 @@ void Client::unminimize( bool avoid_animation ) updateAllowedActions(); workspace()->updateMinimizedOfTransients( this ); updateWindowRules(); + workspace()->updateAllTiles(); if( effects && !avoid_animation ) static_cast( effects )->windowUnminimized( effectWindow() ); + // when tiling, request a rearrangement + workspace()->notifyWindowMinimizeToggled( this ); + // Update states of all other windows in this group if( clientGroup() ) clientGroup()->updateStates( this ); diff --git a/geometry.cpp b/geometry.cpp index 801129c5e4..8880a053bd 100644 --- a/geometry.cpp +++ b/geometry.cpp @@ -2155,6 +2155,7 @@ void Client::move( int x, int y, ForceGeometry_t force ) workspace()->checkActiveScreen( this ); workspace()->updateStackingOrder(); workspace()->checkUnredirect(); + workspace()->notifyWindowMove( this, moveResizeGeom, initialMoveResizeGeom ); // client itself is not damaged const QRect deco_rect = decorationRect().translated( geom.x(), geom.y() ); addWorkspaceRepaint( deco_rect_before_block ); @@ -2807,15 +2808,56 @@ bool Client::startMoveResize() void Client::finishMoveResize( bool cancel ) { + // store for notification + bool wasResize = isResize(); + bool wasMove = isMove(); + leaveMoveResize(); - if( isElectricBorderMaximizing() ) + + if( workspace()->tilingMode() ) { - cancel = true; + if( wasResize ) + workspace()->notifyWindowResizeDone( this, moveResizeGeom, initialMoveResizeGeom, cancel ); + else if( wasMove ) + workspace()->notifyWindowMoveDone( this, moveResizeGeom, initialMoveResizeGeom, cancel ); } + else + { if( cancel ) setGeometry( initialMoveResizeGeom ); else setGeometry( moveResizeGeom ); + } + if( cancel ) + setGeometry( initialMoveResizeGeom ); + + if( isElectricBorderMaximizing() ) + { + cancel = true; + } + if( isElectricBorderMaximizing() ) + { + switch( electricMode ) + { + case ElectricMaximizeMode: + if( maximizeMode() == MaximizeFull ) + setMaximize( false, false ); + else + setMaximize( true, true ); + workspace()->restoreElectricBorderSize( ElectricTop ); + break; + case ElectricLeftMode: + setQuickTileMode( QuickTileLeft ); + workspace()->restoreElectricBorderSize( ElectricLeft ); + break; + case ElectricRightMode: + setQuickTileMode( QuickTileRight ); + workspace()->restoreElectricBorderSize( ElectricRight ); + break; + } + electricMaximizing = false; + workspace()->hideElectricBorderWindowOutline(); + } if( isElectricBorderMaximizing() ) { switch( electricMode ) @@ -2841,6 +2883,7 @@ void Client::finishMoveResize( bool cancel ) } checkMaximizeGeometry(); // FRAME update(); + Notify::raise( isResize() ? Notify::ResizeEnd : Notify::MoveEnd ); if( effects ) static_cast(effects)->windowUserMovedResized( effectWindow(), false, true ); @@ -2999,6 +3042,11 @@ void Client::handleMoveResize( int x, int y, int x_root, int y_root ) bool update = false; if( isResize()) { + // query layout for supported resize mode + if( workspace()->tilingMode() ) + { + mode = workspace()->supportedTilingResizeMode( this, mode ); + } // first resize (without checking constrains), then snap, then check bounds, then check constrains QRect orig = initialMoveResizeGeom; Sizemode sizemode = SizemodeAny; @@ -3033,10 +3081,19 @@ void Client::handleMoveResize( int x, int y, int x_root, int y_root ) sizemode = SizemodeFixedW; break; case PositionCenter: + // exception for tiling + // Center means no resizing allowed + if( workspace()->tilingMode() ) + { + finishMoveResize( false ); + buttonDown = false; + return; + } default: abort(); break; } + workspace()->notifyWindowResize( this, moveResizeGeom, initialMoveResizeGeom ); // adjust new size to snap to other windows/borders moveResizeGeom = workspace()->adjustClientSize( this, moveResizeGeom, mode ); @@ -3208,9 +3265,13 @@ void Client::handleMoveResize( int x, int y, int x_root, int y_root ) if( update ) performMoveResize(); + if ( isMove() ) + { + workspace()->notifyWindowMove( this, moveResizeGeom, initialMoveResizeGeom ); workspace()->checkElectricBorder(globalPos, xTime()); } + } void Client::performMoveResize() { @@ -3229,6 +3290,7 @@ void Client::performMoveResize() if( rules()->checkMoveResizeMode ( isResize() ? options->resizeMode : options->moveMode ) == Options::Opaque ) { + if( !workspace()->tilingMode() ) setGeometry( moveResizeGeom ); positionGeometryTip(); } diff --git a/kcmkwin/kwinoptions/windows.cpp b/kcmkwin/kwinoptions/windows.cpp index 2ede7a6dec..d029247bf4 100644 --- a/kcmkwin/kwinoptions/windows.cpp +++ b/kcmkwin/kwinoptions/windows.cpp @@ -66,6 +66,9 @@ #define KWIN_AUTOGROUP_FOREGROUND "AutogroupInForeground" #define KWIN_SEPARATE_SCREEN_FOCUS "SeparateScreenFocus" #define KWIN_ACTIVE_MOUSE_SCREEN "ActiveMouseScreen" +#define KWIN_TILINGON "TilingOn" +#define KWIN_TILING_DEFAULT_LAYOUT "TilingDefaultLayout" +#define KWIN_TILING_RAISE_POLICY "TilingRaisePolicy" //CT 15mar 98 - magics #define KWM_BRDR_SNAP_ZONE "BorderSnapZone" @@ -636,6 +639,54 @@ KAdvancedConfig::KAdvancedConfig (bool _standAlone, KConfig *_config, const KCom connect(hideUtilityWindowsForInactive, SIGNAL(toggled(bool)), SLOT(changed())); vLay->addWidget( hideUtilityWindowsForInactive, 1, 0, 1, 2 ); + tilBox = new KButtonGroup(this); + tilBox->setTitle(i18n("Tiling")); + QGridLayout *tilBoxLay = new QGridLayout(tilBox); + + tilingOn = new QCheckBox( i18n( "Enable Tiling" ), tilBox ); + tilingOn->setWhatsThis( + i18n( "A tiling window manager lays out all the windows in a non-overlapping manner." + " This way all windows are always visible.") ); + tilBoxLay->addWidget( tilingOn ); + connect( tilingOn, SIGNAL( toggled(bool) ), SLOT( tilingOnChanged(bool) ) ); + connect( tilingOn, SIGNAL( toggled(bool) ), SLOT( changed() ) ); + + tilingLayoutLabel = new QLabel( i18n("Default Tiling &Layout"), tilBox ); + tilBoxLay->addWidget( tilingLayoutLabel, 1, 0 ); + + tilingLayoutCombo = new QComboBox( tilBox ); + + // NOTE: add your layout to the bottom of this list + tilingLayoutCombo->addItem( "Spiral" ); + tilingLayoutCombo->addItem( "Columns" ); + tilingLayoutCombo->addItem( "Floating" ); + + tilingLayoutLabel->setBuddy( tilingLayoutCombo ); + connect( tilingLayoutCombo, SIGNAL( activated(int) ), SLOT( changed() ) ); + tilBoxLay->addWidget( tilingLayoutCombo, 1, 1 ); + + tilingRaiseLabel = new QLabel( i18n("Floating &Windows Raising"), tilBox ); + tilBoxLay->addWidget( tilingRaiseLabel, 2, 0 ); + + tilingRaiseCombo = new QComboBox( tilBox ); + tilingRaiseCombo->addItem( i18nc( "Window Raising Policy", "Raise/Lower all floating windows" ) ); + tilingRaiseCombo->addItem( i18nc( "Window Raising Policy", "Raise/Lower current window only") ); + tilingRaiseCombo->addItem( i18nc( "Window Raising Policy", "Floating windows are always on top" ) ); + wtstr = i18n("The window raising policy determines how floating windows are stacked" + "
    " + "
  • Raise/Lower all will raise all floating windows when a" + " floating window is activated.
  • " + "
  • Raise/Lower current will raise only the current window.
  • " + "
  • Floating windows on top will always keep floating windows on top, even" + " when a tiled window is activated." + "
") ; + tilingRaiseCombo->setWhatsThis( wtstr ); + connect( tilingRaiseCombo, SIGNAL( activated(int) ), SLOT( changed() ) ); + tilingRaiseLabel->setBuddy( tilingRaiseCombo ); + tilBoxLay->addWidget( tilingRaiseCombo, 2, 1 ); + + lay->addWidget( tilBox ); + lay->addStretch(); load(); @@ -661,6 +712,24 @@ void KAdvancedConfig::shadeHoverChanged(bool a) { shadeHover->setEnabled(a); } +void KAdvancedConfig::setTilingOn( bool on ) { + tilingOn->setChecked( on ); +} + +void KAdvancedConfig::setTilingLayout( int l ) { + tilingLayoutCombo->setCurrentIndex( l ); +} + +void KAdvancedConfig::setTilingRaisePolicy( int l ) { + tilingRaiseCombo->setCurrentIndex( l ); +} + +void KAdvancedConfig::tilingOnChanged( bool a ) { + tilingLayoutLabel->setEnabled( a ); + tilingLayoutCombo->setEnabled( a ); + tilingRaiseLabel->setEnabled( a ); + tilingRaiseCombo->setEnabled( a ); +} void KAdvancedConfig::showEvent( QShowEvent *ev ) { @@ -719,6 +788,10 @@ void KAdvancedConfig::load( void ) setAutogroupSimilarWindows( cg.readEntry( KWIN_AUTOGROUP_SIMILAR, false)); setAutogroupInForeground( cg.readEntry( KWIN_AUTOGROUP_FOREGROUND, true)); + setTilingOn( cg.readEntry( KWIN_TILINGON, false ) ); + setTilingLayout( cg.readEntry( KWIN_TILING_DEFAULT_LAYOUT, 0 ) ); + setTilingRaisePolicy( cg.readEntry( KWIN_TILING_RAISE_POLICY, 0 ) ); + emit KCModule::changed(false); } @@ -769,6 +842,10 @@ void KAdvancedConfig::save( void ) QDBusConnection::sessionBus().send(message); } + + cg.writeEntry( KWIN_TILINGON, tilingOn->isChecked() ); + cg.writeEntry( KWIN_TILING_DEFAULT_LAYOUT, tilingLayoutCombo->currentIndex() ); + cg.writeEntry( KWIN_TILING_RAISE_POLICY, tilingRaiseCombo->currentIndex() ); emit KCModule::changed(false); } @@ -778,6 +855,9 @@ void KAdvancedConfig::defaults() setShadeHoverInterval(250); setPlacement(SMART_PLACEMENT); setHideUtilityWindowsForInactive( true ); + setTilingOn( false ); + setTilingLayout( 0 ); + setTilingRaisePolicy( 0 ); setInactiveTabsSkipTaskbar( false ); setAutogroupSimilarWindows( false ); setAutogroupInForeground( true ); diff --git a/kcmkwin/kwinoptions/windows.h b/kcmkwin/kwinoptions/windows.h index 2890209072..29c73dd7e0 100644 --- a/kcmkwin/kwinoptions/windows.h +++ b/kcmkwin/kwinoptions/windows.h @@ -196,6 +196,7 @@ private slots: void changed() { emit KCModule::changed(true); } + void tilingOnChanged( bool a ); private: int getShadeHoverInterval (void ); @@ -225,7 +226,21 @@ private: int getPlacement( void ); //CT void setPlacement(int); //CT + KComboBox *placementCombo; + + // ------------------------------ + // Tiling related widgets/methods + // ------------------------------ + KButtonGroup *tilBox; + QCheckBox *tilingOn; + QLabel *tilingLayoutLabel; + QLabel *tilingRaiseLabel; + QComboBox *tilingLayoutCombo; + QComboBox *tilingRaiseCombo; + void setTilingOn( bool ); + void setTilingLayout( int ); + void setTilingRaisePolicy( int ); }; #endif // KKWMWINDOWS_H diff --git a/kcmkwin/kwinrules/ruleswidget.cpp b/kcmkwin/kwinrules/ruleswidget.cpp index 0d7f7e5426..89c9febe26 100644 --- a/kcmkwin/kwinrules/ruleswidget.cpp +++ b/kcmkwin/kwinrules/ruleswidget.cpp @@ -103,6 +103,7 @@ RulesWidget::RulesWidget( QWidget* parent ) SETUP( autogroupid, force ); SETUP( opacityactive, force ); SETUP( opacityinactive, force ); + SETUP( tilingoption, force ); SETUP( shortcut, force ); // workarounds tab SETUP( fsplevel, force ); @@ -153,6 +154,7 @@ UPDATE_ENABLE_SLOT( autogroupfg ) UPDATE_ENABLE_SLOT( autogroupid ) UPDATE_ENABLE_SLOT( opacityactive ) UPDATE_ENABLE_SLOT( opacityinactive ) +UPDATE_ENABLE_SLOT( tilingoption ) void RulesWidget::updateEnableshortcut() { shortcut->setEnabled( enable_shortcut->isChecked() && rule_shortcut->currentIndex() != 0 ); @@ -269,6 +271,16 @@ int RulesWidget::comboToDesktop( int val ) const return val + 1; } +int RulesWidget::tilingToCombo( int t ) const + { + return qBound(0, t, 1); + } + +int RulesWidget::comboToTiling( int val ) const + { + return val; // 0 is tiling, 1 is floating + } + static int placementToCombo( Placement::Policy placement ) { static const int conv[] = @@ -431,6 +443,7 @@ void RulesWidget::setRules( Rules* rules ) LINEEDIT_FORCE_RULE( autogroupid, ); LINEEDIT_FORCE_RULE( opacityactive, intToStr ); LINEEDIT_FORCE_RULE( opacityinactive, intToStr ); + COMBOBOX_FORCE_RULE( tilingoption, tilingToCombo ); LINEEDIT_SET_RULE( shortcut, ); COMBOBOX_FORCE_RULE( fsplevel, ); COMBOBOX_FORCE_RULE( moveresizemode, moveresizeToCombo ); @@ -524,6 +537,7 @@ Rules* RulesWidget::rules() const LINEEDIT_FORCE_RULE( autogroupid, ); LINEEDIT_FORCE_RULE( opacityactive, strToInt ); LINEEDIT_FORCE_RULE( opacityinactive, strToInt ); + COMBOBOX_FORCE_RULE( tilingoption, comboToTiling ); LINEEDIT_SET_RULE( shortcut, ); COMBOBOX_FORCE_RULE( fsplevel, ); COMBOBOX_FORCE_RULE( moveresizemode, comboToMoveResize ); @@ -644,6 +658,7 @@ void RulesWidget::prefillUnusedValues( const KWindowInfo& info ) //LINEEDIT_PREFILL( autogroupid, ); LINEEDIT_PREFILL( opacityactive, intToStr, 100 /*get the actual opacity somehow*/); LINEEDIT_PREFILL( opacityinactive, intToStr, 100 /*get the actual opacity somehow*/); + COMBOBOX_PREFILL( tilingoption, tilingToCombo, 0 ); //LINEEDIT_PREFILL( shortcut, ); //COMBOBOX_PREFILL( fsplevel, ); //COMBOBOX_PREFILL( moveresizemode, moveresizeToCombo ); diff --git a/kcmkwin/kwinrules/ruleswidget.h b/kcmkwin/kwinrules/ruleswidget.h index e6aafd6e07..e4b02d7a50 100644 --- a/kcmkwin/kwinrules/ruleswidget.h +++ b/kcmkwin/kwinrules/ruleswidget.h @@ -77,6 +77,7 @@ class RulesWidget void updateEnableautogroupid(); void updateEnableopacityactive(); void updateEnableopacityinactive(); + void updateEnabletilingoption(); // workarounds tab void updateEnablefsplevel(); void updateEnablemoveresizemode(); @@ -92,6 +93,8 @@ class RulesWidget private: int desktopToCombo( int d ) const; int comboToDesktop( int val ) const; + int tilingToCombo( int t ) const; + int comboToTiling( int val ) const; void prefillUnusedValues( const KWindowInfo& info ); DetectDialog* detect_dlg; bool detect_dlg_ok; diff --git a/kcmkwin/kwinrules/ruleswidgetbase.ui b/kcmkwin/kwinrules/ruleswidgetbase.ui index 46c9f3f55f..d2a5e19b0b 100644 --- a/kcmkwin/kwinrules/ruleswidgetbase.ui +++ b/kcmkwin/kwinrules/ruleswidgetbase.ui @@ -1,7 +1,8 @@ - + + KWin::RulesWidgetBase - - + + 0 0 @@ -9,14 +10,14 @@ 441 - - + + 0 - - - 0 + + + 3 @@ -30,141 +31,141 @@ &Window - - - - + + + + De&scription: - + false - + description - - + + - - - + + + Window &class (application type): - + false - + wmclass - - + + - - - + + + Match w&hole window class - - + + - + Unimportant - + Exact Match - + Substring Match - + Regular Expression - - - + + + false - + Edit - + - - - + + + Window &role: - + false - + role - - + + - - + + - + Unimportant - + Exact Match - + Substring Match - + Regular Expression - - - + + + false - + Edit - - + + - - + + Qt::Horizontal - + 40 20 @@ -173,18 +174,18 @@ - - + + &Detect Window Properties - + Qt::Horizontal - + 40 20 @@ -194,15 +195,15 @@ - + - + Qt::Vertical - + QSizePolicy::Expanding - + 20 40 @@ -210,12 +211,12 @@ - - - + + + Qt::Horizontal - + 40 20 @@ -223,12 +224,12 @@ - - - + + + Qt::Horizontal - + 40 20 @@ -236,9 +237,9 @@ - - - + + + Qt::Horizontal @@ -257,242 +258,242 @@ Window &Extra - - - - + + + + Window &types: - + false - + types - - - + + + QAbstractItemView::ExtendedSelection - + Normal Window - + Dialog Window - + Utility Window - + Dock (panel) - + Toolbar - + Torn-Off Menu - + Splash Screen - + Desktop - + Override Type - + Standalone Menubar - - - + + + Window t&itle: - + false - + title - - + + - - + + - + Unimportant - + Exact Match - + Substring Match - + Regular Expression - - - + + + false - + Edit - + - - - + + + Extra role: - + false - + extra - - + + - - + + - + Unimportant - + Exact Match - + Substring Match - + Regular Expression - - - + + + false - + Edit - + - - - + + + &Machine (hostname): - + false - + machine - - + + - - + + - + Unimportant - + Exact Match - + Substring Match - + Regular Expression - - - + + + false - + Edit - + - - - + + + Qt::Horizontal - + 40 20 @@ -500,12 +501,12 @@ - - - + + + Qt::Horizontal - + 40 20 @@ -513,12 +514,12 @@ - - - + + + Qt::Horizontal - + 40 20 @@ -526,12 +527,12 @@ - - - + + + Qt::Vertical - + 20 0 @@ -553,494 +554,494 @@ &Geometry - - - - + + + + false - + Do Not Affect - + Apply Initially - + Remember - + Force - + Apply Now - + Force Temporarily - - - + + + false - + 0123456789-+,xX: - - - + + + &Size - - - + + + &Position - - - + + + false - + 0123456789-+,xX: - - - + + + false - + Do Not Affect - + Apply Initially - + Remember - + Force - + Apply Now - + Force Temporarily - - - + + + false - + - - - + + + false - + Do Not Affect - + Apply Initially - + Remember - + Force - + Apply Now - + Force Temporarily - - - + + + false - + - - - + + + Maximized &horizontally - - - + + + false - + - - - + + + false - + Do Not Affect - + Apply Initially - + Remember - + Force - + Apply Now - + Force Temporarily - - - + + + &Fullscreen - - - + + + false - + Do Not Affect - + Apply Initially - + Remember - + Force - + Apply Now - + Force Temporarily - - - + + + Maximized &vertically - - - + + + false - + Do Not Affect - + Apply Initially - + Remember - + Force - + Apply Now - + Force Temporarily - - - + + + false - - - + + + &Desktop - - - + + + Sh&aded - - - + + + false - + - - - + + + false - + Do Not Affect - + Apply Initially - + Remember - + Force - + Apply Now - + Force Temporarily - - - + + + false - + - - - + + + M&inimized - - - + + + false - + Do Not Affect - + Force - + Force Temporarily - - - + + + false - + Default - + No Placement - + Smart - + Maximizing - + Cascade - + Centered - + Random - + Top-Left Corner - + Under Mouse - + On Main Window - - - + + + P&lacement - + - + Qt::Vertical - + QSizePolicy::Expanding - + 20 16 @@ -1048,38 +1049,38 @@ - - - + + + false - + Do Not Affect - + Apply Initially - + Remember - + Force - + Apply Now - + Force Temporarily @@ -1099,52 +1100,52 @@ &Preferences - - - - + + + + Keep &above - - - + + + Keep &below - - - + + + Skip pa&ger - - - + + + Skip &taskbar - - - + + + &No border - - - + + + Accept &focus - - - + + + &Closeable @@ -1183,17 +1184,17 @@ false - + Do Not Affect - + Force - + Force Temporarily @@ -1204,73 +1205,83 @@ false - + 0123456789 - - - + + + false - + - - - + + + false - + - - - + + + false - + - - - + + + false - + - - - + + + false - + - - - + + + false - + - - + + + + false + + + + + + + + false @@ -1377,25 +1388,10 @@ false - - - Do Not Affect - - - - - Force - - - - - Force Temporarily - - - - + + false @@ -1416,8 +1412,8 @@ - - + + false @@ -1428,174 +1424,240 @@ + Force + + + + + Force Temporarily + + + + + + + + false + + + + Do Not Affect + + + + + Force + + + + + Force Temporarily + + + + + + + + false + + + + Do Not Affect + + + + + Force + + + + + Force Temporarily + + + + + + + + false + + + + Do Not Affect + + + + Apply Initially - + Remember - + Force - + Apply Now - + Force Temporarily - - - + + + false - + Do Not Affect - + Apply Initially - + Remember - + Force - + Apply Now - + Force Temporarily - - - + + + false - + Do Not Affect - + Apply Initially - + Remember - + Force - + Apply Now - + Force Temporarily - - - + + + false - + Do Not Affect - + Apply Initially - + Remember - + Force - + Apply Now - + Force Temporarily - - - + + + false - + Do Not Affect - + Apply Initially - + Remember - + Force - + Apply Now - + Force Temporarily @@ -1603,13 +1665,13 @@ - + Qt::Vertical - + QSizePolicy::Expanding - + 20 80 @@ -1623,17 +1685,17 @@ false - + Do Not Affect - + Force - + Force Temporarily @@ -1644,7 +1706,7 @@ false - + 0123456789 @@ -1661,7 +1723,7 @@ Shortcut - + @@ -1672,32 +1734,32 @@ false - + Do Not Affect - + Apply Initially - + Remember - + Force - + Apply Now - + Force Temporarily @@ -1717,6 +1779,52 @@ + + + + T&iling + + + + + + + false + + + + Do Not Affect + + + + + Force + + + + + Force Temporarily + + + + + + + + false + + + + Tiled + + + + + Floating + + + + @@ -1731,326 +1839,326 @@ W&orkarounds - - - - + + + + &Focus stealing prevention - - - + + + false - + Do Not Affect - + Force - + Force Temporarily - - - + + + false - + Normal Window - + Dialog Window - + Utility Window - + Dock (panel) - + Toolbar - + Torn-Off Menu - + Splash Screen - + Desktop - + Override Type - + Standalone Menubar - - - + + + false - + Opaque - + Transparent - - - + + + Window &type - - - + + + &Moving/resizing - - - + + + false - + Do Not Affect - + Force - + Force Temporarily - - - + + + false - + Do Not Affect - + Force - + Force Temporarily - - - + + + false - + None - + Low - + Normal - + High - + Extreme - - - + + + false - + 0123456789-+,xX: - - - + + + M&inimum size - - - + + + false - + Do Not Affect - + Force - + Force Temporarily - - - + + + M&aximum size - - - + + + false - + 0123456789-+,xX: - - - + + + false - + Do Not Affect - + Force - + Force Temporarily - - - + + + Ignore requested &geometry - - - + + + false - + Do Not Affect - + Force - + Force Temporarily - - - + + + false - + - + - + Qt::Vertical - + QSizePolicy::Expanding - + 20 160 @@ -2058,85 +2166,85 @@ - - - + + + Strictly obey geometry - + - - - + + + false - + Do Not Affect - + Force - + Force Temporarily - - - + + + false - + - - - + + + false - + - - - + + + Block global shortcuts - + - - - + + + false - + Do Not Affect - + Force - + Force Temporarily @@ -2150,9 +2258,9 @@ - KComboBox - QComboBox -
kcombobox.h
+ KPushButton + QPushButton +
kpushbutton.h
KLineEdit @@ -2160,9 +2268,9 @@
klineedit.h
- KPushButton - QPushButton -
kpushbutton.h
+ KComboBox + QComboBox +
kcombobox.h
KRestrictedLine @@ -2285,11 +2393,11 @@ KWin::RulesWidgetBase detectClicked() - + 20 20 - + 20 20 @@ -2301,11 +2409,11 @@ KWin::RulesWidgetBase wmclassMatchChanged() - + 20 20 - + 20 20 @@ -2317,11 +2425,11 @@ KWin::RulesWidgetBase roleMatchChanged() - + 20 20 - + 20 20 @@ -2333,11 +2441,11 @@ KWin::RulesWidgetBase titleMatchChanged() - + 20 20 - + 20 20 @@ -2349,11 +2457,11 @@ KWin::RulesWidgetBase extraMatchChanged() - + 20 20 - + 20 20 @@ -2365,11 +2473,11 @@ KWin::RulesWidgetBase machineMatchChanged() - + 20 20 - + 20 20 @@ -2381,11 +2489,11 @@ KWin::RulesWidgetBase shortcutEditClicked() - + 20 20 - + 20 20 diff --git a/kwin.notifyrc b/kwin.notifyrc index f3f499b69b..34cfb73c39 100644 --- a/kwin.notifyrc +++ b/kwin.notifyrc @@ -6775,3 +6775,18 @@ Comment[x-test]=xxSome effects are not supported by backend or hardware.xx Comment[zh_CN]=因为后端或硬件的关系,一些效果不受支持。 Comment[zh_TW]=有些效果未被後端介面或硬體支援。 Action=Popup + +[Event/tilingenabled] +Name=Tiling Enabled +Comment=Tiling mode has been enabled +Action=Popup + +[Event/tilingdisabled] +Name=Tiling Disabled +Comment=Tiling mode has been disabled +Action=Popup + +[Event/tilinglayoutchanged] +Name=Tiling Layout Changed +Comment=Tiling Layout has been changed +Action=Popup diff --git a/kwinbindings.cpp b/kwinbindings.cpp index 529e086c86..a0f56bf388 100644 --- a/kwinbindings.cpp +++ b/kwinbindings.cpp @@ -219,6 +219,28 @@ along with this program. If not, see . DEF( I18N_NOOP("Block Global Shortcuts"), 0, slotDisableGlobalShortcuts()); DEF( I18N_NOOP("Suspend Compositing"), Qt::SHIFT+Qt::ALT+Qt::Key_F12, slotToggleCompositing()); + a = actionCollection->addAction( "Group:Tiling" ); + a->setText( i18n("Tiling") ); + DEF( I18N_NOOP("Toggle Tiling"), Qt::SHIFT+Qt::ALT+Qt::Key_F11, slotToggleTiling() ); + DEF( I18N_NOOP("Toggle Orientation"), Qt::META+Qt::Key_Space, slotToggleOrientation() ); + DEF( I18N_NOOP("Increase Ratio"), Qt::META+Qt::Key_Right, slotTileIncreaseRatio() ); + DEF( I18N_NOOP("Decrease Ratio"), Qt::META+Qt::Key_Left, slotTileDecreaseRatio() ); + DEF( I18N_NOOP("Toggle Floating"), Qt::META+Qt::Key_F, slotToggleFloating() ); + + DEF( I18N_NOOP("Switch Focus Left") , Qt::META+Qt::Key_H, slotLeft() ); + DEF( I18N_NOOP("Switch Focus Right") , Qt::META+Qt::Key_L, slotRight() ); + DEF( I18N_NOOP("Switch Focus Up") , Qt::META+Qt::Key_K, slotTop() ); + DEF( I18N_NOOP("Switch Focus Down") , Qt::META+Qt::Key_J, slotBottom() ); + DEF( I18N_NOOP("Move Left") , Qt::SHIFT+Qt::META+Qt::Key_H, slotMoveLeft() ); + DEF( I18N_NOOP("Move Right") , Qt::SHIFT+Qt::META+Qt::Key_L, slotMoveRight() ); + DEF( I18N_NOOP("Move Up") , Qt::SHIFT+Qt::META+Qt::Key_K, slotMoveTop() ); + DEF( I18N_NOOP("Move Down") , Qt::SHIFT+Qt::META+Qt::Key_J, slotMoveBottom() ); + DEF( I18N_NOOP("Next Layout"), Qt::META+Qt::Key_PageDown, slotNextTileLayout() ); + DEF( I18N_NOOP("Previous Layout"), Qt::META+Qt::Key_PageUp, slotPreviousTileLayout() ); + // NOTE: temporary + DEF( I18N_NOOP("Test Dump tiles"), Qt::META+Qt::Key_D, dumpTiles() ); + DEF( I18N_NOOP("Test Below Cursor"), Qt::META+Qt::Key_X, belowCursor() ); + #undef DEF #undef DEF2 diff --git a/options.cpp b/options.cpp index b960f63edc..1b0f352415 100644 --- a/options.cpp +++ b/options.cpp @@ -124,6 +124,10 @@ unsigned long Options::updateSettings() shadeHover = config.readEntry("ShadeHover", false); shadeHoverInterval = config.readEntry("ShadeHoverInterval", 250 ); + tilingOn = config.readEntry( "TilingOn", false ); + tilingLayout = config.readEntry( "TilingDefaultLayout", 0 ); + tilingRaisePolicy = config.readEntry( "TilingRaisePolicy", 0 ); + // important: autoRaise implies ClickRaise clickRaise = autoRaise || config.readEntry("ClickRaise", true); diff --git a/options.h b/options.h index a21106a4e1..312fac28a1 100644 --- a/options.h +++ b/options.h @@ -116,6 +116,21 @@ class Options : public KDecorationOptions */ int shadeHoverInterval; + /** + * Whether tiling is enabled or not + */ + bool tilingOn; + + /** + * Tiling Layout + */ + int tilingLayout; + + /** + * Tiling window raise policy. + */ + int tilingRaisePolicy; + /** Different Alt-Tab-Styles:
    diff --git a/org.kde.KWin.xml b/org.kde.KWin.xml index 9ed31f1174..9c66c29b71 100644 --- a/org.kde.KWin.xml +++ b/org.kde.KWin.xml @@ -69,5 +69,9 @@ + + + + diff --git a/rules.cpp b/rules.cpp index 81ab820ffa..b6055dd224 100644 --- a/rules.cpp +++ b/rules.cpp @@ -51,6 +51,7 @@ Rules::Rules() , maxsizerule( UnusedForceRule ) , opacityactiverule( UnusedForceRule ) , opacityinactiverule( UnusedForceRule ) + , tilingoptionrule( UnusedForceRule ) , ignorepositionrule( UnusedForceRule ) , desktoprule( UnusedSetRule ) , typerule( UnusedForceRule ) @@ -153,6 +154,7 @@ void Rules::readFromCfg( const KConfigGroup& cfg ) READ_FORCE_RULE( opacityinactive,, 0); if( opacityinactive < 0 || opacityinactive > 100 ) opacityinactive = 100; + READ_FORCE_RULE( tilingoption,, 0 ); READ_FORCE_RULE( ignoreposition,, false); READ_SET_RULE( desktop,,0 ); type = readType( cfg, "type" ); @@ -241,6 +243,7 @@ void Rules::write( KConfigGroup& cfg ) const WRITE_FORCE_RULE( maxsize, ); WRITE_FORCE_RULE( opacityactive, ); WRITE_FORCE_RULE( opacityinactive, ); + WRITE_FORCE_RULE( tilingoption, ); WRITE_FORCE_RULE( ignoreposition, ); WRITE_SET_RULE( desktop, ); WRITE_FORCE_RULE( type, int ); @@ -280,6 +283,7 @@ bool Rules::isEmpty() const && maxsizerule == UnusedForceRule && opacityactiverule == UnusedForceRule && opacityinactiverule == UnusedForceRule + && tilingoptionrule == UnusedForceRule && ignorepositionrule == UnusedForceRule && desktoprule == UnusedSetRule && typerule == UnusedForceRule @@ -576,6 +580,7 @@ APPLY_FORCE_RULE( maxsize, MaxSize, QSize ) APPLY_FORCE_RULE( opacityactive, OpacityActive, int ) APPLY_FORCE_RULE( opacityinactive, OpacityInactive, int ) APPLY_FORCE_RULE( ignoreposition, IgnorePosition, bool ) +APPLY_FORCE_RULE( tilingoption, TilingOption, int ) // the cfg. entry needs to stay named the say for backwards compatibility bool Rules::applyIgnoreGeometry( bool& ignore ) const @@ -672,6 +677,7 @@ void Rules::discardUsed( bool withdrawn ) DISCARD_USED_FORCE_RULE( maxsize ); DISCARD_USED_FORCE_RULE( opacityactive ); DISCARD_USED_FORCE_RULE( opacityinactive ); + DISCARD_USED_FORCE_RULE( tilingoption ); DISCARD_USED_FORCE_RULE( ignoreposition ); DISCARD_USED_SET_RULE( desktop ); DISCARD_USED_FORCE_RULE( type ); @@ -781,6 +787,7 @@ CHECK_FORCE_RULE( MinSize, QSize ) CHECK_FORCE_RULE( MaxSize, QSize ) CHECK_FORCE_RULE( OpacityActive, int ) CHECK_FORCE_RULE( OpacityInactive, int ) +CHECK_FORCE_RULE( TilingOption, int ) CHECK_FORCE_RULE( IgnorePosition, bool ) bool WindowRules::checkIgnoreGeometry( bool ignore ) const diff --git a/rules.h b/rules.h index 60d469d93b..d645962a04 100644 --- a/rules.h +++ b/rules.h @@ -61,6 +61,7 @@ class WindowRules QSize checkMaxSize( QSize s ) const; int checkOpacityActive(int s) const; int checkOpacityInactive(int s) const; + int checkTilingOption( int s ) const; bool checkIgnoreGeometry( bool ignore ) const; int checkDesktop( int desktop, bool init = false ) const; NET::WindowType checkType( NET::WindowType type ) const; @@ -115,6 +116,7 @@ class Rules bool applyMaxSize( QSize& s ) const; bool applyOpacityActive(int& s) const; bool applyOpacityInactive(int& s) const; + bool applyTilingOption( int& s ) const; bool applyIgnoreGeometry( bool& ignore ) const; bool applyDesktop( int& desktop, bool init ) const; bool applyType( NET::WindowType& type ) const; @@ -214,6 +216,8 @@ class Rules ForceRule opacityactiverule; int opacityinactive; ForceRule opacityinactiverule; + int tilingoption; + ForceRule tilingoptionrule; bool ignoreposition; ForceRule ignorepositionrule; int desktop; diff --git a/sm.cpp b/sm.cpp index 7404fafd38..2e926f407b 100644 --- a/sm.cpp +++ b/sm.cpp @@ -82,6 +82,17 @@ void Workspace::storeSession( KConfig* config, SMSavePhase phase ) KConfigGroup cg(config, "Session"); int count = 0; int active_client = -1; + + if( phase == SMSavePhase2 || phase == SMSavePhase2Full ) + { + cg.writeEntry( "tiling", tilingMode() ); + if( tilingMode() ) + { + kDebug(1212) << "Tiling was ON"; + setTilingMode( false ); + } + } + for (ClientList::Iterator it = clients.begin(); it != clients.end(); ++it) { Client* c = (*it); @@ -167,6 +178,9 @@ void Workspace::loadSessionInfo() { session.clear(); KConfigGroup cg(kapp->sessionConfig(), "Session"); + + setTilingMode( cg.readEntry( "tiling", false ) ); + int count = cg.readEntry( "count",0 ); int active_client = cg.readEntry( "active",0 ); for ( int i = 1; i <= count; i++ ) diff --git a/tile.cpp b/tile.cpp new file mode 100644 index 0000000000..907b228452 --- /dev/null +++ b/tile.cpp @@ -0,0 +1,152 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2009 Nikhil Marathe + +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 +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*********************************************************************/ + +#include "tile.h" + +#include + +#include + +#include "client.h" +#include "workspace.h" + +namespace KWin +{ + +Tile::Tile( Client *c, const QRect& area) + : m_client(c), + m_floating(false) + { + setGeometry(area); + if( c ) + { + m_prevGeom = c->geometry(); + } + if( c && !c->isResizable() ) + floatTile(); + } + +/* + * NOTE: Why isn't left/right/parent copied? + * Because they might be deleted at any point, so we can't keep pointers to them + * Also it doesn't make sense in the areas where copy is actually going to be used. + * Since we will be getting a new parent and children. + */ +Tile::Tile( const Tile& orig ) + : m_client( orig.client() ), + m_floating( orig.floating() ), + m_prevGeom( orig.m_prevGeom ) + { + setGeometry( orig.geometry() ); + } + +Tile::~Tile() + { + restorePreviousGeometry(); + + m_client = NULL; + } + +void Tile::commit() + { + m_client->setGeometry(geometry(), ForceGeometrySet); + } + +void Tile::setGeometry(int x, int y, int w, int h) + { + QRect old = m_geom; + m_geom.setTopLeft( QPoint(x, y) ); + m_geom.setWidth( w ); + m_geom.setHeight( h ); + + if( old == m_geom ) + return; + + if( floating() ) + m_prevGeom = m_geom; + + } + +void Tile::floatTile() + { + if( floating() ) return; + + // note, order of setting m_floating to true + // then calling restore is important + // childGeometryChanged will check for ignoreGeometry() + m_floating = true; + + restorePreviousGeometry(); + + commit(); + client()->workspace()->notifyWindowActivated( client() ); + // TODO: notify layout manager + } + +void Tile::unfloatTile() + { + if( !floating() ) return; + + m_floating = false; + m_prevGeom = m_client->geometry(); + + setGeometry( m_client->workspace()->clientArea( PlacementArea, m_client ) ); + commit(); + // TODO: notify layout manager + + } + +void Tile::restorePreviousGeometry() + { + // why this check? + // sometimes we remove a Tile, but don't want to remove the children + // so the children are set to NULL. In this case leaf() will return + // true but m_client will still be null + if( !m_client ) return; + if( m_prevGeom.isNull() ) + { + QRect area = m_client->workspace()->clientArea( PlacementArea, m_client ); + m_client->workspace()->place( m_client, area ); + } + else + { + m_client->setGeometry( m_prevGeom, ForceGeometrySet ); + } + setGeometry( m_client->geometry() ); + } + +inline bool Tile::minimized() const + { + return m_client->isMinimized(); + } + +void Tile::focus() + { + m_client->workspace()->activateClient( m_client, true ); + } + +void Tile::dumpTile(const QString& indent ) const + { + kDebug(1212) << indent << m_client + << ( floating() ? "floating" : "not floating" ) + << ( ignoreGeometry() ? "ignored" : "tiled" ) + << m_geom ; + } +} diff --git a/tile.h b/tile.h new file mode 100644 index 0000000000..f2dc0c4237 --- /dev/null +++ b/tile.h @@ -0,0 +1,125 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2009 Nikhil Marathe + +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 +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*********************************************************************/ + +#ifndef KWIN_TILE_H +#define KWIN_TILE_H + +#include +#include + +namespace KWin +{ + +class Client; +class Workspace; + +class Tile + { + public: + enum Direction { + Top, + Right, + Bottom, + Left + }; + + enum LayoutMode { + UseRatio, // uses m_ratio + UseGeometry, // uses current geometry of children + Equal // distribute equally + }; + + Tile( Client *c, const QRect& area); + Tile(const Tile& orig); + virtual ~Tile(); + void setGeometry(const QRect& area); + void setGeometry(int x, int y, int w, int h); + + void resize( const QRect area ); + + void restorePreviousGeometry(); + + void commit(); + + void focus(); + + // :| the float datatype interferes with naming + void floatTile(); + void unfloatTile(); + + bool minimized() const; + bool floating() const; + bool ignoreGeometry() const; + QRect geometry() const; + Client* client() const; + + void dumpTile( const QString& indent = "" ) const; + + private: + + // ------------- + // PROPERTIES + // ------------- + + // our client + Client *m_client; + + // tiled geometry + QRect m_geom; + // before tiling was enabled, if any + QRect m_prevGeom; + + bool m_floating; + + }; + +inline QRect Tile::geometry() const + { + return m_geom; + } + +inline Client* Tile::client() const + { + return m_client; + } + +inline bool Tile::floating() const + { + return m_floating; + } + +/* + * should be respected by all geometry modifying methods. + * It returns true if the Tile is 'out' of the layout, + * due to being minimized, floating or for some other reason. + */ +inline bool Tile::ignoreGeometry() const + { + return minimized() || floating(); + } + +inline void Tile::setGeometry(const QRect& area) + { + setGeometry(area.x(), area.y(), area.width(), area.height()); + } + +} // namespace +#endif + diff --git a/tiling.cpp b/tiling.cpp new file mode 100644 index 0000000000..cb6367f5e5 --- /dev/null +++ b/tiling.cpp @@ -0,0 +1,504 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2009 Nikhil Marathe + +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 +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*********************************************************************/ + +// all the tiling related code that is extensions to existing KWin classes +// Includes Workspace for now + +#include "client.h" +#include "workspace.h" +#include "tile.h" +#include "tilinglayout.h" +#include "tilinglayoutfactory.h" + +#include +#include +#include "lib/kdecoration.h" + + +namespace KWin +{ + +bool Workspace::tilingMode() const + { + return tilingMode_; + } + +void Workspace::setTilingMode( bool tiling ) + { + if( tilingMode() == tiling ) return; + tilingMode_ = tiling; + + if( tilingMode_ ) + { + tilingLayouts.resize( numberOfDesktops() + 1 ); + foreach( Client *c, stackingOrder() ) + { + createTile( c ); + } + } + else + { + foreach( TilingLayout *t, tilingLayouts ) + { + if( t ) + delete t; + } + tilingLayouts.clear(); + } + } + +void Workspace::slotToggleTiling() + { + if ( tilingMode() ) + { + setTilingMode( false ); + QString message = i18n( "Tiling Disabled" ); + KNotification::event( "tilingdisabled", message, QPixmap(), NULL, KNotification::CloseOnTimeout, KComponentData( "kwin" ) ); + } + else + { + setTilingMode( true ); + QString message = i18n( "Tiling Enabled" ); + KNotification::event( "tilingenabled", message, QPixmap(), NULL, KNotification::CloseOnTimeout, KComponentData( "kwin" ) ); + } + } + +void Workspace::createTile( Client *c ) + { + if( c == NULL ) return; + if( c->desktop() < 0 || c->desktop() >= tilingLayouts.size() ) return; + + kDebug(1212) << "Now tiling " << c->caption(); + if( !tilingMode() || !tileable(c) ) + return; + + Tile *t = new Tile( c, clientArea( PlacementArea, c ) ); + if( !tileable( c ) ) + { + kDebug(1212) << c->caption() << "is not tileable"; + t->floatTile(); + } + + if( !tilingLayouts.value(c->desktop()) ) + { + tilingLayouts[c->desktop()] = TilingLayoutFactory::createLayout( TilingLayoutFactory::DefaultL, this ); + } + tilingLayouts[c->desktop()]->addTile( t ); + tilingLayouts[c->desktop()]->commit(); + } + +void Workspace::removeTile( Client *c ) + { + if( tilingLayouts[ c->desktop() ] ) + tilingLayouts[ c->desktop() ]->removeTile( c ); + } + +bool Workspace::tileable( Client *c ) + { + kDebug(1212) << c->caption(); + // TODO: if application specific settings + // to ignore, put them here + + // as suggested by Lucas Murray + // see Client::manage ( ~ line 270 of manage.cpp at time of writing ) + // This is useful to ignore plasma widget placing. + // According to the ICCCM if an application + // or user requests a certain geometry + // respect it + long msize; + XSizeHints xSizeHint; + XGetWMNormalHints(display(), c->window(), &xSizeHint, &msize); + if( xSizeHint.flags & PPosition || + xSizeHint.flags & USPosition ) { + kDebug(1212) << "Not tileable due to USPosition requirement"; + return false; + } + + kDebug(1212) << "c->isNormalWindow()" << c->isNormalWindow() ; + kDebug(1212) << "c->isUtility() " << c->isUtility() ; + kDebug(1212) << "c->isDialog() " << c->isDialog() ; + kDebug(1212) << "c->isSplash() " << c->isSplash() ; + kDebug(1212) << "c->isToolbar() " << c->isToolbar() ; + kDebug(1212) << "c->isPopupMenu() " << c->isPopupMenu() ; + kDebug(1212) << "c->isTransient() " << c->isTransient() ; + if( !c->isNormalWindow() || + c->isUtility() || + c->isDialog() || + c->isSplash() || + c->isToolbar() || + c->isPopupMenu() || + c->isTransient() ) + { + return false; + } + + return true; + } + +void Workspace::belowCursor() + { + // TODO + } + +Tile* Workspace::getNiceTile() const + { + if( !tilingMode() ) return NULL; + if( !tilingLayouts.value( activeClient()->desktop() ) ) return NULL; + + return tilingLayouts[ activeClient()->desktop() ]->findTile( activeClient() ); + // TODO + } + +void Workspace::updateAllTiles() + { + foreach( TilingLayout *t, tilingLayouts ) + { + if( !t ) continue; + t->commit(); + } + } + +/* + * Resize the neighbouring clients to close any gaps + */ +void Workspace::notifyWindowResize( Client *c, const QRect &moveResizeGeom, const QRect &orig ) + { + if( tilingLayouts.value( c->desktop() ) == NULL ) + return; + tilingLayouts[ c->desktop() ]->clientResized( c, moveResizeGeom, orig ); + } + +void Workspace::notifyWindowMove( Client *c, const QRect &moveResizeGeom, const QRect &orig ) + { + if( tilingLayouts.value( c->desktop() ) == NULL ) + { + c->setGeometry( moveResizeGeom ); + return; + } + tilingLayouts[ c->desktop() ]->clientMoved( c, moveResizeGeom, orig ); + updateAllTiles(); + } + +void Workspace::notifyWindowResizeDone( Client *c, const QRect &moveResizeGeom, const QRect &orig, bool canceled ) + { + if( canceled ) + notifyWindowResize( c, orig, moveResizeGeom ); + else + notifyWindowResize( c, moveResizeGeom, orig ); + } + +void Workspace::notifyWindowMoveDone( Client *c, const QRect &moveResizeGeom, const QRect &orig, bool canceled ) + { + if( canceled ) + notifyWindowMove( c, orig, moveResizeGeom ); + else + notifyWindowMove( c, moveResizeGeom, orig ); + } + +void Workspace::notifyWindowDesktopChanged( Client *c, int old_desktop ) + { + if( c->desktop() < 1 || c->desktop() > numberOfDesktops() ) + return; + + if( tilingLayouts.value( old_desktop ) ) + { + Tile *t = tilingLayouts[ old_desktop ]->findTile( c ); + + // TODO: copied from createTile(), move this into separate method? + if( !tilingLayouts.value( c->desktop() ) ) + { + tilingLayouts[c->desktop()] = TilingLayoutFactory::createLayout( TilingLayoutFactory::DefaultL, this ); + } + + if( t ) + tilingLayouts[ c->desktop() ]->addTile( t ); + + tilingLayouts[ old_desktop ]->removeTile( c ); + tilingLayouts[ old_desktop ]->commit(); + } + } + +// TODO: make this configurable +/* + * If a floating window was activated, raise all floating windows. + * If a tiled window was activated, lower all floating windows. + */ +void Workspace::notifyWindowActivated( Client *c ) + { + if( c == NULL ) + return; + + if( options->tilingRaisePolicy == 1 ) // individual raise/lowers + return; + + if( tilingLayouts.value( c->desktop() ) ) + { + QList tiles = tilingLayouts[ c->desktop() ]->tiles(); + + StackingUpdatesBlocker blocker( this ); + + Tile *tile_to_raise = tilingLayouts[ c->desktop() ]->findTile( c ); + + if( !tile_to_raise ) + { + return; + } + + kDebug(1212) << "FOUND TILE"; + bool raise_floating = false; + if( options->tilingRaisePolicy == 2 ) // floating always on top + raise_floating = true; + else + raise_floating = tile_to_raise->floating(); + + foreach( Tile *t, tiles ) + { + kDebug(1212) << t->client() << t->floating(); + if( t->floating() == raise_floating && t != tile_to_raise ) + raiseClient( t->client() ); + } + // raise the current tile last so that it ends up on top + // but only if it supposed to be raised, required to support tilingRaisePolicy + kDebug(1212) << "Raise floating? " << raise_floating << "to raise is floating?" << tile_to_raise->floating(); + if( tile_to_raise->floating() == raise_floating ) + raiseClient( tile_to_raise->client() ); + } + } + +void Workspace::notifyWindowMinimizeToggled( Client *c ) + { + if( tilingLayouts.value( c->desktop() ) ) + { + tilingLayouts[ c->desktop() ]->clientMinimizeToggled( c ); + } + } + +void Workspace::notifyWindowMaximized( Client *c, Options::WindowOperation op ) + { + if( tilingLayouts.value( c->desktop() ) ) + { + Tile *t = tilingLayouts[ c->desktop() ]->findTile( c ); + if( !t ) + { + createTile( c ); + t = tilingLayouts[ c->desktop() ]->findTile( c ); + + // if still no tile, it couldn't be tiled + // so ignore it + if( !t ) + return; + } + + // if window IS tiled and a maximize + // is attempted, make the window float. + // That is all we do since that can + // mess up the layout. + // In all other cases, don't do + // anything, let the user manage toggling + // using Meta+F + if ( !t->floating() + && ( op == Options::MaximizeOp + || op == Options::HMaximizeOp + || op == Options::VMaximizeOp ) ) + { + tilingLayouts[ c->desktop() ]->toggleFloatTile( c ); + } + + } + } + +Tile* Workspace::findAdjacentTile( Tile *ref, int d ) + { + QRect reference = ref->geometry(); + QPoint origin = reference.center(); + + Tile *closest = NULL; + int minDist = -1; + + QList tiles = tilingLayouts[ ref->client()->desktop() ]->tiles(); + + foreach( Tile *t, tiles ) + { + if( t->client() == ref->client() || t->ignoreGeometry() ) + continue; + + bool consider = false; + + QRect other = t->geometry(); + QPoint otherCenter = other.center(); + + switch( d ) + { + case Tile::Top: + consider = otherCenter.y() < origin.y() + && other.bottom() < reference.top(); + break; + + case Tile::Right: + consider = otherCenter.x() > origin.x() + && other.left() > reference.right(); + break; + + case Tile::Bottom: + consider = otherCenter.y() > origin.y() + && other.top() > reference.bottom(); + break; + + case Tile::Left: + consider = otherCenter.x() < origin.x() + && other.right() < reference.left(); + break; + + default: + abort(); + } + + if( consider ) + { + int dist = ( otherCenter - origin ).manhattanLength(); + if( minDist > dist || minDist < 0 ) + { + minDist = dist; + closest = t; + } + } + } + return closest; + } + +void Workspace::focusTile( int d ) + { + Tile *t = getNiceTile(); + if( t ) + { + Tile *adj = findAdjacentTile(t, d); + if( adj ) + activateClient( adj->client() ); + } + } + +void Workspace::moveTile( int d ) + { + Tile *t = getNiceTile(); + if( t ) + { + Tile* adj = findAdjacentTile( t, d ); + + tilingLayouts[ t->client()->desktop() ]->swapTiles( t, adj ); + } + } + +void Workspace::slotLeft() + { + focusTile( Tile::Left ); + } + +void Workspace::slotRight() + { + focusTile( Tile::Right ); + } + +void Workspace::slotTop() + { + focusTile( Tile::Top ); + } + +void Workspace::slotBottom() + { + focusTile( Tile::Bottom ); + } + +void Workspace::slotMoveLeft() + { + moveTile( Tile::Left ); + } + +void Workspace::slotMoveRight() + { + moveTile( Tile::Right ); + } + +void Workspace::slotMoveTop() + { + moveTile( Tile::Top ); + } + +void Workspace::slotMoveBottom() + { + moveTile( Tile::Bottom ); + } + +void Workspace::slotToggleFloating() + { + Client *c = activeClient(); + if( tilingLayouts.value( c->desktop() ) ) + { + tilingLayouts[ c->desktop() ]->toggleFloatTile( c ); + } + } + +void Workspace::slotNextTileLayout() + { + if( tilingLayouts.value( currentDesktop() ) ) + { + + tilingLayouts.replace( currentDesktop(), TilingLayoutFactory::nextLayout( tilingLayouts[currentDesktop()] ) ); + + tilingLayouts[currentDesktop()]->commit(); + } + } + +void Workspace::slotPreviousTileLayout() + { + if( tilingLayouts.value( currentDesktop() ) ) + { + + tilingLayouts.replace( currentDesktop(), TilingLayoutFactory::previousLayout( tilingLayouts[currentDesktop()] ) ); + + tilingLayouts[currentDesktop()]->commit(); + } + } + +KDecorationDefines::Position Workspace::supportedTilingResizeMode( Client *c, KDecorationDefines::Position currentMode ) + { + if( tilingLayouts.value( c->desktop() ) ) + { + return tilingLayouts[c->desktop()]->resizeMode( c, currentMode ); + } + return currentMode; + } + +void Workspace::dumpTiles() const + { + foreach( TilingLayout *t, tilingLayouts ) + { + if( !t ) { + kDebug(1212) << "EMPTY DESKTOP"; + continue; + } + kDebug(1212) << "Desktop" << tilingLayouts.indexOf( t ); + foreach( Tile *tile, t->tiles() ) + { + tile->dumpTile( "--" ); + } + } + } +} // namespace + diff --git a/tilinglayout.cpp b/tilinglayout.cpp new file mode 100644 index 0000000000..38b5035421 --- /dev/null +++ b/tilinglayout.cpp @@ -0,0 +1,218 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2009 Nikhil Marathe + +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 +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*********************************************************************/ + +#include "tilinglayout.h" + +#include + +#include "client.h" +#include "tile.h" +#include "workspace.h" + +namespace KWin +{ + +TilingLayout::TilingLayout( Workspace *w ) + : m_workspace( w ) + { + } + +TilingLayout::~TilingLayout() + { + } + +unsigned int TilingLayout::findTilePos( Client *c ) const + { + unsigned int i = 0; + foreach( Tile *t, m_tiles ) + { + if( t->client() == c ) + return i; + i++; + } + return -1; + } + +Tile* TilingLayout::findTile( Client *c ) const + { + unsigned int i = findTilePos( c ); + if( i != -1 ) + return m_tiles[ i ]; + return NULL; + } + +void TilingLayout::clientMinimizeToggled( Client *c ) + { + Tile *t = findTile( c ); + if( t ) + arrange( layoutArea( t ) ); + } + +void TilingLayout::clientResized( Client *c, const QRect &moveResizeGeom, const QRect &orig ) + { + if( moveResizeGeom == orig ) + return; + + Tile *t = findTile( c ); + if( !t || t->ignoreGeometry() ) + { + c->setGeometry( moveResizeGeom ); + return; + } + + t->setGeometry( moveResizeGeom ); + commit(); + } + +void TilingLayout::clientMoved( Client *c, const QRect &moveResizeGeom, const QRect &orig ) + { + if( moveResizeGeom == orig ) + return; + + Tile *t = findTile( c ); + if( !t ) + { + c->setGeometry( moveResizeGeom ); + return; + } + if( t->floating() ) + { + t->setGeometry( moveResizeGeom ); + t->commit(); + return; + } + + Tile *r = findTileBelowPoint( QCursor::pos() ); + // TODO: if the client moved in from another desktop, don't swap, add + if( r && t ) + { + swapTiles( r, t ); + } + } + +void TilingLayout::swapTiles( Tile *a, Tile *b ) + { + if( a && b ) + { + // t is the tile the user requested a move of + // r is the tile below it + int a_index = tiles().indexOf( a ); + int b_index = tiles().indexOf( b ); + + // use m_tiles since tiles() is const + // not sure how good an idea this is + m_tiles.replace( a_index, b ); + m_tiles.replace( b_index, a ); + arrange( layoutArea( a ) ); + } + } + +void TilingLayout::addTileNoArrange( Tile * t ) + { + m_tiles.append( t ); + postAddTile( t ); + } + +void TilingLayout::addTile( Tile *t ) + { + addTileNoArrange( t ); + arrange( layoutArea( t ) ); + } + +void TilingLayout::addTile( Client *c ) + { + } + +void TilingLayout::removeTileNoArrange( Tile * t ) + { + if( t == NULL ) + return; + preRemoveTile( t ); + m_tiles.removeOne( t ); + } + +const QRect TilingLayout::layoutArea( Tile *t ) const + { + return m_workspace->clientArea( PlacementArea, t->client() ); + } + +void TilingLayout::removeTile( Tile *t ) + { + if( t == NULL ) + return; + removeTileNoArrange( t ); + arrange( layoutArea( t ) ); + } + +void TilingLayout::removeTile( Client *c ) + { + removeTile( findTile( c ) ); + } + +void TilingLayout::toggleFloatTile( Client *c ) + { + Tile *t = findTile( c ); + if( t && t->floating() ) + t->unfloatTile(); + else if( t ) + t->floatTile(); + + if( t ) + arrange( layoutArea( t ) ); + } + +Tile* TilingLayout::findTileBelowPoint( const QPoint &p ) const + { + foreach( Tile *t, tiles() ) + { + if( t->floating() ) + continue; + if( t->geometry().contains( p ) ) + return t; + } + + return NULL; + } + +void TilingLayout::commit() + { + foreach( Tile *t, m_tiles ) + t->commit(); + } + +/* + * Default is to allow no resizing + */ +KDecorationDefines::Position TilingLayout::resizeMode( Client *c, KDecorationDefines::Position currentMode ) const + { + Tile *t = findTile( c ); + // if not tiled, allow resize + if( !t ) + return currentMode; + + if( t && t->floating() ) + return currentMode; + // We return PositionCenter since it makes + // no sense in resizing. + return KDecorationDefines::PositionCenter; + } + +} // end namespace + diff --git a/tilinglayout.h b/tilinglayout.h new file mode 100644 index 0000000000..48c988c016 --- /dev/null +++ b/tilinglayout.h @@ -0,0 +1,118 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2009 Nikhil Marathe + +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 +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*********************************************************************/ + +#ifndef KWIN_TILINGLAYOUT_H +#define KWIN_TILINGLAYOUT_H + +#include +#include + +#include "lib/kdecoration.h" + +namespace KWin +{ + +class Workspace; +class Client; +class Tile; + +class TilingLayout + { + public: + TilingLayout( Workspace *w ); + virtual ~TilingLayout(); + + virtual void clientResized( Client *c, const QRect &moveResizeGeom, const QRect &orig ); + void clientMoved( Client *c, const QRect &moveResizeGeom, const QRect &orig ); + void clientMinimizeToggled( Client *c ); + + void commit(); + + void setLayoutType( int t ); + int layoutType() const; + + void addTile( Tile *t ); + void addTile( Client *c ); + void removeTile( Tile *t ); + void removeTile( Client *c ); + void toggleFloatTile( Client *c ); + void swapTiles( Tile *a, Tile *b ); + + virtual KDecorationDefines::Position resizeMode( Client *c, KDecorationDefines::Position currentMode ) const; + + const QList& tiles() const; + Tile* findTile( Client *c ) const; + + protected: + Workspace * workspace() const; + const QRect layoutArea( Tile *t ) const; + // currently only required by floating layout + virtual void postAddTile( Tile *t ); + virtual void preRemoveTile( Tile *t ); + + private: + unsigned int findTilePos( Client *c ) const; + virtual void arrange( QRect wgeom ) = 0; + + void addTileNoArrange( Tile *t ); + void removeTileNoArrange( Tile *t ); + + Tile* findTileBelowPoint( const QPoint &p ) const; + + + QList m_tiles; + int m_layoutType; + Workspace *m_workspace; + + + friend class TilingLayoutFactory; + }; + +inline void TilingLayout::setLayoutType( int t ) + { + m_layoutType = t; + } + +inline int TilingLayout::layoutType() const + { + return m_layoutType; + } + +inline const QList& TilingLayout::tiles() const + { + return m_tiles; + } + +inline Workspace* TilingLayout::workspace() const + { + return m_workspace; + } + +inline void TilingLayout::postAddTile( Tile *t ) + { + } + +inline void TilingLayout::preRemoveTile( Tile *t ) + { + } + +} // end namespace + +#endif diff --git a/tilinglayoutfactory.cpp b/tilinglayoutfactory.cpp new file mode 100644 index 0000000000..a684ebe90c --- /dev/null +++ b/tilinglayoutfactory.cpp @@ -0,0 +1,126 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2009 Nikhil Marathe + +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 +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*********************************************************************/ + +#include "tilinglayoutfactory.h" + +#include +#include +#include +#include +#include + +#include "tile.h" +#include "client.h" + +#include "tilinglayouts/spiral/spiral.h" +#include "tilinglayouts/columns/columns.h" +#include "tilinglayouts/floating/floating.h" + +// w is the workspace pointer +#define ADD_LAYOUT( lay ) \ + case lay##L:\ + kDebug(1212) << #lay;\ + layout = new lay( w );\ + layout->setLayoutType( lay##L );\ + KNotification::event( "tilinglayoutchanged", \ + i18n( "Layout changed to %1" ).arg( #lay ),\ + QPixmap(), NULL, KNotification::CloseOnTimeout, KComponentData( "kwin" ) );\ + break + +namespace KWin +{ + +TilingLayout* TilingLayoutFactory::createLayout( int type, Workspace *w ) + { + Q_ASSERT( type != First && type != Last ); + TilingLayout *layout; + + /* For new layouts, make a case entry here */ + switch( type ) + { + case DefaultL: // NOTE: fall through makes first layout default + layout = createLayout( indexToLayoutIndex( options->tilingLayout ), w ); + break; + + ADD_LAYOUT( Spiral ); + ADD_LAYOUT( Columns ); + ADD_LAYOUT( Floating ); + + default: + kDebug(1212) << "INVALID LAYOUT!"; + return NULL; + } + return layout; + } + +// if next, goes next, otherwise previous +TilingLayout* TilingLayoutFactory::cycleLayout( TilingLayout *curr, bool next ) + { + int type = curr->layoutType(); + + if( next ) + { + type++; + + if( type >= Last ) + type = First + 1; + } + else + { + type--; + + if( type <= First ) + type = Last - 1; + } + + QList tiles = curr->tiles(); //root->flatten(); + + TilingLayout *l = createLayout( type, curr->workspace() ); + + foreach( Tile *t, tiles ) + { + curr->removeTileNoArrange( t ); + } + + Tile *last = tiles.takeLast(); + foreach( Tile *t, tiles ) + { + l->addTileNoArrange( t ); + } + l->addTile( last ); + + return l; + } + +/** + * Returns the appropriate layout enum item + * Meant to be used with a combo box. + * This function handles the issues of DefaultL and First and Last layouts + */ +int TilingLayoutFactory::indexToLayoutIndex( int index ) + { + int layout = DefaultL + index + 1; + if( layout >= Last ) + layout = DefaultL + 1; + if( layout <= First ) + layout = Last - 1; + return layout; + } +} // end namespace diff --git a/tilinglayoutfactory.h b/tilinglayoutfactory.h new file mode 100644 index 0000000000..c4a8b8376a --- /dev/null +++ b/tilinglayoutfactory.h @@ -0,0 +1,71 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2009 Nikhil Marathe + +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 +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*********************************************************************/ + +#ifndef KWIN_TILINGLAYOUTFACTORY_H +#define KWIN_TILINGLAYOUTFACTORY_H + +namespace KWin +{ + +class Workspace; +class TilingLayout; +class Tile; +class TilingLayoutFactory + { + public: + /** When adding your own layout, edit this + * Remember to suffix an L for now + */ + enum Layouts { + First, // special, do not modify/move + DefaultL, + + /* Actual layouts */ + SpiralL, + ColumnsL, + FloatingL, + /* Put your layout above this line ^^^ */ + + Last // special, do not modify/move + }; + + static TilingLayout* createLayout( int type, Workspace * ); + static TilingLayout* nextLayout( TilingLayout *curr ); + static TilingLayout* previousLayout( TilingLayout *curr ); + + static int indexToLayoutIndex( int index ); + + private: + static TilingLayout* cycleLayout( TilingLayout *curr, bool next ); + + }; + +inline TilingLayout* TilingLayoutFactory::nextLayout( TilingLayout *curr ) + { + return cycleLayout( curr, true ); + } + +inline TilingLayout* TilingLayoutFactory::previousLayout( TilingLayout *curr ) + { + return cycleLayout( curr, false ); + } +} // end namespace + +#endif diff --git a/tilinglayouts/columns/columns.cpp b/tilinglayouts/columns/columns.cpp new file mode 100644 index 0000000000..c6481f912d --- /dev/null +++ b/tilinglayouts/columns/columns.cpp @@ -0,0 +1,162 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2009 Nikhil Marathe + +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 +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*********************************************************************/ + +#include "columns.h" + +#include "client.h" +#include "tile.h" + +#include "lib/kdecoration.h" + +namespace KWin +{ + +// TODO: caching of actually tiled windows +// Columns is doing a lot of looping +// checking which tiles are actually *tiled* +// ( ie. not floating or minimized ) +// This can probably be moved to TilingLayout +// and cached. But remember to preserve order! + +Columns::Columns( Workspace *w ) + : TilingLayout( w ) + , m_leftWidth( 0 ) + { + } + +KDecorationDefines::Position Columns::resizeMode( Client *c, KDecorationDefines::Position currentMode ) const + { + Tile *t = findTile( c ); + + if( !t ) + return currentMode; + + if( t && t->floating() ) + return currentMode; + + QList tiled( tiles() ); + + QMutableListIterator i(tiled); + while( i.hasNext() ) + { + Tile *tile = i.next(); + if( tile->ignoreGeometry() ) + i.remove(); + } + + if( tiled.first() == t + && ( currentMode == KDecorationDefines::PositionRight + || currentMode == KDecorationDefines::PositionTopRight + || currentMode == KDecorationDefines::PositionBottomRight ) ) + { + return KDecorationDefines::PositionRight; + } + + // in right column so only left resize allowed + if( tiled.contains( t ) + && ( tiled.first() != t ) + && ( currentMode == KDecorationDefines::PositionLeft + || currentMode == KDecorationDefines::PositionTopLeft + || currentMode == KDecorationDefines::PositionBottomLeft ) ) + { + return KDecorationDefines::PositionLeft; + } + + return KDecorationDefines::PositionCenter; + } + +void Columns::clientResized( Client *c, const QRect &moveResizeGeom, const QRect &orig ) + { + TilingLayout::clientResized( c, moveResizeGeom, orig ); + + Tile *t = findTile( c ); + + QList tiled( tiles() ); + + QMutableListIterator i(tiled); + while( i.hasNext() ) + { + Tile *tile = i.next(); + if( tile->ignoreGeometry() ) + i.remove(); + } + + if( tiled.first() == t ) + { + m_leftWidth = moveResizeGeom.width(); + } + else + { + m_leftWidth = layoutArea( t ).width() - moveResizeGeom.width(); + } + + arrange( layoutArea( t ) ); + } + +void Columns::arrange( QRect wgeom ) + { + QList tiled( tiles() ); + + QMutableListIterator i(tiled); + while( i.hasNext() ) + { + Tile *t = i.next(); + if( t->ignoreGeometry() ) + i.remove(); + } + + int n = tiled.length(); + if( n < 1 ) + return; + if( n == 1 ) + { + tiled.first()->setGeometry( wgeom ); + tiled.first()->commit(); + return; + } + + // save the original before we mangle it + int totalWidth = wgeom.width(); + if( m_leftWidth == 0 ) + m_leftWidth = wgeom.width() / 2; + + if( n > 1 ) + wgeom.setWidth( m_leftWidth ); + tiled.first()->setGeometry( wgeom ); + tiled.first()->commit(); + + wgeom.moveLeft( wgeom.x() + m_leftWidth ); + wgeom.setWidth( totalWidth - m_leftWidth ); + int ht = wgeom.height()/(n-1); + wgeom.setHeight( ht ); + + int mult = 0; + int originalTop = wgeom.y(); + for( QList::const_iterator it = ++tiled.begin() ; it != tiled.end() ; it++ ) + { + if( (*it)->floating() ) + continue; + (*it)->setGeometry( wgeom ); + (*it)->commit(); + mult++; + wgeom.moveTop( originalTop + mult*ht ); + } + } +} diff --git a/tilinglayouts/columns/columns.h b/tilinglayouts/columns/columns.h new file mode 100644 index 0000000000..33ab808c74 --- /dev/null +++ b/tilinglayouts/columns/columns.h @@ -0,0 +1,49 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2009 Nikhil Marathe + +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 +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*********************************************************************/ + +#ifndef COLUMNS_H +#define COLUMNS_H + +#include + +#include "tilinglayout.h" + +namespace KWin +{ +class Workspace; +class Tile; +class Client; + +// simulates a 2 column right layout for now, make it arbitrary +// in columns and direction +class Columns : public TilingLayout + { + public: + Columns( Workspace * ); + KDecorationDefines::Position resizeMode( Client *c, KDecorationDefines::Position currentMode ) const; + void clientResized( Client *c, const QRect &moveResizeGeom, const QRect &orig ); + + private: + void arrange( QRect wgeom ); + int m_leftWidth; // width of left column + }; +} // end namespace + +#endif diff --git a/tilinglayouts/floating/floating.cpp b/tilinglayouts/floating/floating.cpp new file mode 100644 index 0000000000..0428d6f1a8 --- /dev/null +++ b/tilinglayouts/floating/floating.cpp @@ -0,0 +1,61 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2009 Nikhil Marathe + +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 +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*********************************************************************/ + +#include "floating.h" + +#include "tile.h" +#include "client.h" + +namespace KWin +{ + +Floating::Floating( Workspace *w ) + : TilingLayout( w ) + { + } + +void Floating::postAddTile( Tile *t ) + { + if( t->floating() ) + was_floating.insert( t ); + } + +void Floating::arrange( QRect wgeom ) + { + foreach( Tile *t, tiles() ) + { + if( !t->floating() ) + t->floatTile(); + workspace()->place( t->client(), wgeom ); + t->setGeometry( t->client()->geometry() ); + } + } + +void Floating::preRemoveTile( Tile *t ) + { + if( ! was_floating.contains( t ) ) + t->unfloatTile(); + } + +Floating::~Floating() + { + } + +} diff --git a/tilinglayouts/floating/floating.h b/tilinglayouts/floating/floating.h new file mode 100644 index 0000000000..5481b1af80 --- /dev/null +++ b/tilinglayouts/floating/floating.h @@ -0,0 +1,59 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2009 Nikhil Marathe + +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 +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*********************************************************************/ + +#ifndef FLOATING_H +#define FLOATING_H + +#include +#include + +#include "tilinglayout.h" +#include "tile.h" + +namespace KWin +{ +class Workspace; +class Floating : public TilingLayout + { + public: + Floating( Workspace * ); + ~Floating(); + + private: + void arrange( QRect wgeom ); + void postAddTile( Tile *t ); + void preRemoveTile( Tile *t ); + + Tile::Direction m_dir; + Tile *m_split; + + /* + * Tiles are added to was_floating if they + * were floating before being added to this layout + * + * When the layout is changed, was_floating is + * referred to, to restore the final state of the + * Tile + */ + QSet was_floating; + }; +} // end namespace + +#endif diff --git a/tilinglayouts/spiral/spiral.cpp b/tilinglayouts/spiral/spiral.cpp new file mode 100644 index 0000000000..d7596cb8f2 --- /dev/null +++ b/tilinglayouts/spiral/spiral.cpp @@ -0,0 +1,82 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2009 Nikhil Marathe + +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 +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*********************************************************************/ + +#include "spiral.h" + +#include "client.h" +#include "tile.h" + +namespace KWin +{ + +Spiral::Spiral( Workspace *w ) + : TilingLayout( w ) + { + } + +Spiral::~Spiral() + { + } + +void Spiral::arrange( QRect wgeom ) + { + QList tiled( tiles() ); + + QMutableListIterator it(tiled); + while( it.hasNext() ) + { + Tile *t = it.next(); + if( t->ignoreGeometry() ) + it.remove(); + } + + int n = tiled.length(); + int i = 1; + + foreach( Tile *t, tiled ) + { + if( t->floating() ) + continue; + + if( i < n ) + { + if( i % 2 == 0 ) + wgeom.setHeight( wgeom.height() / 2 ); + else + wgeom.setWidth( wgeom.width() / 2 ); + } + + if( i % 4 == 0 ) + wgeom.moveLeft( wgeom.x() - wgeom.width() ); + else if( i % 2 == 0 || ( i % 4 == 3 && i < n ) ) + wgeom.moveLeft( wgeom.x() + wgeom.width() ); + + if( i % 4 == 1 && i != 1 ) + wgeom.moveTop( wgeom.y() - wgeom.height() ); + else if( i % 2 == 1 && i != 1 + || ( i % 4 == 0 && i < n ) ) + wgeom.moveTop( wgeom.y() + wgeom.height() ); + + t->setGeometry( wgeom ); + t->commit(); + i++; + } + } +} diff --git a/tilinglayouts/spiral/spiral.h b/tilinglayouts/spiral/spiral.h new file mode 100644 index 0000000000..01d5e4214b --- /dev/null +++ b/tilinglayouts/spiral/spiral.h @@ -0,0 +1,49 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2009 Nikhil Marathe + +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 +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*********************************************************************/ + +#ifndef SPIRAL_H +#define SPIRAL_H + +#include + +#include "tilinglayout.h" + +namespace KWin +{ +class Workspace; +class Tile; +class Client; + +class Spiral : public TilingLayout + { + public: + Spiral( Workspace * ); + ~Spiral(); + + void addTile( Tile *t ); + void removeTile( Tile *t ); + + private: + void arrange( QRect wgeom ); + + }; +} // end namespace + +#endif diff --git a/useractions.cpp b/useractions.cpp index 5f71163712..c98b9d4773 100644 --- a/useractions.cpp +++ b/useractions.cpp @@ -668,6 +668,15 @@ void Workspace::performWindowOperation( Client* c, Options::WindowOperation op ) if ( !c ) return; + if( tilingMode() + && ( op == Options::MaximizeOp + || op == Options::HMaximizeOp + || op == Options::VMaximizeOp + || op == Options::RestoreOp ) ) + { + notifyWindowMaximized( c, op ); + } + if (op == Options::MoveOp || op == Options::UnrestrictedMoveOp ) QCursor::setPos( c->geometry().center() ); if (op == Options::ResizeOp || op == Options::UnrestrictedResizeOp ) diff --git a/workspace.cpp b/workspace.cpp index 57a4668d8d..dfcd65c337 100644 --- a/workspace.cpp +++ b/workspace.cpp @@ -44,6 +44,7 @@ along with this program. If not, see . #include #include "client.h" +#include "tile.h" #include "tabbox.h" #include "desktopchangeosd.h" #include "atoms.h" @@ -56,6 +57,7 @@ along with this program. If not, see . #include "scene.h" #include "deleted.h" #include "effects.h" +#include "tilinglayout.h" #include #include @@ -151,6 +153,7 @@ Workspace::Workspace( bool restore ) , transSlider( NULL ) , transButton( NULL ) , forceUnredirectCheck( true ) + , tilingMode_( false ) { (void) new KWinAdaptor( this ); @@ -469,6 +472,9 @@ void Workspace::init() outline_bottom = XCreateWindow( QX11Info::display(), QX11Info::appRootWindow(), 0, 0, 1, 1, 0, CopyFromParent, CopyFromParent, CopyFromParent, CWOverrideRedirect, &attr ); + // Enable/disable tiling + setTilingMode( options->tilingOn ); + // SELI TODO: This won't work with unreasonable focus policies, // and maybe in rare cases also if the selected client doesn't // want focus @@ -550,6 +556,11 @@ Client* Workspace::createClient( Window w, bool is_mapped ) return NULL; } addClient( c, Allowed ); + + tilingLayouts.resize( numberOfDesktops() + 1 ); + + createTile( c ); + if( scene ) scene->windowAdded( c ); if( effects ) @@ -649,6 +660,11 @@ void Workspace::removeClient( Client* c, allowed_t ) tab_box->nextPrev( true ); Q_ASSERT( clients.contains( c ) || desktops.contains( c )); + if( tilingMode() && tilingLayouts.value(c->desktop()) ) + { + removeTile( c ); + } + // TODO: if marked client is removed, notify the marked list clients.removeAll( c ); desktops.removeAll( c ); unconstrained_stacking_order.removeAll( c ); @@ -1134,6 +1150,10 @@ void Workspace::slotReconfigure() (*it)->applyWindowRules(); discardUsedWindowRules( *it, false ); } + + setTilingMode( options->tilingOn ); + // just so that we reset windows in the right manner, 'activate' the current active window + notifyWindowActivated( activeClient() ); rootInfo->setSupported( NET::WM2FrameOverlap, mgr->factory()->supports( AbilityExtendIntoClientArea ) ); } @@ -1398,7 +1418,14 @@ bool Workspace::setCurrentDesktop( int new_desktop ) rootInfo->setCurrentDesktop( currentDesktop() ); if( movingClient && !movingClient->isOnDesktop( new_desktop )) + { + int old_desktop = movingClient->desktop(); movingClient->setDesktop( new_desktop ); + if( tilingMode() ) + { + notifyWindowDesktopChanged( movingClient, old_desktop ); + } + } for( int i = stacking_order.size() - 1; i >= 0 ; --i ) if( stacking_order.at( i )->isOnDesktop( new_desktop )) @@ -1532,6 +1559,7 @@ void Workspace::setNumberOfDesktops( int n ) ++it) if( !(*it)->isOnAllDesktops() && (*it)->desktop() > numberOfDesktops() ) sendClientToDesktop( *it, numberOfDesktops(), true ); + // TODO: Tile should have a method allClients, push them into other tiles } if( old_number_of_desktops > numberOfDesktops() ) { @@ -1550,6 +1578,8 @@ void Workspace::setNumberOfDesktops( int n ) for( int i = 0; i < int( desktop_focus_chain.size() ); i++ ) desktop_focus_chain[i] = i+1; + tilingLayouts.resize( numberOfDesktops() + 1 ); + // reset the desktop change osd desktop_change_osd->numberDesktopsChanged(); // inform effects @@ -1564,6 +1594,7 @@ void Workspace::setNumberOfDesktops( int n ) */ void Workspace::sendClientToDesktop( Client* c, int desk, bool dont_activate ) { + int old_desktop = c->desktop(); bool was_on_desktop = c->isOnDesktop( desk ) || c->isOnAllDesktops(); c->setDesktop( desk ); if( c->desktop() != desk ) // No change or desktop forced @@ -1582,6 +1613,8 @@ void Workspace::sendClientToDesktop( Client* c, int desk, bool dont_activate ) else raiseClient( c ); + notifyWindowDesktopChanged( c, old_desktop ); + ClientList transients_stacking_order = ensureStackingOrder( c->transients() ); for( ClientList::ConstIterator it = transients_stacking_order.constBegin(); it != transients_stacking_order.constEnd(); diff --git a/workspace.h b/workspace.h index 80b53d21d0..368443b079 100644 --- a/workspace.h +++ b/workspace.h @@ -61,6 +61,8 @@ class TabBox; } class Client; +class Tile; +class TilingLayout; class ClientGroup; class DesktopChangeOSD; class RootInfo; @@ -169,6 +171,23 @@ class Workspace : public QObject, public KDecorationDefines void reserveElectricBorderActions( bool reserve ); void reserveElectricBorderSwitching( bool reserve ); + //------------------------------------------------- + // Tiling + public: + bool tilingMode() const; + void setTilingMode( bool tiling ); + void updateAllTiles(); + void notifyWindowResize( Client *c, const QRect &moveResizeGeom, const QRect &orig ); + void notifyWindowMove( Client *c, const QRect &moveResizeGeom, const QRect &orig ); + void notifyWindowResizeDone( Client *c, const QRect &moveResizeGeom, const QRect &orig, bool canceled ); + void notifyWindowMoveDone( Client *c, const QRect &moveResizeGeom, const QRect &orig, bool canceled ); + void notifyWindowDesktopChanged( Client *c, int old_desktop ); + void notifyWindowActivated( Client *c ); + void notifyWindowMinimizeToggled( Client *c ); + void notifyWindowMaximized( Client *c, WindowOperation op ); + + Position supportedTilingResizeMode( Client *c, Position currentMode ); + //------------------------------------------------- // Desktop layout @@ -286,6 +305,9 @@ class Workspace : public QObject, public KDecorationDefines int currentDesktop_; bool desktopLayoutDynamicity_; + bool tilingMode_; + QVector tilingLayouts; + //------------------------------------------------- // Unsorted @@ -396,6 +418,9 @@ class Workspace : public QObject, public KDecorationDefines void circulateDesktopApplications(); bool compositingActive(); bool waitForCompositingSetup(); + void toggleTiling(); + void nextTileLayout(); + void previousTileLayout(); void setCurrentScreen( int new_screen ); @@ -652,6 +677,24 @@ class Workspace : public QObject, public KDecorationDefines void suspendCompositing(); void suspendCompositing( bool suspend ); + void slotToggleTiling(); + void slotToggleFloating(); + void slotNextTileLayout(); + void slotPreviousTileLayout(); + + void slotLeft(); + void slotRight(); + void slotTop(); + void slotBottom(); + void slotMoveLeft(); + void slotMoveRight(); + void slotMoveTop(); + void slotMoveBottom(); + void belowCursor(); + + // NOTE: debug method + void dumpTiles() const; + void slotSwitchToTabLeft(); // Slot to move left the active Client. void slotSwitchToTabRight(); // Slot to move right the active Client. void slotRemoveFromGroup(); // Slot to remove the active client from its group. @@ -799,6 +842,16 @@ class Workspace : public QObject, public KDecorationDefines static NET::WindowType txtToWindowType( const char* txt ); static bool sessionInfoWindowTypeMatch( Client* c, SessionInfo* info ); + Tile* getNiceTile() const; + void createTile( Client *c ); + void removeTile( Client *c ); + bool tileable( Client *c ); + // int, and not Tile::Direction because + // we are using a forward declaration for Tile + Tile* findAdjacentTile( Tile *ref, int d ); + void focusTile( int d ); + void moveTile( int d ); + Client* active_client; Client* last_active_client; Client* most_recently_raised; // Used ONLY by raiseOrLowerClient() @@ -1276,6 +1329,30 @@ inline void Workspace::removeClientGroup( ClientGroup* group ) clientGroups.removeAll( group ); } +/* + * Called from D-BUS + */ +inline void Workspace::toggleTiling() + { + slotToggleTiling(); + } + +/* + * Called from D-BUS + */ +inline void Workspace::nextTileLayout() + { + slotNextTileLayout(); + } + +/* + * Called from D-BUS + */ +inline void Workspace::previousTileLayout() + { + slotPreviousTileLayout(); + } + } // namespace #endif