2007-12-06

A Few Other Things

A few more things sorted out today.

(Rummages around for the changelog.)

I’ve fixed the scroll limit and drawing problems in the TextViewer. It now works as well as it ever did (ie. still no clipping). The bugs were all caused by the off-by-one problem in the TextWriter.

The Gadget class no longer has a _deleteQueueActive variable - this info is extracted from the size of the deletion queue. Much more sensible. The erase() function now calls the rect cache invalidation routines - previously, closing a window didn’t redraw lower gadgets properly. Some more of the gadgets now use the GraphicsPort, notably the TextBox (and therefore the Button class, and any others that inherit from the TextBox but don’t override its draw() method).

Gadgets are set to invisible by default when created, and are only made visible when they are added to a parent object (except the Woopsi class, which is visible by default). This prevents the gadgets from being drawn before they can get their parent’s co-ordinates, so there isn’t a mess of gadgets visible in the top-left of the bottom screen when the system initialises.

One point I should make a note of regarding the invisible flag - children do not check their ancestors’ visibility before drawing themselves. This is bad.

For some reason, the Woopsi class was set as a decoration. I’ve removed that line.

Destructors are now virtual when appropriate. Previously, none of them were virtual, despite the classes containing virtual methods. I’m told this is a bad move, which makes sense.

Think that’s it for today. Lastly, SourceForge is back up again, and the SVN code is all up to date.

EDIT:

Oops, put the width decrement in the wrong place in the horiz line function and broke everything. Now fixed. Also removed logo.c, as it used up 100K in the demo ROM and wasn’t being used.

Comments

Jeff on 2007-12-06 at 02:33 said:

How about ‘constifying’ ? Last time I looked there were lots of routines that didn’t declare their args as const which meant that you couldn’t use the pagfx generated headers

Jeff on 2007-12-06 at 09:59 said:

Looks to me like something is broken. I thought it must be my code but the same symptom shows in the demo thats downloaded from SVN.

Specifically, the Screen title bar looks like its being asked to draw its text with one too few pixels, or its missing the middle pixel in the row. When you drag the screen down the ‘hardware screen’, you see the whole thing distort. Window titles look pretty crappy too - like the font has been scaled up or something??? In fact, the text in the multi-line text window looks like crap as well.

Initially I though it wass something to do with the clipping rectangles - it gets distorted if its immediately over the top of the bottom line in an underlying window. But closing all the underlying windows, you still get the distortion. Its only really visible when you drag screens (since dragging windows drags an outline, not the actual image).

Perhaps you didn’t check your TextViewer change in? Or its done more damage than good???

Jeff on 2007-12-06 at 10:05 said:

Have you ever noticed how saying “Hey, look at this” is the #1 best way to nail down an embarassingly stupid bug?

Sorry folks, nothing to see here. For those who don’t want to be embarrassed like I am now, it helps if you don’t resize your stupid DesMume window. Yes, somehow I had scaled it down a smidge, and no amount of ’re-opening’ the ROM was resizing it. When I put it back to 1x, it all snapped back to perfect font rendering again.

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

I’m still wrestling with the off-by-ones that are so easy to do, but for the most part its looking pretty easy.

One question. Isn’t Screen::getClientRect() supposed to remove the border and titleheight from the available space? At the moment, it returns a rectangle that includes the decorations.

In fact, decorations are another question. I’m cool on the theory, I was just wondering whether I am allowed to define any, or Window/Screen reserve that right for themselves? I was thinking that my backgrounds (I have one solid, and one gradient working) should be decorations (though that may be unnecessary since they don’t repond to clicks, etc)

ant on 2007-12-06 at 11:13 said:

If you set “_flags.decoration = true” in your class’ constructor, it is automagically added as a decoration gadget in the addGadget() function. If you’re not dealing with subclasses, I’ll probably need to write a “demoteToDecoration()” function that will set a gadget’s decoration flag and move it down its parent’s gadget vector into the decoration space (and increase the decoration count). I’d need the opposite, too - “promoteToGadget()”. I’m not sure if there’s a setDecoration(bool) function yet - if I add that, you could do something like this:

Background* background = new Background(); background->setDecoration(true); screen->addGadget(background);

It’s better to define backgrounds as decorations. If you look at the Screen::addSuperBitmap() function, it calls the SuperBitmap’s constructor with “true” as the value of a new “isDecoration” parameter. This makes the SuperBitmap into a background decoration. The Screen::addSuperBitmap() code will eventually get replaced with Screen::addBackground(), but you can see the theory at work.

Speaking of which, I need another reserved area at some point at the upper end of the gadget vector for gadgets which must always be in front of others.

The Screen is unusual, in that its decorations (title bar, etc) can be overlapped. This gives us an Amiga-style title bar that effectively uses up no space, instead of a Mac-style title bar that can’t be overlapped. The client area is basically the entire screen, which is what the getClientRect() function (and therefore the newGraphicsPort() function) returns.

All gadgets implement the concept of decorations, so any subclasses you create can have decorations.

Jeff on 2007-12-07 at 02:41 said:

Yes, it does take getting used to being able to overwrite the screen title bar, and I understand that thats by design; anyway, I can always subclass if I hate it too much; I’ll let you know when it doesn’t work ;-)

Directly accessing the _flags field is a bit dubious, in my opinion. However, I can see that it can be done. I guess I was concerned as to whether you expected it to be done.

You may well have been making assumptions elsewhere; for example, I think its Window used to assume that if it has a title bar, then the first three(or four) gadgets must be the ones that it inserted. Thats a tad unsafe to assume - I can see where I might have been tempted to slip a background in as gadget 0, making it easier than having to ask each of the other decorations “how big are you” and then resize the background appropriately.

