From 72abf668d2565abd389ae7231cf8ecdedef872de Mon Sep 17 00:00:00 2001 From: Matthias Ettrich Date: Fri, 23 Jun 2000 16:26:44 +0000 Subject: [PATCH] iconify/deiconify animation. Don't forget to update libkdecore and kicker svn path=/trunk/kdebase/kwin/; revision=54131 --- client.cpp | 165 ++++++++++++++++++++++++++++++++++++++++++-------- client.h | 11 ++-- main.cpp | 20 ++++++ workspace.cpp | 52 ++++++---------- 4 files changed, 184 insertions(+), 64 deletions(-) diff --git a/client.cpp b/client.cpp index 186d831390..9d65be1f79 100644 --- a/client.cpp +++ b/client.cpp @@ -16,6 +16,7 @@ Copyright (C) 1999, 2000 Matthias Ettrich #include #include #include +#include #include #include #include @@ -50,27 +51,25 @@ public: virtual void changeDesktop(Q_UINT32 desktop) { m_client->setDesktop( desktop ); } - virtual void changeState(Q_UINT32 state, Q_UINT32 /* mask */) { - // What's the mask ? - // Warning: this code was written by David who has no clue about window managers :/ + virtual void changeState(Q_UINT32 state, Q_UINT32 mask ) { + // state : kwin.h says: possible values are or'ed combinations of NET::Modal, + // NET::Sticky, NET::MaxVert, NET::MaxHoriz, NET::Shaded, NET::SkipTaskbar - // state : kwin.h says: possible values are or'ed combinations of NET::Modal, - // NET::Sticky, NET::MaxVert, NET::MaxHoriz, NET::Shaded, NET::SkipTaskbar - - m_client->setSticky( state & NET::Sticky ); - m_client->setShade( state & NET::Shaded ); - - if ( state & NET::MaxVert ) - if ( state & NET::MaxHoriz ) - m_client->maximize( Client::MaximizeFull ); - else - m_client->maximize( Client::MaximizeVertical ); - else if ( state & NET::MaxHoriz ) - m_client->maximize( Client::MaximizeHorizontal ); - - // if ( state & NET::Modal ) ??? - // if ( state & NET::SkipTaskbar ) ??? + state &= mask; // for safety, clear all other bits + + if ( mask & NET::Sticky ) + m_client->setSticky( state & NET::Sticky ); + if ( mask & NET::Shaded ) + m_client->setShade( state & NET::Shaded ); + if ( mask & NET::Max ) { + if ( state & NET::Max == NET::Max ) + m_client->maximize( Client::MaximizeFull ); + else if ( state & NET::MaxVert ) + m_client->maximize( Client::MaximizeVertical ); + else if ( state & NET::MaxHoriz ) + m_client->maximize( Client::MaximizeHorizontal ); + } } private: ::Client * m_client; @@ -376,7 +375,8 @@ Client::Client( Workspace *ws, WId w, QWidget *parent, const char *name, WFlags NET::WMState | NET::WMWindowType | NET::WMStrut | - NET::WMName + NET::WMName | + NET::WMIconGeometry ; info = new WinInfo( this, qt_xdisplay(), win, qt_xrootwin(), properties ); @@ -1209,6 +1209,8 @@ void Client::move( int x, int y ) */ void Client::showEvent( QShowEvent* ) { + if ( isIconified() && !isTransient() ) + animateIconifyOrDeiconify( FALSE ); setMappingState( NormalState ); } @@ -1324,6 +1326,10 @@ void Client::invalidateWindow() } + +/*! + Iconifies this client plus its transients + */ void Client::iconify() { if (!isMovable()) @@ -1339,11 +1345,19 @@ void Client::iconify() } Events::raise( Events::Iconify ); setMappingState( IconicState ); + + if ( !isTransient() ) + animateIconifyOrDeiconify( TRUE ); hide(); - // TODO animation (virtual function) + workspace()->iconifyOrDeiconifyTransientsOf( this ); } + +/*! + Closes the window by either sending a delete_window message or + using XKill. + */ void Client::closeWindow() { Events::raise( Events::Close ); @@ -1358,6 +1372,10 @@ void Client::closeWindow() } } + +/*! + Kills the window via XKill + */ void Client::killWindow() { // not sure if we need an Events::Kill or not.. until then, use @@ -1511,14 +1529,14 @@ void Client::gravitate( bool invert ) /*! Reimplement to handle crossing events (qt should provide xroot, yroot) - + Crossing events are necessary for the focus-follows-mouse focus policies, to do proper activation and deactivation. */ bool Client::x11Event( XEvent * e) { if ( e->type == EnterNotify && e->xcrossing.mode == NotifyNormal ) { - if ( options->focusPolicy == Options::ClickToFocus ) + if ( options->focusPolicy == Options::ClickToFocus ) return TRUE; if ( options->focusPolicy != Options::FocusStrictlyUnderMouse && ( isDesktop() || isDock() ) ) @@ -2107,9 +2125,9 @@ bool Client::isDock() const } -/*! +/*! Returns \a area with the client's strut taken into account. - + Used from Workspace in updateClientArea. */ QRect Client::adjustedClientArea( const QRect& area ) const @@ -2128,6 +2146,103 @@ QRect Client::adjustedClientArea( const QRect& area ) const } +void Client::animateIconifyOrDeiconify( bool iconify) +{ + // the function is a bit tricky since it will ensure that an + // animation action needs always the same time regardless of the + // performance of the machine or the X-Server. + + float lf,rf,tf,bf,step; + + int options_dot_ResizeAnimation = 1; + + step = 40. * (11 - options_dot_ResizeAnimation); + + NETRect r = info->iconGeometry(); + QRect icongeom( r.pos.x, r.pos.y, r.size.width, r.size.height ); + if ( !icongeom.isValid() ) + return; + + QPixmap pm = animationPixmap( iconify ? width() : icongeom.width() ); + + QRect before, after; + if ( iconify ) { + before = QRect( x(), y(), width(), pm.height() ); + after = QRect( icongeom.x(), icongeom.y(), icongeom.width(), pm.height() ); + } else { + before = QRect( icongeom.x(), icongeom.y(), icongeom.width(), pm.height() ); + after = QRect( x(), y(), width(), pm.height() ); + } + + lf = (after.left() - before.left())/step; + rf = (after.right() - before.right())/step; + tf = (after.top() - before.top())/step; + bf = (after.bottom() - before.bottom())/step; + + + XGrabServer( qt_xdisplay() ); + + QRect area = before; + QRect area2; + QPixmap pm2; + + QTime t; + t.start(); + float diff; + + QPainter p ( workspace()->desktopWidget() ); + bool need_to_clear = FALSE; + QPixmap pm3; + do { + if (area2 != area){ + pm = animationPixmap( area.width() ); + pm2 = QPixmap::grabWindow( qt_xrootwin(), area.x(), area.y(), area.width(), area.height() ); + p.drawPixmap( area.x(), area.y(), pm ); + if ( need_to_clear ) { + p.drawPixmap( area2.x(), area2.y(), pm3 ); + need_to_clear = FALSE; + } + area2 = area; + } + XFlush(qt_xdisplay()); + XSync( qt_xdisplay(), FALSE ); + diff = t.elapsed(); + if (diff > step) + diff = step; + area.setLeft(before.left() + int(diff*lf)); + area.setRight(before.right() + int(diff*rf)); + area.setTop(before.top() + int(diff*tf)); + area.setBottom(before.bottom() + int(diff*bf)); + if (area2 != area ) { + if ( area2.intersects( area ) ) + p.drawPixmap( area2.x(), area2.y(), pm2 ); + else { // no overlap, we can clear later to avoid flicker + pm3 = pm2; + need_to_clear = TRUE; + } + } + } while ( t.elapsed() < step); + if (area2 == area || need_to_clear ) + p.drawPixmap( area2.x(), area2.y(), pm2 ); + + p.end(); + XUngrabServer( qt_xdisplay() ); +} + +QPixmap Client::animationPixmap( int w ) +{ + QFont font = options->font(isActive()); + QFontMetrics fm( font ); + QPixmap pm( w, fm.lineSpacing() ); + pm.fill( options->color(Options::TitleBar, isActive() || isIconified() ) ); + QPainter p( &pm ); + p.setPen(options->color(Options::Font, isActive() || isIconified() )); + p.setFont(options->font(isActive())); + p.drawText( pm.rect(), AlignLeft|AlignVCenter|SingleLine, caption() ); + return pm; +} + + NoBorderClient::NoBorderClient( Workspace *ws, WId w, QWidget *parent, const char *name ) : Client( ws, w, parent, name ) { diff --git a/client.h b/client.h index 4fa85c4b6b..1bed5a206b 100644 --- a/client.h +++ b/client.h @@ -117,7 +117,7 @@ public: bool isSticky() const; void setSticky( bool ); - + // auxiliary functions, depend on the windowType bool wantsTabFocus() const; bool isMovable() const; @@ -145,8 +145,6 @@ public: void move( const QPoint & p ) { move( p.x(), p.y() ); } - - bool providesContextHelp() const; bool performMouseCommand( Options::MouseCommand, QPoint globalPos ); @@ -155,7 +153,7 @@ public: QCString sessionId(); QRect adjustedClientArea( const QRect& area ) const; - + public slots: void iconify(); void closeWindow(); @@ -200,6 +198,11 @@ protected: virtual MousePosition mousePosition( const QPoint& ) const; virtual void setMouseCursor( MousePosition m ); + + virtual void animateIconifyOrDeiconify( bool iconify ); + virtual QPixmap animationPixmap( int w ); + + // handlers for X11 events bool mapRequest( XMapRequestEvent& e ); bool unmapNotify( XUnmapEvent& e ); diff --git a/main.cpp b/main.cpp index d0f346d855..ff402cc047 100644 --- a/main.cpp +++ b/main.cpp @@ -79,6 +79,26 @@ int x11ErrorHandler(Display *d, XErrorEvent *e){ return 0; } +/*! + Updates kwin_time by receiving a current timestamp from the server. + + Use this function only when really necessary. Keep in mind that it's + a roundtrip to the X-Server. + */ +void kwin_updateTime() +{ + static QWidget* w = 0; + if ( !w ) + w = new QWidget; + long data = 1; + XChangeProperty(qt_xdisplay(), w->winId(), atoms->kwin_running, atoms->kwin_running, 32, + PropModeAppend, (unsigned char*) &data, 1); + XEvent ev; + XWindowEvent( qt_xdisplay(), w->winId(), PropertyChangeMask, &ev ); + kwin_time = ev.xproperty.time; +} + + Application::Application( ) : KApplication( ) { diff --git a/workspace.cpp b/workspace.cpp index 09c3f65d14..99a3919a23 100644 --- a/workspace.cpp +++ b/workspace.cpp @@ -78,6 +78,7 @@ private: extern Time kwin_time; +extern void kwin_updateTime(); // used to store the return values of // XShapeQueryExtension. @@ -136,31 +137,12 @@ bool Motif::noBorder( WId w ) + /*! - Updates kwin_time by receiving a current timestamp from the server. - - Use this function only when really necessary. Keep in mind that it's - a roundtrip to the X-Server. - */ -static void updateTime() -{ - static QWidget* w = 0; - if ( !w ) - w = new QWidget; - long data = 1; - XChangeProperty(qt_xdisplay(), w->winId(), atoms->kwin_running, atoms->kwin_running, 32, - PropModeAppend, (unsigned char*) &data, 1); - XEvent ev; - XWindowEvent( qt_xdisplay(), w->winId(), PropertyChangeMask, &ev ); - kwin_time = ev.xproperty.time; -} - - -/*! Creates a new client for window \a w, depending on certain hints (like Motif hints and the NET_WM_TYPE. - - Shaped windows always get a NoBorderClient. + + Shaped windows always get a NoBorderClient. */ Client* Workspace::clientFactory( WId w ) { @@ -454,7 +436,7 @@ bool Workspace::workspaceEvent( XEvent * e ) return TRUE; return destroyClient( findClient( e->xdestroywindow.window ) ); case MapRequest: - updateTime(); + kwin_updateTime(); c = findClient( e->xmaprequest.window ); if ( !c ) { if ( e->xmaprequest.parent == root ) { @@ -859,7 +841,7 @@ Client* Workspace::previousStaticClient( Client* c ) const } -/*! +/*! Returns topmost visible client. Windows on the dock and the desktop are excluded. */ @@ -1434,7 +1416,7 @@ void Workspace::cascadeDesktop() } } -/*! +/*! Unclutters the current desktop by smart-placing all clients again. */ @@ -1711,7 +1693,7 @@ void Workspace::setCurrentDesktop( int new_desktop ){ -/*! +/*! Returns the workspace's desktop widget. The desktop widget is sometimes required by clients to draw on it, for example outlines on moving or resizing. @@ -2424,7 +2406,7 @@ void Workspace::slotResetAllClients() /*! Stores the current session in the config file - + \sa loadSessionInfo() */ void Workspace::storeSession( KConfig* config ) @@ -2455,7 +2437,7 @@ void Workspace::storeSession( KConfig* config ) /*! Loads the session information from the config file. - + \sa storeSession() */ void Workspace::loadSessionInfo() @@ -2481,10 +2463,10 @@ void Workspace::loadSessionInfo() } -/*! +/*! Returns the SessionInfo for client \a c. The returned session info is removed from the storage. It's up to the caller to delete it. - + May return 0 if there's no session info for the client. */ SessionInfo* Workspace::takeSessionInfo( Client* c ) @@ -2508,13 +2490,13 @@ SessionInfo* Workspace::takeSessionInfo( Client* c ) /*! Updates the current client area according to the current clients. - + If the area changes, the new area is propagate to the world. The client area is the area that is available for clients (that which is not taken by windows like panels, the top-of-screen menu etc). - + \sa clientArea() */ void Workspace::updateClientArea() @@ -2539,11 +2521,11 @@ void Workspace::updateClientArea() } -/*! +/*! returns the area available for clients. This is the desktop geometry minus windows on the dock. Placement algorithms should - refer to this rather than geometry(). - + refer to this rather than geometry(). + \sa geometry() */ QRect Workspace::clientArea()