need advice on writing custom widget

Are you writing your own components and need help with how to set them up or have questions about the components you are deriving from ? Ask them here.
Post Reply
jamesrf
Earned a small fee
Earned a small fee
Posts: 14
Joined: Fri Sep 03, 2004 11:26 am
Location: Oxford, UK
Contact:

need advice on writing custom widget

Post by jamesrf »

Hi people,

For the last six months (on and off) I've been developing my own version of the generic tree control which is an exact replica of the MS windows tree control in terms of appearance and behaviour, but with some bug fixes and nice features added (column support - ie tree list, double buffering, state based implementation, multiple selection and multiple drag/copy drop and some more esoteric improvements). Sorry - just felt I needed to justify why I have written yet another tree control. It works great but the drawing implementation is really sub-optimal, which is what my question is about.

Anyway, to my question. Hmm, this is quite complicated.... where to begin?

At the moment when the tree is painted, it paints the entire thing to a memory dc (overflows and crashes when gets large at the moment) and then blits it to a PaintDC. While it is doing its recursive draw, the bounding box of each item is stored in virtual coordinates, beginning at y=0. My first improvement will be to split out the size calculation process from the drawing so that it will be possible to not have to draw the whole tree. The size re-calculation will then be triggered from expand/collapse/set font/set label/etc operations.

I am not sure whether to store each item's bounding box in virtual coordinates or in client coordinates (so they will be negative if scrolled out of view). On the one hand, if I store them in client coordinates it makes drawing simple because I can now just create a memory DC which is the size of the window rather than the size of the tree control's contents. It also makes hit testing easy because no coordinate conversion needs to take place. However, the downside is that scrolling will require constant bounding box recalculation which could potentially be damn slow with a large tree. On the other hand, if they are stored in virtual coordinates then coordinate conversion needs to be performed on all incoming mouse event positions, and on all drawing operations, so that I can still draw to a memory DC that is the size of the client area.

So, assuming I want virtual coordinates (I think I do), how could I implement the drawing efficiently? An obvious and dumb way would be to do a coordinate conversion on every single drawing call, which clearly is stupid. Is it possible to somehow make it so that all drawing calls somehow automatically subtract the scroll position from all y coordinates? Can I set some device origin or something to accomplish this? I've got to admit I am not too clued up on GDI.

If you read this far, thanks very much!

James
Jorg
Moderator
Moderator
Posts: 3971
Joined: Fri Aug 27, 2004 9:38 pm
Location: Delft, Netherlands
Contact:

Post by Jorg »

Hi James,

Are you developing a wxTreeCtrl with list columns inside? There is a control called wxTreeListCtrl that does just that . If that control is not satisfying for you then maybe this will help..
I am not sure whether to store each item's bounding box in virtual coordinates or in client coordinates (so they will be negative if scrolled out of view). On the one hand, if I store them in client coordinates it makes drawing simple because I can now just create a memory DC which is the size of the window rather than the size of the tree control's contents. It also makes hit testing easy because no coordinate conversion needs to take
I would go with logical coordinates. It is easier to keep hold of those and you don't need to recalculate anything. I have one method in wxTreeMultiCtrl that positions all the controls properly and updates their logical coordinates when I encounter i.e. a collapsed node. This works pretty well .. How I do it is returning a delta to the caller to tell it where the next y pos should be relative from the previous. Something like:

[syntax="c"]
void wxTreeMultiCtrl::RecalculateNodePositions()
{
int currentY = _spacingY;
// go recursive on every node, and store the information in the node

for(int i = 0; i < _root.GetNodeCount(); i++)
currentY += CalculateNodeDimensions(_root.GetNode(i), currentY, 0);
}
[/syntax]

And CalculateNodeDimensions does the same but for all subnodes. The _root is an array of nodes on the highest level.:

[syntax="c"]
int wxTreeMultiCtrl::CalculateNodeDimensions(TreeMultiItemBase *b, int currentY, int level)
{
int gutter = (_gutterWidth * 2) + _iconWidth;
int y = 0, topSpacing = 0;

// return same if no proper object
wxCHECK(b, 0);

// if we are not visible, skip recalculation and descending
if(b->IsVisible())
{
b->SetY(currentY);
// if level is 0, calculate with front gutter, else without

y = currentY + b->GetHeight();
if(b->IsTreeMultiItemNode())
{
TreeMultiItemNode *n = (TreeMultiItemNode *)b;

if(level == 0)
b->SetX(gutter);
else
b->SetX(gutter + (level * (_gutterWidth + _iconWidth)));

// now do children of this node

for(int i = 0; i < n->GetNodeCount(); i++)
y += CalculateNodeDimensions(n->GetNode(i), y + _spacingY, level+1);
}
else if(b->IsTreeMultiItemWindow())
{
TreeMultiItemWindow *w = (TreeMultiItemWindow *)b;

if(level == 0)
b->SetX(gutter + w->GetFrontSpacing());
else
b->SetX(_gutterWidth + (level * (_gutterWidth + _iconWidth)) + w->GetFrontSpacing());

topSpacing = w->GetTopSpacing();

// reposition the window

wxWindow *wnd = w->GetWindow();
if(wnd)
{
int x = 0, y = 0;
CalcScrolledPosition(w->GetX(), w->GetY(), &x, &y);
//wxSize size = wnd->GetSize();
//wnd->SetSize(x, y, size.GetWidth(), size.GetHeight());
wnd->SetSize(x, y, w->GetWidth(), w->GetHeight());
}
}

if(y > 0)
return (y - currentY) + _spacingY + topSpacing; // return delta
else
return 0;
}

return 0; // not visible, thus we skip calculations
}
[/syntax]

If this proves useful you can download the wxTreeMultiCtrl here: http://www.xs4all.nl/~jorgb/wxTreeMultiCtrl.html

- Jorgen[/url]
jamesrf
Earned a small fee
Earned a small fee
Posts: 14
Joined: Fri Sep 03, 2004 11:26 am
Location: Oxford, UK
Contact:

Thanks

Post by jamesrf »

Thanks for your help Jorgen. Much appreciated.

James
klaust
In need of some credit
In need of some credit
Posts: 1
Joined: Sun Sep 19, 2004 2:51 pm
Location: Germany

TreeListCtrl

Post by klaust »

Hi,

for delphi and bcc-users there is a great treecontrol, called VirtualTreeView (http://www.soft-gems.net/VirtualTreeview/VT.php), developed by Mike Lischke. It is worth to look inside the source, a lot of interesting features (multicols, header, etc.) are implemented, especially the handling of (huge) data in the "virtual"-paradigm makes it a very good piece of code.

I my dreams I like to see these control ported to wx, and 'til now nobody kept me from dreaming this dream ...
Regards
Klaus
Post Reply