SWT ScrolledComposite Explained

Home  >>  Eclipse  >>  SWT ScrolledComposite Explained

SWT ScrolledComposite Explained

On March 1, 2016, Posted by , In Eclipse, By ,,,, , With 15 Comments

Like a friend of mine once put it, SWTs ScrolledComposite is a nasty beast. And to some point, I do agree. This is likely the reason why there are so many questions asked about how to use this widget.

But it’s not only the authors of the ScrolledComposite to blame. When a piece of software doesn’t work the way you expect it to work, then … you curse … and try a little different … and curse differently … and ask Google for help … and (hopefully) find this lovely little post. Which tells you: RTFM! – ups, ehm, I mean of course take a look at the documentation.

And if you read the JavaDoc you will learn that there are two ways to use the ScrolledComposite and see corresponding sample code.

Two in One

The first way is suitable for fixed size content and will show scrollbars if the contained control cannot be fully seen and hide them otherwise.

Alternatively, if set up accordingly, the ScrolledComposite imitates the way a browser would work. The content will grow and shrink with the size of the ScrolledComposite – until the visible area is less than the specified minimum size. In the latter case, scrollbars will be shown, and the content never decreases below the minimum size.

The video shows the different operation modes side by side:

On the left side, the size of content remains the same while the window is enlarged and scrollbars vanish when no longer needed. On the right side, however, the initial windows size is smaller than the specified minimum size and therefore scrollbars appear. As the window grows larger, the content grows with the window.

Scrolling Fixed Content

To Set up the ScrolledComposite to show fixed content is really simple:

 
scrolledComposite = new ScrolledComposite( parent, SWT.H_SCROLL | SWT.V_SCROLL );
Label label = new Label( scrolledComposite, SWT.NONE );
label.setBackground( display.getSystemColor( SWT.COLOR_DARK_GREEN ) );
label.setSize( 400, 400 );
scrolledComposite.setContent( label );

Note that you need to explicitly specify the H_SCROLL and V_SCROLL style flags. Otherwise no scrollbars are created and the ScrolledComposite is of little use (more on scrollbars later).

The other noteworthy part of the snippet is where the content – a 400 x 400 px green label here – is created. The parent of the content must be the ScrolledComposite itself. If not you will see funny results. And finally, the ScrolledComposite must be told the content that it should manage with setContent().

The example demonstrates the simplest case in which the content is a single control. If however the content to scroll consists of multiple controls, they need to be wrapped into a Composite with a suitable layout like shown in contact details example.

The content control is assumed to be a direct child control of the ScrolledComposite. Though the ScrolledComposite does not prevent setting other controls, this is usually not what you want.

Fixed size content in this context does not mean that the content must or can not change its size. If that happens, the ScrolledComposite will adapt to the new size and show or hide scrollbars accordingly. The content, however, does not change its size if the ScrolledComposite it resized.

Scrolling Expanding Content

With a few additional lines, the above snippet can be extended to expand its content:

 
scrolledComposite.setExpandHorizontal( true );
scrolledComposite.setExpandVertical( true );
scrolledComposite.setMinSize( 250, 250 );

Afterwards, the ScrolledComposite will expand the content when resized and only show scrollbars if its size is reduced so that it is smaller than the minimum size. The SWT API offers further ways to set the minimum size. Either for the minimum width and height independently with setMinWidth() and setMinHeight(), or with setMinSize(Point).

See also  Service Update for the Clean Sheet Eclipse IDE Look and Feel

It is legal to set the minimum size to zero (the default value) when expanding content. As a result, the content will shrink with no lower limit and no scrollbars will ever be shown.

These are the two primary modes of the ScrolledComposite: managing content of fixed size or expanding and reducing content.

Scrolling Vertically Only

Another use case that I have come across is to have a list of yet unknown length of items. Think of a lengthy contact details form with many lines each consisting of a label and an input field. The width of each line occupies as much space as there is but if the number of lines exceed the available height, it should be scrollable.

