Designers are visual tools used for assembling the elements of a user
interface (UI) quickly and easily. The Designer subsystem consists of a
component palette, an Inspector, one of three designers, and a component
tree. To access the design tools, open a source file in the content pane,
then click its Design tab. (NOTE: The Design tab appears
only if the source file inherits java.awt.Container.
)
The JBuilder designers include:
The Designer framework uses CMT to discover the subcomponents and property settings that make up a Java file. This information is used to determine relationships between subcomponents, to add nodes to a structure tree representing those relationships, and to provide a visual representation of the file's UI subcomponents and relationships in the UI Designer.
The Designer framework can be extended in a number of ways. The most obvious is that property editors and customizers included with JavaBeans are automatically exposed through the designer. Developers can use CMT functionality to enhance their custom property editors.
Less obvious ways of extending the Designer subsystem include:
com.borland.jbuilder.designer.DesignerManager
. Each designer
that makes up the system is an Open Tool that must register itself with the
DesignerManager
. The DesignerManager
registers the four designers included
with JBuilder itself rather than having them mentioned in the manifest file; therefore, the initialization can be delayed until the user clicks on the Design Tab for
the first time. The LayoutAssistants
used by the UI Designer are also
registered in this way as well.
To register a custom designer:
public static void initOpenTool(byte majorVersion, byte minorVersion) MyDesigner myDesigner = new MyDesigner(); DesignerManager.registerDesigner(myDesigner); }
Other major pieces of the designer subsystem are the Inspector
and all the property editors that it uses, and the component
palette. The Inspector listens to selection change events in the
structure view to know which subcomponent needs its CmtPropertyStates
displayed. The ComponentPalette
provides a customizable multi-page toolbar of components that can be visually designed.
CMT is the tool by which subcomponents and
property settings for a source file are discovered. CMT resides in
com.borland.jbuilder.cmt
as a collection of interfaces.
CMT uses the Java Object Toolkit (JOT) to find all the subcomponents in the file under design, and to determine the
properties and events that each type of subcomponent exposes. For each
subcomponent, the method calls made to it in the file's jbInit()
method are discovered and stored in an array in the subcomponent. In
particular, the property settings are processed so that each property setting is
associated with a CmtPropertyState
.
Through introspection or the information in a BeanInfo file, CmtComponent
determines the properties (encapsulated in CmtProperty
) that
the component supports. CmtPropertyStates
are the value
of a particular property for a particular subcomponent, whether or not they have
an associated CmtPropertySetting
.
The CmtComponent
for the file under design is a special case. Most of the CmtComponent
instances are for representing the types of subcomponents used in the file under design. The file under design will be the only one that
uses the methods dealing with CmtComponentSource
. The
CmtComponent
Source file (the file being designed) uses the CmtComponent
of its superclass to determine the properties that it exposes.
The first call to CmtComponentSource.getSubcomponents()
causes buildSubcomponents()
to be called. This method uses JOT to
determine all the fields that make up the source file, and then gets the
CmtComponent
for each type and creates a CmtSubcomponent
for
each field. There is a CmtComponentManager
that maintains
a collection of all the CmtComponent
instances this project has processed,
so that extra reading of BeanInfo and component class files can be avoided.
Since events are rarely listed explicitly in a BeanInfo, reflection is almost
always used to determine the events. Using reflection to determine a
JavaBean's properties or events requires walking the superclass hierarchy, so
CmtComponents
for all the superclasses are created as well. Creating a
CmtComponent
for all the classes used in the bean being designed is one of the
most expensive portions of the design process. As a way to speed up the
process of creating CmtComponents
, beans that do not do anything out of the
ordinary in their BeanInfo have their property and events info written to a .pme
(Property, Methods, and Events) file in the
[userhome]/.jbuilder/pme
directory so that the next time
they are encountered, one tiny simple file is processed to create the
CmtComponent
instead of having to examine the entire superclass hierarchy.
JBProject
, you
can retrieve a CmtComponent
Manager instance that will allow access to CMT
functionality through:
JBProject project; // this is a valid non-null instance of JBProject CmtComponentManager componentManager = CmtComponentManager)project.getComponents();
To obtain the file currently under design, first retrieve the current
DesignerViewer
node viewer, then:
CmtComponentSource componentSource = designerViewer.getComponentSource(); CmtSubcomponent subcomponents[] = designerViewer.getSubcomponents();
You can manipulate the methods, properties, and events of a subcomponent, as well as retrieve a live instantiation of the component or determine how the subcomponent is nested.
DesignerViewer
instance creates its own instance of the palette
UI. There is one ComponentPaletteManager
(com.borland.jbuilder.designer.palette.ComponentPaletteManager
)
which owns the models used by the palette and manages the reading/writing of the
palette.ini
.
PropertyGrids
which model the propertyStates
and EventStates
of the selected component.
The Inspector listens to the TreeSelection
from the structure view to know when to refresh its model.
The grid consists of two columns, the first being the name of the
property/event and the second being the text representation of the value of the
proby the/event as maintained bythe propertyState
. Cell editors for the
value column are dynamic. They are based on the property editor associated
with the CmtProperty
of each CmtPropertyState
. They all extend DefaultCellEditor
.
Cell editors use their cell's associated
java.beans.PropertyEditor
to perform their edit on the appropriate
piece of Java code. The cell editors are also responsible for supplying
the various "special knowledge" property editor extensions with the data they
require by making the extra method calls provided for in their interfaces.
CmtPropertyEditor
provides the CmtPropertyState
to the editor so that it can
know about the CmtSubcomponent
, CmtComponentSource
, etc. This
setPropertyState()
method is called prior to
setValue()
and again with null after the cell edit is over.
CmtPropertyEditor2
also provides the DesignerViewer
instance via its
setContext()
method that allows the property editor to know about
the project as well as providing a method for setup and cleanup.
CmtPropertyEditors
should be careful that they only perform their expensive operations when their propertyState
is non-null, which is usually the case unless they store some other data and forget to null it out when their
propertyState
is reset to null by the cell editor.
PropertyEditors
are shared often, therefore they should not attempt to store data from one edit session to another. The cell editors will make calls to
getTags()
and getCustomEditor()
when CmtPropertyState
is null as part of their determination of which cell editor to use. For a
CmtPropertyEditor
that is a tagEditor
and whose propertyState
is null,
getTags()
should return an empty array, leaving the determination
of what to place in tag list to the times that getTags()
is called
when its propertyState
is non-null.
add()
method calls
to decipher the structure of the UI. The UI Designer also substitutes the
real layout property state created CmtComponent
with a special layout property state of type LayoutPropertyState
. Containers whose BeanInfo attribute
isContainer()
is false have their layout property suppressed.
Another synthetic property state that the UI Designer sometimes makes is a
buttonGroup
property state for subcomponents that extend
javax.swing.AbstractButton
so that the user can specify the
ButtonGroup
of the button and get the ButtonGroup.add()
method
created as a result.
Property states whose name
property is in brackets are treated slightly
differently by the Inspector
- the brackets and the first character are removed when forming the property
name, and the property name is rendered in bold type. All such names
appear at the top of the Inspector's grid, with the first character being used
to determine the order of these special property states. This convention
is used to distinquish synthetic properties from real ones.
The UI Designer uses LayoutAssistants
to govern the use of nibs and component outlines based on the layout to which the container is set. The
LayoutAssistant
is responsible for establishing the constraints for newly dropped components, adjusting the constraints of moved components, and establishing
constraints for each child of a container when the layout is changed.
LayoutAssistant
to incorporate the availability of the custom layout
manager into the UI Designer and Inspector. Custom layout assistants must
register themselves with the UI Designer
specifically:
public static void initOpenTool(byte majorVersion, byte minorVersion) { UIDesigner.registerAssistant( { MyLayoutManagerAssistant.class, "com.borland.samples.opentools.layoutassistant.MyLayoutManager", true); }
There is a basic layout assistant implementation available at
com.borland.jbuilder.designer.ui.BasicLayoutAssistant
that you may
wish to extend, or your layout assistant can just implement
com.borland.jbuilder.designer.ui.LayoutAssistant
which defines the
methods for the layout specific UI interaction model. Within your layout
assistant you will have to define a property editor to be used for changing any
properties your custom layout may provide such as horizontal and vertical gaps. You may also wish to define custom behaviour for component outlines
when they are being moved within a container that uses your layout manager. For
instance, instead of component outlines bouncing from layout position to
layout position, you can specify that the component outlines will track the
mouse instead.
There is a sample layout assistant available in your
samples/OpenToolsAPI/layoutAssistant
directory. This sample
illustrates how to write a layout assistant for a custom layout, register
it with the UI Designer so that it shows up as a layout option in the Inspector,
and customize the component outline behavior.