iconify/deiconify animation.

Don't forget to update libkdecore and kicker

svn path=/trunk/kdebase/kwin/; revision=54131
This commit is contained in:
Matthias Ettrich 2000-06-23 16:26:44 +00:00
parent 2322242681
commit 72abf668d2
4 changed files with 184 additions and 64 deletions

View file

@ -16,6 +16,7 @@ Copyright (C) 1999, 2000 Matthias Ettrich <ettrich@kde.org>
#include <qlayout.h>
#include <qpainter.h>
#include <qwhatsthis.h>
#include <qdatetime.h>
#include <qtimer.h>
#include <kwin.h>
#include <netwm.h>
@ -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 )
{

View file

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

View file

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

View file

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