Woopsi 0.46 Beta Released

March 9th, 2010 Ant No comments

Yet another release. This one is special as it marks Woopsi’s transition from alpha to beta. Woopsi is now in beta, which means that the feature set is frozen and I’m going to concentrate on documentation and bug fixing.

Woopsi is the poster child for the dangers of feature creep. I started it back in September 2007 with the idea that I’d support screens, windows, buttons and a textbox, and get the whole thing written in a month. Two and a half years later, Woopsi has evolved into a fairly complete windowing system with scrollers, listboxes, UTF-8 encoded unicode strings, TrueType font support, a suite of clipped 2D drawing tools, etc. A full SVN repository checkout consumes over 500MB of disk space.

I’m finally happy with the feature set. There are, of course, more features I’d like to add, such as skinning and bitmap loading, but it’s reached the point that I’d prefer to stop adding to the code and document what I’ve got so far.

I’m also very interested in feedback about the library. If you’ve been tempted to use Woopsi but have been put off by the ever-changing API, now is a good time to get started with it. The more feedback I get, the more bugs I’ll be able to fix.

Once all of the documentation is done and I hit version 1.0, I’m considering talking to some of the homebrew websites to see if they’d be interested in running a Woopsi-based competition. Just an idea at the moment, and I’m not sure how practical it will be or if they will be interested. Still, worth a try.

This latest release includes the last set of new features. MultiLineTextBoxes implement the cursor fully. The cursor can be moved up, down, left or right with the d-pad. It moves to the cursor’s position when the textbox is tapped with the stylus. The textbox scrolls to follow the cursor when it is moved. The MultiLineTextBox has been significantly refactored. All of its alignment options finally work, and it even shows the keyboard when it is double-clicked (though this behaviour can be disabled via a new disableKeyboardPopup() method).

I’ve added a few base classes that define the interface for some of the gadgets, enabling composite gadgets that mimic their behaviour to present a more consistent set of methods.

There are a few other bugfixes and improvements; they are all listed in the changelog, as usual.

Download the new release from SourceForge:

Igmongonious: A MongoDB Framework for C#

February 27th, 2010 Ant 1 comment

I’ve been playing with NoSQL databases lately. The DB I’m most impressed with at the moment is MongoDB. I was so impressed with just how easy it is to get data in and out of the database that I ripped ALite apart and reassembled it as a framework for creating objects that persist to MongoDB automatically.

That’s automatically - there’s no CRUD code needed. No longer do you need to fight against the object/relational impedance problem. Never again will you need to suffer bug-infested template generators, that produce bug-infested templates, that contain millions of lines of sub-optimal code to perform a single “SELECT * FROM table WHERE ObjectID = 3” query. Just inherit from a single class and let the database and the framework take care of persistence.

Igmongonious (the name is a corruption of “ignominious”, but rather harder to pronounce) also includes the rules system from ALite, and a very similar undo system.

The code is available on BitBucket:

The code is in the form of a Visual Studio 2010 project, but I don’t think it uses anything that isn’t compatible with .NET2.0.

The BitBucket wiki has more information about what the framework can do and some simple examples.

Hmm. I’ve jumped to VS2010, C#, Mercurial, MongoDB and BitBucket from VS2008, C++, SVN, embedded development and SourceForge in the space of one blog post. Blimey, I’ve become cutting-edge again.

Woopsi 0.45 Released

February 19th, 2010 Ant No comments

Because I’m lazy, this post is mostly taken verbatim from the Woopsi thread on GBADEV.

Version 0.45 is now available:

http://www.sourceforge.net/projects/woopsi

This version includes TrueType font support via wrappers around the open-source FreeType library. Coupled with the unicode support added in the last release, this means that it is now possible to develop GUIs with characters outside of the standard ASCII set. Many thank to Lakedaemon for his work with both unicode and FreeType.

Instructions for installing the bundled FreeType library are included in the “Installation.txt” file in the source archive.

