Hide/Show Titlebar

Hi,

I have a SplitDockStation which has several dockables in it. These dockables are hidden and redisplayed when the user does certain actions.

I would like to change the titlebar of the dockables, such that if there is only one displayed dockable then it should not have a titlebar (and the space that was earlier given to the titlebar should now be made available to the docked component).

The following code in my Dockable achieves this functionality.

void setTitlebarVisibility(boolean visibility)
{
    DockTitle dockTitles[] = listBoundTitles();
    for(DockTitle dockTitle : dockTitles)
    {
        ((AbstractDockTitle)dockTitle).setVisible(visibility);
    }
}

where visibility is ‘false’ when I want to hide the titlebar and true when I want to show it. The titlebar does get hidden properly, however the area that was earlier available to the titlebar is left blank and the component does not actually fill the area.

Is there a way to allow the component which is docked to take the space earlier dedicated for the titlebar when the titlebar is made invisible?


Regards
Parag

Thats going to need a workaround… I see two useful solutions.

  1. Solution
    New DockTitles are created every time when the DockController of a DockStation changes. DockTitles are created through the method “Dockable.getDockTitle”. So we rewrite “getDockTitle” in order to respect whether we want to see a title or not, and we “shake” the DockStation by quickly removing and adding the DockController.
  • general solution, works with every station
  • every “shake” triggers a lot of events, and if things grow more complex, then failures could emerge.
  • chances of unexpected side effects are high
    public static void main( String[] args ){
        DockController controller = new DockController();
        
        SplitDockStation station = new SplitDockStation();
        JFrame frame = new JFrame( "Demo" );
        frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
        frame.setBounds( 20, 20, 400, 400 );
        frame.add( station.getComponent() );
        
        controller.add( station );

        SplitDockGrid grid = new SplitDockGrid();
        grid.addDockable( 0, 0, 1, 1, createDockable( "A" ) );
        grid.addDockable( 0, 1, 1, 1, createDockable( "B" ) );
        station.dropTree( grid.toTree() );
        
        frame.setVisible( true );
    }

    public static Dockable createDockable( String title ){
        final JToggleButton change = new JToggleButton( "Show title" );
        change.setSelected( true );
        final DefaultDockable dockable = new DefaultDockable( change, title ){
            @Override
            public DockTitle getDockTitle( DockTitleVersion version ) {
                if( change.isSelected() )
                    return super.getDockTitle( version );
                else
                    return null;
            }
        };
        change.addActionListener( new ActionListener(){
            public void actionPerformed( ActionEvent e ) {
                shake( dockable );
            }
        });
        return dockable;
    }
    
    private static void shake( Dockable dockable ){
        DockStation parent = dockable.getDockParent();
        DockController controller = parent.getController();
        parent.setController( null );
        parent.setController( controller );
    }
}```

2. Solution
Every pair Dockable and DockTitle is put onto a DockableDisplayer. We can write our own displayer and give it the necessary behavior to hide invisible titles.

+ perfectly save method, chances of unexpected side effects are very low
- Needs to be done for each DockTheme

```public class Dock3 {
    public static void main( String[] args ){
        DockController controller = new DockController();
        
        SplitDockStation station = new SplitDockStation();
        
        station.getDisplayerFactory().setDelegate( new BasicDisplayerFactory(){
            @Override
            protected BasicDockableDisplayer create( Dockable dockable, DockTitle title, Location location ) {
                return new BetterDisplayer( dockable, title, location );
            }
        });
        
        JFrame frame = new JFrame( "Demo" );
        frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
        frame.setBounds( 20, 20, 400, 400 );
        frame.add( station.getComponent() );
        
        controller.add( station );

        SplitDockGrid grid = new SplitDockGrid();
        grid.addDockable( 0, 0, 1, 1, createDockable( "A" ) );
        grid.addDockable( 0, 1, 1, 1, createDockable( "B" ) );
        station.dropTree( grid.toTree() );
        
        frame.setVisible( true );
    }

    public static Dockable createDockable( String title ){
        final JToggleButton change = new JToggleButton( "Show title" );
        change.setSelected( true );
        final DefaultDockable dockable = new DefaultDockable( change, title );
        
        change.addActionListener( new ActionListener(){
            public void actionPerformed( ActionEvent e ) {
                boolean show = change.isSelected();
                for( DockTitle title : dockable.listBoundTitles() )
                    title.getComponent().setVisible( show );
            }
        });
        return dockable;
    }
    
    private static class BetterDisplayer extends BasicDockableDisplayer{
        public BetterDisplayer( Dockable dockable, DockTitle title, Location location ){
            super( dockable, title, location );
        }
        
        @Override
        public void doLayout() {
            if( getTitle() == null || getTitle().getComponent().isVisible() ){
                super.doLayout();
            }
            else{
                Dockable dockable = getDockable();
                if( dockable != null ){
                    Insets insets = getInsets();
                    if( insets == null )
                        insets = new Insets(0,0,0,0);

                    int x = insets.left;
                    int y = insets.top;
                    int width = getWidth() - insets.left - insets.right;
                    int height = getHeight() - insets.top - insets.bottom;

                    width = Math.max( 0, width );
                    height = Math.max( 0, height );
                    getComponent( dockable ).setBounds( x, y, width, height );
                }
            }
        }
    }
}```

3. Solution
It might be possible to write a DockTitle that wraps around another title, and sets its preferred size to 0/0 when it gets invisible. While this solution might be the most general, it would also be the most difficult to implement.

4. Solution
Someone could add a new feature to the framework, unfortunately that someone would need the time to do that...

Hi,

Thanks a lot for giving such a detailed answer.

I will try the second option to start with. I think it should work well, because I do not intend to switch themes. Will be using only the FlatTheme with NoStackTheme.


Regads
Parag

[QUOTE=Beni]Thats going to need a workaround… I see two useful solutions.

  1. Solution
    Every pair Dockable and DockTitle is put onto a DockableDisplayer. We can write our own displayer and give it the necessary behavior to hide invisible titles.
  • perfectly save method, chances of unexpected side effects are very low
  • Needs to be done for each DockTheme
    public static void main( String[] args ){
        DockController controller = new DockController();
        
        SplitDockStation station = new SplitDockStation();
        
        station.getDisplayerFactory().setDelegate( new BasicDisplayerFactory(){
            @Override
            protected BasicDockableDisplayer create( Dockable dockable, DockTitle title, Location location ) {
                return new BetterDisplayer( dockable, title, location );
            }
        });
        
        JFrame frame = new JFrame( "Demo" );
        frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
        frame.setBounds( 20, 20, 400, 400 );
        frame.add( station.getComponent() );
        
        controller.add( station );

        SplitDockGrid grid = new SplitDockGrid();
        grid.addDockable( 0, 0, 1, 1, createDockable( "A" ) );
        grid.addDockable( 0, 1, 1, 1, createDockable( "B" ) );
        station.dropTree( grid.toTree() );
        
        frame.setVisible( true );
    }

    public static Dockable createDockable( String title ){
        final JToggleButton change = new JToggleButton( "Show title" );
        change.setSelected( true );
        final DefaultDockable dockable = new DefaultDockable( change, title );
        
        change.addActionListener( new ActionListener(){
            public void actionPerformed( ActionEvent e ) {
                boolean show = change.isSelected();
                for( DockTitle title : dockable.listBoundTitles() )
                    title.getComponent().setVisible( show );
            }
        });
        return dockable;
    }
    
    private static class BetterDisplayer extends BasicDockableDisplayer{
        public BetterDisplayer( Dockable dockable, DockTitle title, Location location ){
            super( dockable, title, location );
        }
        
        @Override
        public void doLayout() {
            if( getTitle() == null || getTitle().getComponent().isVisible() ){
                super.doLayout();
            }
            else{
                Dockable dockable = getDockable();
                if( dockable != null ){
                    Insets insets = getInsets();
                    if( insets == null )
                        insets = new Insets(0,0,0,0);

                    int x = insets.left;
                    int y = insets.top;
                    int width = getWidth() - insets.left - insets.right;
                    int height = getHeight() - insets.top - insets.bottom;

                    width = Math.max( 0, width );
                    height = Math.max( 0, height );
                    getComponent( dockable ).setBounds( x, y, width, height );
                }
            }
        }
    }
}```

