Õ¾ÄÚËÑË÷: ÇëÊäÈëËÑË÷¹Ø¼ü´Ê
µ±Ç°Ò³Ãæ: ͼÊéÊ×Ò³ > SWT: The Standard Widget Toolkit

4.8 Painting - SWT: The Standard Widget Toolkit

Previous Section  < Day Day Up >  Next Section

4.8 Painting

Painting is the standard mechanism that the operating system uses to inform a control that it is time to draw. With the exception of class Canvas (see the section Class Canvas), whose function is to be a general-purpose "drawing area," controls are responsible for drawing themselves. This means that this section is mostly of interest to those programmers who are implementing their own controls or using canvases to draw arbitrary graphics.

4.8.1 Deferred Update Strategy

When it is time to draw a control, the windowing system issues one or more paint events for the control. Paint events are generated for a variety of reasons. For example, when a shell comes forward, replacing the topmost shell, areas within the shell that were obscured by the previous topmost shell need to be drawn. Similarly, when a control is moved, resized, or hidden, other controls within the shell can be uncovered or exposed and need to be drawn.

To minimize the number of paint events needed to draw a control, SWT uses a deferred update strategy. This strategy is implemented directly by most of the underlying windowing systems.[10] Each control maintains a graphics region called the damaged region. When a redraw is necessary, instead of issuing paint events immediately, the newly exposed area is added to the damaged region. When a paint event is finally issued, the damaged area is filled with the background color of the control,[11] and the damaged region is cleared to make it ready for the next redraw request. In this manner, multiple redraws are merged into a single paint event, which is delivered from the event loop.

[10] X Windows provides only a partial implementation of this strategy. SWT uses the underlying X mechanism but augments it with an emulated version that provides the missing capabilities.

[11] The background is not filled when SWT.NO_BACKGROUND is set. See Filling the Background in this section.

4.8.2 Paint Events

An SWT.Paint event is sent when a control needs to be drawn. Controls listen for paint events to draw their contents. Table 4.3 shows the painting events that are provided by SWT.

Table 4.3. Paint Events

Untyped Event

Description

SWT.Paint

A control was asked to draw

Typed Event

Listener

Methods

PaintEvent

PaintListener

paintControl(PaintEvent)


Table 4.4 shows the relevant event fields during a paint event.

Table 4.4. Public Fields of Class Event That Are Valid during SWT.Paint

Field

Meaning

gc

The GC (graphics context) that should be used to draw the control, configured with the font, foreground and background colors of the control, clipped to the damaged region (see Graphics Context)

x

The x coordinate of the largest rectangle surrounding the damaged region

y

The y coordinate of the largest rectangle surrounding the damaged region

width

The width of the largest rectangle surrounding the damaged region

height

The height of the largest rectangle surrounding the damaged region


Using SWT.Paint to Draw in a Canvas

The following example uses SWT.Paint to draw an oval in a shell.






public static void main(String[] args) {

    Display display = new Display();

    final Shell shell = new Shell(display);

    shell.addListener(SWT.Paint, new Listener() {

        public void handleEvent(Event event) {

            GC gc = event.gc;

            Rectangle rect = shell.getClientArea();

            gc.drawArc(0,0,rect.width,rect.height,0,360);

        }

    });

    shell.setSize(150, 150);

    shell.open();

    while (!shell.isDisposed()) {

        if (!display.readAndDispatch()) display.sleep();

    }

    display.dispose();

}


Notice that the example code uses getClientArea() to determine the bounds of the oval. Drawing occurs in the client area of a control, not the bounds.

Figure 4.6 shows the result of running the code. Although you cannot tell from the picture, only the area that needs to be painted is actually drawn, because the GC that is provided in the event is clipped to the damaged region. For more on clipping, regions, and class GC, see the Graphics Fundamentals chapter.

Figure 4.6.

graphics/04fig06.gif


4.8.3 Causing a Redraw