The release includes a number of API improvements. Wiring up the keyboard gadget to a textbox is now very simple. In fact, double-clicking a textbox now automatically shuffles the touchscreen display into the top display and opens a keyboard on the touchscreen. Closing it shuffles everything back to how it was.

The various click(), release(), etc, methods that needed to be overridden when creating new UI gadgets have been replaced with simpler alternatives, called onClick(), onRelease(), and so on. The shift-click system (in which the shoulder buttons work as modifier keys to enable right-click style functionality and a context-sensitive menu) has been greatly simplified.

The drawing API has similarly been simplified, and all drawing - whether done to a bitmap or a gadget - can now take advantage of the clipping system.

Categories: Woopsi Tags: ,

GraphicsPort Refactored

February 19th, 2010 Ant No comments

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, uesd 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.

Keyboards Working

February 14th, 2010 Ant No comments

I’ve implemented the suggestion in the previous post (thanks to Jeff for the vote of confidence). The keyboard now appears if a textbox is clicked. The bottom screen jumps up to the top display and the keyboard screen takes its place. When the keyboard is closed, the original screen drops back down into place in the bottom display.

The keyboard can be closed by either clicking on the return key (which also fires an action event) or by clicking a new “OK” button at the bottom of the keyboard. I’ve changed the keyboard so that it no longer inherits from the AmigaWindow class, giving it much more flexibility. The automatically-appearing keyboard is an instance of a new “WoopsiKeyboardScreen” class hosted by the Woopsi gadget.

Getting the textbox to listen to d-pad presses to move the cursor was very simple. The WoopsiKeyboardScreen and keyboard no longer attempt to steal focus, which means that the textbox that opened the keyboard will keep focus even though the keyboard is open and being interacted with. This allowed me to change the cursor so that it is only drawn when the textbox has focus, solving the dilemma of indicating which textbox is being edited with the keyboard.

I noticed that pulling down the top screen did not correctly erase that screen from the top display. It transpires that this is an issue with the GraphicsPort class - for various reasons it isn’t possible to have a gadget that exists on both displays, which the Woopsi gadget attempts to do. To fix this I’ve added two decoration screens to the Woopsi gadget that sit behind all other screens. They ensure that exposed areas of the Woopsi gadget are cleared correctly.

Here’s a demo of the new textbox/keyboard functionality:

Double-click the textbox (move the debug screen out of the way to see it) to bring up the keyboard. You will be able to type into it, delete characters from it, and close the keyboard by tapping return or OK.

Categories: Woopsi Tags: ,

Active TextBox and the Keyboard

February 13th, 2010 Ant 1 comment

One issue that still remains in Woopsi is the question of how the active textbox should be represented visually. One possibility that I tried was to make the cursor visible only when the textbox had focus. However, when a textbox is being used for input, the active textbox does not have focus; the keyboard does instead.

I’ve got another possible solution for this. When a textbox is clicked:

  • Flip the bottom screen to the top display.
  • Automatically open a new keyboard screen on the bottom display.
  • Automatically wire up the keyboard to the textbox so that the textbox receives input from the keyboard.
  • Automatically wire up d-pad events so that the textbox receives cursor left/right/up/down key presses from the keyboard.
  • Add a “close” button the keyboard so that it can be closed.

When the keyboard’s close button is clicked:

  • Close the keyboard screen.
  • Flip the top screen back down to the bottom display.

Thoughts? One objection is that clicking the textbox accidentally will cause the entire display to shuffle itself around. In that case, perhaps the textbox should have an “edit” icon somewhere? Or need to be double-clicked before it could be edited? Or should I not worry about this at all, and leave it up to the developer to implement their own solution?

Categories: Woopsi Tags: , ,

Simplifying the API Part 5

February 13th, 2010 Ant No comments

More tidying up and throwing away. A while ago I altered the TextBox so that it would scroll to follow the cursor. The code worked but it was ugly, and the scrolling effect was ugly too. The text was always aligned to the left of the textbox, so if the cursor scrolled to the right partial characters would appear on the right-hand side of the box. What a user would expect to happen is see the characters on the right aligned nicely whilst the characters on the left were partially visible. I ripped out the existing code and replaced it with a much improved system.

