Showing posts with label MacApp. Show all posts
Showing posts with label MacApp. Show all posts

Sunday, March 7, 2010

performClickWithFrame, small menus and Señor Calviño

An advantage that MacApp had over Cocoa was that you got the source code. My experience of MacApp was that it was far more buggy but having the code meant that you could see how things work and (of course) fix bugs. Cocoa seems rather bug free - but when you find a bug you have no option but to work around it. I am not pining for the days of MacApp, things move on, Cocoa is very, very good - but I do miss the source.

My current issue is contextual menus. I am using performClickWithFrame to popup the menu - however as the Timeline is quite small I don't want the distraction of a normal size menu - it looks odd and out of place. The solution that I have is to use setAttributedTitle to set the menu items to a small, and comfortable, size. This all works just fine:
The rub come when you have a contextual menu with just one item. It can happen - contextual menus are contextual - they only show items that can be invoked - items that can't (would be disabled in normal menus) are simply omitted. This can leave you with a contextual menu with just one item. Unfortunately it looks like this:
Hello Señor Calviño - yep it is bald as an egg - not even a hint of text. If instead of setAttributedTitle I use setTitle it looks fine (but big).
This seems to be an either/or decision - big or bad. The workaround I had for this good/bad/ugly place was to give the popup a header label. The resulting menu looks like this.

Wednesday, January 7, 2009

Re-factoring

Prior to starting on the UI I have been doing some re-factoring. I have read that the important thing when re-factoring is that the code still works. My experience supports this. The ideal situation is that you re-factor in small steps and intersperse these steps with running unit tests that will ensure that you haven't broken anything. In the case of Cello as most of the work I have done is for functionality that allows Cello to mesh onto OSX, I have also written a lot of Unit Tests so I feel relatively secure about a modest amount of re-factoring.

The main things that I am doing re-factoring is:
  • Working through consistent naming.
  • Wrapping most the core foundation functions as methods.
Wrapping of core foundation functions as methods is something that I first came across in ACS (a part of MacApp). ACS aimed to present the what was originally the tool-box functionality as a set of C++ objects. In later versions following the introduction of CoreFoundation it did this, where possible, by defining the anonymous types. So in the case of CFData, a CFDataRef and CFMutableDataRef are defined as:
typedef const struct __CFData * CFDataRef;
typedef struct __CFData * CFMutableDataRef;
What ACS did was to then define the anonymous __CFData and add methods to it. So to add a method that would wrap CFDataGetLength it would did something like this:
struct __CFData
{
CFIndex GetLength() const;
};
and then defined an implementation for GetLength in terms of CFDataGetLength
inline CFIndex __CFData::GetLength() const
{
return CFDataGetLength(this);
}
When I started work on the Cello project I actively resisted this in favor of calling (typically) core foundation directly. What I found was that it made life a lot easier if I wrapped all the 'create' or 'copy' functions so that they returned boost::intrusive_ptr wrapped CF types. I did this as I used boost::intrusive_ptr to take care of all the ownership/ownercounting issues. When working on the Sound part of Cello I 'rediscovered' this ACS style wrapping, partly because it just made the code easier to read.

It means that instead of calling:
int length = CFDataGetLength(myData);
You write:
int length = myData->GetLength();
If you are dealing with boost::intrusive_ptr it eleminates a lot of 'get' calls so instead of
int length = CFDataGetLength(myData.get());
You write:
int length = myData->GetLength();
The other thing that comes into play is that I have had to wrap small amounts of Objective-C so that it can be used in C++. I have opted to keep Objective C out of the general C++ not just to properly isolate the complete madness of two different incompatible types exception handling but also to keep things simpler. It is generally natural to express these as C++ methods

Sunday, May 11, 2008

Boost - Core Foundation

I have started plumbing in some of Core Foundation into Cello. Core Foundation objects are owner-counted which leaves you to take care of calling CFRetain and CFRelease at appropriate moments. It is very straight forward except that adding all of the CFRetain and CFRelease clutters your code and (more importantly) can be tricky to get right - it is really easy to forget a release, especially if you consider exceptions. .

Things are eased a little if you have classes that wrap some of the Core Foundation objects - but the thing that really makes the difference is smart pointers.

In a previous life I used Apple Class Suites (ACS) which was a part of MacApp. ACS wrapped most of the underlying OS APIs in good C++ and introduced me to using smart pointers that would automatically call CFRetain and CFRelease. So if you wanted a pointer to a CFString that would automatically call CFRelease when it went out of scope there was an AutoCFString_AC smart pointer.

There is excellent support for smart pointers in boost. Declaring and implementing a smart pointer using intrusive_ptr to do the same as MacApp's AutoCFString_AC takes just three lines of code in boost:
 typedef boost::intrusive_ptr auto_CFString;
inline void intrusive_ptr_add_ref(CFStringRef p)
{ ::CFRetain(p); }
inline void intrusive_ptr_release(CFStringRef p)
{ ::CFRelease(p); }
For those curious in the MacApp annex of history, MacApp though 'dodo dead' has played an important part in the development of many application arameworks, there is an insightful article in wikipedia here .