javaeclipsercpe4swt-awt

Refreshing Eclipse RCP part containing an AWT-SWT bridge when contents of the bridge change


I'm working on an RCP application that's in a transition from a Swing version. So we have a lot of UI components that still need to live in the Swing world during this transition. I'm able to properly place the existing Swing components in AWT-SWT bridge frames.

I've wrapped these Swing components in a JScrollable pane before adding them to the bridge so that I don't have to resize the containing-part when the size of the Swing UI elements change. The code where I place an old Swing component in a part looks like this:

@PostConstruct
public void postConstruct(final Composite parent) {

    /* Create embedding composite */
    final Frame bridgeFrame;
    final Composite embed;
    embed = new Composite(parent, SWT.EMBEDDED);
    embed.setLayout(new FillLayout());
    bridgeFrame = SWT_AWT.new_Frame(embed);
    bridgeFrame.setLayout(new BorderLayout());
    bridgeFrame.add(
            new JScrollPane(getTestPanel()),
            BorderLayout.CENTER);
}

My Swing component has a behavior where when the user clicks a button, things that were hidden in the component are made visible, or new UI elements are added to the Swing component. For example:

private JPanel getTestPanel() {
    final JPanel output;
    final JButton eastBttn, westBttn;
    output = new JPanel();
    eastBttn = new JButton("East Button");
    westBttn = new JButton("West Button");
    output.setLayout(new BorderLayout());
    output.add(eastBttn, BorderLayout.EAST);
    output.add(westBttn, BorderLayout.WEST);

    eastBttn.addActionListener(evt -> {
        System.out.println("East Button Clicked");
        output.add(new JLabel("East Button Clicked"), BorderLayout.CENTER);
    });

    return output;
}

My problem is, when the elements in the Swing-component change, the parent bridge-frame doesn't properly get rendered.

When the parts are first created, my application looks like this: before

After I click on the EastButton it's supposed to add a text label in the center of that bridge frame. However, nothing changes in the application view.

But, when I even begin to resize the containing part-sash a little, the part containing the bridge-frame updates correctly:after resizing

What can I do to make the bridge-frame update containing part update automatically?

To test whether this was a repainting issue on the bridge-frame, I had a menu item which would trigger a repaint / revalidate / pack of the bridge-frame, but that didn't solve the issue. I suspect it has something to do with the renderer of the containing part, but have no idea how to go about addressing it.


Solution

  • I eventually got around the problem by attaching a custom ControlListener / ComponentListener to the part containing the bridge. If any changes within the workings of the bridge-frame caused it to resize to beyond the parent, I'd have the listener resize it to fit within the parent thus forcing the scroll-pane to take over.

    Here's my listener:

    public class BridgeComponetAdapter
            extends ComponentAdapter
            implements ControlListener {
    
        private final Composite parent;
        private final Frame bridgeFrame;
        private Point parentSize;
    
        public BridgeComponetAdapter(
                final Composite parent,
                final Frame bridgeFrame) {
            this.parent = parent;
            this.bridgeFrame = bridgeFrame;
            bridgeFrame.addComponentListener(this);
            parent.addControlListener(this);
        }
    
        @Override
        public void componentResized(final ComponentEvent e) {
            System.out.println(e);
            if (e.getSource() != bridgeFrame)
                return;
    
            final Dimension currentBridgeSize;
            currentBridgeSize = bridgeFrame.getSize();
            if (currentBridgeSize.getWidth() > parentSize.x
                    || currentBridgeSize.getHeight() > parentSize.y) {
                bridgeFrame.setSize(parentSize.x, parentSize.y);
            }
        }
    
        @Override
        public void controlMoved(final ControlEvent e) {}
    
        @Override
        public void controlResized(final ControlEvent e) {
            System.out.println(e);
            if (e.getSource() == parent)
                parentSize = parent.getSize();
        }
    }
    

    It's not an elegant solution; I'm still open to other ideas to solve the problem.