I’ve removed the “_padding” variable from the Label, Requester, FileRequester and Alert classes. This variable was used to force the gadgets’ children to leave a border between themselves and the real gadget border. I’ve replaced it with the newly improved border code. Related to this, a number of gadgets have improved and simplified drawing routines.

Finally, I’ve added a new set of XOR drawing routines. These accept a colour parameter to XOR against, allowing for more interesting XOR effects.

Categories: Woopsi Tags: , , ,

Simplifying the API Part 4

February 11th, 2010 Ant No comments

The basic Woopsi gadget includes the concept of a border. This is one pixel wide and surrounds the entire gadget. It can have one of a variety of appearances, such as bevelled in, bevelled out, bevelled depending on whether or not the gadget is clicked, and so on. Other gadgets, such as the AmigaWindow and TextBox, have wider borders. The AmigaWindow is particularly unusual because its top border has a different size to the other borders. It eschews the standard border appearance, opting for a border built out of other gadgets instead.

Up until now, the border system has not been implemented very well. Most of the gadget calculate the amount of space taken up by their border using something like this:

u8 width = (!_flags.borderless) << 1;

If the gadget has a border, this evaluates to “2”. If not, it evaluates to “0”. Efficient, yes. Maintainable, no.

Instead, the Gadget class now includes a “GadgetBorderSize” struct, that contains individual sizes for each of the four borders. All of the hacky bitshifting code has been removed and replaced with calculations based on the new struct. This has allowed me to remove all custom “getClientRect()” methods from all gadgets and make the method in the base class non-virtual.

Next, I have removed the “bonus” folder, which contains the skinned gadgets, bitmap I/O classes, hashmap and linked list classes, out of the Woopsi section of the SVN repository and into a new section called “extras”. I’ve decided not to include these in any future distributions, at least until Woopsi 1.0 is out. Too much scope creep will prevent me from ever finishing this library.

To trim some of the flab from the API, I’ve been cutting unused functionality throughout the codebase. The following methods have been removed:

  • Gadget::clear()
  • Gadget::clear(clipRect)
  • Gadget::newInternalGraphicsPort(isForeground)
  • AmigaWindow::getBorderSize()
  • Screen::getBorderSize()
  • TextBox::getBorderSize()
  • Screen::getTitleHeight()
  • Window::getTitleHeight()
  • Gadget::getBackgroundRegions()
  • Woopsi::closeChild()
  • Woopsi::shelveChild()
  • Woopsi::release()
  • Woopsi::drag()
  • Woopsi::click()
  • Woopsi::shiftClick()
  • Woopsi::shelve()

Many more of the Gadget methods that used to be virtual cannot be overridden any more. This prevents essential pieces of functionalty being replaced.

The GraphicsPort has seen a few changes. It no longer tries to delete a null pointer if its clipRect has not been allocated. More interestingly, the gadgets use the GraphicsPort in a different way. Previously, a gadget would define the code to be called when it was to be drawn like this:

void draw(Rect clipRect) {
    GraphicsPort* port = newGraphicsPort(clipRect);
    port->drawFilledRect(0, 0, _width, _height, woopsiRGB(31, 31, 31));
    delete port;
}

Behind the scenes, this was very inefficient. A GraphicsPort object was created and deleted every time this method was called. As this method is called for every visible region of the gadget (and there could be dozens of visible regions), GraphicsPort objects were being created and deleted far too often.

The draw() method has been replaced with two new methods: drawBorder(), which can draw across the entire visible surface of a gadget (including its border space, hence the name) and drawContents(), which can only draw inside the border. The code above would look like this in the new model:

void drawBorder(GraphicsPort* port) {
    port->drawFilledRect(0, 0, _width, _height, woopsiRGB(31, 31, 31));
}

