2007-12-06

Clipping to Parents

Gadgets are now clipped to their parent’s dimensions.

(Cue round of applause and champagne corks.)

This was incredibly difficult to implement. First of all, I needed to write a routine that would return a clipped rect - if the x value of the gadget is less than the x value of the parent, use the parent x, and if the gadget’s (x + width) is greater than the parent’s (x + width), set the width of the rect to the difference between the two.

Once I’d got this, I could amend the visible rect caching function to use the clipped rect’s dimensions instead of the gadget’s. After that, I could amend the rectangle splitting function to use the same routine. The collision detection routines needed to be updated, too.

Doesn’t sound to difficult. In truth, it wasn’t. None of it worked, though, which was the problem. There seemed to be something very strange happening with the screen class - all of its children drew themselves OK, but the screen refused to co-operate. It just wouldn’t draw itself at all. Memory leak? Another deleted pointer? Maths problems? I went through everything over and over again, but couldn’t find anything wrong.

In the end, I modified the Debug class to output a non-transparent background so that I could see what it was writing out, and started printing values to the screen. Seems the Woopsi instance was reporting its width as 0 - aha! The clipping routine only goes up one level, so if the screen says it is 256 pixels wide, all gadgets will work on the assumption that they have 256 pixels to play with. When the screen tries to draw itself, though, it clips to Woopsi’s width of 0 pixels and nothing gets drawn.

It turns out that I’d got the parameters in the call to the base Gadget class the wrong way around in Woopsi’s constructor. Fignuts.

Other things I’ve fixed include marking the WindowDepthButton as a decoration, making a minor improvement to the Gadget::getClientRect function (missed out some pointless re-calculation of values), removing Woopsi::removeOverlappedRects (as it did exactly the same thing as the version in the base class), and re-writing the Gadget::eraseGadget function. This now handles gadget erasing using the gadget’s own visible rect cache, instead of bubbling the request up to the Woopsi instance and allowing that to send the redraw request back down through the hierarchy. As part of all of these changes, I’ve fiddled around with various functions that either invalidate the rect cache or call the cache function.

Hopefully I haven’t broken anything, but there’s a lot of fiddly changes here.

Pressing the “A” button in the latest SVN demo will cause the uppermost bitmap button tp move one pixel to the right. If everything has gone to plan, it gradually disappears into the edge of the containing window.

This should lay the groundwork for exciting new things such as scrolling panels (think SuperBitmap-style scrolling, but with scrollbars, that can have developer-defined “allowHorizontalScroll”, “allowVerticalScroll”, “maxHorizontalScroll” and “maxVerticalScroll” values, and that can contain child gadgets). If that works as I hope, I should be able to put together my AmigaGuide interface (scrolling text panel interspersed with buttons) without any real effort. (Aside from, y’know, making the gadgets and everything in the first place.)

Comments

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

The question of coordinate systems remains, I think. Do you place the child gadgets inside a window using screen coordinates? Does the childs (x,y) take the windows decorations into account?

Your scrolling panel will need to recognise that it should scroll gadgets, but not decorations, I think? Which begs the question of whether I’m allowed to mark my own gadgets (think: background) as a decoration or not?

Or are you planning on embedding the scrolling panel (I’ve seen them called “panoramas” elsewhere) inside a window rather than have the window be the scrolling panel itself…

ant on 2007-12-07 at 01:06 said:

The co-ordinates system is still on the to-do list, along with const-ness and the other things. Getting there slowly…

Hmm, not sure about decorations. Good question. What if you give the scrolling panel a background?

I’d embed the panels into windows in the same way as the SuperBitmap - just another gadget. Scrolling the window would be horribly difficult.

I think I covered the decoration issue here, but let me know if I missed anything or didn’t answer the question properly:

http://simianzombie.com/?p=204#comment-461

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

You don’t have a fixed definition of “background” - as such its a bit hard to answer.

A superbitmap DECORATION would qualify as a background, provided it was at the correct level in the Z order.

(I’ve commented on your other post as well)

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

A “background” class is on the list of things-to-do. Well, not on the SourceForge list (yet), but in the list in my head.

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

In the interests of giving rather than just taking, have a look at