This video demonstrates a contact details form that scrolls vertically:

With a little tweak, the above shown expanding ScrolledComposite can be extended to scroll only vertically. The key change is to set the minimum size dynamically. Whenever the ScrolledComposite changes its size, the minimum width of the content is set to the available width of the ScrolledComposite.

The code to accomplish the described behavior is rather simple:

scrolledComposite.addListener( SWT.Resize, event -> {
  int width = scrolledComposite.getClientArea().width;
  scrolledComposite.setMinSize( parent.computeSize( width, SWT.DEFAULT ) );
} );

The resize listener queries the available space of the ScrolledComposite with getClientArea()
and computes the necessary size for the content given the client areas width. Finally the resulting size is set as the minimum size of the ScrolledComposite.

In this example, the appearance of the content is governed by a two-column layout. The label column is as wide as necessary to show the longest label, and the input field column uses the remaining width. Each row, in turn, uses the optimal height (i.e. is as high as necessary to show a single-lined input field).

Even though I haven’t seen a real-world use case, this very approach would also work when expanding horizontally.


Computing the Preferred Size for a ScrolledComposite
There is nothing more saddening than to see an application that is designed with a single screen resolution or font size or color scheme (or all of them) in mind. To avoid this, the contact details form uses an adaptive strategy to compute its initial size.

As the content consists of repeating items (labeled input fields here), the size of a single item is taken as measuring unit. The number of items here is a figure gained from evidence but could be the (limited) number of rows obtained from a data source in other cases.

The formula itself is simple:

numberOfItems = 10
initialHeight = numberOfItems * ( itemHeight + spacing )

A further refinement would be to check the resulting height against the screen size and reduce it if necessary.

The resulting layout scales well across different platforms, screen resolutions, and font settings. If this caught your interest, you may also want to read Responsive UIs with Eclipse and SWT.

Vertical and Horizontal ScrollBar

To get hold of the scrollbars use getVerticalBar() and getHorizontalBar() respectively. As seen in the snippets, the V_SCROLL and H_SCROLL style flags need to be specified in order to have vertical and/or horizontal scrollbars created. If the respective style flag is omitted, no scrollbar is created and getVerticalBar() or getHorizontalBar() will return null.

See also  Announcing Extras for Eclipse

Existing scrollbars, however, can be shown or hidden, enabled or disabled anytime. By default, the ScrolledComposite shows scrollbars only when necessary but with setAlwaysShowScrollBars() this behavior can be changed to always show scrollbars. Therefore, you would usually want to have both scrollbars created and let the ScrolledComposite decide when to show or hide individual bars.

In general, be aware that scrollbars are managed by the ScrolledComposite. Hence, it is safe to query their state, but manipulating the properties will most likely interfere with the ScrolledComposite’s view of things.

To change the position of scrollbars to bring certain parts of the content into view, use the designated methods discussed below.

The only exception of the above said may be the enabled state of scrollbars. If this is truly desired, it should be safe to call setEnabled().

Scrolling into View

The ScrolledComposite has several methods to change the scrollbar position. The most basic is setOrigin(). It scrolls the content control so that the specified point in the content is in the top left corner. The desired position can be given as separate x and y coordinates or as a Point. And consequently there is a getOrigin() method that returns the point that currently appears in the top left corner.

To spare clients some coordinate mapping, there is showControl() which builds on setOrigin() and scrolls the content so that the given control is visible.

And if the focused control should always be visible, the ScrolledComposite can be advised with setShowFocusedControl() to scroll automatically the focused control into view.

Concluding SWT ScrolledComposite

Indeed, the ScrolledComposite isn’t the most intuitive widget in the SWT collection, and I hope that the recipes presented here will help to get along with it better. The use cases discussed here are those that I came across mostly so far. However, I would be curious to learn further uses, and if you would like to share yours, please leave a comment.