In the new design, two GraphicsPorts are created every time the gadget is redrawn. One can draw to the border area, whilst the other can only draw within that area. They are re-used for every region of the gadget drawn during that redraw operation.

It is still possible to get the clipping region by calling the GraphicsPort’s new “getClipRect()’ method.

The Gadget class’ GadgetStyle object used to be allocated using “new” and referenced with a pointer; it’s now allocated as a standard object. There’s no real reason for this change other than it’s a little tidier.

There were a number of fiddly problems with the Gadget::closeChild() and shelveChild() methods, which are now fixed. They were causing the focus to jump between objects even if the closed/shelved gadget did not have focus.

Lastly, I hit a problem with the shift-click code from yesterday. The clicks were allowed to propagate back up the hierarchy, which caused nasty problems. If the front-most screen did not contain a context menu, the screen below it would be sent the shift-click. If this screen had a context menu then it would be displayed despite the screen itself being totally obscured by the screen above it. I’ve added a new “checkCollisionWithForegroundRects()” method to the Gadget class that fixes this problem. It checks to see if the click falls within one of the regions of the gadget that is not obscured by its siblings or ancestors. If not, the shift-click is ignored.

Simplifying the API Part 3

February 9th, 2010 Ant No comments

When I introduced the context-sensitive shift-click menu I ran into a bit of a problem. Clicks are automatically propagated down through the gadget hierarchy until the gadget that the stylus is touching is found. But what happens if we’re trying to capture a shift-click on a gadget that isn’t exactly the gadget that we clicked? Suppose we shift-click a button, but we want the window that contains the button to show a context-sensitive menu?

My solution was to introduce a new flag in the Gadget class, “shiftClickChildren”, that would prevent clicks from propagating down if it was set. This definitely worked, but it added more complexity to getting the context menu working. It also introduces some subtle problems - a gadget cannot have a context menu if its parent has a context menu.

I’ve changed the way that this works and removed the shiftClickChildren concept from Woopsi. Instead, shift-clicks propagate down through the hierarchy, but then propagate back up again until a gadget is located that defines a context menu. Thus, if a window contains a context menu definition it will be shown regardless of which sub-gadget is clicked. If one of those sub-gadgets contains a context menu definition, however, that will be shown instead.

Much simpler.

Along with this, I have fixed the Window gadget’s dragging routines so that they work correctly if the window’s parent is not a screen. In other words, windows can now contain other windows without it causing graphical glitches, should you feel like putting such an abomination together.

Lastly, I’ve added test projects for the Alert and Requester classes.

Simplifying the API Part 2

February 8th, 2010 Ant No comments

In a continuation of yesterday’s refactoring session, I have added a bunch of new stub methods:

  • onKeyPress()
  • onKeyRepeat()
  • onKeyRelease()
  • onFocus()
  • onBlur()
  • onLidOpen()
  • onLidClose()
  • onEnable()
  • onDisable()
  • onResize()

As with the methods listed yesterday, these are now the methods that should be overridden in gadget subclasses instead of the old disable(), enable(), blur() (etc) methods. I have made the majority of the old methods non-virtual (should that be “concrete”?) to prevent them from being overridden.

This has several benefits. Firstly, it is now no longer necessary to know exactly how the methods should work before they are overridden. For example, overriding the resize() method was particularly complex, requiring at least two dozen lines of code to work correctly, or a dozen if you’re willing to live with a redundant call to redraw().

Overrides of the new methods need not contain any code at all, which is a huge improvement. Not only are the stub methods easier to override and implement, but they are faster and more resilient too. Good news all around.

Related to this, I have added onResize() methods to the Alert, Requester and FileRequester classes. These gadgets should now resize correctly.

Finally, I’ve increased the accuracy of the calculations in the scrollbars. They were previously using a fixed-point calculation with an 8-bit fractional part. I’ve changed this to a 16-bit fractional part and the remaining glitches have disappeared.

Categories: Woopsi Tags: , ,