Roots and "Tree" Hierarchy Control

Published June 03, 2011
Advertisement
[size="3"]e_23a.jpg

Woody3D can create tree roots as shown above. There is a problem however, as it currently uses a very simple hierarchy for branch levels. It does not allow for more than one child per branch. This means you can add roots but not branches. That's a pretty boring tree.

Next version will support a more flexible hierarchy. To manage the various parts of the tree, I'll be using a CtreeCtrl in the Tree Forge. Below is a colored [size="3"]CtreeCtrl [size="3"] (with fancy gradient selection bar).

e_23b.jpg

And here is the source for the control if you're interested in how it works. Feel free to modify it and use it in your own MFC projects.

Color_Tree.h
#ifndef ___COLOR_TREE_CONTROL_H___
#define ___COLOR_TREE_CONTROL_H___


class Color_Tree : public CTreeCtrl
{
private:

COLORREF ___select_background_color;
COLORREF ___select_font_color;
COLORREF ___lost_focus_select_background_color;
COLORREF ___lost_focus_select_font_color;

BOOL ___is_select_full_row;
BOOL ___is_select_full_row_gradient;

protected:

DECLARE_MESSAGE_MAP()

afx_msg void OnCustomDraw(NMHDR* pNMHDR, LRESULT* pResult);

public:

// Constructor / Destructor
Color_Tree(void);
~Color_Tree(void);

void enable_select_full_row(BOOL enable, BOOL is_gradient);
void set_select_color(COLORREF select_background_color, COLORREF select_font_color,
COLORREF lost_focus_select_background_color, COLORREF lost_focus_select_font_color);
};


#endif


Color_Tree.cpp
#include "stdafx.h"
#include "Color_Tree.h"



// Local Gradient rectangle function
BOOL draw_gradient_rect(CDC *pDC,
const CRect &draw_rect,
const COLORREF start_color,
const COLORREF end_color,
const bool is_horizontal)
{
// Local data
BYTE red_start, green_start, blue_start,
red_end, green_end, blue_end;
TRIVERTEX vertex_array[2] = { 0 };
GRADIENT_RECT gradient_rect[1] = { 0 };


// Validate DC
if(!pDC || !pDC->GetSafeHdc())
{ return FALSE;
}

// Get RGB bytes from start color
red_start = GetRValue(start_color);
green_start = GetGValue(start_color);
blue_start = GetBValue(start_color);

// Set first vertex
vertex_array[0].x = draw_rect.left;
vertex_array[0].y = draw_rect.top;
vertex_array[0].Red = (static_cast(red_start) << 8);
vertex_array[0].Green = (static_cast(green_start) << 8);
vertex_array[0].Blue = (static_cast(blue_start) << 8);
vertex_array[0].Alpha = 0x0000;

// Get RGB bytes from end color
red_end = GetRValue(end_color);
green_end = GetGValue(end_color);
blue_end = GetBValue(end_color);

// Set second vertex
vertex_array[1].x = draw_rect.right;
vertex_array[1].y = draw_rect.bottom;
vertex_array[1].Red = (static_cast(red_end) << 8 );
vertex_array[1].Green = (static_cast(green_end) << 8 );
vertex_array[1].Blue = (static_cast(blue_end) << 8 );
vertex_array[1].Alpha = 0x0000;

// Gradient vertex index
gradient_rect[0].UpperLeft = 0;
gradient_rect[0].LowerRight = 1;

// Draw gradient rect
return GradientFill( pDC->GetSafeHdc(),
vertex_array,
2,
&gradient_rect,
1,
is_horizontal ? GRADIENT_FILL_RECT_H : GRADIENT_FILL_RECT_V ) == TRUE;
}

// c()
Color_Tree::Color_Tree(void)
{
___is_select_full_row = FALSE;
___is_select_full_row_gradient = FALSE;
}
// d()
Color_Tree::~Color_Tree(void)
{
}

// Message map
BEGIN_MESSAGE_MAP(Color_Tree, CTreeCtrl)
ON_NOTIFY_REFLECT( NM_CUSTOMDRAW, OnCustomDraw)
END_MESSAGE_MAP()