The shown snippets are extracts of small, ready to run example programs, that can be found here:
https://gist.github.com/rherrmann/b1a2a633cd4c9b607fe7

And last but not least I would like to point out that all concepts and code shown here not only apply to SWT but equally well run in a browser with Eclipse RAP. You can even use this RAP online demo to change various flags and properties and see their effect immediately.

Rüdiger Herrmann
Follow me
Latest posts by Rüdiger Herrmann (see all)

15 Comments so far:

  1. Suhel Khan says:

    Awesome explanation. Thanks a lot.

  2. Every time I used ScrolledComposite in the past I have spent half a day getting it to work.
    This time, after reading this post, it took me 10 minutes. Very well explained. This should be included in the official SWT documentation!

  3. Stefan Haustein says:

    In a vertical scrolling case where the minimum width mainly depends on external constraints (e.g. the window size), it might be simpler and cleaner to setExpandHorizonal(true) and setExpandVertical(false), avoiding the resize listener. This is probably also closer to what people would expect for a “browser-like” behavior (compared to the right panel in the first example).

    In general, the main problem with setting expand to true seems to be that in this case the ScrolledComposite builtin layout manager only considers the minimum size explicitly set on ScrollledComposite and the current size of the ScrolledComposite, disregarding the “actual” size of the contents. I assume the intention behind this (not very intuitive) behavior is to avoid a circular dependency in the case where the content doesn’t have a layout manager.

    While this important detail is actually documented in the Javadoc, it probably should have a stronger warning that setExpandXxx changes the base for content size calculations from the actual content size to the values explicitly set via setMinWidth / setMinHeight. It doesn’t really help that the names of these methods sound like they would apply to the scroll composite itself — opposed to its content.

    • Rüdiger Herrmann says:

      Thank you very much for sharing your insights.

      After applying your suggestions to the VerticallyScrolledForm I find it worth mentioning that, of course, the height of the content control need to be set in this case.

      And the resize listener would still be necessary to update the minWidth after said external constraints (e.g. the window size) have changed.

      • Stefan Haustein says:

        Ooops, sorry for the incorrect comment — I hadn’t realized that I was using a hacked version that implicitly computes the content size if expanding is not set.
        It looks like in this specific example, the listener would not be needed, but that’s just because the content height does not depend on the available width, which is not necessarily the case in general (e.g. if there is a wrapping text block that needs to be adjusted to the width).

        • Rüdiger Herrmann says:

          Thank you again for your comment. To summarize, in the “browser-like” use case, the resize listener and scrolledComposite.setExpandVertical( true ) is not necessary if the height of the content control is known in advance and does not depend on the available width

          I hope to find some time soon, to update the article accordingly.

          • Frustrated SWT user says:

            Good explanation. Thanks! Now I understand it a bit better. But I still think ScrolledComposite is really bit of ‘crap’ component. I mean… it supports two different use-cases, neither one of which seems particularly useful for when I’ve actually wanted to wrap some scrollbars around an existing piece of UI.

            Case 1: you know the size of the content ahead of time. Okay… I don’t. And I don’t just want to just put a number on it and fix its size. So moving on to case 2…

            Case 2: you want the content to shrink / grow based on available space… ah that’s more like it. But WTF, you have to set a minimum size? The scrollbars are totally disconnected from the space the actual content takes (so basically when my content can’t shrink any further, the scrollbars don’t appear until I hit that minimum size I have to somehow guess or compute??)

            What I really want is this

            Case 3: content shrinks and grows to accommodate the available space… Content shrinks **until** the content can’t shrink any further (because typically the various widgets inside will probably require some mimimum amount of space). When the content doesn’t shrink any further **then** the scrollbars should appear.

            So how do we make use of a ScrolledComposite to make its do this?

            Note: In general, we can’t statically compute the minimum size, because that size could itself be dynamic. E.g. there could be widgets in there that need various amount of space, depending on their current state. This state may change by user actions (and then the scrollbars should also update). A typical example might be a collapsible element that can show/hide some detailed content when the use clicks on it.

  4. Rüdiger Herrmann says:

    @Frustrated SWT user: Admittedly, the two-fold nature of the Scolledcomposite can cause headaches.

    In SWT, all operations on widgets that (possibly) cause layout changes need to explicitly
    trigger a re-layout – which I find a reasonable choice given the design goals.
    With that in mind, expanding or collapsing sections in a ScrolledComposite requires the
    one who expands or collapses a control to change the minimum size, which in turn re-layouts
    the ScolledComposite and adjusts its scollbars. How else should be ScrolledComposite ‘know’
    it needs to adjust. That’s what setMinSize() is used for.

    I have assembled an example here:
    https://gist.github.com/rherrmann/7d8e96b012875466a9f2c144d4569015

    It follows the browser-like behavior and its content are expansible components like you mentioned.
    The expandable components are simulated by buttons that, upon selection, change their height. For the sake of
    brevity, the heightHint on the respective grid layout data is manipulated.

    The unpleasant part remains that thereafter, the min size of the ScrolledComposite need be adjusted. However, that part could certainly be extracted to a utility class that observes size changes of all children of the content composite and then re-computes the min size accordingly.

    If you have a more intuitive API in mind, you may want to open an enhancement request.

    • Frustrated SWT user says:

      Thanks for explaining and the example. I will definitely take a look (I’m sure I’ll need to try and ‘tame’ the scrolled composite again in the future :-)

      > How else should be ScrolledComposite ‘know’ it needs to adjust.

      Frankly, I don’t care, and I shouldn’t have to. When I use the equivalent of a ‘scrolled composite’ in Android’s widget toolkit and wrap it around some existing ui, it basically just works the way I’d expect it to. I don’t have to figure out how to attach listeners to every sub-widget that might change size and create utility classes just to make the widgets behave the way a sane user probably wants them to behave. When something changes size, everything adjusts around it, automatically, and the scrollbars are no exception.

      The whole of SWT kind of feels too low-level and you spend hours upon hours mucking with it only so that in the end it works ‘almost correctly’.

      Maybe that’s because SWT, when it was built was more concerned with performance than usability at the time, but those ‘design goals’ are a bit anachronistic now that our CPUs are 100s of times faster than 15 years ago.

      Or maybe its because a lot of these widgets where only implemented ‘just enough’ so whoever built them could get their job done, without necessarily putting much time into really ‘designing’ it at all for future user’s convenience. I certainly can symphatize with that kind of thinking, I do it too. But my code typically doesn’t end up in a library that then gets used for the next 15 years by 1000s of people.

      Whatever the case may be, I find finicking with SWT widgets a huge timesink. ScrolledComposite being one of the most prominent examples of ‘one of those widgets that is incredibly hard to beat into submission’.

      • Rüdiger Herrmann says:

        IIRC, SWT was built initially to replace another UI toolkit that turned out to be too slow for the requirements of the Eclipse IDE. So, performance was certainly a design goal.

        I think JFace is meant to provide a higher level API, but like many other parts in Eclipse didn’t get the resources to become a first-class UI abstraction layer.

    • Stefan Haustein says:

      BTW: ScrolledComposite is not a native component , so experimenting with improvements leading to a more intuitive API should be relatively straightforward, using the original java source as a starting point.

  5. Frustrated SWT user says:

    BTW, just to be clear. I’m not blaming you for the difficulties of getting SWT widgets to behave. I thank you for trying to explain, and the explanation is in fact helpful. I just really needed a place to vent my frustration, it obviously isn’t directed at you personally.

  6. Ioana Negru says:

    Hi, on the Rap online demo page you added at the end of your page, the ScrollBars are only visible at hover. How is this implemented? Is there a setting or something from CSS? Thanks!