GraphicsPort in Progress

The GraphicsPort class is underway. It works! I can now do this to draw a correctly clipped (and thus depth-sorted) line on any gadget:

GraphicsPort* port = gadget->getNewGraphicsPort();
port->drawHorizLine(10, 20, 100, PA_Color(0, 0, 0));
delete port;

Is it best to have a factory method that returns a new GraphicsPort object, or have an instance of the GraphicsPort as a member variable? Jeff, over to you!


Jeff on 2007-12-01 at 21:31 said:

Well, I would take a look but you didn’t add graphicsport.h to surceforge yet

Personally I wouldn’t call something ‘getNewAnything()’ - its either ‘getGraphicsPort()’ which returns a port that the gadget owns, or newGraphicsPort() which creates something I can delete, that is initialised from characteristics of the gadget.

As to whether you have a member variable or not, I think the answer is going to lie in “optimisation” land. Do they commit you to backing-store for each port? In which case you don’t really want to allocate a new port for every button you put on the screen, on the chance that it will use the port for drawing.

I was thinking about the whole thing last night and wondering whether I have it back-to-front in my head. You were concerned about having to recalculate the clipping rectangles on every graphic primitive (ie, for every line/text/etc) and the retaining the clip information in each gadget was the solution to that.

It occurred to me that the clip rectangles were being calculated on every call to TopGadgetClass::draw() anyway, aren’t they? Except that it happens inside Gadget::draw().

However, if every draw operation were started from the outer-most Gadget class and it drew its children FIRST, and passed in the current port, then each of the children would remove its rectangle from the port as part of its draw. I suspect that this would mean you only need one port per screen, not one per gadget.

The downside being that the children can’t be transparent and rely on the background that their parent laid down. Then I wondered how that worked anyway, with the current (pre-ports) solution.

I was thinking specifically, about the situation where I have a background with a text label next to a ‘text edit’ gadget. Whilst its fine for the text edit gadget to have its own background, the label really wanted to be drawn over the background. But the clipping logic in Woopsi at the moment precludes this, doesn’t it? The label has to draw its own background, despite what the TextWriter class is capable of.

In Win32 land, this is solved by the port having a ‘background brush/color’ which is passed down from the parent control. Which sort of leads me the long way around to wondering whether your usage is more likely to be:

GraphicsPort* port = gadget->getParent()->getGraphicsPort();

which again led me to the thought that the draw() method should really be passied in a port. ie, the parent allocates a port and tells the gadget to draw into it. The port would already have a set of clipping rectangles associated with it, which is just the parents cache.

In effect, I expected that all the Gadget::drawIntXXX() methods would move out of Gadget and into GraphicsPort. Then Gadget::draw(void) can allocate a new port if it wants to, but Gadget::draw(GraphicsPort*) would be passed the port by its parent. Then Screen::vbl() would allocate/own a port which it then passed to ::draw(port) for each of its children gadgets.

Clipping becomes a function of GraphicsPort, though its computation is based on the overlapping of Gadgets

ant on 2007-12-02 at 01:27 said:

The post was caught by the spam filter again (twice), which is why it disappeared!

I wrote a lengthy reply, but decided to fire up Parallels to submit the files I’d forgotten to add into SVN. Bad idea - Parallels still doesn’t work properly in Leopard and it crashed the whole OS. Not sure if it’s Parallels or Leopard that’s at fault, but I do know that in the last month or so of using Leopard I’ve seen the Mac BSOD more than in the previous ~3 years of using Tiger.

Anyway, it’s too late to type all that again, and I can’t be bothered to fight with Parallels at this hour, so here’s a quick summary:

  • Function should be called “newGraphicsPort”, that makes sense;
  • Drawing from the outermost gadget inwards causes problems with overlapping child gadgets (think tab bars where each tab pops in front of the others when clicked) - it would make working with the cached data considerably more difficult than it is at the moment (but erasing does work like that);
  • Gadgets don’t need to have a solid background - if you make a Label gadget that calls this->erase() instead of this->clear(), you’ll get the parent’s background or any lower siblings drawn instead of the grey box;
  • Drawing functions will move out of Gadget as soon as I’ve got them all implemented in the GraphicsPort.

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

Hmmm, I’m used to thinking of a tab bar as being a single gadget rather than a bunch of related ones. Otherwise, you need an encompassing gadget anyway to handle things like ‘too many tabs to fit on one line, push them onto two’ or ‘too many tabs, scroll them sideways’

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

You need the encompassing gadget to calculate all that sort of stuff anyway. Having each tab as a separate gadget means that I don’t need to write all-new click handling code, though - they can just use the standard code.

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

I’m still thinking through the ramifications of using erase to get the background under my labels. From what I can see (and I haven’t coded it yet), calling Gadget::erase() ends up at Woopsi::eraseGadget() which seems to expect that the gadget being erased is gone, because it takes all child gadgets clipping rectangles into account.

And once the gadget rectangle is erased, the parent then iterates through all existing children asking them to redrawDirty() which will result in a recursive call to my Label::draw(), surely?

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

Of course, as I go back and look one more time (after posting), I see where the loop avoids the erased gadget, so the clip is not a problem. But I still wonder about the recursion?

Jeff on 2007-12-02 at 22:11 said:

And of course, after posting that, I realise that the redraw is protected by the same if as the clip merging.

Don’t mind me, I shouldn’t post before my third morning cup of coffee…