|
SYS-CON.TV Webcasts
Comments
Did you read today's front page stories & breaking news?
SYS-CON.TV
|
Top Links You Must Click On
General Java Scrolling on Demand
A scrollable toolbar component
By: Volker Simonis
Jul. 2, 2004 12:00 AM
Modern GUI programs enable you to easily access status information and functionalities through various menus, toolbars, and information panels. However, as a program becomes more complex or when users have the possibility of configuring and extending these components, they tend to get overfilled. This leads to scrambled or even truncated components. This article introduces a new container component called ScrollableBar that can be used as a wrapper for any Swing component. As long as there is enough space to lay out the contained component, ScrollableBar is completely transparent. As soon as the available space gets too small, ScrollableBar will fade in two small arrow buttons on the left and the right side (or on the top and the bottom if in vertical mode) that can be used to scroll the underlying component, thus avoiding the above mentioned problems. ScrollableBar is a lightweight container derived from JComponent that uses the standard Swing classes JViewport and JButton to achieve its functionality. It fills a gap in the set of the standard Swing components and offers the possibility of creating more robust and intuitive user interfaces. Introduction Another challenge arises from the extensibility of applications. While the possibility of extending an application with various plugins may be a nice feature for the user, the fact that these plugins will populate the menus and toolbars in an unpredictable way presents new problems for the programmer. One way to solve these problems is to limit the GUI components to a certain minimal size; however, this may impose unnecessary restrictions on the user. (Think, for example, of somebody who by default works with an application that needs a resolution of at least 1024x768 but who occasionally gives demo talks with a beamer that only supports an 800x600 resolution.) Furthermore, if an application with a graphical user interface pretends to be resizable by displaying a resizable frame, the user expects he will be able to resize it based on his needs, not the programmer's. The second possibility is to do nothing and wait and see what happens. This is how most GUI applications are written today. Just compare Figure 1 with Figure 2 and see how parts of the status bars and toolbars are cut off if the window is shrunk beyond its optimal size. In the best case, the user could just reenlarge the application if this happens. In the worst case, if he or she is working on a device with a restricted resolution, it may be impossible to access the desired functionality. In any case such an application looks highly unprofessional. Scrollable Menus and Toolbars We now want to achieve the same behavior with menus, toolbars, and other status bars and information panels. To get a visual impression of how the modified components will look, compare Figures 1 and 4. They both show a screenshot of the Stylepad demo application shipping with every JDK, which has been extended by a vertical toolbar and a useful status bar (see Figure 2). While the menu, status bar, and toolbars are truncated and partially inaccessible in Figure 1, they can be scrolled and are fully functional in Figure 4 by using the arrow buttons that have been faded in. The Implementation The Swing Architecture As an example, Figure 6 shows how this Model-Delegate pattern applies to the JButton class. In Swing, all visible components are descendants of the JComponent class. They usually capsule a component-specific model with a delegate object, which is a descendant of the ComponentUI. These delegates are called user interface (UI) classes in Swing. They are look-and-feel specific, i.e., they're used to implement the different look and feel-dependent properties of a component, but they can also be used for other kinds of customization, for example, localization. One of the main responsibilities of the UI delegate is to paint the component it's tied to. In contrast to the AWT library, in Swing it's not the paint() method of every component that does the work of painting itself. Instead, the component's paint() method just calls the paint() method of its delegate with a reference to itself. The ScrollableBar Class ScrollableBar has four properties. The two Boolean properties "horizontal" and "small" store the orientation of the component and the size of the arrows on the scroll buttons. The integer property "inc" stores the amount of pixels by which the enclosed component will be scrolled if one of the arrow buttons is being pressed. Smaller values lead to a smoother but slower scrolling. Finally, the wrapped component is stored in the "comp" property. While "horizontal" is a read-only property that can be set only in the constructor, the other three properties are read/write bound properties as described in the JavaBeans specification. The following code shows the two-argument constructor of the ScrollableBar class: public ScrollableBar(Component comp, int orientation) { Notice the call to updateUI() in the last line of the constructor. As can be seen in the following code, updateUI() calls the static method getUI() from the class UIManager to query the right UI delegate and associates it with the current ScrollableBar object. public String getUIClassID() { UIManager.getUI() calls getUIClassID() to get the key that's used to query the actual UI delegate from a look and feel-dependent internal table. Usually the association of the standard Swing components with the appropriate UI classes is done by the different look and feels while they are initialized. However, as we are writing a new component, we have to establish this link manually, as shown below: static { Notice that linking a component to its UI delegate in this way results in the same UI class being used independent of the actual look and feel. Besides the getter and setter methods for the corresponding properties, there's no more functionality in the ScrollableBar class. All the painting and user interaction is handled by the UI delegate ScrollableBarUI. The ScrollableBarUI Class In our case, the UI delegate queries and stores the component's properties along with a reference to the component as private instance variables. Further on, it creates two arrow buttons and an object of type JViewport, which is used to wrap the scrollable component. Based on the orientation of the associated ScrollableBar object, the newly created elements are then being added to it by using a vertical or horizontal box layout. Notice that the scroll buttons are initially set to be invisible. Finally, the UI object registers itself as a property change listener on the associated component, as a change listener on the viewport, and as a mouse listener on the arrow buttons. The UI delegate is informed about every size change of the ScrollableBar object and the wrapped component by receiving a ChangeEvent from the viewport object. Depending on the new sizes, it can change the visibility state of the arrow buttons and re-lay out the component. Property changes in the ScrollableBar object are signaled to the UI delegate by a PropertyChangeEvent. Based on these events, it can update the internally cached values of these properties. Finally, the events resulting from the user interactions on the scroll buttons are handled by the different mouse listener methods. The UI delegate keeps a private boolean instance variable pressed that's set to true if a button was pressed, and reset to false as soon as the button is released or the mouse pointer leaves the button. As can be seen in Listing 2, pressing one of the buttons also starts a new thread that scrolls the underlying component by "inc" pixels in the corresponding direction and then sleeps for a short time. These two actions are subsequently repeated in the thread as long as the value of the instance variable "pressed" is true, while the amount of sleeping time is reduced in every iteration step. This results in a continuously accelerating scrolling speed, as long as the user keeps on pressing the arrow button. It should be noted that we don't need any special paint method for the ScrollableBarUI class, because painting occurs naturally from the standard Swing button and viewport components that we used. After we've discussed the main parts of the implementation, it should be evident why the advantages of dividing the functionality of the ScrollableBar class into two classes outweigh the coding overhead. First, we cleanly separated the properties of the component from the way that it displays and interacts with the user. Second, it's easy now to define a new UI delegate that renders the component in a different way or to derive a new UI delegate from the existing one that slightly adopts appearance or user interaction properties to a specific look and feel. Using the ScrollableBar Class JToolBar toolbar = new JToolBar(); in order to make the horizontal toolbar scrollable if the space becomes too small to render it as a whole. In general, the ScrollableBar class is recommended for wide and not very high components in horizontal mode and narrow and high components in vertical mode. If used for other components, the scroll buttons would get too big and take up too much space to be really useful. Menu Bars in JFrame Objects The problem arises because JFrame provides a specialized setJMenuBar() method for adding menu bars and this method expects an argument of Type JMenuBar. At first glance, we could just use one of the generic add() methods defined in JFrame's ancestor classes instead. However, if we take a closer look, we'll see that the problem is a bit more complex. First, in the case of JFrame, children are not being added to the component directly, but to the so-called "root pane", which is a special child component of every JFrame. However, we also can't add the menu bar directly to the root pane, because the root pane also has a special method called setJMenuBar() that expects a JMenuBar object as an argument. Using this method to add menu bars is essential, because only then will the RootLayout layout manager used by the JRootPane class honor the presence of the menu bar. RootLayout, which is a protected inner class of JRootPane, uses the protected JRootPane property "menuBar" that has been set by JRootPane.setJMenuBar() for layout calculations. To cut a long story short, we have to create a new SMJFrame class (which stands for Scrollable Menu JFrame) that overrides the createRootPane() method to return a new, customized root pane class. For this purpose we just derive an anonymous class from JRootPane that overrides the two methods setJMenuBar() and createRootLayout(). setJMenuBar(), the first of these two methods, wraps the menu bar into our ScrollableBar class before storing it as a protected instance variable and adding it to the layered pane that's a part of the root pane. The second method, createRootLayout(), returns an anonymous class that inherits from the JRootPane protected inner class RootLayout. It overrides the layout methods in that class in such a way that they use the ScrollableBar instance variable for layout calculations instead of using the bare menu bar, as it was done by the original version of the methods. These modifications finally give the desired result. A call to setJMenuBar() on a SMJFrame object will be forwarded to the customized root pane. There the menu bar will be wrapped into a ScrollableBar object before it will actually be added to the frame. Because the customized root pane uses a customized layout manager, it will handle the scrollable menu bar in the same way in which a JFrame object handles an ordinary menu bar. With respect to all other concerns, SMJFrame behaves exactly like its ancestor JFrame. Limitations Fixing this problem would require extensive changes in BasicToolBarUI, the UI delegate of JToolBar. Unfortunately, since not all the methods that need to be customized are declared public or protected, a complete rewrite of the delegate would be necessary. Conclusion The source code presented in this article is available at www.progdoc.org/ScrollableBar. References Reader Feedback: Page 1 of 1
Your Feedback
Enterprise Open Source Magazine Latest Stories . . .
Subscribe to the World's Most Powerful Newsletters
Subscribe to Our Rss Feeds & Get Your SYS-CON News Live!
|
SYS-CON Featured Whitepapers
Most Read This Week |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||