[/QUOTE]

Hi,

I have been using this solution to remove the DockTitle (when there is only one dockable) very successfully. However, after upgrading to DockingFrames 1.0.8 p2, it started failing.

Scenario:
There are 2 dockables which are docked. I close one of them, so that there is only one dockable remaining. This dockable does not have a title but the area occupied by the title is not taken up by the dockable and appears as a blank area. Also the area on the opposite side of the previous dockable (equal in size to the previous dockable) remains blank. 

I modified the code so that super.doLayout(...) is always called.

private static class BetterDisplayer extends BasicDockableDisplayer{
public BetterDisplayer( Dockable dockable, DockTitle title, Location location ){
super( dockable, title, location );
}

    @Override
    public void doLayout() {
        //changed this line so that super.doLayout() is always called
        super.doLayout();
        if( getTitle() == null || getTitle().getComponent().isVisible() ){
            ...
        }
        else{
            Dockable dockable = getDockable();
            if( dockable != null ){
                Insets insets = getInsets();
                if( insets == null )
                    insets = new Insets(0,0,0,0);

                int x = insets.left;
                int y = insets.top;
                int width = getWidth() - insets.left - insets.right;
                int height = getHeight() - insets.top - insets.bottom;

                width = Math.max( 0, width );
                height = Math.max( 0, height );
                getComponent( dockable ).setBounds( x, y, width, height );
            }
        }
    }
}

}```

This change elliminated the error of blank area being shown to the side of the dockable. However, the problem with the titlebar still remains. So now the titlebar is not shown when there is one dockable, but the area appears as blank area.

I debugged the code to check the bounds which I am giving to the Dockable. I see that when the title is being displayed the height of the component is 476 and the titlebar (I think) occupied 24 pixels in height. When only one dockable is displayed I can see that the bounds are being set properly (height is being set to 500) but somehow the component does not seem to be accepting the height of 500 and still renders at a height of 476.

Has anything changed in the new release that might be causing this problem?

Thanks a lot in advance for your time and effort.

Just visiting the forum the first time this week and already new problems to solve…

Yes, there is a new layer between Dockable and BasicDockableDisplayer (the new layers name is BasicDockableDisplayerDecorator). In the settings you are using, there is exactly one additional component between displayer and dockable. Instead of the “doLayout” method of the displayer you now need to override the components “doLayout” method. Since the new component delegates all work to the displayer, the fix is simply to change the signature of the “doLayout” method:

change every “doLayout()” to “doLayout( JPanel content )”.

Thanks, your solution worked.


Regards
Parag