Difference between revisions of "TreeControl"

From wiki.visual-prolog.com

(Initial Draft)
 
(elaborate)
Line 1: Line 1:
The new treeControl is generic meaning that the entities in the treeControl have the type you decide.  In the example that comes with Visual Prolog 7.3 the control is used four different waysIn one of them the treeControl simply contains strings that represent directory paths.  The program deal with paths not with some artificial node-idsSo the programmer works directly with the "concepts" without some strange ID level in-between. The new treeControl is also model based meaning that the programmer creates a model describing the tree rather than a complete data representation.  This has the advantage that the tree accessed lazily: the children of nodes are requested when they are needed (i.e. when their parent are expanded).  Another advantage is that the treeControl is driven by events from the model, so changes in the model is automatically reflected in the treeControl.  If a single model is shown by several treeControl's changes will automatically be shown in all the treeControls.
The <vp>treeControl</vp> (introduced in Visual Prolog 7.3 Commercial Edition) is a generic model based control.  Meaning:
 
* Generic: it contains data of your choice
* Model based: the control displays the contents of a data model
 
This article is related to the treeControlDemo example that comes with Visual Prolog Commercial Edition.
 
== Basic concepts ==
 
To use the tree cotrol it must be attached to a ''tree model'' and a ''node renderer''.
 
;Tree model
:The tree model defines the tree that the control must display. It only describes the structure and data contents of the tree; the actual presentation in the control is defined by the node renderer. The tree control interrogates the tree model about the parts of the tree that should be shownIt also listens to notifications from the modes such that changes in the tree can be reflected on the display.  Several tree controls can be attached to a single tree model.  The notification strategy ensures that changes in the model are reflected in all attached tree controls.
 
;Node renderer
:The node renderer is responsible for rendering the nodes of the treeMore precisely the node renderer determines the text label, font, icon, etc. of each node.
 
== Code overview ==
 
This section provides an overview of the most important parts of the involved code.
 
=== treeControl ===
 
The following code extract illustrates some of the most fundamental things about the <vp>treeControl</vp>
 
<vip>interface treeControl{@Node} supports control
properties
    model : treeModel{@Node}.
domains
    nodeRenderer = (@Node, treeNodeDC NodeDC).
properties
    nodeRenderer : nodeRenderer.
...</vip>
 
First of all the control is parameterized over the <vp>@Node</vp>'s it contains; where nodes are branches as well as leafs.  It is attached to a model by setting the <vp>model</vp> property and to a node renderer by setting the <vp>nodeRenderer</vp> property.
 
Typically the treeControl is created inside the auto-generated <vp>generatedInitialize</vp> predicate and the model and node renderer is attached in the constructor after the call to <vp>generatedInitialize</vp>:
 
<vip>clauses
    new(Parent):-
        formWindow::new(Parent),
        generatedInitialize(), % treeControl_ctl is created here
        treeControl_ctl:model := This, % This class implements the model
        treeControl_ctl:nodeRenderer := nodeRenderer, % the local predicate nodeRenderer handles the rendering
        ...</vip>
 
=== treeModel ===
 
The tree model describes the tree and sends notifications about changes in the tree.  The part that describes the tree looks like this (condensed):
 
<vip>interface treeModel{@Node}
predicates
    getRoot_nd : () -> @Node Root nondeterm.
    getChild_nd : (@Node Parent) -> @Node Child nondeterm.
    hasChildren : (@Node Parent) determ.
    tryGetParent : (@Node Child) -> @Node Parent determ.</vip>
 
The tree model is also parameterized over the <vp>@Node</vp>'s.  It has predicates for obtaining the roots, and the children and parent of a certain node.  It is a fundamental property that the tree control '''''asks''''' about the nodes it '''''needs''''' to know about.  The tree control is not loaded with everything; it only receive data by need (e.g. when the user expands a node).
 
The treeControl cashes data to a large extend: The first time a certain node is expanded the treeControl ask the tree model about the children of this node, but it does not ask again even if the node is collapsed and expanded again.
 
