2008-10-28

Dates and Text Wrapping

Couple of changes today. The Date class introduced yesterday now has a more optimal “calculateWeekDay()” method, thanks to Jeff. It still seems to work correctly with the calendar, which is curious, because I’m sure it was previously returning “0” as the value for Sunday whereas it now returns “7”. I must be doing some modulus stuff in the Calendar class.

Secondly, the “wrap()” function in the Text class should now be rather faster than before. Instead of re-wrapping the entire string every time something is inserted, appended or removed from the string, it now locates the line of text that contains the modification and re-wraps from that point forward.

I’d tried to implement this a few days ago but came across a couple of problems. First of all, locating the line of text in which the modification fell was slightly tricky. I’d decided to use a binary search (obvious enough), but came unstuck because I was looking for a value that fell within the range indicated by two values in the wrapping data array. I wasn’t searching for an exact match, but a match between two values. Turned out to be fairly easy to implement once I’d scratched it down on paper.

Secondly, the wrap() function remembers the width (in pixels) of the widest line of text to help with other routines elsewhere in the class. If we try to wrap the text from line n, ignoring the previous lines, and the widest line is actually n-1, we won’t have the correct width. We’ll just have the width of the widest line in the range from n to the last line of text (if we ignore the old value we stored) or the width of a line that may no longer exist (if we use the old value).

The solution to that was pretty straightforward, too. I added a second vector to store the widths of every new widest line that the wrap routine comes across, along with the index of that line in the wrapping data vector. So, if the first line is 10px wide, that’s the widest line we’ve seen up until that point, so we add width 10 and index 0 to the vector. If the second line is 8px wide, we ignore it as it is thinner than the currently-identified widest line. If the third line is 20px wide, we add width 20 and index 2 to the vector. That way, when we re-wrap from line n, we discard all of the longest line data from the vector from line n onwards. The last entry in the width vector is then the width of the longest line in the text we’re not re-wrapping.

The upshot of all this is that if a char is appended to a string 100 lines long, only the last line is re-wrapped. Previously, all 100 lines would be re-wrapped.

2008-05-14

An Assortment of Woopsi Changes

It’s been quiet around here lately. The weather has been gorgeous and I’ve been enjoying the first sunny week for 18 months. Knowing the English weather this is probably all the summer we’ll get. It’s already getting colder and I’m told it’ll be raining again by Friday.

Anyway, I’ve made a few fixes and changes that I haven’t got around to documenting yet. Most of the fixes are things that have been raised in the forum. Jeff pointed out an overflow problem with the slider grip calculations, which is now fixed. He also noted that the grip didn’t move as expected when the gutter or scrollbar buttons were clicked; that’s fixed too. The Gadget::addGadget() method will now apply the focus properly if a gadget is added that thinks it already has focus, and I’ve made a number of improvements to the focus system.

The way Woopsi handles clicked gadgets has also been improved. Rather than having a chain of clicked gadgets throughout the hierarchy, the Woopsi class now has a single pointer to the clicked gadget itself. Much tidier. Getting this working meant re-factoring some of the window and screen gadgets, which now rely more on straightforward event handling and have a lot less kludgy code.

Lastly, one for John - the MultiLineTextBox class will now handle single-line text properly. There were a number of problems with it. Firstly, the Text::wrap() function could get into a circumstance where it would read past the string terminator. Secondly, the Text::stripTopLines() function wasn’t recalculating the wrapping locations, which meant that it thought it had more lines than it actually did, and it didn’t know where those lines started or finished. Lastly, the MultiLineTextBox::setText() function wasn’t resizing its canvas properly. Along the way I fixed two of the vertical alignment options, top and centre, but the bottom alignment is still broken. There’s still a lot of potential improvements that could be made to this class.

There are a few new features. There’s a new “EVENT_RELEASE_OUTSIDE” event which gets fired when a gadget is released with the stylus outside the boundaries of the gadget. This is in addition to the usual “EVENT_RELEASE”, which gets fired when the stylus is within the gadget boundaries. Usually we’re only interested in the standard release event (for standard buttons), but there are situations where it is important to handle any release, such as releasing the window titlebar (in order to drop it to its new location).

Related to this, the EventArgs struct has two new properties - “eventVX” and “eventVY”, which are mainly used by the “drag” event to report how far the stylus was dragged.