summaryrefslogtreecommitdiff
path: root/src/gui
diff options
context:
space:
mode:
authorGraeme Geldenhuys <graeme@mastermaths.co.za>2011-10-26 11:50:35 +0200
committerGraeme Geldenhuys <graeme@mastermaths.co.za>2011-10-26 11:50:35 +0200
commita395588c4b27366e015189179e29f5fafad79c41 (patch)
tree9719262e6978e113078a82e5a28b455da2d1c945 /src/gui
parent5cb83741d2c7a6b283cfe84e6b528201282d7732 (diff)
downloadfpGUI-a395588c4b27366e015189179e29f5fafad79c41.tar.xz
treeview: implemented StateImageList support (treeview with checkboxes for example)
- Added StateImageList to TfpgTreeview - Added StateImageIndex to TfpgTreeNode - Added OnStateImageClicked event for TfpgTreeview. - Reworked the TfpgTreeview.HandlePaint() method for the new functionality, and also removed all the code duplication that existed before. - The node selection rectangle now only cover the text portion of a node, and note the images related to that node. - Minor bugfix in the selection rectangle, when depending on various properties, the selection rectangle was too small, and the node text wasn't fully visible.
Diffstat (limited to 'src/gui')
-rw-r--r--src/gui/fpg_tree.pas166
1 files changed, 105 insertions, 61 deletions
diff --git a/src/gui/fpg_tree.pas b/src/gui/fpg_tree.pas
index 87914790..83ddddbb 100644
--- a/src/gui/fpg_tree.pas
+++ b/src/gui/fpg_tree.pas
@@ -1,7 +1,7 @@
{
fpGUI - Free Pascal GUI Toolkit
- Copyright (C) 2006 - 2010 See the file AUTHORS.txt, included in this
+ Copyright (C) 2006 - 2011 See the file AUTHORS.txt, included in this
distribution, for details of the copyright.
See the file COPYING.modifiedLGPL, included in this distribution,
@@ -23,13 +23,11 @@ unit fpg_tree;
{
TODO:
- * Lots!!
+ * Lots! :-)
* Columns need to be reworked. We don't want coluns per node levels. Instead
we want a main column covering the tree. Then extra columns for user
text and data.
* Implement event handlers the user can hook into and do custom drawing.
-
- WARNING: This is still under heavy development. Use at own risk!
}
{.$Define Debug}
@@ -61,6 +59,8 @@ type
TfpgTreeNode = class;
TfpgTreeNodeFindMethod = procedure(ANode: TfpgTreeNode; var AFound: boolean) of object;
+ TfpgTreeExpandEvent = procedure(Sender: TObject; ANode: TfpgTreeNode) of object;
+ TfpgStateImageClickedEvent = procedure(Sender: TObject; ANode: TfpgTreeNode) of object;
TfpgTreeNode = class(TObject)
@@ -77,6 +77,7 @@ type
FPrev: TfpgTreeNode;
FSelColor: TfpgColor;
FSelTextColor: TfpgColor;
+ FStateImageIndex: integer;
FText: TfpgString;
FTextColor: TfpgColor;
FHasChildren: Boolean;
@@ -92,6 +93,7 @@ type
procedure DoRePaint;
procedure SetHasChildren(const AValue: Boolean);
procedure DoTreeCheck(ANode: TfpgTreeNode);
+ procedure SetStateImageIndex(const AValue: integer);
public
constructor Create;
destructor Destroy; override;
@@ -122,12 +124,13 @@ type
property Data: Pointer read FData write FData;
property FirstSubNode: TfpgTreeNode read FFirstSubNode;
property ImageIndex: integer read FImageIndex write FImageIndex;
+ property StateImageIndex: integer read FStateImageIndex write SetStateImageIndex;
property LastSubNode: TfpgTreeNode read FLastSubNode;
property Next: TfpgTreeNode read FNext write FNext;
property Parent: TfpgTreeNode read FParent write SetParent;
property Prev: TfpgTreeNode read FPrev write FPrev;
property Text: TfpgString read FText write SetText;
- { determines the + or - image in the treeview }
+ { determines the + or - image in the treeview }
property HasChildren: Boolean read FHasChildren write SetHasChildren;
// color settings
property InactSelColor: TfpgColor read FInactSelColor write SetInactSelColor;
@@ -138,12 +141,10 @@ type
end;
- TfpgTreeExpandEvent = procedure(Sender: TObject; ANode: TfpgTreeNode) of object;
-
-
TfpgTreeView = class(TfpgWidget)
private
FImageList: TfpgImageList;
+ FStateImageList: TfpgImageList;
FColumnHeight: integer; // height of the column header
FDefaultColumnWidth: word;
FIndentNodeWithNoImage: boolean;
@@ -156,6 +157,7 @@ type
FNoImageIndent: integer;
FOnChange: TNotifyEvent;
FOnExpand: TfpgTreeExpandEvent;
+ FOnStateImageClicked: TfpgStateImageClickedEvent;
FRootNode: TfpgTreeNode;
FScrollWheelDelta: integer;
FSelection: TfpgTreeNode; // currently selected node
@@ -181,7 +183,7 @@ type
function GetNodeHeightSum: integer;
function MaxNodeWidth: integer;
function GetNodeHeight: integer;
- // width of a node inclusive image
+ // width of a node inclusive of image and state image
function GetNodeWidth(ANode: TfpgTreeNode): integer;
function NodeIsVisible(ANode: TfpgTreeNode): boolean;
// returns the node-top in pixels
@@ -233,6 +235,7 @@ type
property RootNode: TfpgTreeNode read GetRootNode;
property Selection: TfpgTreeNode read FSelection write SetSelection;
property ImageList: TfpgImageList read FImageList write FImageList;
+ property StateImageList: TfpgImageList read FStateImageList write FStateImageList;
property PopupMenu: TfpgPopupMenu read FPopupMenu write FPopupMenu;
published
property Align;
@@ -256,6 +259,7 @@ type
property OnExpand: TfpgTreeExpandEvent read FOnExpand write FOnExpand;
property OnDoubleClick;
property OnShowHint;
+ property OnStateImageClicked: TfpgStateImageClickedEvent read FOnStateImageClicked write FOnStateImageClicked;
end;
@@ -351,7 +355,7 @@ end;
procedure TfpgTreeNode.DoRePaint;
begin
- // todo
+// FTree.Invalidate;
end;
procedure TfpgTreeNode.SetHasChildren(const AValue: Boolean);
@@ -369,6 +373,14 @@ begin
raise Exception.Create('Nodes must be of the same tree');
end;
+procedure TfpgTreeNode.SetStateImageIndex(const AValue: integer);
+begin
+ if FStateImageIndex = AValue then
+ exit;
+ FStateImageIndex := AValue;
+ DoRepaint;
+end;
+
constructor TfpgTreeNode.Create;
begin
FData := nil;
@@ -376,6 +388,7 @@ begin
FLastSubNode := nil;
FText := '';
FImageIndex := -1;
+ FStateImageIndex := -1;
FCollapsed := True;
FHasChildren := False;
@@ -1051,7 +1064,7 @@ end;
function TfpgTreeview.GetNodeWidth(ANode: TfpgTreeNode): integer;
var
- AImage: TfpgImageItem;
+ lImageItem: TfpgImageItem;
begin
{$IFDEF DEBUG}
SendDebug(Classname + '.GetNodeWidth');
@@ -1061,16 +1074,33 @@ begin
else
begin
Result := FFont.TextWidth(ANode.Text) + 2;
- if ShowImages and (ImageList <> nil) then
+ if ShowImages and ((ImageList <> nil) or (StateImageList <> nil)) then
begin
- if ANode.ImageIndex > -1 then
+ if ImageList <> nil then
begin
- AImage := ImageList.Items[ANode.ImageIndex];
- if AImage <> nil then
- result := result + AImage.Image.Width + 2;
+ if ANode.ImageIndex > -1 then
+ begin
+ lImageItem := ImageList.Items[ANode.ImageIndex];
+ if lImageItem <> nil then
+ result += lImageItem.Image.Width + 2;
+ end
+ else if IndentNodeWithNoImage then
+ result += NoImageIndent + 2;
end
else if IndentNodeWithNoImage then
- result := result + NoImageIndent + 2;
+ result += NoImageIndent + 2;
+
+ if StateImageList <> nil then
+ begin
+ if ANode.StateImageIndex > -1 then
+ begin
+ lImageItem := StateImageList.Items[ANode.StateImageIndex];
+ if lImageItem <> nil then
+ result += lImageItem.Image.Width + 2;
+ end
+ else if IndentNodeWithNoImage then
+ result += NoImageIndent + 2;
+ end;
end;
end; { if/else }
end;
@@ -1316,6 +1346,7 @@ var
node: TfpgTreeNode;
cancel: boolean;
OldSel: TfpgTreeNode;
+ lNodeXOffset: integer;
begin
inherited HandleLMouseUp(x, y, shiftstate);
@@ -1376,8 +1407,20 @@ begin
end
else
begin
- if x > w - GetColumnWidth(i1) div 2 + 6 then
+ lNodeXOffset := w - GetColumnWidth(i1) div 2 + 6;
+ if x > lNodeXOffset then { we clicked in the actual treenode area }
+ begin
Selection := node;
+ if (StateImageList <> nil) and ShowImages then
+ begin
+ { did we click on the state image }
+ if (x > lNodeXOffset) and (x < (lNodeXOffset+18)) then
+ begin
+ if Assigned(OnStateImageClicked) then
+ FOnStateImageClicked(self, node);
+ end;
+ end;
+ end;
end;
end;
end;
@@ -1459,6 +1502,7 @@ var
col: integer;
ACenterPos: integer;
x: integer;
+ imgx: integer;
y: integer;
AImageItem: TfpgImageItem;
AVisibleHeight: integer;
@@ -1541,67 +1585,67 @@ begin
ACenterPos := YPos - FYOffset + col + (GetNodeHeight div 2);
YPos := YPos + GetNodeHeight;
i := GetColumnLeft(StepToRoot(h)) + GetNodeWidth(h);
+ imgx := 0;
// only paint the node if it is fully visible
if i > FXOffset then
begin
- if h = Selection then // draw the selection rectangle and text
+ // State images must always be 16x16 pixels
+ if (StateImageList <> nil) and ShowImages then
begin
- if Focused then
+ AImageItem := StateImageList.Items[h.StateImageIndex];
+ if Assigned(AImageItem) then
begin
- Canvas.SetColor(h.ParentSelColor);
- Canvas.SetTextColor(h.ParentSelTextColor);
+ Canvas.DrawImagePart(w - FXOffset + 1, ACenterPos - 8, AImageItem.Image, 0, 0, 16, 16);
+ imgx := 18; // 16px max image width + 2px spacing or right
end
else
begin
- Canvas.SetColor(h.ParentInactSelColor);
- Canvas.SetTextColor(h.ParentInActSelTextColor);
+ if FIndentNodeWithNoImage then
+ imgx := 18
+ else
+ imgx := 0;
end;
- // draw selection rectangle
- Canvas.FillRectangle(w - FXOffset, ACenterPos - (GetNodeHeight div 2), GetNodeWidth(h), GetNodeHeight);
- if (ImageList <> nil) and ShowImages then
+ end;
+
+ if (ImageList <> nil) and ShowImages then
+ begin
+ AImageItem := ImageList.Items[h.ImageIndex];
+ if AImageItem <> nil then
begin
- AImageItem := ImageList.Items[h.ImageIndex];
- if AImageItem <> nil then
- begin
- Canvas.DrawImagePart(w - FXOffset + 1, ACenterPos - 8, AImageItem.Image, 0, 0, 16, 16);
- Canvas.DrawString(w - FXOffset + 1 + AImageItem.Image.Width + 2, ACenterPos - (GetNodeHeight div 2), h.text);
- end
- else
- begin
- if FIndentNodeWithNoImage then
- Canvas.DrawString(w - FXOffset + 1 + FNoImageIndent + 2 {spacer}, ACenterPos - (GetNodeHeight div 2), h.text)
- else
- Canvas.DrawString(w - FXOffset + 1, ACenterPos - (GetNodeHeight div 2), h.text);
- end;
+ Canvas.DrawImagePart(w + imgx - FXOffset + 1, ACenterPos - 8, AImageItem.Image, 0, 0, 16, 16);
+ imgx += AImageItem.Image.Width + 2; // 1px spacing on right before node text
end
else
- Canvas.DrawString(w - FXOffset + 1, ACenterPos - (GetNodeHeight div 2), h.text);
- Canvas.SetTextColor(h.ParentTextColor);
- end
- else
- begin
- if (ImageList <> nil) and ShowImages then
begin
- AImageItem := ImageList.Items[h.ImageIndex];
- if AImageItem <> nil then
- begin
- Canvas.DrawImagePart(w - FXOffset + 1, ACenterPos - 8, AImageItem.Image, 0, 0, 16, 16);
- Canvas.DrawString(w - FXOffset + 1 + AImageItem.Image.Width + 2, ACenterPos - (GetNodeHeight div 2), h.text);
- end
+ if FIndentNodeWithNoImage then
+ imgx += FNoImageIndent + 2
else
- begin
- if FIndentNodeWithNoImage then
- Canvas.DrawString(w - FXOffset + 1 + FNoImageIndent + 2 {spacer}, ACenterPos - (GetNodeHeight div 2), h.text)
- else
- Canvas.DrawString(w - FXOffset + 1, ACenterPos - (GetNodeHeight div 2), h.text);
- end
+ imgx += 2;
+ end;
+ end;
+
+ if h = Selection then // draw the selection rectangle and text
+ begin
+ if Focused then
+ begin
+ Canvas.SetColor(h.ParentSelColor);
+ Canvas.SetTextColor(h.ParentSelTextColor);
end
else
- Canvas.DrawString(w - FXOffset + 1, ACenterPos - (GetNodeHeight div 2), h.text);
- end; { if/else }
+ begin
+ Canvas.SetColor(h.ParentInactSelColor);
+ Canvas.SetTextColor(h.ParentInActSelTextColor);
+ end;
+ // draw selection rectangle
+ Canvas.FillRectangle(w + imgx - FXOffset, ACenterPos - (GetNodeHeight div 2), GetNodeWidth(h) - imgx, GetNodeHeight);
+ end;
+ Canvas.DrawString(w + imgx - FXOffset, ACenterPos - (GetNodeHeight div 2), h.text);
+
+ Canvas.SetTextColor(h.ParentTextColor);
Canvas.SetLineStyle(1, FTreeLineStyle);
+
if (h.Count > 0) or h.HasChildren then // do we have subnodes?
begin
// small horizontal line above rectangle for first subnode (with children) only
@@ -1869,7 +1913,7 @@ end;
procedure TfpgTreeview.DoExpand(ANode: TfpgTreeNode);
begin
if Assigned(FOnExpand) then
- FOnExpand(self, ANode);
+ FOnExpand(self, ANode);
end;
function TfpgTreeview.NextVisualNode(ANode: TfpgTreeNode): TfpgTreeNode;