http://rapidshare.com/files/74897116/gradient.zip.html

which is my rudimentary gradient background. You invoke it like this:

AboutScreen::AboutScreen() : Screen(“”, NULL), _gradient(NULL) { setBorderless(true); _gradient = new Gradient( 0,0,_width,_height, PA_RGB(21,21,21), PA_RGB(31,31,31) ); addGadget(_gradient); }

Its not as pretty as I was thinking, but its a start - I suspect it needs to be shifted into HSL space rather than RGB, but that costs a bomb in terms of floating point math.

It currently expects to be a full-screen gradient that doesn’t move, but it should be relatively straight-forward to recalc its internal constants if the gadget size changes

Feel free to do with this what you will, up to and including attaching your own name to it and establishing WoopsiSoft Corp (if you want). I’m not particularly interested in researching licenses, etc. Its a gift, if you want it.

ant on 2007-12-07 at 12:45 said:

Hey, thanks! I’ll have a look at this. Incidentally, I’m going to add you to the credits section of the readme under the heading “tester and technical advice” - let me know if you have any objections!

EDIT:

I’ve fiddled with the names and formatting a bit to follow the rest of Woopsi’s code. Looks good. I’ve also extracted the gradient calculation routines into a separate function and added the resize code. One thing I’ve noticed is that the drawing routine is unclipped (you’ve noted this in a comment). This isn’t a problem when drawing the gradient, because the GraphicsPort will automatically clip to the display. It does mean, however, that the entire gradient has to be calculated for every draw operation. I wonder if it’s worth caching the colour calculations from the draw() function in the new calculate() function, then using those cached values to pre-clip to the clipRect in the draw() function?

ant on 2007-12-07 at 17:02 said:

Added the gadget into SVN, with row colour caching code in place. Caching only uses a maximum of 192 u16s (384 bytes, plus whatever overhead there is in padding, etc) so I think it’s worth wasting the memory.

Jeff on 2007-12-07 at 20:33 said:

No problems with credit, etc. I’m happy to contribute.

Yes, it didn’t do the clipping because I’m still a little unclear on when I need to do the clipped version and when not to. I got caught out in another one I have, which is just a SolidColour - just an exercise really, not that exciting. I only implemented the draw(void) member and had it do a port->drawFilledRect()

When I dragged that screen to the bottom of the hardware, then dragged it back up again, I noticed that a button I had put over the top of the background was smearing back up the screen. By this I mean that the button drew correctly, but the rectangle didn’t redraw where the button had been - when I was more careful about it, I noticed that the window title bar also smeared.

It looked like the draw(void) member did not correctly recalc the clipping or something during a screen drag.

When I switched it across to draw(clipRect), it worked fine so I put it down to “oh, thats how its supposed to work” and left it at that. The Gradient I was only using on the top-screen so it didn’t bother me.

As to the caching, you could push the _numberX and _deltaX values into cacheRowColours() as well since they are only used to determine when to switch and by how much - In fact, all the logic in calculate() should be in cacheRowColours()

Jeff on 2007-12-07 at 20:46 said:

One reason for the confusion on draw() vs draw(clipRect):

void Textbox::draw() { Gadget::draw(); }

There’s no reason for that code that I can see - I was suspicious that its either

a) more magic b) an oversight c) something more cunning

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

Hmmm, or its because the compiler got confused somewhere else. I’ve experienced that now - I think its because a superclass had a method that took no arguments, while the class had a method that took one.

The work around, was to explicitly call the inherited method. ie, replace tb->draw() with tb->Gadget::draw(). Not brilliant, and I think it indicates some sort of problem in the compiler, but it shouldn’t be necessary.

ant on 2007-12-08 at 10:52 said:

That’s it exactly. If you overload an inherited method in a subclass, the compiler complains if you haven’t also overridden the original method. The way around it is to explicitly call the original method, ie. Gadget::draw(). I just took the easy way out and overrode the original method with a call back to the base class.

The Gadget::draw() method should never be overridden, and if it wasn’t for the compiler problem it wouldn’t be a virtual method. It just co-ordinates the re-validation of the visible region cache, sending those regions to draw(clipRect), and drawing any children. The draw(clipRect) function actually draws things to the screen.