// Handle color of select
void Color_Tree::OnCustomDraw(NMHDR* pNMHDR, LRESULT* pResult)
{
// Local data
NMTVCUSTOMDRAW* pcd;
RECT rect;
CDC* pDC;
CBrush brush;


// Get custom draw struct
pcd = (NMTVCUSTOMDRAW*)pNMHDR;

// Handle by draw stage
switch(pcd->nmcd.dwDrawStage)
{
case CDDS_PREPAINT:

*pResult = CDRF_NOTIFYITEMDRAW;

break;
// Pre paint of tree item
case CDDS_ITEMPREPAINT:

// To Get item
HTREEITEM hItem = (HTREEITEM)pcd->nmcd.dwItemSpec;

// Handle control has focus
if(this->GetFocus() == this)
{
// If item is selected
if(pcd->nmcd.uItemState & CDIS_SELECTED)
{
pcd->clrText = ___select_font_color;
pcd->clrTextBk = ___select_background_color;

// Determine if item is in view
// Get text rect
if(___is_select_full_row && this->GetItemRect(hItem, ▭, TRUE))
{
// Adjust rect
pcd->nmcd.rc.left = rect.right;

// Gradient fill
if(___is_select_full_row_gradient)
{
// Get DC and fill
pDC = this->GetDC();
draw_gradient_rect(pDC, pcd->nmcd.rc, ___select_background_color, this->GetBkColor(), true);

// Cleanup
this->ReleaseDC(pDC);
}
// Solid fill
else
{
// Get DC and Create brush
pDC = this->GetDC();
brush.CreateSolidBrush(___select_background_color);

// Solid fill
pDC->FillRect(&pcd->nmcd.rc, &brush);

// Cleanup
brush.DeleteObject();
this->ReleaseDC(pDC);
}
}
}
// If item is hot (mouse over)
else if(pcd->nmcd.uItemState & CDIS_HOT)
{
pcd->clrText = this->GetTextColor();
}
}
else
{
// If item is selected
if(pcd->nmcd.uItemState & CDIS_SELECTED)
{
pcd->clrText = ___lost_focus_select_font_color;
pcd->clrTextBk = ___lost_focus_select_background_color;

// Determine if item is in view
// Get text rect
if(___is_select_full_row && this->GetItemRect(hItem, ▭, TRUE))
{
// Adjust rect
pcd->nmcd.rc.left = rect.right;

// Gradient fill
if(___is_select_full_row_gradient)
{
// Get DC and fill
pDC = this->GetDC();
draw_gradient_rect(pDC, pcd->nmcd.rc, ___lost_focus_select_background_color, this->GetBkColor(), true);

// Cleanup
this->ReleaseDC(pDC);
}
// Solid fill
else
{
// Get DC and Create brush
pDC = this->GetDC();
brush.CreateSolidBrush(___lost_focus_select_background_color);

// Solid fill
pDC->FillRect(&pcd->nmcd.rc, &brush);

// Cleanup
brush.DeleteObject();
this->ReleaseDC(pDC);
}
}
}
// If item is hot (mouse over)
else if(pcd->nmcd.uItemState & CDIS_HOT)
{
pcd->clrText = this->GetTextColor();
}
}

*pResult = CDRF_DODEFAULT;

break;
}
}

// Enable select full row
void Color_Tree::enable_select_full_row(BOOL enable, BOOL is_gradient)
{ ___is_select_full_row = enable;
___is_select_full_row_gradient = is_gradient;
}

// Set the colors for selecting items
void Color_Tree::set_select_color(COLORREF select_background_color, COLORREF select_font_color,
COLORREF lost_focus_select_background_color, COLORREF lost_focus_select_font_color)
{
___select_background_color = select_background_color;
___select_font_color = select_font_color;
___lost_focus_select_background_color = lost_focus_select_background_color;
___lost_focus_select_font_color = lost_focus_select_font_color;
}


Example


CImageList ___tree_image_list;
Color_Tree ___tree_control;


// Local data
CBitmap bitmap;
HTREEITEM o_1, o_2, c_1, c_2;


// Setup tree colors
___tree_control.SetBkColor(RGB(116, 127, 138));
___tree_control.SetTextColor(RGB(255, 255, 255));
___tree_control.SetLineColor(RGB(255, 255, 255));
___tree_control.set_select_color(RGB(196, 210, 224), RGB(0, 0, 0), RGB(102, 114, 124), RGB(255, 255, 255));

// Setup tree images
___tree_image_list.Create(16, 16, ILC_COLOR32, 1, 4);
// Bitmap 1
bitmap.LoadBitmap(IDB_BITMAP1);
___tree_image_list.Add(&bitmap, RGB(0, 0, 0));
bitmap.DeleteObject();
// Bitmap 2
bitmap.LoadBitmap(IDB_BITMAP2);
___tree_image_list.Add(&bitmap, RGB(0, 0, 0));
bitmap.DeleteObject();
// Set image list
___tree_control.SetImageList(&___tree_image_list, TVSIL_NORMAL);

// Set item height
___tree_control.SetItemHeight(20);

// Custom enable select of whole row - do not use 'Full Row Select' property for Control - it disables lines
// Allows for horizontal gradient too.
___tree_control.enable_select_full_row(TRUE, TRUE);

// Add items
o_1 = ___tree_control.InsertItem(_T("Trunk"), 0, 0);
c_1 = ___tree_control.InsertItem(_T("Standard Branch 0"), 1, 1, o_1);
c_2 = ___tree_control.InsertItem(_T("Standard Branch 1"), 1, 1, o_1);
___tree_control.InsertItem(_T("Imposter Branch 0"), 1, 1, c_2);
o_2 = ___tree_control.InsertItem(_T("Leaf Types"), 0, 0);
___tree_control.InsertItem(_T("Mesh Leaf 0"), 1, 1, o_2);
___tree_control.InsertItem(_T("Billboard Leaf 0"), 1, 1, o_2);

0 likes 0 comments

Comments

Nobody has left a comment. You can be the first!
You must log in to join the conversation.
Don't have a GameDev.net account? Sign up!
Profile
Author
Advertisement
Advertisement
Advertisement