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).
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);