<vp>hasChildren</vp> is used by the <vp>treeControl</vp> to determine wheter a node is a branch (which will have a "+" for expansion) or a leaf.
 
<vp>tryGetParent</vp> is used by the <vp>treeControl</vp> to find the parent of nodes that have never yet been rendered.  For example, the <vp>treeControl</vp> has just been shown and only the roots have therefore been displayed.  The program no asks to set focus to a certain node, which have not yet been displayed.  The <vp>treeControl</vp> (repeatedly, if necessary) calls <vp>tryGetParent</vp> to establich the position of the node in the tree.
 
The notification part of the model looks like this (condensed):
 
<vip>interface treeModel{@Node}
...
domains
    event =
        nodePropertyChanged(@Node UpdatedNode); % Only node property changed
        subTreeChanged(@Node ChangedTree); % Entire subtree changed
        branchChanged(@Node ChangedTree); % Immediate children changed
        toplevelChanged(); % Only Toplevel has changed, children are assumed unaltered
        allChanged(). % Entire forest has changed
domains
    treeChangedListener = (event* Events).
properties
    changedEvent : eventSource{event*} (o).</vip>
 
The <vp>treeControl</vp> attach a <vp>treeChangedListener</vp> and is therefore informed about various kinds of changes so that it can update the contents of the window accordingly.
 
You should notice that it is the user (=programmer) of the <vp>treeControl</vp> that provides and implements the <vp>treeModel</vp>, so it is the programmer that should fire these events to trigger update.
 
Examples below will illustrate this.
 
=== nodeRenderer ===
 
The <vp>nodeRenderer</vp> defines the graphical appearences of the nodes in the tree.  For a certain node it must at least define the text label that will be displayed in the tree, but it can also control icons, fonts, and text color.
 
The <vp>nodeRenderer</vp> is a predicate which receives a node and a tree node device context (<vp>treeNodeDC</vp>), and it must render the node to the device context.
 
The treeNodeDC looks like this:
 
<vip>interface treeNodeDC
    open vpiDomains
properties
    text : string (i).
    bitmapIdx : integer (i).
    selectedBitmapIdx : integer (i).
    stateBitmapIdx : integer (i).
    font : font (i).
    textColor : color (i).
    backColor : color (i).
end interface treeNodeDC</vip>
 
So when the <vp>nodeRenderer</vp> is called with a certain node, it must set the <vp>text</vp> property to the text that will be displayed for this node, and so forth.  Bitmap handling will be discussed below.
 
Notice that the treeControl only ask the rendering predicate one time for each node, unless it receives a notification that makes the node invalid. The <vp>nodePropertyChanged</vp> event in particular means that the rendering of the node is invalid.  If the node is in a changed subtree or branch it is also invalid and the nodeRenderer will be invoked again.

Revision as of 15:16, 7 June 2010

The treeControl (introduced in Visual Prolog 7.3 Commercial Edition) is a generic model based control. Meaning:

  • Generic: it contains data of your choice
  • Model based: the control displays the contents of a data model

This article is related to the treeControlDemo example that comes with Visual Prolog Commercial Edition.

Basic concepts

To use the tree cotrol it must be attached to a tree model and a node renderer.

Tree model
The tree model defines the tree that the control must display. It only describes the structure and data contents of the tree; the actual presentation in the control is defined by the node renderer. The tree control interrogates the tree model about the parts of the tree that should be shown. It also listens to notifications from the modes such that changes in the tree can be reflected on the display. Several tree controls can be attached to a single tree model. The notification strategy ensures that changes in the model are reflected in all attached tree controls.
Node renderer
The node renderer is responsible for rendering the nodes of the tree. More precisely the node renderer determines the text label, font, icon, etc. of each node.

Code overview

This section provides an overview of the most important parts of the involved code.

treeControl

The following code extract illustrates some of the most fundamental things about the treeControl

