2007-12-08

Fonts 'n' Stuff

More progress with fonts. The system font is now called “sysfont”, and has been moved out of the “all_gfx” files into a separate “sysfont.h” include. It’s also been moved into the main woopsi folder.

I’ve scrapped the font inheritance idea - it caused more problems than it solved. Instead, the Woopsi class now has a static “getSystemFont()” method that will return a pointer to the default font. If a gadget’s font is not specified when it is constructed, the gadget will automatically fetch its own pointer to the system font and use that instead. The font parameter on all gadgets is, therefore, now optional, and has been set up as such in all of the constructors (this means that the parameter order has now changed on every single gadget). Gadgets that do not need a font (ie. window borders) no longer have the parameter in their constructors.

Gadgets that use the Text class for their text manipulation need to update that class’ font pointer too. I’ve added that functionality into the TextWriter and MultiLineTextBox classes, and added the “setFont()” function into the Text class.

There are a few fiddly changes - Woopsi uses a sensible value for its height, instead of an arbitrary number I made up to make the clipping work. Private class members are now set to protected where necessary - they were only set to private in the first place out of habit (I find that simple business apps rarely require clever subclassing, or cleverness in general, so I find myself using private by default).

I’ve removed the “removeGadget()” function I added a week or so ago. I don’t quite know why I didn’t see this at the time, but that function did little more than duplicate some of the functionality of the “close()” function. Unfortunately, it didn’t duplicate all of the functionality, so it was doing little more than causing problems.

One long-standing problem with window borders has been the way that they decide what colour their background should be. They were abusing the focus() system - when a window received or lost focus, it sent a focus() or blur() command to all of its border gadgets. This technically meant that the window had up to 7 active gadgets at any one time (6 border gadgets and one other gadget) but did not know that the borders were active. Nasty bodge, but it wasn’t a problem until I used border->close() to remove the border gadgets. The close() function tells the gadget’s parent that the gadget is losing focus (if it has focus), and that bubbles up the gadget tree. Thus, calling setBorderless(true) made the borders lose focus, which confused the window and lost focus there too.

Window borders now use the parent’s “isActive()” function to determine which colour they should be, and when a window gains or loses focus it just tells its borders to redraw. Much nicer.

Whilst tinkering with the border code, I noticed that making a gadget borderless didn’t invalidate its visible region cache. That’s now fixed.

Finally, the SuperBitmap class now includes the floodfill routine I wrote the other day. One more item knocked off the to-do list! It improves on the original routine by using the colour of its starting pixel as the “oldColour” parameter.

In unrelated news, I’ve found another bug in the devKitPro compiler. I documented a bug with arrays a while ago - trying to create an array with more than 32767 elements (ie. more than the upper bound of a signed short) causes overflow problems and crashes. The only way around it is to use malloc(). The new bug is the same kind of thing - trying to create a multidimensional array with more than 255 elements in the first array (ie. char arr[256][23]) also causes overflow/crashing problems.

Comments

Jeff on 2007-12-08 at 21:58 said:

I was sure I put a bug in about needing removeGadget() but I can’t find it now.

Anyway, I suspect there’s still a bug there, though its a subtle one. I have one screen with a button - pressing the button creates a second screen, with no title bar (its modal) - it has a button which makes itself go away again, and the first screen becomes visible again.

If you drag the first screen down the hardware prior to starting, then the _secondscreen->close() does not redraw the ‘grayspace’ above the first screen. ie, the top of the second screen remains visible, though if you drag the first screen up then down, it goes back to being gray.

I hacked the app to just have the first screen close() itself when I pushed the button, and the expected behaviour occurred - that is, the bug manifested; the screen becomes completely non-responsive because its not really there, but its graphics have been left on-screen.

I’d say its a matter of gadget->erase() not erasing but relying on invalidating whatever is under it and relying on that to redraw - in this case there is nothing underneath it.

Jeff on 2007-12-08 at 22:06 said:

I should point out that prior to learning I was supposed to be using close(), I was doing:

void removeScreen(_screen) { theApp->eraseGadget(_screen); theApp->removeGadget(_screen); }

and that was working perfectly, redrawing the gray “desktop” space when the second screen was removed. I’ve tried things like this:

    _screen->hide();
    woopsiApplication->draw();
    _screen->close()

but it still looks like the visible rectangle stuff is confused.

