Posts filed under 'Cocoa'

Triplatform GUI chaos

For the last few months (no not 24/7) I’ve been working on a multiplatform library in C++. Now, the first thing you might expect as a pro-open-source-and-lunix-person, that GUI development on Linux is going to be awesome. Unfortunately, this isn’t very close to reality.

The first thing in the world of Linux that you’ll come across, is that there are no default toolkits. The X-windows system is basically just a remote pixelbuffer with support for keyboard and mouse events. Now, how wonderful it may be to develop your own fancy toolkit and to draw buttons and gadgets, it will take you literally months to develop a well-tested and feature-rich toolkit like that. So you look at the available options of libraries to use, and you’ll browse the websites of the 2 major players around. The first one being the basis of KDE, named QT, and the other one of GNOME, called GTK+.

I have worked with QT before, on both Windows and Linux, and it feels generally ok. All the necessary components are there, decent callback (signals) mechanics, object oriented. The set of features in QT is quite extensive, but then you’ll come across the itchy area. It’s dual licensed, with a price tag on the commercial one that is not exactly forgiving, or you’ll have to go and make your applications open source. I’m convinced that the price is justified and that they deserve to get paid for it, but I wasn’t so sure I could take such a gamble at the time.

The next major player is GTK+, which is available for windows and linux as well. It’s used in things like GIMP, and pretty much the whole of the GNOME desktop application base. It’s based on LGPL, so you can pretty much do whatever you want when statically linking to it. So you start investigating it, make some hello worlds and try to make them compile. The linking process under Windows is just too horrible to endure, but when you fall you learn and write it down, it becomes manageable. Linking under Linux won’t be so hard, as you’ll just use some default pkg-config lines in your makefile – which can be done under Windows as well, but is a tad more complicated.

The thing that will eventually make you hit a large wall of nastyness, is the fact that it promises amazing MVC qualities and it doesn’t deliver quite so amazingly. I don’t really know how to explain it. It just doesn’t feel right. I recognize the hard work they put into the project, using a C-only base with a really inventive way of creating custom types and inheritance with it, but then using an overexcited amount of runtime type checking and using several other libraries for the dirty work. Little things in the gui-area of the library actually feel like you expect it to work.

There are a lot of other toolkits out there for linux, like WxWidgets or FLTK, and if you like to be old-school, you can go for Xaw – but I went for GTK, and I’ll have to stick to that decision. Speaking of WxWidgets, as wonderful it may seem on the cover, the actual performance of even the simplest Mac OSX examples is very poor somehow.

So here you are, after weeks of surfing around, investigating and testing, while your native Microsoft Windows API support was done in half the time. I’ve really grown back towards the Windows API. I generally know out of experience what to expect and what workarounds I should use, and it’s documented on MSDN up-to the weirdest behaviour you’ll come across. The best thing about the Windows API is that you can pretty much do anything you want, yes it will be very complicated with fancy window messages and special functions, but it’ll work eventually. Where as with GTK, you’ll come across limitations when you want to emulate non-default-behaviour.

Yet on top of the world, there lies Cocoa. Cocoa is the Objective-C based toolkit for Mac OSX, and is despite the oddness of Objective-C, it has a wonderful separation of data and view when it comes to the really bulky stuff like Grids. I was so happy when I came across the – complicated but workable – mechanism of requesting table data via function calls instead of having to fill some custom datatype. It enabled me to make callbacks to my own code, get the data directly from my own data-types at runtime, and Cocoa makes sure it turns up on the screen.

The biggest problem with Cocoa is that you’ll come across things that don’t work the way you’re supposed to when working primarily on Windows. For example; I tried to make menu’s pop-up on any X-Y-location on a window, and there wasn’t such a thing. Following some examples that described workarounds didn’t really work either. And then it got me starting to think about what I really wanted it to do. I didn’t really want the menu to pop-up ‘Anywhere’ like I first thought. You don’t want a popup to appear on some panel, textlabel or even a button, you want it on specific controls like listboxes, grids and editboxes, and even more specifically; you want it when you right-click on a specific spot. And there lies the solution, because that is exactly what Cocoa asks of you. It wants you to say if you want a menu when such an event occurs, and then it will display your menu.

Most downsides of Cocoa can be overcome, but are annoying at first. The first thing you’ll notice when coding interfaces manually instead of with the fancy XCode Designer, is that the Y coordinate is upside down. There is supposedly a delegate function that can reverse the coordinates, but I just solved it in a more mathematical way – as in subtracting the control’s expected y from the parent control’s height, thus mirroring the coordinates as they are handed to Cocoa.

The second thing is a weird bug that I solved with a dirty hack. The test applications didn’t respond to much mouse events, and not at all at keyboard events. Instead, it decided the Console deserved the keystrokes more than my application. For some reason I came across a function that requested the attention of the entire display, and then releasing it again.

CGCaptureAllDisplaysWithOptions( kCGCaptureNoFill );
CGReleaseAllDisplays();

I really have no idea how I came up with the idea, I googled my ass off and didn’t find anything at all on the subject. I have no idea how to solve it any other way, but it works.

The third thing is the Garbage Collector. It is absolutely annoying, but yes it makes sense. It sounded reasonable to set up autorelease pools, allocate and release whatever I could do myself, but then I stumbled upon a large amount of warnings that eventually after a very long time of debugging led me to the culprit. The warning occurred when I tried to setText or setTitle of certain objects. It seems that the moment of setting, automatically thus leaves the old variable without a home, and putting an autoreleasepool around the setfunction makes the old value tumble right into the garbagebin. Like this:

NSAutoreleasePool *tmpPool = [[NSAutoreleasePool alloc] init];
NSString *s = [[NSString alloc] initWithCString: caption encoding: NSUTF8StringEncoding];
[control setTitle: s];
[s release];
[tmpPool release];

Well I suppose I could go on and on about this, but it’s not exactly the best blog-entry I’ve ever written so far. So it’s perhaps best I leave it at this and try to make some kind of sense next time.

 

Until then.

 

Add comment May 20, 2008


RSS Twitter

 

November 2009
S M T W T F S
« Sep    
1234567
891011121314
15161718192021
22232425262728
2930  

Categories

Blogroll

Meta