2010-02-19

GraphicsPort Refactored

There have been quite a few changes and improvements since the last blog post. The biggest one is another redesign of the GraphicsPort class. Back in November last year I stripped the drawing code out of the bitmap class and separated it into a new hierarchy. The GraphicsUnclipped class contained drawing methods that were not clipped. The Graphics class was a subclass of GraphicsUnclipped and clipped to the confines of a bitmap. The GraphicsPort also subclasses GraphicsUnclipped and clipped to the visible regions of a Gadget.

This seemed like a reasonable design, but it did have its downsides. As the Graphics and GraphicsPort classes were peers within the inheritance hierarchy, rather than one subclassing the other, there was inevitably some repeated code. More disturbing was the spaghetti-like integration between the classes, in which GraphicsPort would call a method in GraphicsUnclipped which would in turn call a method in GraphicsPort.

In addition to the inheritance problems, the GraphicsPort had its own internal problems. Its drawing methods used a variety of different co-ordinate systems. Some used the co-ordinates of the entire UI as their reference point, whilst some used the co-ordinates of the gadget, and others used the co-ordinates of the GraphicsPort itself.

The GraphicsPort is designed to work in two ways - it can be constructed and used within a gadget’s draw methods, or it can be created outside of the gadget and used to draw over the gadget. This meant that it needed to retain either a single clipping rectangle, used when drawing from within a draw method, or a list of clipping rectangles, used when drawing from outside of the gadget’s code.

All of these problems led to a horrible mess, which I have now put right. I’ve even introduced some new features in the process.

First of all, I’ve merged the Graphics and GraphicsUnclipped classes into a single class: “Graphics”. It contains all of the drawing methods available to the entire Woopsi system. The class can be given a clipping rectangle in which it can draw, which means that all of the drawing methods now clip. Any potential speed loss is negligible, since the drawing methods were already clipping to the confines of the bitmap being drawn to. I’ve just made that clipping area user-definable.

The GraphicsPort class no longer inherits from either of the other Graphics classes. Instead, it includes an instance of the Graphics class and presents a facade over the top. The GraphicsPort no longer has a convoluted set of responsibilities; it now:

  • Maintains a list of clipping rects for the gadget it relates to;
  • Receives drawing instructions;
  • Converts the co-ordinates from “GraphicsPort space” to framebuffer space;
  • Uses its Graphics object to draw to all clipping rects in its list.

The GraphicsPort does not maintain a single clipping rect in addition to a separate list of clipping rects. It now adds that single clipping rect to its list, erasing any previous data in its list. In this way, it achieves the same functionality without the extra complexity of two separate data storage mechanisms.

I mentioned GraphicsPort space and framebuffer space in the above descrition. I’ve put some work into trying to formalise the different co-ordinate systems that Woopsi uses. Descriptions of these will be included in the documentation whenever I get around to finishing it.

The GraphicsPort class is now entirely separate from the Gadget class. Previously the GraphicsPort included a pointer to the gadget that it was drawing to. This is no longer necessary, which should result in a (negligible) speed increase, since the GraphicsPort no longer needs to query the gadget’s Woopsi space co-ordinates using its recursive getX() and getY() methods.

I’ve removed the OutlineType enum from the Graphics class. This was not relevant to all gadgets so should not have been in the base class. This resulted in the addition of a new CalendarDayButton class and some changes to the WoopsiKey class so that they could remain “stuck down” when selected, which is represented by their outlines changing from bevelled out of the screen to bevelled into the screen.

I removed the padding variables from a few classes a week or so ago. I’ve now done the same to the MultiLineTextBox and replaced it with larger border sizes. This change, coupled with the rationalisation of the GraphicsPort’s co-ordinates, finally enabled me to identify the bug that caused the textbox to have graphical glitches when using a padding of greater than 12. That’s the first bug I’ve closed in the SourceForge tracker in months. The textbox’s vertical top alignment option works correctly, too.

Lastly, I’ve improved the cursor-following code in the TextBox again. When deleting characters from a string that is wider than the textbox, it is no longer possible to create a large gap between the end of the string and the right edge of the textbox.