As was previously stated, most controls are responsible for drawing themselves. However, for applications that draw their own content (on a canvas, for example), it is possible to exploit the deferred update strategy provided by the operating system to minimize the amount of drawing that is done. Calling one of the following methods indicates that a particular area of the control needs to be painted.

redraw() Causes the entire client area of the control to be added to the damaged region. The next time a paint event is issued, the control will be completely redrawn. The children of the control are not damaged.

redraw(int x, int y, int width, int height, boolean all) Causes a rectangular area of the control to be added to the damaged region. The next time a paint event is issued, the union of this rectangle and the previous damage will be drawn. The boolean flag indicates that the area that is the intersection of the specified rectangle and the children is added to the damaged regions of the children to be redrawn, as well.

The redraw() method can be used to get rid of multiple drawing operations, reducing flicker. For example, in a simple implementation of a control that just draws a string, if setting the string were to draw right away, the control would flash when the string was set multiple times. If instead, setting the string simply called redraw(), the control would draw the string once, in a paint event.

Normally, collapsing multiple drawing operations is a desirable behavior. However, there are circumstances when multiple draws are not only necessary but required for correctness. Consider this code fragment where a text control is used to show progress.






//WRONG – only draws last percentage

text.setText("0 %");

for (int i=1; i<=100; i++) {

    try {

        Thread.sleep(100);

    } catch (Throwable th) {}

    text.setText(i + " %");

}


You might expect that running this fragment would cause the text control to draw successive percentages, but this is not the case. Instead, only the last one is drawn. This happens because the text control uses the deferred painting strategy, merging the 100 paint requests into a single request that is delivered when your application returns to its event loop. Fortunately, SWT provides the ability to force paint events to happen outside of the event loop.

4.8.4 Forcing an Update

The update() method is used to force paint events for a control (see Updating the Display for information about forcing paint events for the Display).

update() Forces all outstanding paint events for this control to be delivered before returning. The children of the control are unaffected. Only paint events are dispatched.

Using update(), the text control from the previous section can be forced to show each percentage as it occurs.






//CORRECT – draws every percentage

text.setText("0 %");

text.update();

for (int i=1; i<=100; i++) {

     try {

          Thread.sleep(100);

     } catch (Throwable th) {}

    text.setText(i + " %");

    text.update();

}


The update() Method Is Powerful and Dangerous

Using update() is generally unnecessary and can cause flickering and performance problems. This is true because update() defeats the merging of paint events implemented by the operating system.


4.8.5 Turning off Redraw

Although it is considered good practice and reduces the number of drawing operations, there is no guarantee that a control will always use the deferred update strategy. For example, to increase performance, many text editors draw each character immediately as it is typed, rather than calling redraw() with the area where the character should appear and drawing only inside paint events.

To further reduce the number of drawing operations for controls that draw outside of paint, setRedraw() can be used.

setRedraw(boolean redraw) Turns drawing on or off for the control. If the redraw parameter is false, all subsequent drawing operations are ignored. No drawing of any kind will occur until this method is called again with true. At that time, the entire client area of the control will be redrawn, because there is no way for SWT to detect which areas of the control would have been changed by any interim drawing operations. Nested calls to setRedraw() stack, so setRedraw(true) must be called once for each call to setRedraw(false). Only after the last call with true will the control be redrawn.

Using setRedraw() in its intended role can be quite tricky. In particular, although the method is available on all platforms, it prevents drawing only on platforms that provide the underlying capability. Furthermore, turning redraw back on ensures that the entire control will redraw, which may look worse than allowing each of the individual graphics operations to occur.

Other Uses for setRedraw()

Despite its drawbacks, setRedraw() is often used by both the operating system and SWT to optimize operations that are unrelated to drawing. For example, on every platform, new items can be added more quickly to lists and tables when redraw is turned off. When many items are going to be added, turning redraw off and adding the items, then turning it back on and letting the widget redraw can be faster than adding and redrawing each separately.


    Previous Section  < Day Day Up >  Next Section