Window flicker when displaying flap

I’ve ran into a bug when a cdockable containing a FlapDockStation displays a flap and is maximized. The flap looks like it is trying to be painted in 2 locations at once. It’s alot of flickering, don’t run the example if you have epilepsy!

Attached is some sample code. If you run the example, open the flap in ‘dockable 3’ and then maximize ‘dockable 3’ while the flap is open, you should see the issue.

import bibliothek.extension.gui.dock.theme.FlatTheme;
import bibliothek.gui.DockController;
import bibliothek.gui.DockStation;
import bibliothek.gui.Dockable;
import bibliothek.gui.dock.DefaultDockable;
import bibliothek.gui.dock.FlapDockStation;
import bibliothek.gui.dock.SplitDockStation;
import bibliothek.gui.dock.action.DockActionSource;
import bibliothek.gui.dock.common.CContentArea;
import bibliothek.gui.dock.common.CControl;
import bibliothek.gui.dock.common.CLocation;
import bibliothek.gui.dock.common.DefaultSingleCDockable;
import bibliothek.gui.dock.layout.DockableProperty;
import bibliothek.gui.dock.station.AbstractDockStation;
import bibliothek.gui.dock.station.support.PlaceholderMap;
import bibliothek.gui.dock.title.NullTitleFactory;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Rectangle;
import java.io.IOException;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public class Example {

    public static void main(String[] args) {

        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                createWindow();
            }
        });
    }

    private static void createWindow() {
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLayout(new BorderLayout());

        CControl control = new CControl(frame);

        CContentArea contentArea = control.getContentArea();
        frame.add(contentArea, BorderLayout.CENTER);

        create(control, "dockable one", CLocation.base().normalNorth(.5));
        create(control, "dockable two", CLocation.base().normalSouth(.5));
        create(control, "dockable three", CLocation.base().normalSouth(.5).east(.5));

        frame.setMinimumSize(new Dimension(800, 600));
        frame.setLocationRelativeTo(null);
        frame.pack();
        frame.setVisible(true);
    }

    private static DefaultSingleCDockable create(CControl control, String title, CLocation location) {
        DefaultSingleCDockable dockable = new ExampleCDockable(title);
        dockable.setLocation(location);

        control.add(dockable);
        dockable.setVisible(true);
        
        return dockable;

    }

    private static class ExampleCDockable extends DefaultSingleCDockable {
        private ExampleCDockable(String id) {
            super(id);

            DockController dockController = new DockController();
            FlatTheme theme = new FlatTheme();
            theme.setTitleFactory(new NullTitleFactory());
            dockController.setTheme(theme);
            FlapDockStation dockStation = new FlapDockStation();
            dockController.add(dockStation);

            JPanel flapPanel = new JPanel();
            flapPanel.setPreferredSize(new Dimension(100, 100));
            flapPanel.setOpaque(true);
            flapPanel.setBackground(Color.blue);

            DefaultDockable flapDockable = new DefaultDockable(flapPanel);
            flapDockable.setTitleText("Flap");
            dockStation.add(flapDockable);
            dockStation.setWindowSize(flapDockable, 230);

            setTitleText(id);
            setCloseable(true);

            JPanel simplePanel = new JPanel();
            simplePanel.setPreferredSize(new Dimension(300, 300));
            simplePanel.setOpaque(true);
            simplePanel.setBackground(Color.black);

            BasicDockStation contentStation = new BasicDockStation(simplePanel);
            dockController.add(contentStation);

            getContentPane().setLayout(new BorderLayout());
            getContentPane().add(dockStation.getComponent(), BorderLayout.WEST);
            getContentPane().add(contentStation.getComponent(), BorderLayout.CENTER);
        }
    }

    /**
     * A very basic DockStation that wraps a Component but plays nice with FlapDockStation
     */
    public static class BasicDockStation extends AbstractDockStation {

        private DefaultDockable dockable;

        public BasicDockStation(Component component) {
            this.dockable = new DefaultDockable(component);
        }

        public Component getComponent(){
            return dockable.getComponent();
        }

        @Override
        protected void callDockUiUpdateTheme() throws IOException {
        }

        @Override
        public DockActionSource getDirectActionOffers(Dockable dockable) {
            return null;
        }

        @Override
        public DockActionSource getIndirectActionOffers(Dockable dockable) {
            return null;
        }

        @Override
        public int getDockableCount() {
            return 1;
        }

        @Override
        public Dockable getDockable(int index) {
            return dockable;
        }

        @Override
        public Dockable getFrontDockable() {
            return null;
        }

        @Override
        public void setFrontDockable(Dockable dockable) {
        }

        @Override
        public PlaceholderMap getPlaceholders() {
            return null;
        }

        @Override
        public void setPlaceholders(PlaceholderMap placeholders) {
        }

        @Override
        public DockableProperty getDockableProperty(Dockable child, Dockable target) {
            return null;
        }

        @Override
        public boolean prepareDrop(int mouseX, int mouseY, int titleX, int titleY, boolean checkOverrideZone, Dockable dockable) {
            return false;
        }

        @Override
        public void drop() {
        }

        @Override
        public void drop(Dockable dockable) {
        }

        @Override
        public boolean drop(Dockable dockable, DockableProperty property) {
            return false;  //To change body of implemented methods use File | Settings | File Templates.
        }

        @Override
        public boolean prepareMove(int mouseX, int mouseY, int titleX, int titleY, boolean checkOverrideZone, Dockable dockable) {
            return false;  //To change body of implemented methods use File | Settings | File Templates.
        }

        @Override
        public void move() {
            //To change body of implemented methods use File | Settings | File Templates.
        }

        @Override
        public void move(Dockable dockable, DockableProperty property) {
            //To change body of implemented methods use File | Settings | File Templates.
        }

        @Override
        public void draw() {
            //To change body of implemented methods use File | Settings | File Templates.
        }

        @Override
        public void forget() {
            //To change body of implemented methods use File | Settings | File Templates.
        }

        @Override
        public <D extends Dockable & DockStation> boolean isInOverrideZone(int x, int y, D invoker, Dockable drop) {
            return false;  //To change body of implemented methods use File | Settings | File Templates.
        }

        @Override
        public boolean canDrag(Dockable dockable) {
            return false;  //To change body of implemented methods use File | Settings | File Templates.
        }

        @Override
        public void drag(Dockable dockable) {
            //To change body of implemented methods use File | Settings | File Templates.
        }

        @Override
        public boolean canReplace(Dockable old, Dockable next) {
            return false;  //To change body of implemented methods use File | Settings | File Templates.
        }

        @Override
        public void replace(Dockable old, Dockable next) {
            //To change body of implemented methods use File | Settings | File Templates.
        }

        @Override
        public void replace(DockStation old, Dockable next) {
            //To change body of implemented methods use File | Settings | File Templates.
        }

        @Override
        public Rectangle getStationBounds() {
            return null;  //To change body of implemented methods use File | Settings | File Templates.
        }

        @Override
        public Dockable asDockable() {
            return null;  //To change body of implemented methods use File | Settings | File Templates.
        }

        @Override
        public DockStation asDockStation() {
            return null;  //To change body of implemented methods use File | Settings | File Templates.
        }

        @Override
        public String getFactoryID() {
            return null;  //To change body of implemented methods use File | Settings | File Templates.
        }
    }

}```

Thanks, will be fixed in the next release. The bug is in the SplitDockStation which does resize its maximized child two times. This triggers the FlapDockStation to resize its window, which in return triggers the SplitDockStation to update the boundaries of its children…

Until I make the release (a few days), you can use this piece of code to stop the bug:

dockStation.setDirection( Direction.EAST );
dockStation.setAutoDirection( false );```

That code fix does fix the constant flicker, but if you mouse over the buttons on the CDockable or the flap pushpin the flicker still occurs.

Hmyes, thats because there are still revalidate-events flying around. This will be gone as soon the real fix, not the emergency workaround, is applied and the SplitDockStation stops with wrong and unnecessary updates of boundaries.

Outstanding! Just wanted to make sure you were aware of all the repaint issues.

Hi - Any progress on this bug? It looks like it is still an issue in the latest 1.1.0 snapshot build from maven.

Really? Because on my machine your example works without flickering.

Ok, what buttons in which order do I have to press to get the flickering again? Perhaps I’m missing some case (altough there are not that many buttons…).

[Edit: and I did check in the fix.]

[Edit2: … but not in the maven repository. Actually the newest version in maven seems to be more than a month old :confused: . Perhaps Andrei knows more]

Ah, that is what the issue is. I downloaded the latest from javaforge (df_1.1.0p2.zip) and the bug is fixed.

I did not reach Andrei yet, so you better keep the non-maven version for now.

Andrei repaired the maven repository, it contains the newest version again.