-
Notifications
You must be signed in to change notification settings - Fork 263
Circuit Editor Programming Discussion
The ATF Circuit Editor Sample allows constructing circuits by dragging circuit items from the palette onto a document canvas and connecting their pins. You can edit the circuit, including grouping items, as well as creating reusable prototypes and templates from groups of elements. The editor allows collections of items to be added to layers, and the visibility of of layers and their individual items can be toggled.
This sample makes heavy use of the graph interfaces and classes in the Sce.Atf.Controls.Adaptable.Graphs
namespace, especially the circuit related ones.
- For all topics related to graph support in ATF, see Graphs in ATF.
- For a general description of circuits, see Circuit Graphs.
- For details on circuit support, see Circuit Graph Support.
- For information on circuit groups and templates features, see Circuit Group Feature.
CircuitEditor shows how to use the graph interfaces and classes in the Sce.Atf.Controls.Adaptable.Graphs
namespace. A circuit is simply a generalization of a graph, and this sample implements the IGraph
interface with appropriate parameters for circuits.
Graph support requires the ATF DOM, and this application defines its data model using an XML schema, which makes it easier to support the DOM and also makes it easy to persist data in XML.
Much of the function of CircuitEditor comes from the DOM adapters defined for most of the application data types. ATF provides a particularly rich set of DOM adapters for circuit graphs, and CircuitEditor needs to do very little in some cases to derive from these classes.
The Editor
class handles circuit editing and is the document client for circuit documents. This client looks very similar to the document client for the ATF Simple DOM Editor Sample. Editor
is also the control host client for a D2dAdaptableControl
control. Circuits are displayed using the D2dAdaptableControl
, employing Direct2D for GPU-accelerated rendering. A set of control adapters is created for each D2dAdaptableControl
that performs many functions, including rendering, view changes, and selection.
The CircuitEditingContext
is the editing context for containers, such as circuits and groups. Other contexts handle layering and prototypes.
CircuitEditor contains several windows, mainly to handle ways of grouping and reusing controls: Prototypes, Templates, and Layers. Templates and Layers can have folders, which are handled as types with their own DOM adapters. CircuitEditor also provides a Mastering facility for reusable items. Some of these facilities also need their own contexts and command clients.
A circuit is a graph, and so this sample uses the graphical classes in the Sce.Atf.Controls.Adaptable.Graphs
namespace. These classes include ones specifically tailored to circuit graphs, and these classes do most of the work of the sample application. In fact, about half the classes in CircuitEditor are derived from classes of the same name in Sce.Atf.Controls.Adaptable.Graphs
, such as Circuit
, CircuitEditingContext
, and Pin
. In this discussion, the class in CircuitEditor is referred to by its name, such as Circuit
; the class it derives from in Sce.Atf.Controls.Adaptable.Graphs
is referred to as the base class, to avoid having to specify the namespace each time.
In the most general form, ATF supports graphs using nodes, edges, and routes in its IGraph<IGraphNode, IGraphEdge<IGraphNode, IEdgeRoute>, IEdgeRoute>
interface:
public interface IGraph<out TNode, out TEdge, out TEdgeRoute>
where TNode : class, IGraphNode
where TEdge : class, IGraphEdge<TNode, TEdgeRoute>
where TEdgeRoute : class, IEdgeRoute
where the elements are:
-
IGraphNode
: Interface for a node in a graph; nodes are connected by edges. -
IGraphEdge<TNode, TEdgeRoute>
: Interface for routed edges that connect nodes and have a defined source and destination route from and to the nodes. -
IEdgeRoute
: Interface for edge routes, which act as sources and destinations for graph edges.
Sce.Atf.Controls.Adaptable.Graphs.Circuit
class is specific to circuits and uses this variant of the IGraph
interface:
public abstract class Circuit : DomNodeAdapter, IGraph<Element, Wire, ICircuitPin>, IAnnotatedDiagram, ICircuitContainer
The parameters in the IGraph<Element, Wire, ICircuitPin>
interface are:
-
Element
: AdaptsDomNode
to a circuit element with pins. ImplementsICircuitElement
, which implementsIGraphNode
. -
Wire
: AdaptsDomNode
to a connection in a circuit. ImplementsIGraphEdge<Element, ICircuitPin>
. -
ICircuitPin
: Interface for pins, which are the sources and destinations for wires betweenElement
s.
Circuit
class, derived from the above, uses its own IGraph
variant:
public class Circuit : Sce.Atf.Controls.Adaptable.Graphs.Circuit, IGraph<Module, Connection, ICircuitPin>
The parameters in the IGraph<Module, Connection, ICircuitPin>
interface are:
-
Module
: Adapter for circuit modules. Derives fromElement
. -
Connection
: Adapter for connections in a circuit. Derives fromWire
. -
ICircuitPin
: Interface for pins.
IGraph
and other graph interfaces, see ATF Graph Interfaces.
CircuitEditor defines its data model with an XML Schema in the Circuit.xsd
type definition file. The DomGen tool is used to generate a Schema
class containing metadata classes for the types, as in many of the samples, like ATF Simple DOM Editor Sample. CircuitEditor also defines a SchemaLoader
class that uses these metadata classes in a variety of ways, to define DOM adapters, for instance. For general information about data modeling in graphs, see Graph Data Model.
Most of CircuitEditor's types have associated classes that are DOM adapters. Thus each item in a circuit is represented by a DomNode
in a tree of DomNode
s in the application data. The key circuit item classes are:
-
Module
("moduleType"): Represents all the circuit modules found on the palette, such as AND gates. Derives fromSce.Atf.Controls.Adaptable.Graphs.Element
. -
Connection
("connectionType"): A connection between two module pins. Derives fromSce.Atf.Controls.Adaptable.Graphs.Wire
. -
Pin
("pinType"): A pin on a module. Derives fromSce.Atf.Controls.Adaptable.Graphs.Pin
. -
Circuit
("circuitType"): A circuit, containing modules, connections among them, and annotations. Derives fromSce.Atf.Controls.Adaptable.Graphs.Circuit
. -
Group
("groupType"): Collection of modules, connections between them, input and output pins (group pins), and annotations. Derives fromSce.Atf.Controls.Adaptable.Graphs.Group
.
Sce.Atf.Controls.Adaptable.Graphs
namespace.
Circuit
and Group
both behave as circuit containers, and each container can be displayed separately in a window. A circuit is a (document) top level container, and it can contain any other type of element (including a group), except for another circuit. A group can be a child node of the circuit, and it can contain other group child nodes hierarchically.
All the sample's data types are shown in this figure from the Visual Studio XML Schema Explorer, which shows that some types are extensions of others:
Several types are based on the "circuitType", "pinType", and "moduleType" types, whose nodes are open to show their attributes.
In this list, the second level entries are listed under the type they extend:
- annotationType: comment on the circuit canvas.
- circuitType: circuit, containing modules, connections among them, and annotations.
- circuitDocumentType: document holding subcircuits (masters), prototype folders, and template folders.
- subCircuitType: circuit that defines a master module type, with custom input and output pins and can be referenced by instances, which are all equivalent.
- connectionType: connects the output pin on the output module to the input pin of the input module.
- layerFolderType: hierarchy containing layers.
- moduleRefType: reference to a module in the circuit, used to build lists of module references in a layer.
- moduleType: module with name, label, and position, and can be referenced by connections.
- groupTemplateRefType: reference instance to a group.
- groupType: collection of modules, internal connections between them, input and output pins (group pins), and annotations.
- missingModuleType: module for external template that is missing.
- moduleTemplateRefType: reference instance to a module.
- subCircuitInstanceType: module whose type is defined by a subcircuit (master).
- pinType: pin with name and type that determines connection type.
- groupPinType: pin on a group, preserving the internal pin/module which is connected to the outside circuit.
- prototypeFolderType: hierarchy containing prototypes.
- prototypeType: set of modules and connections among them that can be copied and pasted into a circuit.
- templateFolderType: hierarchy containing templates.
- templateType: module that can be referenced in a circuit.
- missingTemplateType: module for external template that is missing.
Examining the XML in a circuit file saved from the application illustrates the circuit data types. Here is a circuit graph in CircuitEditor:
Here's is its .circuit
file, with some data omitted (...) to clarify the data hierarchy:
<circuit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns="http://sony.com/gametech/circuits/1_0">
<module xsi:type="buttonType" name="Button_3" label="In1" x="64" y="352" />
<module xsi:type="andType" name="And_5" x="192" y="352" />
...
<module xsi:type="groupType" name="Group_1" label="Group" x="768" y="128" showExpandedGroupPins="true">
<input name=":In2" type="boolean" module="And_9" pin="1" />
<output name=":Out" type="boolean" module="Button_9" />
<output name=":Out_1" type="boolean" module="And_9" index="1" pinY="64" />
<module xsi:type="buttonType" name="Button_9" />
<module xsi:type="andType" name="And_9" x="128" y="64" />
<module xsi:type="lightType" name="Light_9" x="256" />
<connection outputModule="And_9" inputModule="Light_9" />
<connection outputModule="Button_9" inputModule="And_9" />
</module>
<connection outputModule="Button_3" inputModule="And_5" />
<connection outputModule="And_5" inputModule="Light_3" />
...
<layerFolder name="Layer1">
<layerFolder name="Layer2">
<moduleRef ref="Button_3" />
<moduleRef ref="And_5" />
...
</layerFolder>
<moduleRef ref="Light_7" />
<moduleRef ref="And_7" />
<moduleRef ref="Button_7" />
</layerFolder>
<annotation text="Try dragging some circuit modules
from the Circuits Tab in the Palette.
...
<annotation text="Complex circuits can be also be simplified
by creating layers. Copy some
...
<subCircuit name="MasterInstance_1">
<module xsi:type="lightType" name="Light_2" x="272" y="16" />
<module xsi:type="andType" name="And_2" x="144" y="80" />
...
</subCircuit>
<prototypeFolder>
<prototype name="Prototype1">
<module xsi:type="buttonType" name="Button_5" label="In1" x="64" y="352" />
<module xsi:type="andType" name="And_3" x="192" y="352" />
...
<connection outputModule="Button_5" inputModule="And_3" />
<connection outputModule="And_3" inputModule="Light_5" />
...
</prototype>
<prototype name="Prototype2">
<module xsi:type="lightType" name="Light_8" x="320" y="64" />
<module xsi:type="andType" name="And_8" x="192" y="128" />
...
<connection outputModule="And_8" inputModule="Light_8" />
<connection outputModule="Button_8" inputModule="And_8" />
</prototype>
</prototypeFolder>
<templateFolder name="_TemplateRoot_">
<template guid="852d657f-c7ef-4474-933f-743737be1b59" label="Group">
<module xsi:type="groupType" name="Group" label="Group" x="96" y="96" showExpandedGroupPins="true">
<input name=":In2" type="boolean" module="And_1" pin="1" />
<output name=":Out" type="boolean" module="Button_1" />
...
<module xsi:type="buttonType" name="Button_1" />
<module xsi:type="andType" name="And_1" x="128" y="64" />
...
<connection outputModule="Button_1" inputModule="And_1" />
<connection outputModule="And_1" inputModule="Light_1" />
</module>
</template>
</templateFolder>
</circuit>
The main container is the "circuitDocument" type. Its name is "circuit" and it contains all the other items. In this case, the circuitDocument contains these type items:
- module
- connection
- layerFolder
- annotation
- subCircuit
- prototypeFolder
- templateFolder
As previously mentioned, nearly all the types in CircuitEditor have DOM adapters (missingTemplateType
is the only exception). Several types have more than one adapter, such as circuitDocumentType
(the root element type) and circuitType
.
Some adapters are defined in SchemaLoader.OnSchemaSetLoaded()
. These adapters are illustrated in the following table that lists
- Type.
- Type's DOM adapter (in
CircuitEditorSample
unless noted otherwise). - Interfaces the adapter implements.
- Base class the adapter derives from in
Sce.Atf.Controls.Adaptable.Graphs
(unless indicated otherwise). - Class this base derives from (not shown if it's
DomNodeAdapter
), plus any interfaces this class implements.
CategoryUniqueIdValidator
ensures locally unique IDs, which is necessary because the template tree has mixed internal and external (imported from other document) nodes, where the node IDs in external documents may collide with the IDs of internal nodes.
Type | DOM Adapter | Adapter implements |
Adapter derives from (its base) |
Base derives from, implements |
---|---|---|---|---|
circuitDocumentType |
CircuitDocument
|
CircuitDocument
|
Sce.Atf.Dom.DomDocument
|
|
circuitDocumentType |
Sce.Atf.Dom.MultipleHistoryContext
|
Sce.Atf.Dom.Observer
|
||
circuitDocumentType |
PrototypingContext
|
PrototypingContext
|
Sce.Atf.Dom.SelectionContext ,
IInstancingContext , IPrototypingContext ,
IObservableContext , INamingContext
|
|
circuitDocumentType |
TemplatingContext
|
Sce.Atf.Dom.TemplatingContext
|
Sce.Atf.Dom.SelectionContext ,
IInstancingContext , ITemplatingContext ,
IObservableContext , INamingContext
|
|
circuitDocumentType |
MasteringValidator
|
Sce.Atf.Dom.Validator
|
Sce.Atf.Dom.Observer
|
|
circuitDocumentType |
CategoryUniqueIdValidator
|
Sce.Atf.Dom.CategoryUniqueIdValidator
|
Sce.Atf.Dom.IdValidator
|
|
circuitDocumentType |
CircuitValidator
|
CircuitValidator
|
Sce.Atf.Dom.Validator
|
|
circuitDocumentType |
ReferenceValidator
|
Sce.Atf.Dom.Validator
|
Sce.Atf.Dom.Observer
|
|
circuitType |
GlobalHistoryContext
|
Sce.Atf.Dom.Observer
|
||
circuitType |
ViewingContext
|
IViewingContext , ILayoutContext
|
Sce.Atf.Dom.Validator
|
Sce.Atf.Dom.Observer
|
circuitType |
LayeringContext
|
LayeringContext
|
Sce.Atf.Dom.SelectionContext ,
IInstancingContext , IHierarchicalInsertionContext , ILayeringContext ,
IObservableContext , INamingContext
|
|
circuitType |
PrintableDocument
|
IPrintableDocument
|
DomNodeAdapter
|
|
groupType |
CircuitEditingContext
|
IEditableGraphContainer<Module, Connection, ICircuitPin>
|
CircuitEditingContext
|
EditingContext ,
IEnumerableContext , INamingContext , IInstancingContext ,
IObservableContext , IColoringContext ,
IEditableGraphContainer<Element, Wire, ICircuitPin>
|
groupType |
Group
|
ICircuitGroupType<Module, Connection, ICircuitPin> , IGraph<Module, Connection, ICircuitPin>
|
Group
|
Element ,
ICircuitGroupType<Element, Wire, ICircuitPin> , IGraph<Element, Wire, ICircuitPin> , IAnnotatedDiagram , ICircuitContainer
|
groupType |
ViewingContext
|
IViewingContext , ILayoutContext
|
Validator
|
Sce.Atf.Dom.Observer
|
subCircuitType |
SubCircuit
|
SubCircuit
|
Circuit , ICircuitElementType
|
|
subCircuitInstanceType |
SubCircuitInstance
|
SubCircuitInstance
|
Sce.Atf.Controls.Adaptable.Graphs.Element
|
|
connectionType |
WireStyleProvider<Module, Connection, ICircuitPin>
|
IEdgeStyleProvider
|
DomNodeAdapter
|
Other adapters are defined in SchemaLoader.RegisterCircuitExtensions()
, which adapts the default implementation of circuit types for most of the types. These adapters are illustrated in the following table, which has the same format as the preceding table.
Type | DOM Adapter | Adapter implements |
Adapter derives from (its base) |
Base derives from, implements |
---|---|---|---|---|
annotationType |
Annotation
|
Annotation
|
IAnnotation
|
|
circuitType |
Circuit
|
IGraph<Module, Connection, ICircuitPin>
|
Circuit
|
IGraph<Element, Wire, ICircuitPin> , IAnnotatedDiagram , ICircuitContainer
|
circuitType |
CircuitEditing - Context
|
IEditableGraphContainer <Module, Connection, ICircuitPin>
|
CircuitEditingContext
|
EditingContext , IEnumerableContext , INamingContext , IInstancingContext , IObservableContext , IColoringContext , IEditableGraphContainer <Element, Wire, ICircuitPin>
|
connectionType |
Connection
|
IGraphEdge<Module, ICircuitPin>
|
Wire
|
IGraphEdge<Element, ICircuitPin>
|
groupPinType |
GroupPin
|
ICircuitGroupPin<Module>
|
GroupPin
|
Pin , ICircuitGroupPin<Element>
|
groupTemplateRefType |
GroupInstance
|
ICircuitGroupType <Module, Connection, ICircuitPin> , IReference<Module> , IReference<DomNode> , IReference <Sce.Atf.Controls. Adaptable. Graphs.Group>
|
CircuitEditorSample.Module
|
|
groupType |
Group
|
ICircuitGroupType <Module, Connection, ICircuitPin> , IGraph <Module, Connection, ICircuitPin>
|
Group
|
Element , ICircuitGroupType <Element, Wire, ICircuitPin> , IGraph<Element, Wire, ICircuitPin> , IAnnotatedDiagram , ICircuitContainer
|
layerFolderType |
LayerFolder
|
LayerFolder
|
||
moduleRefType |
ModuleRef
|
ElementRef
|
||
moduleTemplateRefType |
ModuleInstance
|
IReference<Module> , IReference<DomNode>
|
CircuitEditorSample.Module
|
|
moduleType |
Module
|
Element
|
ICircuitElement , IVisible
|
|
pinType |
Pin
|
Pin
|
ICircuitPin
|
|
prototypeFolderType |
PrototypeFolder
|
PrototypeFolder
|
||
prototypeType |
Prototype
|
Prototype
|
||
subCircuitType |
SubCircuit
|
SubCircuit
|
Circuit , ICircuitElementType
|
|
subCircuitInstanceType |
SubCircuit -Instance
|
SubCircuitInstance
|
Element
|
|
templateFolderType |
TemplateFolder
|
Sce.Atf.Dom.TemplateFolder
|
||
templateType |
Template
|
Sce.Atf.Dom.Template
|
IReference<DomNode>
|
All the derived from classes are in Sce.Atf.Controls.Adaptable.Graphs
, except as noted. All these base classes derive directly from DomNodeAdapter
, except for CircuitEditingContext
, which derives from EditingContext
, which ultimately derives from DomNodeAdapter
. For more information on the DOM adapter base classes, see Circuit DOM Adapters.
What do these DOM adapters do?
DOM adapters typically implement properties containing information about the object.
For instance, the Annotation
DOM adapter derives from Sce.Atf.Controls.Adaptable.Graphs.Annotation
, which defines these properties:
// required DOM attributes info
protected abstract AttributeInfo TextAttribute { get; }
protected abstract AttributeInfo XAttribute { get; }
protected abstract AttributeInfo YAttribute { get; }
protected abstract AttributeInfo WidthAttribute { get; }
protected abstract AttributeInfo HeightAttribute { get; }
protected abstract AttributeInfo BackColorAttribute { get; }
These properties provide the kind of information needed for an annotation, such as its location and text. These properties are all overridden in Annotation
, typically using the data in the DOM metadata classes defined in Schema
, as in:
protected override AttributeInfo TextAttribute
{
get { return Schema.annotationType.textAttribute; }
}
Nearly all CircuitEditor's DOM adapters override base class properties in this way. Half the adapters, such as Annotation
, LayerFolder
, Module
, and Pin
, do nothing more than this.
Every DOM adapter has an OnNodeSet()
method. Most of the DOM adapters in CircuitEditor use their base class's OnNodeSet()
, but some of the DOM adapters provide their own, such as Group
:
protected override void OnNodeSet()
{
m_modules = new DomNodeListAdapter<Module>(DomNode, Schema.groupType.moduleChild);
m_connections = new DomNodeListAdapter<Connection>(DomNode, Schema.groupType.connectionChild);
m_annotations = new DomNodeListAdapter<Annotation>(DomNode, Schema.groupType.annotationChild);
m_inputs = new DomNodeListAdapter<GroupPin>(DomNode, Schema.groupType.inputChild);
m_outputs = new DomNodeListAdapter<GroupPin>(DomNode, Schema.groupType.outputChild);
m_thisModule = DomNode.Cast<Module>();
base.OnNodeSet();
}
The types used in this method are all the types that can be children of a "groupType" DomNode
. This OnNodeSet()
method saves a DomNodeListAdapter
list of all the DomNode
's child objects. The Circuit
DOM adapter does the same for its child objects in its OnNodeSet()
method.
For example, the Circuit
DOM adapter implements IGraph
:
#region IGraph Members
/// <summary>
/// Gets all visible nodes in the circuit</summary>
///<remarks>IGraph.Nodes is called during circuit rendering, and picking(in reverse order). </remarks>
IEnumerable<Module> IGraph<Module, Connection, ICircuitPin>.Nodes
{
get
{
return m_modules.Where(x => x.Visible);
}
}
/// <summary>
/// Gets all connections between visible nodes in the circuit</summary>
IEnumerable<Connection> IGraph<Module, Connection, ICircuitPin>.Edges
{
get
{
return m_connections.Where(x => x.InputElement.Visible && x.OutputElement.Visible);
}
}
...
CircuitEditor's document class is CircuitDocument
, which merely overrides the SubCircuitChildInfo
property in its derived class Sce.Atf.Controls.Adaptable.Graphs.CircuitDocument
. This base class, in turn, derives from DomDocument
, which implements IDocument
. This base class is also a DOM adapter, and it implements an OnNodeSet()
method to initialize the editor type and other information.
Several samples implement IDocument
through DomDocument
, including ATF Fsm Editor Sample, ATF Simple DOM Editor Sample, and ATF State Chart Editor Sample. For details on how the ATF Simple DOM Editor Sample uses DomDocument
, see EventSequenceDocument Class, which is the document class in that sample.
The Editor
class handles circuit editing and is the document client for circuit documents. Editor
is also the control host client for a D2dAdaptableControl
control, which displays circuit documents.
CircuitEditor's implementation of IDocumentClient
has a lot of similarities to the document client of ATF Simple DOM Editor Sample, which is discussed in Document Handling.
The implementations of IDocumentClient
are very similar in the two samples. For example, here are the CircuitEditor Info
property and CanOpen()
method:
public DocumentClientInfo Info
{
get { return EditorInfo; }
}
/// <summary>
/// Document editor information for circuit editor</summary>
public static DocumentClientInfo EditorInfo =
new DocumentClientInfo(Localizer.Localize("Circuit"), ".circuit", null, null);
/// <summary>
/// Returns whether the client can open or create a document at the given URI</summary>
/// <param name="uri">Document URI</param>
/// <returns>True iff the client can open or create a document at the given URI</returns>
public bool CanOpen(Uri uri)
{
return EditorInfo.IsCompatibleUri(uri);
}
And the same property and method for SimpleDOMEditor:
public DocumentClientInfo Info
{
get { return DocumentClientInfo; }
}
/// <summary>
/// Information about the document client</summary>
public static DocumentClientInfo DocumentClientInfo = new DocumentClientInfo(
Localizer.Localize("Event Sequence"),
new string[] { ".xml", ".esq" },
Sce.Atf.Resources.DocumentImage,
Sce.Atf.Resources.FolderImage,
true);
/// <summary>
/// Returns whether the client can open or create a document at the given URI</summary>
/// <param name="uri">Document URI</param>
/// <returns>True iff the client can open or create a document at the given URI</returns>
public bool CanOpen(Uri uri)
{
return DocumentClientInfo.IsCompatibleUri(uri);
}
The IDocumentClient.Open()
method also has many commonalities in the two samples. For instance, both use DomXmlReader
to read the file and create a tree of DomNode
s, because both samples use the ATF DOM for data persistence. Both set up an editing context derived from the EditingContext
class. A notable difference is that CircuitEditor's Open()
method creates a D2dAdaptableControl
to display circuits, as discussed in Circuit Document Display and Control Adapters.
The IDocumentClient.Show()
, IDocumentClient.Save()
, and IDocumentClient.Close()
methods are all very similar in the two samples. Both samples' Save()
methods use DomXmlWriter
to write the document to XML, although CircuitEditor does this through a private class CircuitWriter
derived from DomXmlWriter
.
Circuits are displayed using a D2dAdaptableControl
employing Direct2D for GPU-accelerated rendering. A set of control adapters is created for each D2dAdaptableControl
.
Control adapters reside in several namespaces. Some are not circuit or even graph specific, such as HoverAdapter
.
Although a typical circuit control has more than a dozen control adapters, each adapter has well defined, unique responsibilities. The adapters' code is relatively short and easy to follow and extend. Divide and conquer is a good strategy here. Note that:
- Viewing related operations (pan and zoom) use
ITransformAdapter
, implemented inTransformAdapter
. - Mouse click and drag selection use
ISelectionAdapter
, implemented inSelectionAdapter
. - Rendering, picking and selection is delegated to a circuit rendering class, such as
D2dCircuitRenderer
, which is a parameter in theD2dGraphAdapter
constructor. For information on circuit renderers, see Circuit Rendering Classes.
A D2dAdaptableControl
is used as a canvas for displaying a main graph (circuit) or a subgraph (group, or master of a sub-circuit) in:
- A circuit document.
- A subgraph displayed in a tab after double-clicking on a group or master instance.
- A subgraph displayed when hovering over a master instance.
D2dAdaptableControl
controls are in tab windows, are fully editable, and are created by the CreateCircuitControl()
method. The D2dAdaptableControl
for the hover is created in Editor
's constructor:
m_d2dHoverControl = new D2dAdaptableControl();
m_d2dHoverControl.Dock = DockStyle.Fill;
var xformAdapter = new TransformAdapter();
xformAdapter.EnforceConstraints = false;//to allow the canvas to be panned to view negative coordinates
m_d2dHoverControl.Adapt(xformAdapter, new D2dGraphAdapter<Module, Connection, ICircuitPin>(m_circuitRenderer, xformAdapter));
m_d2dHoverControl.DrawingD2d += new EventHandler(m_d2dHoverControl_DrawingD2d);
Here the D2dAdaptableControl
is created and configured, adapters are created and configured, and then AdaptableControl.Adapt()
sets the D2dAdaptableControl
's adapters. This process is also performed in CreateCircuitControl()
with many more adapters.
Editor
's constructor also creates two objects that are used later in the D2dAdaptableControl
's adapters created in CreateCircuitControl()
to render circuits:
-
D2dCircuitRenderer
: Graph renderer that draws graph nodes as circuit elements, and edges as wires. Elements have zero or more output pins where wires originate, and zero or more input pins where wires end. Input pins are on the left side and output pins are on the right of elements. For more information, see D2dCircuitRenderer Class. -
D2dSubCircuitRenderer
: Subgraph (group or master) renderer that draws subnodes as circuit elements, and subedges as wires. Also draws virtual representations of group pins for editing. For more details, see D2dSubCircuitRenderer Class.
CreateCircuitControl()
method, because D2dAdaptableControl
is central to the sample's operation.
For general information on adaptation, including adapting controls, see Adaptation in ATF and Adaptable Controls.
After the D2dAdaptableControl
is created and configured, its adapters are created. These adapters all derive from ControlAdapter
, the base class for control adapters.
internal D2dAdaptableControl CreateCircuitControl(DomNode circuitNode)
{
var control = new D2dAdaptableControl();
control.SuspendLayout();
control.BackColor = SystemColors.ControlLight;
control.AllowDrop = true;
var transformAdapter = new TransformAdapter();
transformAdapter.EnforceConstraints = false; //to allow the canvas to be panned to view negative coordinates
transformAdapter.UniformScale = true;
transformAdapter.MinScale = new PointF(0.25f, 0.25f);
transformAdapter.MaxScale = new PointF(4, 4);
var viewingAdapter = new ViewingAdapter(transformAdapter);
viewingAdapter.MarginSize = new Size(25, 25);
var canvasAdapter = new CanvasAdapter();
((ILayoutConstraint) canvasAdapter).Enabled = false; //to allow negative coordinates for circuit elements within groups
var autoTranslateAdapter = new AutoTranslateAdapter(transformAdapter);
var mouseTransformManipulator = new MouseTransformManipulator(transformAdapter);
var mouseWheelManipulator = new MouseWheelManipulator(transformAdapter);
var scrollbarAdapter = new ScrollbarAdapter(transformAdapter, canvasAdapter);
var hoverAdapter = new HoverAdapter();
hoverAdapter.HoverStarted += control_HoverStarted;
hoverAdapter.HoverStopped += control_HoverStopped;
var annotationAdaptor = new D2dAnnotationAdapter(m_theme); // display annotations under diagram
These adapters allow the control to be used in all sorts of ways:
-
TransformAdapter
: implementITransformAdapter
for scaling and translating the control's contents, and is also needed by several other adapters. This adapter is configured by setting its properties.EnforceConstraints
is false to allow the canvas to be panned to view negative coordinates.UniformScale
is set true to guarantee that the canvas is scaled the same on both the x- and y-axis.MinScale
andMaxScale
enforce a minimum and maximum scaling.MarginSize
indicates the margin used when framing a selection. -
ViewingAdapter
: implement methods inIViewingContext
to frame the control's items and ensure their visibility. -
CanvasAdapter
: allow setting the canvas bounds and visible window size. SeetingEnabled
false allows negative coordinates for circuit elements within groups. -
MouseTransformManipulator
: convert mouse drags into translation and scaling the canvas using theTransformAdapter
. This allows panning items on the canvas with Alt+Left button mouse dragging and scaling with Alt+Right button mouse dragging. -
MouseWheelManipulator
: convert mouse wheel rotation into scaling using theTransformAdapter
. -
ScrollbarAdapter
: add horizontal and vertical scrollbars to the adapted control, using theTransformAdapter
andCanvasAdapter
. -
HoverAdapter
: add hover events over pickable items. This allows seeing the items inside a master element in a window when hovering over the master. The adapter subscribes toHoverStarted
andHoverStopped
events. -
D2dAnnotationAdapter
: display and edit annotations on the control.
AdaptableControl.Adapt()
:
-
RectangleDragSelector
: allow user to drag-select rectangular regions on the adapted control to modify the selection. -
KeyboardIOGraphNavigator
: navigate an "input-output" graph using the arrow keys. This kind of graph has inputs on only one side of a node and outputs on another side, like a circuit. -
LabelEditAdapter
: edit element labels in place. -
SelectionAdapter
: add mouse click and drag selection of canvas items. The context must be convertible toISelectionContext
. -
DragDropAdapter
: add drag and drop support for dragging circuit elements onto the canvas from the palette, prototype window, or template window. -
ContextMenuAdapter
: support a context menu on right button mouse-up on circuit elements.
CreateCircuitControl()
's DomNode
parameter indicates what kind of circuit is being displayed: Circuit
for an open circuit document, or Group
if a group was double-clicked on. The control functions slightly differently in these cases, defining different additional adapters. For a circuit:
if (circuitNode.Is<Circuit>())
{
var circuitAdapter = new D2dGraphAdapter<Module, Connection, ICircuitPin>(m_circuitRenderer, transformAdapter);
var circuitModuleEditAdapter = new D2dGraphNodeEditAdapter<Module, Connection, ICircuitPin>(
m_circuitRenderer, circuitAdapter, transformAdapter);
circuitModuleEditAdapter.DraggingSubNodes = false;
var circuitConnectionEditAdapter =
new D2dGraphEdgeEditAdapter<Module, Connection, ICircuitPin>(m_circuitRenderer, circuitAdapter, transformAdapter);
control.Adapt(
hoverAdapter,
scrollbarAdapter,
autoTranslateAdapter,
new RectangleDragSelector(),
transformAdapter,
viewingAdapter,
canvasAdapter,
mouseTransformManipulator,
mouseWheelManipulator,
new KeyboardIOGraphNavigator<Module, Connection, ICircuitPin>(),
new D2dGridAdapter(),
annotationAdaptor,
circuitAdapter,
circuitModuleEditAdapter,
circuitConnectionEditAdapter,
new LabelEditAdapter(),
new SelectionAdapter(),
new DragDropAdapter(m_statusService),
new ContextMenuAdapter(m_commandService, m_contextMenuCommandProviders)
);
}
The additional adapters are:
-
D2dGraphAdapter
: provide support for graph drawing, viewing, and hit testing on the canvas. -
D2dGraphNodeEditAdapter
: add node dragging to move circuit elements. This adapter is instantiated with aD2dCircuitRenderer
. Pressing the Shift key constrains dragging to be parallel to the x- or y-axis. ItsDraggingSubNodes
property is setfalse
to prevent moving subnodes. -
D2dGraphEdgeEditAdapter
: add graph edge editing capabilities to make and move connections between pins in the circuit. This adapter is created with aD2dCircuitRenderer
.
This portion of code runs when creating a control for a Group
:
else if (circuitNode.Is<Group>())
{
var circuitAdapter = new D2dSubgraphAdapter<Module, Connection, ICircuitPin>(m_subGraphRenderer,
transformAdapter);
var circuitModuleEditAdapter = new D2dGraphNodeEditAdapter<Module, Connection, ICircuitPin>(
m_subGraphRenderer, circuitAdapter, transformAdapter);
circuitModuleEditAdapter.DraggingSubNodes = false;
var circuitConnectionEditAdapter =
new D2dGraphEdgeEditAdapter<Module, Connection, ICircuitPin>(m_subGraphRenderer, circuitAdapter, transformAdapter);
var groupPinEditor = new GroupPinEditor(transformAdapter);
groupPinEditor.GetPinOffset = m_subGraphRenderer.GetPinOffset;
canvasAdapter.UpdateTranslateMinMax = groupPinEditor.UpdateTranslateMinMax;
control.Adapt(
hoverAdapter,
scrollbarAdapter,
autoTranslateAdapter,
new RectangleDragSelector(),
transformAdapter,
viewingAdapter,
canvasAdapter,
mouseTransformManipulator,
mouseWheelManipulator,
new KeyboardIOGraphNavigator<Module, Connection, ICircuitPin>(),
new D2dGridAdapter(),
annotationAdaptor,
circuitAdapter,
circuitModuleEditAdapter,
circuitConnectionEditAdapter,
new LabelEditAdapter(),
groupPinEditor,
new SelectionAdapter(),
new DragDropAdapter(m_statusService),
new ContextMenuAdapter(m_commandService, m_contextMenuCommandProviders)
);
}
else throw new NotImplementedException(
"graph node type is not supported!");
Additional adapters for a Group
are:
-
D2dSubgraphAdapter
: reference and render a subgraph, that is, a group diagram. Also provides hit testing and viewing support on the canvas. -
D2dGraphNodeEditAdapter
: add node dragging capabilities to move circuit elements. This object is instantiated with aD2dSubCircuitRenderer
. Pressing the Shift key constrains dragging to be parallel to the x- or y-axis.DraggingSubNodes
isfalse
to prevent moving subnodes. -
D2dGraphEdgeEditAdapter
: add graph edge editing capabilities to make and move connections between pins in the circuit. This adapter is instantiated with aD2dSubCircuitRenderer
. -
GroupPinEditor
: add floating group pin location and label editing capabilities to a subgraph control. TheGetPinOffset
property is set to a callback fromD2dSubCircuitRenderer
to compute the group pin y offset.
CreateCircuitControl()
finishes up with this:
control.ResumeLayout();
control.DoubleClick += new EventHandler(control_DoubleClick);
control.MouseDown += new MouseEventHandler(control_MouseDown);
return control;
}
The DoubleClick
event handler displays a window when a group or master is double-clicked. It first ascertains whether the hit element is a subcircuit (master) or group.
If the clicked on item is a master, a new control is created to display the content of the master. It creates an editing context in which the schema loader is specified, to enable copying and pasting to other applications. It creates a ControlInfo
for the new control and registers it with the CircuitControlRegistry
. For information about this component, see CircuitControlRegistry Component.
If the clicked on item is a group, the process is similar, but a document is not created.
In both cases, CreateCircuitControl()
itself is called to create a control to display the circuit elements.
The MouseDown
handler does different things, depending on which part of a circuit element was clicked:
- Group expander button: toggle the group contents' visibility.
- Pin: allow dragging a connection to another pin without having to keep the mouse button pressed.
- Group pin visibility icon ( ): toggle the group pin's visibility.
ITransactionContext
adapted from the DomNode
tree's root DomNode
, ultimately provided by the CircuitEditingContext
, described in CircuitEditingContext Class.
CircuitEditor uses several context classes, most of which are derived from Sce.Atf.Controls.Adaptable.Graphs
classes.
An editing context is usually associated with a container (i.e., a circuit or group), so editing is constrained to items in the container and currently selected items. The current active context is usually associated with the current active window in the user interface. For instance, when the circuit canvas is the active window, the CircuitEditingContext
is the active context.
CircuitEditingContext
derives from Sce.Atf.Controls.Adaptable.Graphs.CircuitEditingContext
, which provides all its function. The base class cannot be used directly, because it is abstract. This base class itself derives from EditingContext
, which implements ISelectionContext
to enable selecting circuit elements. This base class also implements several interfaces, including IInstancingContext
to allow editing selected items. For information on the base CircuitEditingContext
class, see CircuitEditingContext Class in Circuit Graph Support.
A circuit and group both behave as containers, and each can be displayed and edited separately in a graph window. Therefore, both "circuitType" and "groupType" have the DOM adapter CircuitEditingContext
defined in SchemaLoader
.
CircuitEditingContext
contains the property WireType
that must be overridden, which CircuitEditor does in the usual fashion:
protected override DomNodeType WireType
{
get { return Schema.connectionType.Type; }
}
CircuitEditingContext
implements IEditableGraphContainer<Module, Connection, ICircuitPin>
to allow editing an expanded group by dragging items in and out of the group. The methods in this implementation simply call methods in the base CircuitEditingContext
's IEditableGraphContainer<Element, Wire, ICircuitPin>
implementation.
For example, here is the CircuitEditingContext
implementation of IEditableGraphContainer.CanMove()
bool IEditableGraphContainer<Module, Connection, ICircuitPin>.CanMove(object newParent, IEnumerable<object> movingObjects)
{
if (newParent.Is<IReference<Module>>())
return false;
var editableGraphContainer =
DomNode.Cast<CircuitEditingContext>() as IEditableGraphContainer<Element, Wire, ICircuitPin>;
return editableGraphContainer.CanMove(newParent, movingObjects);
}
The editableGraphContainer
variable is an instance of the base class, Sce.Atf.Controls.Adaptable.Graphs.CircuitEditingContext
, because this base class implements IEditableGraphContainer<Element, Wire, ICircuitPin>
. Thus the next line's call editableGraphContainer.CanMove(newParent, movingObjects)
is to the CanMove()
method in the base class.
The other methods that CircuitEditingContext
implements in IEditableGraphContainer<Module, Connection, ICircuitPin>
also use the IEditableGraphContainer<Element, Wire, ICircuitPin>
methods in the base class.
IEditableGraphContainer
also derives from IEditableGraph
, which governs making graph connections:
public interface IEditableGraphContainer<in TNode, TEdge, in TEdgeRoute>:
IEditableGraph<TNode, TEdge, TEdgeRoute>
where TNode : class, IGraphNode
where TEdge : class, IGraphEdge<TNode, TEdgeRoute>
where TEdgeRoute : class, IEdgeRoute
CircuitEditingContext
also implements this interface, again by calling the corresponding methods in the base class. For example, here is IEditableGraph.CanConnect()
:
public bool CanConnect(Module fromNode, ICircuitPin fromRoute, Module toNode, ICircuitPin toRoute)
{
var editableGraphContainer =
DomNode.Cast<CircuitEditingContext>() as IEditableGraphContainer<Element, Wire, ICircuitPin>;
return editableGraphContainer.CanConnect(fromNode, fromRoute, toNode, toRoute);
}
This method's code is very similar to IEditableGraphContainer.CanMove()
shown previously: the editableGraphContainer
variable is created exactly the same way. As a result, this method calls the IEditableGraph.CanConnect()
method in the base CircuitEditingContext
class.
CircuitEditingContext
is the DOM adapter defined for both the "circuit" and "group" types. CircuitEditor allows you to open multiple separate windows to view a group, in addition to the main document (circuit) window. Each opened graph window has an associated editing context with its own undo history. However, GlobalHistoryContext
is also defined as a DOM adapter for the "circuit" type. GlobalHistoryContext
is an adapter that implements a single global history on a DOM node tree containing multiple local HistoryContext
s. This adapter tracks all other HistoryContext
s in the subtree rooted at DomNode
and passes their transactions to a global HistoryContext
, which must be on the same DomNode
as this adapter. As a result, there is only one undo/redo history stack for all circuits, including groups.
Both classes derive from classes of the same name in Sce.Atf.Controls.Adaptable.Graphs
. These classes simply override properties in the base class to get type metadata from the Schema
class. For details on what these base context classes do, see the sections PrototypingContext_Class_in_Circuit_Graph_Support._For_details_on_how_prototyping_works_in_Circuit_Editor,_see_Prototype_Handling and PrototypingContext Class in Circuit Graph Support. For details on how prototyping works in CircuitEditor, see [Prototype]].
TemplatingContext
derives from Sce.Atf.Dom.TemplatingContext
. It is the editing context for the templates library, and is the context bound to the TemplateLister
component when a circuit document becomes the active context. For details on working with templates in CircuitEditor, see Template Handling.
CircuitEditor has several windows that handle ways of grouping and reusing controls, such as the Prototypes window. Mastering also provides re-usability, although masters are not listed in a window. The Layering window resembles the Templates window, but provides a mechanism for layering circuits and controlling their visibility.
You create a prototype by copying circuit items and pasting them into the Prototypes window. The prototype can then be dragged back onto the canvas.
CircuitEditor does very little itself to implement prototyping. As with many of the other classes in CircuitEditor, its prototype classes all derive from classes with the same name in the Sce.Atf.Controls.Adaptable.Graphs
namespace. These classes are all DOM adapters and override properties in the base class to get type metadata from the Schema
class:
-
Prototype
: Circuit prototype, which containsElement
s andWire
s that can be copied into a circuit. -
PrototypeFolder
: Folder ofPrototype
s. -
PrototypingContext
: Editing context for prototypes in the circuit document. For a discussion of this context's base class, see PrototypingContext Class.
Prototype
, are discussed in DOM Adapters.
The PrototypeLister
component completes the prototype implementation, providing a tree control in which to list prototypes. CircuitEditor simply includes PrototypeLister
in its MEF TypeCatalog
.
Templates are similar to prototypes in that both allow saving parts of a circuit and organizing these items in a window with a tree control, from which the item can be dragged back onto the circuit canvas. The two differ in what can be copied and how it is copied: a template can be made only from module or group using a menu item. A template may also reference a module or group in an external file. (Provision is made for these files being missing.) Note that promoting a module or group to a template changes the selected item into a template. In addition, changing one instance of a template changes them all.
Like prototypes, most of the template classes derive from classes by the same name in another namespace: Sce.Atf.Dom
in this case. These classes are all DOM adapters.
The TemplateLister
component provides a tree control in which to list templates. CircuitEditor simply includes TemplateLister
in its MEF TypeCatalog
.
Template
adapts a template, containing information about the group or module in the template. The Template
and TemplateFolder
DOM adapters are discussed briefly in DOM Adapters. The Template
base class contains several abstract properties that describe the template and must be overridden. For instance, the Name
property:
public override string Name
{
get { return (string)DomNode.GetAttribute(Schema.templateType.labelAttribute); }
set { DomNode.SetAttribute(Schema.templateType.labelAttribute, value); }
}
In the usual fashion of such overrides, the information is obtained from the data model's metadata classes in the Schema
class.
There is also a Guid
property with a GUID identifying the template:
public override Guid Guid
{
get { return new Guid((string)DomNode.GetAttribute(Schema.templateType.guidAttribute)); }
set { DomNode.SetAttribute(Schema.templateType.guidAttribute, value.ToString()); }
}
TemplateFolder
has a similar, simple implementation, overriding the properties in the base TemplateFolder
class, getting information from the metadata classes.
A template instance references a module or group, so the data model defines two types: "moduleTemplateRefType" and "groupTemplateRefType". If a circuit contains a template instance, the DomNode
for the instance in the application data is a module with a "moduleTemplateRefType" or "groupTemplateRefType" type.
GroupInstance
and ModuleInstance
represent instances of a template. They are actually implemented in CircuitEditor and both derive from Module
. These classes are DOM adapters for the template data types:
Schema.moduleTemplateRefType.Type.Define(new ExtensionInfo<ModuleInstance>());
Schema.groupTemplateRefType.Type.Define(new ExtensionInfo<GroupInstance>());
Both classes implement IReference<Module>
and IReference<DomNode>
. IReference
has a method and a property:
-
CanReference()
: Can the givenDomNode
be referenced, that is, is it of the right type? -
Target
: Actual targetModule
of the reference.
TemplatingContext
uses GroupInstance
or ModuleInstance
to create a template instance for a group or module.
Keep in mind that these instances reference a module or group. The information for the circuit items in a template module instance resides in the template itself, not in the template instance.
ProxyGroup
delegates communications with the targeted group on behalf of a GroupInstance
. ProxyGroup
's constructor is
public ProxyGroup(GroupInstance owner, Group target)
and ProxyGroup
is constructed by GroupInstance
in its ProxyGroup
property:
m_proxyGroup = new ProxyGroup(this, Target.As<Group>());
so the ProxyGroup
is constructed using the underlying Group
the group template instance refers to. ProxyGroup
handles functions for a group template instance that are different from a regular group.
TemplatingCommands
is a command client that provides these commands:
- Add Template Folder: Add a template folder to the Templates window.
- Import Templates from Document...: Import templates from an external
.circuit
document. - Promote To Template Library: Make the selected module a template, copying it to the Templates window. The selected item is copied and through the
TemplatingContext
'sInsert()
method, added to the list of templates. The original item is replaced by a template instance. - Demote To Copy Instance: Make the selected template instance a module that is no longer a template.
TemplatingCommands
sets up the commands, implements ICommandClient
, and implements the command to add a template folder.
The TemplatingCommands
class in CircuitEditor implements the promote and demote commands, which is the bulk of the work for templating.
TemplatingContext
provides the editing context for the Templates window. The base class's definition is:
public abstract class TemplatingContext : SelectionContext,
IInstancingContext,
ITemplatingContext,
IObservableContext,
INamingContext
SelectionContext
is a DOM adapter that implements ISelectionContext
to allow selecting templates in the Templates window. The other interfaces perform a similar role to the interfaces implemented for PrototypingContext
. For a discussion of these, see PrototypingContext Class in Circuit Graph Support.
ITemplatingContext
, similarly to IPrototypingContext
, helps present a tree view of the templates and creates IDataObject
instances from template items. ITemplatingContext
also checks to see if a reference can reference the specified target item and actually get the reference. ITemplatingContext
's implementation is split between the TemplatingContext
class and its base. The main work is done by the non-base TemplatingContext
, which implements the referencing methods, CanReference()
and CreateReference()
. CreateReference()
creates a new GroupInstance
or ModuleInstance
as a template instance for a group or module.
TemplatingContext
also gets notified if a template is removed and turns any template references into instances in that case.
Mastering is similar to templating, in that it creates a master from selected circuit items, that is, a subcircuit, creating a new type of circuit element. Any changes to this master element affect all instances of it. Mastering differs from templating in that any selection of items in the circuit can be converted to a master, not just a module or group. The master appears as a module displaying whatever pins were not internally connected. There is no Mastering window.
Mastering is implemented almost entirely in CircuitEditor, except for the SubCircuit
and SubCircuitInstance
classes, which are DOM adapters derived from classes of the same name in Sce.Atf.Controls.Adaptable.Graphs
.
SubCircuit
adapts a DomNode
to a mastered subcircuit. Like many DOM adapters in CircuitEditor, it simply overrides the base class's properties, getting information from the Schema
class's metadata classes.
SubCircuitInstance
adapts a DomNode
to an instance of a mastered subcircuit. Like SubCircuit
, it simply overrides the base class's properties, getting information from the Schema
class's metadata classes.
Note that SubCircuitInstance
derives from Element
— not Circuit
as the SubCircuit
DOM adapter does. A SubCircuitInstance
represents a single element in a circuit, whereas a SubCircuit
represents one or more elements in a circuit. These types have adapters defined in this way:
Schema.subCircuitType.Type.Define(new ExtensionInfo<SubCircuit>());
Schema.subCircuitInstanceType.Type.Define(new ExtensionInfo<SubCircuitInstance>());
The Editor
component uses SubCircuitInstance
when creating an adaptable control for a master subcircuit instance, either when the master is double-clicked or hovered over.
MasteringCommands
is a command client implementing these commands under the Edit menu:
- Master: Create a custom module type from the selection.
- Unmaster: Expand the last selected custom module into the original circuit elements, no longer a master instance.
DomNode
s, setting up the pins correctly for the new master, and then creating a DomNode
for the master module, represented by a SubCircuitInstance
DomNode
. The canvas is updated accordingly. Unmastering is a simpler process, simply removing the existing master DomNode
and creating a new list of DomNode
s from copies of the master's items.
MasteringValidator
tracks changes to subcircuits and subcircuit instances in the document and throws an InvalidTransactionException
for certain errors. MasteringValidator
is a DOM adapter that is defined for the circuit document type in SchemaLoader
:
Schema.circuitDocumentType.Type.Define(new ExtensionInfo<MasteringValidator>()); // validates sub-circuits
The allows the validator to track changes throughout the entire circuit document, that is, whenever the circuit changes. The OnNodeSet()
subscribes to changes to the DomNode
tree:
protected override void OnNodeSet()
{
base.OnNodeSet();
DomNode.ChildInserting += DomNodeChildInserting;
DomNode.ChildRemoving += DomNodeOnChildRemoving;
}
These event handlers show examples of the kind of circuit checking that can be done. For instance:
private void DomNodeChildInserting(object sender, ChildEventArgs e)
{
if (Validating)
{
// inserting an instance of a sub-circuit into itself?
SubCircuitInstance subCircuitInstance = e.Child.As<SubCircuitInstance>();
SubCircuit subCircuit = e.Parent.As<SubCircuit>();
if (subCircuitInstance != null &&
subCircuit != null &&
subCircuitInstance.SubCircuit == subCircuit)
{
throw new InvalidTransactionException(
"Can't use a sub-circuit inside itself".Localize());
}
}
}
Attempting to insert a SubCircuitInstance
that is an instance of the SubCircuit
into the SubCircuit
fails.
You can copy and paste circuit items into a layer to control their visibility. The layering classes in CircuitEditor all derive from classes of the same name (except for ModuleRef
) in the Sce.Atf.Controls.Adaptable.Graphs
namespace that provide their functionality. These classes are all DOM adapters:
-
LayerFolder
: Adapter for layer folders. -
LayeringCommands
: Component for layering commands. For details, see LayeringCommands Component. -
LayeringContext
: Context for layering. For details on this context, see LayeringContext Class. -
ModuleRef
: Derives fromSce.Atf.Controls.Adaptable.Graphs.ElementRef
and is used within layer folders to represent circuit modules that belong to that layer.
LayerLister
component, which uses the LayeringContext
to present a tree view of the layers with a TreeControl
.
These classes provide a variety of capabilities.
CircuitValidator
derives from the DOM adapter Sce.Atf.Controls.Adaptable.Graphs.CircuitValidator
and overrides its properties with data it gets from the metadata classes in Schema
. For details on the base class, see CircuitValidator Class.
The GroupingCommands
component is the command client for grouping commands and derives from Sce.Atf.Controls.Adaptable.Graphs.GroupingCommands
. For more information on this base component, see GroupingCommands Component.
ModulePlugin
is a palette client component that creates the circuit items palette. In some ways, it functions like other palette clients, such as the one for ATF Simple DOM Editor Sample described in Using a Palette.
The component's IInitializable.Initialize()
method sets up palette data using its DefineModuleType()
method, which performs several functions, including adding an item to the palette. It also adds type metadata information by calling the NamedMetadata.SetTag()
method for the types on the palette.
PrintableDocument
is an implementation of IPrintableDocument
to print the circuit canvas. This implementation uses a private class CircuitPrintDocument
, which derives from CanvasPrintDocument
. CanvasPrintDocument
is an abstract base class for canvas printing. PrintableDocument
overrides its methods to get "page" bounds for the various PrintRange
values, as well as the Render()
method to actually render a circuit to the page.
PrintableDocument
is defined as the DOM adapter for the circuit type, because the document represents a circuit.
- Circuit Editor Programming Discussion: Learn how ATF handles graphs, and provides editors for kinds of graphs, such as circuits.
- Code Editor Programming Discussion: Shows how to interface third party software to an ATF application: the ActiproSoftware SyntaxEditor.
- Diagram Editor Programming Discussion: Very simply combines components from the CircuitEditor, FsmEditor, and StateChartEditor samples into one application, with the abilities of all three, showing the power of components.
-
DOM Property Editor Programming Discussion: Shows how to use the ATF DOM with an XML Schema to define application data with a large variety of attribute types, whose values can be viewed and edited using the ATF
PropertyEditor
component, using various value editors to view and edit attributes. - DOM Tree Editor Programming Discussion: Shows how to edit DOM data using a tree control and display properties in a variety of value editors.
- File Explorer Programming Discussion: Discusses the ATF File Explorer Sample using list and tree controls with adapters.
- FSM Editor Programming Discussion: Tells you about how the ATF FSM Editor Sample edits simple graphs for state machines, using DOM adapters for contexts and validation.
-
Model Viewer Programming Discussion: Shows how the ATF Model Viewer Sample is written, discussing how ATGI and Collada model data is handled, using rendering components, and using a
DesignControl
as a canvas for rendering. -
Simple DOM Editor Programming Discussion: Programming the ATF Simple DOM Editor Sample, creating a palette, using DOM adapters and contexts, editing application data, and searching
DomNode
s. - Simple DOM Editor WPF Programming Discussion: Programming the ATF Simple DOM Editor WPF Sample, which is similar to ATF Simple DOM Editor Sample, but implemented using ATF's WPF framework.
- Simple DOM No XML Editor Programming Discussion: Programming the ATF Simple DOM No XML Editor Sample, which is very similar to ATF Simple DOM Editor Sample, except that it doesn't use XML for either its data model or persisting application data.
- State Chart Editor Programming Discussion: Shows using ATF graph and other classes to create a statechart editor, using DOM adapters, documents, contexts, and validators.
- Target Manager Programming Discussion: Description of how a target manager is implemented using ATF components to manage target devices, such as PlayStation®Vita or PS3™ consoles. A target manager is used in other tools, such as the StateMachine tool.
- Timeline Editor Programming Discussion: Discusses how to create a fairly full-featured timeline editor using the ATF timeline facilities, such as the timeline renderer and the timeline control and its manipulators.
-
Tree List Control Programming Discussion: Demonstrates using the
TreeListControl
andTreeListItemRenderer
classes to display and edit hierarchical data in a tree view with details in columns. -
Tree List Editor Programming Discussion: Demonstrates how to use the ATF tree controls
TreeListView
and its enhancement,TreeListViewEditor
.TreeListView
usesTreeListViewAdapter
, which adaptsTreeListView
to display data in a tree. - Using Dom Programming Discussion: Shows how to use the various parts of the ATF DOM: an XML Schema, a schema metadata class file generated by DomGen, DOM adapters for the data types, a schema loader, and saving application data to an XML file.
- Home
- Getting Started
- Features & Benefits
- Requirements & Dependencies
- Gallery
- Technology & Samples
- Adoption
- News
- Release Notes
- ATF Community
- Searching Documentation
- Using Documentation
- Videos
- Tutorials
- How To
- Programmer's Guide
- Reference
- Code Samples
- Documentation Files
© 2014-2015, Sony Computer Entertainment America LLC