Introduction

Core Animation is one very cool and interesting API provided by Apple in the Leopard version of Mac OS X. Core Animation can be used to attain some really cool effects like the Cover Flow view, which is available and seen in both iTunes and Finder. The Cover Flow has also been included in the new iPods and in the iPhone. It is an animated view of the files that one is browsing, whether the files are images, videos or documents. Cover Flow displays the preview…

This functionality is now available in Leopard and allows developers to take advantage of this feature by means of Core Animation. Core Animation is a part of the Quartz Core framework and is programmable by means of the objective-c API. This is nice if one’s application UI is written completely in Cocoa. However, why should Carbon developers not be able to take advantage of Core Animation? Well, Apple has thought of that as well. They have exposed a new API named the HICocoaView.

HICocoaView

The HICocoaView allows the developer to embed an NSVIew or any control derived from NSView into a Carbon HIView. This means that Core Animation could be included in a Carbon application. For more information on embedding Cocoa NSViews in a Carbon HIView please visit this link:

http://developer.apple.com/documentation/Cocoa/Conceptual/CarbonCocoaDoc/Articles/HICocoaView.html

Since Trolltech’s Qt framework is currently sitting directly on top of Carbon, I thought I would try and get Cover Flow working in a Qt Application by means of creating a custom widget, which I call the QtCoverFlowWidget.

QtCoverFlowWidget

The first thing we need to do is locate the code for Cover Flow. Conveniently, Apple has an example which ships with the freely available developer tools and it’s named CovertFlow. The entire CovertFlow project, including implementation is available in the following location on your disk:

/Developer/Examples/Quartz/Core Animation/CovertFlow

Basically, the bulk of the work is already done for us, thanks to Apple’s example. Now we must figure out how to wrap a QWidget around this implementation, such that we can use this CovertFlow view inside our Qt Application. So Let us begin.

Creating the project

I simply created a main.cpp from which I then generated an Xcode project. My main.cpp looked something like this:

#include <QtGui>
#include <QtCore>
class MyWidget : public QWidget
{
   Q_OBJECT
 public:
   MyWidget(QWidget *parent) : QWidget(parent) { }
 virtual ~MyWidget() { }
};
#include "main.moc"
int main(int argc, char** argv)
{
   QApplication app(argc, argv);
   QMainWindow *win = new QMainWindow;
   win->resize(600,300);
   win->show();
   MyWidget *w = new MyWidget(win);
   w->resize(400,200);
   w->show();
   return app.exec();
}

now let’s create the project:

qmake -project
qmake -spec macx-xcode QtCoverFlowExample.pro

The above will generate a QtCoverFlowExample.xcodeproj which we can open using Xcode 3.0.

Once the project is opened, there are some things that must be done. They are as follows:

Right click on main.cpp and click Info. In the General tab switch the File Type to “sourcecode.cpp.objcpp”. This is done to let the compiler know that this file contains objective c++ code.
Add QuartzCore and Cocoa frameworks to the project. AppKit framwork can be removed since the Cocoa framework acts as an umbrella around AppKit.
Import the relevant files from the CovertFlow project into our project. They are:

Catalog.h
Catalog.cpp
Controller.h
Controller.cpp
DesktopImage.h
DesktopImage.cpp
DesktopImageLayout.h
DesktopImageLayout.cpp
View.h
View.cpp

and View.nib must go to the Resources folder (create one if one does not exist)

The project should resemble something similar to this once the aforementioned steps have been completed…

Now that the project is created, we must create our custom widget that will display the CovertFlow view which is written using the Quartz Core framework.

Below is the code to our custom widget named QtCoverFlowWidget

class QtCoverFlowWidget : public QWidget
{
   Q_OBJECT
   public:
      QtCoverFlowWidget(QWidget *parent = 0) : QWidget(parent)
      {
         // instantiate controller for the covertflow view
	 HIViewRef coverFlowRef;
         controller = [[Controller alloc] init];
         //HICocoaView wrapper around covert flow view
         HICocoaViewCreate([controller view], 0, &coverFlowRef);
         create(WId(coverFlowRef));
         // connect resize of parent to resize of ourselves
         connect(parent, SIGNAL(resized(int, int)), this, SLOT(slotResize(int, int)));
      }
      virtual ~QtCoverFlowWidget() { }

   protected slots:
      void slotResize ( int w, int h )
      {
         // resize ourselves
         resize(w, h-20);
      }
   private:
      Controller *controller;
};

We derive the QtCoverFlowWidget from the QWidget class. The CovertFlow example that Apple provides is a classic example of an MVC application. Therefore, in the constructor of our custom widget, we instantiate the controller associated with the CovertFlow example. We then create and HICocoaView which wraps the NSView containing the Cover Flow view and then associate our widget with the HIViewRef associated with the HIView wrapping the NSView which contains the Cover Flow view. This is made possible by the fact that a QWidget is just an HIView on its own. Carbon API can be used to communicate directly to the QWidget, which is an HIView. There are some useful Qt functions which help bridge Carbon and the Qt API. These functions are:

// returns an HIViewRef/WindowPtr which is associated with the QWidget
HIViewRef qt_mac_hiview_for(const QWidget *w)
WindowPtr qt_mac_window_for(const QWidget *w)
// returns HIViewRef for a native Carbon Window and vise versa
HIViewRef qt_mac_hiview_for(WindowPtr w)
WindowPtr qt_mac_window_for(HIViewRef hiview)

In order for the above functions to be used in code, all you need to do is #include <QtGui>.

We also have a slot implemented named slotResize(int, int). This slot is hooked to the resized(int, int) signal which is emitted by the parent of this widget (our QMainWindow implementation). This can be seen in the example code here:

connect(parent, SIGNAL(resized(int, int)), this, SLOT(slotResize(int, int)));

The parent widget is actually a custom widget as well. Specifically, it is a custom implementation of the QMainWindow. Not much happens in this implementation except the interception of the resizeEvent(QResizeEvent *) and the action of emitting the resized(int, int) signal once that occurs.

Last steps

There are also a few things that must be done in order to have a properly integrated Cocoa event loop. The main function needs the following:

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSApplicationLoad();
...
...
...
[pool release];

The above code ensures the Cocoa event loop is running and properly handling Cocoa events.

Now we can simply instantiate our custom QtCoverFlowWidget just like we do any other Qt Widget. All the code, including the Xcode project as well as the binaries can be found here…

QtCoverFlowExample.zip Binary Universal Bundle
QtCoverFlowExample_src.zip Source

And the results are….

Conclusion

The HICocoaView made the above possible. The HICocoaView is a power class allowing developers to mix Cocoa views inside their Carbon application. Perhaps next time we’ll do something with Apple’s QTKit.

Share on Technorati . del.icio.us . Digg . Reddit . Slashdot . Facebook . StumbleUpon

1 Comment

1 Comment to QtCoverFlowWidget using Core Animation and Qt

[...] Blog Archive QtCoverFlowWidget using Core Animation and Qt Posted by root 11 minutes ago (http://blogs.seapine.com) Nov 21 2007 appkit framwork can be removed since the cocoa framework acts as an umbrella around appkit import the relevant files from the below is the code to our custom widget named qtcoverflowwidget post a comment name required is powered by wordpress e Discuss  |  Bury |  News | Blog Archive QtCoverFlowWidget using Core Animation and Qt [...]

Leave a comment

WP_Big_City

Spam Protection by WP-SpamFree