[prev in list] [next in list] [prev in thread] [next in thread] 

List:       kde-core-devel
Subject:    Support for animated icons (on mouseover)
From:       David Faure <david () mandrakesoft ! com>
Date:       2002-01-16 14:45:48
[Download RAW message or body]

The two patches attached provide support for animations on mouseover, as 
requested by our beloved artist (aka tackat ;)

The main question is: given the current feature freeze, is it ok to commit 
this nonetheless ?

Details: The patch for am_edit makes it possible to install the mng already 
present in kdelibs/pics/hicolor.
When the mouse goes over an icon (in konq/kdesktop), KIconLoader locates an 
mng with the same name as the icon (currently only folder.mng exists), then a 
QMovie is created to play it - each frame of the MNG is set as the 
QIconViewItem pixmap, quite simple.

The memory overhead is one pointer per icon (the movie, 0L most of the time),
and the filename for the icon in a QString (e.g. "folder").

The patch moves m_size from KFileIVI to KIconViewItem -> no change for most 
iconviews, however this makes the patch BIC.

The effect looks nice - although this is pure eye-candy, no doubt there.
Whether it will need to be made configurable or not.... I say yes, Tackat says 
no, we'll see what users (or the testers of this patch) say ;)

-- 
David FAURE, david@mandrakesoft.com, faure@kde.org
http://people.mandrakesoft.com/~david, http://www.konqueror.org
KDE 3.0: Konquering the Desktops


["animated_icons_kdebase.diff" (text/x-diff)]

Index: kfileivi.cc
===================================================================
RCS file: /home/kde/kdebase/libkonq/kfileivi.cc,v
retrieving revision 1.63
diff -u -p -r1.63 kfileivi.cc
--- kfileivi.cc	2002/01/10 14:46:47	1.63
+++ kfileivi.cc	2002/01/16 14:31:03
@@ -26,6 +26,7 @@
 #include <kurldrag.h>
 #include <kiconeffect.h>
 #include <kfileitem.h>
+#include <kdebug.h>
 
 #undef Bool
 
@@ -43,15 +44,18 @@ struct KFileIVI::Private
 KFileIVI::KFileIVI( KonqIconViewWidget *iconview, KFileItem* fileitem, int size )
     : KIconViewItem( iconview, fileitem->text(),
 		     fileitem->pixmap( size, KIcon::DefaultState ) ),
-  m_size(size), m_state( KIcon::DefaultState ),
+    m_state( KIcon::DefaultState ),
     m_bDisabled( false ), m_bThumbnail( false ), m_fileitem( fileitem )
 {
+    setIconSize( size );
     setDropEnabled( S_ISDIR( m_fileitem->mode() ) );
     d = new KFileIVI::Private;
 
     // Cache entry for the icon effects
     d->icons.reset( *pixmap(), QIconSet::Large );
     d->state = KIcon::DefaultState;
+
+    setMouseOverAnimation( fileitem->iconName() );
 }
 
 KFileIVI::~KFileIVI()
