API-Design or How I Learned to Love Writing Docs
After a short trip back back home to see the “Kölner Lichter”, one of the most impressive fireworks in germany with my gf, I went back to Berlin on Sunday. With plenty of time on my hand during the train trip, I decided to revisit a project I started in 2005: CollapsibleWidget. After I have probably missed every deadline for inclusion of this little thing into kdelibs for KDE 4.0, I still want to bring it into shape for use in individual parts of KDE, especially the control center.
The widget is lurking around in playground for a good while now, and Aaron, our plasma god, has even added nice collapse animations to it. So it works, looks fancy, why the worry?
Being a good libs coder, I was unsatisfied with the class I originally wrote for an number of reasons:
1. The API involved the concept of a container class that holds a number of CollapsibleWidgets. This class is orthogonal to other widgets that might want to hold such an item and is therefore redundant.
2. The API of the widget followed no particular class known from Qt or KDE.
3. The naming conventions, code indentation levels, etc were non-consistant
4. The API documentation was basically non-existant.
5. As the Widget is composed of several items internally, it still uses an widget()/setWidget() idiom to handle setting the to-be-collapsed widgets.
6. No sensible focus/buddy handling
The first problem I was able to solve was number two: After discussing the issue a bit with some fellow trolls in the berlin office, we decided that it would probably be best to model the class closely around the API of QGroupBox. However I noticed that it does not make much sense to inherit from QGroupBox, as the API has several things that I have no use for (setFlat(), setCheckable(), etc.). I also don’t see a lot of potential for code reuse there. So I went and implemented most of the properties of QGroupBox, which looks pretty good, even if I am not yet sure if it makes sense to allow setting the labels alignment for instance.
Also I was looking for a way to get rid of number one, the container. Qt has a container class that allows for scrolling: QScrollArea. The container class actually inherits from QScrollArea, but sets it up in a convinient way, e.g. adds multiple, resizable widgets to a QScrollArea, which takes some extra API calls. I just got rid of the class, documenting the QScrollArea setup procedure in the docs. I was contemplating to add a static factory method to CollapsibleWidget, but since we live in times of GUI designers, I think I should rather try to work out a convinient way to use those classes in designer. So I went to talk to Friedemann, our local designer guru here at Trolltech. and this morning he implemented support for QScrollArea! This means you can now put arbitrary widgets and layout inside a ScrollArea in designer. Yay!
The need for designer integration also might also help me to get rid of number five and might ease to implement number six: Using the QPainter and QStyle API directly rather than using a QLabel and a QAbstractButton might help in reducing flicker, speeding things up and might also allow plain simple layouts, therefore avoiding the setWidget()/widget() hack.
As for number three: Code reindention was done quickly, and the API inconsistancy was pretty much automatically solved by approaching number four: While writing the API docs, one is quick to notice a lot of inconsistancies within the API. I corrected all I noticed along the way. I also realized that the Widget wasn’t actually a collapsible widget, but much more an expandable widget, since it’s collapsed by default. Also, talking about expansion is a lot more consistant with other parts of Qt (think Item views). Also, the widget is better described as an expandable group box, than just an expandible widget. After all, it’s even modelled after QGroupBox. After prepending a K (the class is supposed to become part of kdelibs some day, right? :), I ended up with the nameKExpandableGroupBox.
The code is in playground and comes with a small example to play around with. I’d appreciate your comments, especially on the name.
Writing documentation really helped me to improve the API a lot. So I put my thumbs up and put on a cheesy smile to tell you: “While writing API documentation, I expirienced enlightenment – and so can you!”
(ade, you SO owe me a beer for this one)