interface treeControl{@Node} supports control
properties
    model : treeModel{@Node}.
domains
    nodeRenderer = (@Node, treeNodeDC NodeDC).
properties
    nodeRenderer : nodeRenderer.
...

First of all the control is parameterized over the @Node's it contains; where nodes are branches as well as leafs. It is attached to a model by setting the model property and to a node renderer by setting the nodeRenderer property.

Typically the treeControl is created inside the auto-generated generatedInitialize predicate and the model and node renderer is attached in the constructor after the call to generatedInitialize:

clauses
    new(Parent):-
        formWindow::new(Parent),
        generatedInitialize(), % treeControl_ctl is created here
        treeControl_ctl:model := This,  % This class implements the model
        treeControl_ctl:nodeRenderer := nodeRenderer, % the local predicate nodeRenderer handles the rendering
        ...

treeModel

The tree model describes the tree and sends notifications about changes in the tree. The part that describes the tree looks like this (condensed):

interface treeModel{@Node}
predicates
    getRoot_nd : () -> @Node Root nondeterm.
    getChild_nd : (@Node Parent) -> @Node Child nondeterm.
    hasChildren : (@Node Parent) determ.
    tryGetParent : (@Node Child) -> @Node Parent determ.

The tree model is also parameterized over the @Node's. It has predicates for obtaining the roots, and the children and parent of a certain node. It is a fundamental property that the tree control asks about the nodes it needs to know about. The tree control is not loaded with everything; it only receive data by need (e.g. when the user expands a node).

The treeControl cashes data to a large extend: The first time a certain node is expanded the treeControl ask the tree model about the children of this node, but it does not ask again even if the node is collapsed and expanded again.

hasChildren is used by the treeControl to determine wheter a node is a branch (which will have a "+" for expansion) or a leaf.

tryGetParent is used by the treeControl to find the parent of nodes that have never yet been rendered. For example, the treeControl has just been shown and only the roots have therefore been displayed. The program no asks to set focus to a certain node, which have not yet been displayed. The treeControl (repeatedly, if necessary) calls tryGetParent to establich the position of the node in the tree.

The notification part of the model looks like this (condensed):

interface treeModel{@Node}
...
domains
    event =
        nodePropertyChanged(@Node UpdatedNode); % Only node property changed
        subTreeChanged(@Node ChangedTree); % Entire subtree changed
        branchChanged(@Node ChangedTree); % Immediate children changed
        toplevelChanged(); % Only Toplevel has changed, children are assumed unaltered
        allChanged(). % Entire forest has changed
domains
    treeChangedListener = (event* Events).
properties
    changedEvent : eventSource{event*} (o).

The treeControl attach a treeChangedListener and is therefore informed about various kinds of changes so that it can update the contents of the window accordingly.

You should notice that it is the user (=programmer) of the treeControl that provides and implements the treeModel, so it is the programmer that should fire these events to trigger update.

Examples below will illustrate this.

nodeRenderer

The nodeRenderer defines the graphical appearences of the nodes in the tree. For a certain node it must at least define the text label that will be displayed in the tree, but it can also control icons, fonts, and text color.

The nodeRenderer is a predicate which receives a node and a tree node device context (treeNodeDC), and it must render the node to the device context.

The treeNodeDC looks like this:

interface treeNodeDC
    open vpiDomains
properties
    text : string (i).
    bitmapIdx : integer (i).
    selectedBitmapIdx : integer (i).
    stateBitmapIdx : integer (i).
    font : font (i).
    textColor : color (i).
    backColor : color (i).
end interface treeNodeDC

So when the nodeRenderer is called with a certain node, it must set the text property to the text that will be displayed for this node, and so forth. Bitmap handling will be discussed below.

Notice that the treeControl only ask the rendering predicate one time for each node, unless it receives a notification that makes the node invalid. The nodePropertyChanged event in particular means that the rendering of the node is invalid. If the node is in a changed subtree or branch it is also invalid and the nodeRenderer will be invoked again.