Jeff on 2007-12-08 at 22:22 said:

Its easier to show than to tell.

http://rapidshare.com/files/75255115/source.zip.html

click the browse button. click the cancel button. looks ok drag the menu screen down half way click the browse button click the cancel button. top half of the list still shows drag the menu screen up and down top half of the list gets erased finally

ant on 2007-12-09 at 01:12 said:

I think the latest SVN code should fix it. Woopsi’s draw(rect) function wasn’t doing anything, so when the screen closed the vacant space didn’t get wiped.

Looking through this section of the Woopsi code, I think I can improve the Screen::drag() function, remove the Woopsi::eraseRect() function, and tidy up the way screens swap depths. At the moment, if you send a screen to the back and then click on the front screen, it flickers as it redraws. It doesn’t need to redraw at that point. I also think I can improve gadget drawing in general - at present, calling draw(rect) will draw the background of a gadget whether it is visible or not. The draw() function needs to:

  • Create a new vector of visible rects;
  • Split those rects with the gadget’s immediate children;
  • Draw the gadget’s children;
  • Draw the gadget.

ant on 2007-12-09 at 01:29 said:

OK, the draw() function now does all of that, which has fixed the majority of the flicker in the screen. I can still sort the other things out, though.

ant on 2007-12-09 at 01:52 said:

I like the lister gadget. That’s the sort of thing I’m planning on doing for the ListBox, which will be one of the main components of the file requester.

Jeff on 2007-12-09 at 04:20 said:

Yes, I’ve compiled in the latest updates and the problem is gone. Thanks for that.

Once I’m done with the List, and if I get there before you, I’ll send it on as per the Gradient - I’m happy to contribute these sort of things, its easier for me if they become part of the standard library.

One thing I’m not completely happy about is the notification from List to enclosing screen. As you can see, I’ve nobbled the raiseClick and raiseRelease from inside the Lists click() and release(), because the List had sent its own “higher-order” notification, selectionChanged().

Its clunky because the ListSource is also assumed to be the ListOwner who is interested in selection events. Whilst thats true, its not a given.

It seems to me like it would be worth adding one more field to EventArgs that allows ‘application-defined’ events to be raised. It doesn’t need any more fields (because you can add those by subclassing EventArgs) but it would be helpful if we had Gadget::raiseAppEvent(). ie, my list currently does:

if (_source) _source->selectionChanged(_selrow);

That would change to

ListEvent e; e.code = List::SELECTION_CHANGED; e.newSel = _selrow; this->raiseAppEvent(e);

Another thing I haven’t gotten to the bottom of is that I want the list to process UP and DOWN keystrokes, but pass the rest (A,B, etc) on to its “caller”. My FileSelector.cpp would then recognise A as and B as in the same manner that most DS games do. However, the keystroke seems to be getting lost.

The code in question is:

bool List::keyPress(KeyCode keyCode) { if (!_flags.enabled) return false; switch (keyCode) { default: raiseKeyPressEvent(keyCode); break;

    case KEY_CODE_UP:
            if (_selrow > 0) {
                    changeSelection(_selrow-1);
            }
            break;

    case KEY_CODE_DOWN:
            if (_selrow < _nrows-1) {
                    changeSelection(_selrow+1);
            }
            break;
    }

    return true;

}

and I thought that raiseKeyPressEvent() does not pass the event along to the enclosing gadget, it passes it to the EventHandler for the list. However, the FileSelector is the event handler for the list but it does not seem to be firing.

Jeff on 2007-12-09 at 04:22 said:

HTML!!!! AAARRRHHH

I meant to say “A as [select] and B as [cancel]”

Jeff on 2007-12-09 at 04:35 said:

And, of course, the “look at this” factor cuts in once again. Stupidly, I had the method name wrong when declaring my EventHandler for the enclosing class. I was providing keyPress(), not handleKeyPress().

And the default case in List::keyPress() should be calling Gadget::keyPress(), not raising the keyPress event itself. Still, I’m getting there - once I have one nicely formatted “modal input window” written, the rest should follow on. I try to only make each mistake once. “Try” being the operative word.

ant on 2008-01-25 at 22:26 said:

Replying to an old post!

The array crash isn’t a compiler bug. The DS only has a 16K stack, so trying to allocate large arrays overflows the stack and crashes it. Allocating memory on the heap instead by using malloc() avoids the problem.