@@ -87,7 +91,7 @@ void KFileIVI::invalidateThumb( int stat
 
 void KFileIVI::setIcon( int size, int state, bool recalc, bool redraw )
 {
-    m_size = size;
+    setIconSize( size );
     m_bThumbnail = false;
     if ( m_bDisabled )
       m_state = KIcon::DisabledState;
@@ -113,7 +117,7 @@ void KFileIVI::setIcon( int size, int st
     // called with any state and not just normal state. So we just
     // create a dummy empty iconset as base object.
     d->icons = QIconSet();
-    d->icons.setPixmap( m_fileitem->pixmap( m_size, m_state ),
+    d->icons.setPixmap( m_fileitem->pixmap( iconSize(), m_state ),
 			QIconSet::Large, mode );
     d->state = m_state;
     QIconViewItem::setPixmap( d->icons.pixmap( QIconSet::Large, mode ),
@@ -126,7 +130,7 @@ void KFileIVI::setDisabled( bool disable
     {
         m_bDisabled = disabled;
         m_state = m_bDisabled ? KIcon::DisabledState : KIcon::DefaultState;
-        QIconViewItem::setPixmap( m_fileitem->pixmap( m_size, m_state ), false, true );
+        QIconViewItem::setPixmap( m_fileitem->pixmap( iconSize(), m_state ), false, true );
     }
 }
 
@@ -186,7 +190,7 @@ void KFileIVI::setEffect( int group, int
 	else
 	{
 	    if( d->icons.isGenerated( QIconSet::Large, mode ) )
-		d->icons.setPixmap( m_fileitem->pixmap( m_size, state ),
+		d->icons.setPixmap( m_fileitem->pixmap( iconSize(), state ),
 				    QIconSet::Large, mode );
 	}
 	QIconViewItem::setPixmap( d->icons.pixmap( QIconSet::Large, mode ) );
@@ -197,7 +201,7 @@ void KFileIVI::setEffect( int group, int
 void KFileIVI::refreshIcon( bool redraw )
 {
     if ( !isThumbnail())
-        setIcon( m_size, m_state, true, redraw );
+        setIcon( iconSize(), m_state, true, redraw );
 }
 
 bool KFileIVI::acceptDrop( const QMimeSource *mime ) const
Index: kfileivi.h
===================================================================
RCS file: /home/kde/kdebase/libkonq/kfileivi.h,v
retrieving revision 1.41
diff -u -p -r1.41 kfileivi.h
--- kfileivi.h	2002/01/10 14:46:47	1.41
+++ kfileivi.h	2002/01/16 14:31:03
@@ -138,7 +138,7 @@ private:
     virtual void setPixmap ( const QPixmap & icon ) { KIconViewItem::setPixmap( icon ); }
     virtual void setPixmap ( const QPixmap & icon, bool recalc, bool redraw = TRUE )
         { KIconViewItem::setPixmap( icon, recalc, redraw ); }
-    int m_size, m_state;
+    int m_state;
     bool m_bDisabled;
     bool m_bThumbnail;
     /** Pointer to the file item in KDirLister's list */

["animated_icons_kdelibs.diff" (text/x-diff)]

Index: admin/am_edit
===================================================================
RCS file: /home/kde/kde-common/admin/am_edit,v
retrieving revision 1.299
diff -u -p -r1.299 am_edit
--- admin/am_edit	2002/01/13 12:59:32	1.299
+++ admin/am_edit	2002/01/16 14:30:47
@@ -1160,11 +1160,11 @@ sub tag_ICON()
             if ( $iconauto )
               {
                   push(@files, $entry)
-                    if ($entry =~ /\.xpm/ || $entry =~ /\.png/);
+                    if ($entry =~ /\.xpm/ || $entry =~ /\.png/ || $entry =~ \
/\.mng/);  } else {
                   foreach $appname (@appnames) {
                       push(@files, $entry)
-                        if ($entry =~ /-$appname\.xpm/ || $entry =~ \
/-$appname\.png/); +                        if ($entry =~ /-$appname\.xpm/ || $entry \
=~ /-$appname\.png/ || $entry =~ /-$appname\.mng/);  }
               }
         }
@@ -1175,7 +1175,7 @@ sub tag_ICON()
         foreach $file (@files) {
             my $newfile = $file;
             my $prefix = $file;
-            $prefix =~ s/\.(png|xpm)$//;
+            $prefix =~ s/\.(png|xpm|mng)$//;
             my $appname = $prefix;
             $appname =~ s/^[^-]+-// if ($appname =~ /-/) ;
             $appname =~ s/^[^-]+-// if ($appname =~ /-/) ;
@@ -1223,7 +1223,7 @@ sub tag_ICON()
                'hisc' => 'hicolor/scalable'
               );
             
-            $newfile =~ s@.*-($appname\.(png|xpm?))@$1@;
+            $newfile =~ s@.*-($appname\.(png|xpm|mng?))@$1@;
             
             if (! defined $dir_hash{$prefix}) {
                 print STDERR "unknown icon prefix $prefix in $printname\n";
Index: kdeui/kiconview.cpp
===================================================================
RCS file: /home/kde/kdelibs/kdeui/kiconview.cpp,v
retrieving revision 1.50
diff -u -p -r1.50 kiconview.cpp
--- kdeui/kiconview.cpp	2001/12/10 23:10:28	1.50
+++ kdeui/kiconview.cpp	2002/01/16 14:30:47
@@ -19,6 +19,7 @@
 #include <qpainter.h>
 #include <qpixmapcache.h>
 #include <qcleanuphandler.h>
+#include <qmovie.h>
 
 #include "kiconview.h"
 #include "kwordwrap.h"
@@ -31,6 +32,8 @@
 #include <kcursor.h>
 #include <kpixmap.h>
 #include <kpixmapeffect.h>
+#include <kicontheme.h>
+#include <kiconloader.h>
 
 #ifdef Q_WS_X11
 #include <X11/Xlib.h>
@@ -89,24 +92,43 @@ KIconView::Mode KIconView::mode() const
 
 void KIconView::slotOnItem( QIconViewItem *item )
 {
-    if ( item && m_bChangeCursorOverItem && m_bUseSingle )
-        viewport()->setCursor( KCursor().handCursor() );
+    if ( item ) {
+        if ( m_bUseSingle ) {
+            if ( m_bChangeCursorOverItem )
+                viewport()->setCursor( KCursor().handCursor() );
 
-    if ( item && (m_autoSelectDelay > -1) && m_bUseSingle ) {
-      m_pAutoSelect->start( m_autoSelectDelay, true );
-      m_pCurrentItem = item;
+            if ( (m_autoSelectDelay > -1) ) {
+                m_pAutoSelect->start( m_autoSelectDelay, true );
+            }
+        }
+        KIconViewItem *kivi = dynamic_cast<KIconViewItem *>( item );
+        if ( kivi && kivi->hasAnimation() && !kivi->animationStarted() )
+            kivi->startAnimation( this, SLOT( slotMovieUpdate() ) );
+        m_pCurrentItem = item;
     }
 }
 
 void KIconView::slotOnViewport()
 {
-    if ( m_bChangeCursorOverItem )
+    if ( m_bUseSingle && m_bChangeCursorOverItem )
         viewport()->unsetCursor();
 
+    if ( m_pCurrentItem ) {
+        KIconViewItem *kivi = dynamic_cast<KIconViewItem *>( m_pCurrentItem );
+        if ( kivi && kivi->hasAnimation() )
+            kivi->stopAnimation();
+    }
     m_pAutoSelect->stop();
     m_pCurrentItem = 0L;
 }
 
+void KIconView::slotMovieUpdate()
+{
+    KIconViewItem *kivi = dynamic_cast<KIconViewItem *>( m_pCurrentItem );
+    if ( kivi ) // seems stopAnimation triggers one last update
+        kivi->updatePixmap();
+}
+
 void KIconView::slotSettingsChanged(int category)
 {
     if ( category != KApplication::SETTINGS_MOUSE )
@@ -343,12 +365,14 @@ QPixmap KIconView::selectedIconPixmap( Q
 void KIconViewItem::init()
 {
     m_wordWrap = 0L;
+    m_movie = 0L;
     calcRect();
 }
 
 KIconViewItem::~KIconViewItem()
 {
     delete m_wordWrap;
+    delete m_movie;
 }
 
 void KIconViewItem::calcRect( const QString& text_ )
@@ -556,6 +580,46 @@ void KIconViewItem::paintItem( QPainter 
     m_wordWrap->drawText( p, textX, textY, align );
 
     p->restore();
+}
+
+
+void KIconViewItem::setMouseOverAnimation( const QString& movieFileName )
+{
+    //kdDebug() << "KIconViewItem::setMouseOverAnimation " << movieFileName << endl;
+    m_animatedIcon = movieFileName;
+}
+
+void KIconViewItem::startAnimation( QObject * receiver, const char * member )
+{
+    //kdDebug() << "KIconViewItem::startAnimation" << endl;
+    Q_ASSERT( !m_movie ); // see animationStarted()
+    // If the file can't be loaded (e.g. no mng support), the movie will
+    // simply never call updatePixmap, so no problem.
+    QMovie movie = KGlobal::iconLoader()->loadMovie( m_animatedIcon, KIcon::Desktop, \
m_size ); +    if ( !movie.isNull() )
+    {
+        m_movie = new QMovie( movie ); // shallow copy, don't worry
+        m_movie->connectUpdate( receiver, member );
+    }
+    else
+    {
+        // No movie available, remember it
+        m_animatedIcon = QString::null;
+    }
+}
+
+void KIconViewItem::updatePixmap()
+{
+    //kdDebug() << "KIconViewItem::updatePixmap" << endl;
+    setPixmap( m_movie->framePixmap() );
+}
+
+void KIconViewItem::stopAnimation()
+{
+    //kdDebug() << "KIconViewItem::stopAnimation" << endl;
+    Q_ASSERT( m_movie );
+    delete m_movie;
+    m_movie = 0L;
 }
 
 #include "kiconview.moc"
Index: kdeui/kiconview.h
===================================================================
RCS file: /home/kde/kdelibs/kdeui/kiconview.h,v
retrieving revision 1.28
diff -u -p -r1.28 kiconview.h
--- kdeui/kiconview.h	2001/12/10 23:10:28	1.28
+++ kdeui/kiconview.h	2002/01/16 14:30:47
@@ -117,7 +117,7 @@ signals:
 protected slots:
   void slotOnItem( QIconViewItem *item );
   void slotOnViewport();
-
+  void slotMovieUpdate();
   void slotSettingsChanged(int);
 
   /**
@@ -164,6 +164,8 @@ class KWordWrap;
  * KIconViewItem exists to improve the word-wrap functionality of QIconViewItem
  * Use KIconViewItem instead of QIconViewItem for any iconview item you might have \
                :)
  *
+ * This class also provides support for animated icons.
+ *
  * @short A variant of QIconViewItem that wraps words better.
  * @author David Faure <david@mandrakesoft.com>
  */
@@ -188,12 +190,41 @@ public:
     KIconViewItem( QIconView *parent, QIconViewItem *after, const QString &text, \
const QPicture &picture )  : QIconViewItem( parent, after, text, picture ) { init(); \
}  virtual ~KIconViewItem();
+
+    /**
+     * Return the theorical size in pixels, if setSize() was called.
+     * 0 means the globally configured default size.
+     */
+    int iconSize() const { return m_size; }
+
+    /**
+     * Enable an animation on mouseover, if there is an available mng.
+     * @param movieFileName the base name for the mng, e.g. "folder".
+     * Nothing happens if there is no animation available.
+     */
+    void setMouseOverAnimation( const QString& movieFileName );
+    bool hasAnimation() const { return !m_animatedIcon.isEmpty(); }
+    bool animationStarted() const { return m_movie; }
+    void startAnimation( QObject * receiver, const char * member );
+    void updatePixmap();
+    void stopAnimation();
+
 protected:
+    /**
+     * Set the theorical size in pixels - this is only useful for derived classes
+     */
+    void setIconSize( int size ) { m_size = size; }
+
     void init();
     virtual void calcRect( const QString& text_ = QString::null );
     virtual void paintItem( QPainter *p, const QColorGroup &c );
 private:
+    int m_size; // theorical size in pixels. Zero means the globally configured \
default size. +
     KWordWrap* m_wordWrap;
+
+    QString m_animatedIcon;
+    QMovie* m_movie;
 };
 
 #endif



[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic