BookNBPlatformCookbookCH0702

Contents

NetBeans Platform Cookbook Chapter 07 02

Creating Widgets

The Widget class documentation

A scene is a tree of small building blocks called widgets and represented by this class.

Each widget has an origin location specified relatively to the location its parent widget and placement is specified be its boundary.

The widget is also responsible for rendering the region. The widget is an abstract implementation and does not have render anything except borders and background. There are various built-in widget each for a specific visualization. The widget also holds general properties like foreground, opacity, ... that could be reused by the high-level widgets.

The widget has a layout assigned. The layout takes care about resolving the placement of children widgets. For that it can use various properties like preferredLocation, preferredBounds, ... When the widget is resolved (placed) than the read only location and bounds properties contains resolved location and boundary of a widget.

Each widget has a chain of actions. Actions defined defines a behaviour of the widget. E. g. MoveAction makes the widget moveable. Also there is possible to create/assign other chains that will be activated based on the active tool of a scene. (From API documentation)

The widget have its state specified by ObjectState class. When the widget state gets changed, notifyStateChanged() is called to notify about it. Used states are: objectSelected, objectHighlighted, objectHovered, objectFocused, widgetHovered, widgetFocused, widgetAimed.The state is automatically updated by high-level scenes and actions. Therefore you can define your own look and feel directly in the that method. Since version 2.6 Widget class implements Accessible interface.

Most used methods

Tree aspect methods: addChild(Widget child) or addChild(Widget child, Object constraint) and removeChild(Widget ch), removeFromParent(), List<Widget> getChildren(), getParentWidget(), getScene(), setLayout(Layout layout). The addDependency(Widget.Dependency dep) method adds a dependency listener which is notified when the widget placement or boundary is going to be changed. E.g. the Anchor implements Dependency interface.

Visualization: bringToBack() and bringToFront(), setVisible(boolean v), isVisible(), isValidated(), revalidate(), revalidate(boolean repaintOnly). Conversion: convertLocalToScene(Point localLocation), convertSceneToLocal(Point sceneLocation).

Action: getActions(), getActions(String tool) and createActions(String tool).

Font: getFont(), setFont(), setFontFromResource(String property). Warning: the font is default set to null and the getParent().getFont() is returned recursively until getScene().getDefaultFont() is returned. If the child was not added as a child (it has no parent) you receive null.

Graphics: getGraphics() to use for painting in the subclass.

Bounds: getLocation(), getBounds(), getPreferredBounds(), getPreferredLocation() and setPreferredLocation(), setMaximumSize(Dimension dim), setMinimumSize(Dimension dim), setPreferredBounds(Rectangle b), setPreferredLocation(Point loc), setPreferredSize(Dimension dim). The isHitAt(Point loc) checks if this point is managed (painted) by this widget.

Object State: getState(), setState(). ObjectState.createNormal(), ObjectState.deriveWidgetFocused().

State: isEnabled(), isValidated(), isVisible(), setEnabled(boolean en).

Propeties: setBorder(Border border), setCursor(Cursor cursor), setForeground(Color foreground), setForegroundFromResource(String property), setToolTipText(), setResourceTable(ResourceTable table). ResourceTable is some map of String propertyName and Object value pairs. Table can have a parent. There is a BorderFactory to create Borders.

Widgets predefined by library have the common method setPaintAsDisabled(boolean pdis).

Predefined Widgets

LabelWidget shows a single line text. The widget origin is a point on the left side of the baseline of the text.

ImageWidget paints an image. The widget origin is top-left corner of the image. You can use animated images. Warning from documentation: For proper work, do not load images using Utilities.loadImage because the method is producing static images. Instead use standard Toolkit.createImage method for loading.

IconNodeWidget class consists of ImageWidget and LabelWidget arranged vertical or horizontal using flow layout.

SeparatorWidget is horizontal or vertical rectangle of given thickness and orientation.

ScrollWidget and SwingScrollWidget is similar the JScrollPane. The second uses JScrollBar as bars.

ComponentWidget allows to include swing JComponent into the scene.

ConnectionWidget represents a connection between two locations. The locations are resolved by Anchor according to connection widget and source/target location. The path is specified by controll points. There are resolved by Router. You can specify source-, target- and end point-shape. There are factories to create basic Anchor, AnchorShape, PointShape for spoken points, Router.

ConvolveWidget paints its children to off-screen buffer and applies the convolve filter on it. You can e.g. blur undefined or forbidden widgets.

LevelOfDetailsWidget shows children according to zoom of the scene. You can place children at finer level and show them only when the user zooms into it.

LayerWidget is a transparent widget to group widgets by type, state or function. It is similar to JGlassPane. There are these layers directly under the scene: background, main layer for normal node widgets, connection layer for connections and interaction layer used by actions to place temporary used widgets, e.g. control points for resizing, dashed lines for alignment.

The Scene itself is a subclass of the Widget, too. For more information see the Introduction section or the first recipe of this chapter.

Factories

There are factories to create instances needed for these classes/methods. You have not to create your own. If some method needs special parameter and you do not know how to create it try going to parameter class's documentation an try Use link at the page top. E.g. AnchorShapeLocationResolver.

ActionFactory creates predefined actions for you. Note: keep in mind that the ObjectScene provides its own actions for selection, objectHover and widgetHover instead of the ActionFactory's methods. With actions is dealt in the Add Actions recipe.

AnchorFactory creates anchors for connections. There are prepared different behavior (where is the anchor located related to joined widget). Interesting is the proxy anchor that delegates its work to one of several anchors by StateModel state.

AnchorShapeFactory for AnchorShapes. You can inspire yourself for your own shapes.

BorderFactory creates borders like a swing. Created borders can be used by more widgets. Warning: add the right import from the VisualLibrary, not of the Swing.

GraphLayoutFactory creates layouts for GraphScene class - hierarchical, orthogonal and tree.

LayoutFactory creates layouts. We describe them shortly:

  • Absolute Layout places widgets by preferred Location property.
  • Card Layout shows only one child, other ones are hidden.
  • Devolve Layout is used for one-time layout. It enables animation.
  • Horizontal and Vertical Flow Layout places widgets horizontaly/verticaly after previous ones.
  • Graph Scene Layout for GraphScene or GraphPinScene uses Graph Layout.

PointShapeFactory creates a PointShape of given size or using an image. It can be used by multiple ConnectionWidgets.

RouterFactory creates Router instances. Router resolves path of the ConnectionWidget. Some used routers are:

DirectRouter is the default. It creates direct line from source to the target.

FreeRouter has it path set by user using AddRemoveControlPointAction.

OrthogonalSearchRouter collects all regions occupied by other widgets or uses given CollisionCollector and then resolves the path.

image:Nbpcook_07_02_widgets.png

Figure 7.2 Kinds of Widgets

image:Nbpcook_07_03_widgetsLod.png

Figure 7.3 LOD widget

Preparation

We suppose you have prepared any component for scene view, e. g. the JPanel or TopComponent subclass. It contains any subclass of the Scene. The scene contains at least two layers: main layer for widgets and connection layer for edges. The interaction layer could be fine.

How to

To be complete we will show how to create predefined widgets. For more details see example sources and documentation. We use the ObjectScene to illustrate all needed code. We show those trivial for a completeness.

Create LabelWidget, ImageWIdget, IconNodeWidget

       widget = new LabelWidget(objscene, thing.getName() ); 
       widget.setPreferredLocation( new Point( 280, 333 ) );
       widget.setPreferredSize( new Dimension(90, 40) );
       widget.setBorder( dashedBorder );
       widget.setAlignment(LabelWidget.Alignment.CENTER);
       widget.setVerticalAlignment(LabelWidget.VerticalAlignment.CENTER);
       mainLayer.addChild(widget);
       objscene.addObject(thing, widget);
       widget = new ImageWIdget(objscene, image);
       ...
       widget = new IconNodeWidget(objscene);
       widget.setImage(image)	;
       widget.setLabel(“some text”);
       ...


Create UML class like widget

How to create widget like a class node in the UML diagram? Use VerticalFlowLayout for LabelWidget children. The SeparatorWidget divides separated groups.

private static Widget createClassLikeWidget(ObjectScene objscene, Thing thing) {
    Widget widget = new Widget(objscene);
    widget.setBorder( lineBorder );
    widget.setLayout( verticalLayout );
    Widget childLine;

    // header
    childLine = new LabelWidget(objscene, thing.getName());
    // Warning: The font is default set to null and the getParent().getFont()
    // is returned recursively until getScene().getDefaultFont() is returned.
    // If the child was not added as a child (it has no parent) you receive null.
    Font font = childLine.getFont();
    if (font == null) font = objscene.getDefaultFont();
    childLine.setFont( font.deriveFont( Font.BOLD) );
    childLine.setBorder( lineBorder2 );
    widget.addChild( childLine );
    // scene.addObject( thing, childwi);     // if you need

    ThingItem[] items = thing.getItems();
    int type_last = ThingItem.TYPE_NONE;

    for (int i = 0; i < items.length; i++) {
        ThingItem itm = items[i];
        int type_itm = itm.getType();
        if (type_itm != type_last) {
            childLine = new SeparatorWidget(objscene,
                    SeparatorWidget.Orientation.HORIZONTAL);
            childLine.addChild( childLine );
        }
        childLine = new LabelWidget(objscene, itm.getText());
        childLine.addChild( childLine );
        // scene.addObject(thing, chiline);     // if you need
        type_last = type_itm;
    } 
    objscene.addObject( thing, childLine);
    widget.revalidate();
    return widget;
}

If you want add an icon at line beginning create the line as a widget with HorizontalFlowLayout and add an ImageLabel and a LabelWidget as its children.

Custom Widget

If you want create your own widget you have to care about these points at least. The method calculateClientArea() must return a Rectangle occupied and maintained by the widget excluding bounds. So if your widget considers its location in its centre you return Rectangle(-width/2, -height/2, width, height).

Implement paintWidget() method. It has an area prepared for painting according to previously calculated clientArea. The [0,0] origin is located into widget location point. The Graphics2D instance is received by getGrapics() method.

If you want change widget's appearance according to state override the notifyStateChanged (ObjectState oldState, ObjectState state) method. We show a simplified example for UML Use Case widget.

public class UseCaseWidget extends Widget {

    private int a = 80;
    private int b =  50;
    private String name;

    private int labW = 0, labH = 0;
    private int labX = 0,  labY = 0;

    public UseCaseWidget(Scene scene) {
        this(scene, "");
    }

    public UseCaseWidget(Scene scene, String name) {
        super(scene);
        this.name = name;
        setLayout( LayoutFactory.createAbsoluteLayout() );
        setFont(scene.getDefaultFont());
        setName(name);
        revalidate();
    }

    protected Rectangle computeLabelBounds() {
        Graphics2D gr = getGraphics ();
        gr.setFont (getFont());

        FontMetrics fontMetrics = gr.getFontMetrics ();
        Rectangle2D labSized = fontMetrics.getStringBounds(name, gr);
        Rectangle labSize = roundRectangle(labSized);
        Rectangle myArea = calculateClientArea(); 

        labW = labSize.width;
        labH = labSize.height;
        labX = (myArea.width - labW) / 2;
        labY = myArea.height / 2;  // baseline is the string position

        Rectangle rec = new Rectangle(labX, labY, labSize.width, labSize.height)

        return rec;
    }

    protected Rectangle calculateClientArea() {
        return new Rectangle (0, 0, 2 * a + 1, 2 * b + 1);
    }

    protected void paintWidget() {
        Graphics2D g = getGraphics();
        Color oldc = g.getColor();

        g.setColor (getForeground());   
        g.drawOval (0, 0, 2 * a, 2 * b);
        
        g.drawString( name, labX, labY);

        g.setColor(oldc);
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
        computeLabelBounds();
        revalidate();
    }


Notes/Tips

You can use animation to set new preferred location, preferred bounds, zoom factor, background and foreground color.

Notes/Tips

Have a look on example sources in 07_Visual_Library / vl02_create_widgets and try experiment a little.
Visual Library 2.0 - Examples are stored on this page
http://platform.netbeans.org/graph/examples.html

Text and sources were created under NB 6.8, 6.9, 7.1.

Navigation

Not logged in. Log in, Register

By use of this website, you agree to the NetBeans Policies and Terms of Use. © 2012, Oracle Corporation and/or its affiliates. Sponsored by Oracle logo