In general, I suspect that the ability to turn on/off the borders may be being a bit TOO flexible in windows and screens - I can’t think of a reasonable application that would want to do that.

If you added a new subclass “AmigaScreen” and “AmigaWindow”, I could choose not to compile them into my app and be done with it. Note, this isn’t the same as just Screen passing the flag bit in - AmigaScreen goes OVER Screen (ie, its a SUBCLASS of Screen) and then Screen does not contain all the amiga-oid decoration stuff. That way, I don’t need to compile any of the superclass decoration classes into my binary either. Cutting down on that whole “forcing stuff on the GUI user” issue - 4MB still feels like a really tight code space.

I don’t think the same flexibility is necessary in all the other gadgets since there would be a lot less overhead being removed.

I can definitely see where I want a Screen with a title bar, but no depth gadgets (because I’m going to be flipping the screens around under my own control scheme) but to do that, I need to fake out stuff in my subclass of Screen (ie, I use your title Gadget, but not your depth changers) - again, I’m not sure what assumptions this might break

As to “gadgets that must always be at the front”, what is the application need that isn’t addressed by telling the application “add them last” ? I can see where that makes adding “non-topmost” gadgets a bit more complex, but seriously, would an application really need to do that? And if it did, can’t it do the searching itself? I’m assuming that all you really need to do is look backwards through the gadgets array for the first gadget that has _flags.TOPMOST zero.

Perhaps a TOPMOST gadget might be useful for drag/drop type operations, but since they’d be created last, they’d already be topmost surely?

The issue with TOPMOST over on Win32 is that you can have more than one - as such, they complicate the ‘pull it forward’ logic since pulling a non-TOPMOST forward doesn’t bring it to the “real” front, whereas pulling a TOPMOST forward does. (Whats worst on Win32 is that XP doesn’t remember the order as well as it should and sometimes does not deliver “you became the front” messages to a newly exposed TOPMOST window)

Jeff on 2007-12-07 at 03:00 said:

Actually, the “forcing stuff on the user” also raises the issue about the Window::newButton(), etc

Because those methods are not ‘inline’, they can’t be left out by the compiler if you don’t use them. And if they aren’t left out, then the classes they refer to can’t be left out, etc.

Thus, my app is including the radio button group code, even though I don’t use it myself, because the Window class thinks its providing a convenience…

ant on 2007-12-07 at 10:38 said:

Window and Screen used to make assumptions about the positions of their decorations, but that code got removed when I added the decoration concept in.

Being able to switch between bordered and borderless wasn’t originally part of my plan (in fact, I made a decision to leave it out), but it’s in there now so I may as well leave it in!

Splitting the Screen and Window classes is actually a really good idea. If I split out all of the standard decorations and the addSuperBitmap/addWindow/etc helper functions from the basic Screen class, that will give me a generic, empty Screen baseclass that I can then inherit from to do other things. I’d add in an AmigaScreen class that contained the Amiga borders, a SimpleScreen class that extended the AmigaScreen and included the helper functions (so that they are there for less adept programmers, but don’t get in the way of everyone else), and a SkinnedScreen class (that uses a still-to-be-written BitmapScreenTitleBar class) for skinned interfaces.

I’d do the same to the Window class, and have Window, AmigaWindow, SimpleWindow and SkinnedWindow classes.

The basic Screen class then gives you a bare-bones class to which you can add your own decorations - you can add the title bar/buttons if you need them, or leave them out if you prefer. I don’t think there are any assumptions about any of the decoration gadgets, with the exception of the getTitleHeight() and getBorderSize() functions in both Screen and Window classes. The base version of that could just return 0, and subclasses would just need to override it as required. There might be some others, but they’re all easy for me to work around.

The topmost gadget comes in handy solely for depth swapping. If you want a window to always be in front of the others (imagine you’ve made a taskbar-style or dockbar-style window), at the moment it will get depth-swapped out of the way if a window behind it is clicked. I would make the depth-swap routines ignore any gadgets within the topmost region of the vector (same as decorations). That way, if multiple gadgets exist in the topmost region they will always appear in the order in which they were added to the screen.

It’s worth expanding on the SimpleScreen and SimpleWindow idea outlined above, with regard to the second post. As I understand it, if I shift the helper functions (addButton, addTextbox, etc) out of Screen and into SimpleScreen, that will solve your problem - if you don’t use SimpleScreen and you don’t use RadioButtonGroup, neither one will be compiled into your code. At the same time, anyone who doesn’t want the hassle of instantiating their gadgets with the full constructor and manually adding them to a parent can use the shortcuts. Have I got this right?

Jeff on 2007-12-07 at 11:53 said:

Yes, from what I can see, that should do it, though I’m sure that some people will be confused about the class hierarchy

Screen <- AmigaScreen <- SimpleScreen <- SkinnedScreen

Perhaps you could make it

ScreenBase <- Screen <- SimpleScreen <- SkinnedScreen

or something similiar. I expect that the “base screen class” is far more likely to be subclassed in apps so giving it a “base”ish name seems appropriate.

ant on 2007-12-07 at 11:56 said:

That’s nearly it - I was going to have this hierarchy:

Screen <- AmigaScreen <- SimpleScreen

And this hierarchy:

Screen <- SkinnedScreen

The skinned version needs to use new bitmap versions of the decoration gadgets, so it doesn’t make sense to inherit from AmigaScreen.

Calling the base class “ScreenBase” is a good plan.