All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.alee.extended.dock.data.DockableListContainer Maven / Gradle / Ivy

There is a newer version: 1.2.14
Show newest version
/*
 * This file is part of WebLookAndFeel library.
 *
 * WebLookAndFeel library is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * WebLookAndFeel library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with WebLookAndFeel library.  If not, see .
 */

package com.alee.extended.dock.data;

import com.alee.api.annotations.NotNull;
import com.alee.api.annotations.Nullable;
import com.alee.api.data.Orientation;
import com.alee.api.jdk.Objects;
import com.alee.extended.dock.WebDockablePane;
import com.alee.utils.TextUtils;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import com.thoughtworks.xstream.annotations.XStreamAsAttribute;
import com.thoughtworks.xstream.annotations.XStreamImplicit;

import java.awt.*;
import java.util.ArrayList;
import java.util.List;

/**
 * {@link com.alee.extended.dock.data.DockableContainer} representing either horizontal or vertical list of elements.
 *
 * @author Mikle Garin
 * @see How to use WebDockablePane
 * @see com.alee.extended.dock.WebDockablePane
 */
@XStreamAlias ( "DockableList" )
public class DockableListContainer extends AbstractDockableElement implements DockableContainer
{
    /**
     * List orientation.
     */
    @NotNull
    @XStreamAsAttribute
    protected Orientation orientation;

    /**
     * List of structure elements.
     */
    @Nullable
    @XStreamImplicit
    protected List elements;

    /**
     * Constructs new elements list.
     *
     * @param orientation list orientation
     * @param elements    elements to add
     */
    public DockableListContainer ( @NotNull final Orientation orientation, @NotNull final DockableElement... elements )
    {
        this ( orientation, new Dimension ( 0, 0 ), elements );
    }

    /**
     * Constructs new elements list.
     *
     * @param orientation list orientation
     * @param size        container size
     * @param elements    elements to add
     */
    public DockableListContainer ( @NotNull final Orientation orientation, @NotNull final Dimension size,
                                   @NotNull final DockableElement... elements )
    {
        super ( TextUtils.generateId ( "EL" ), size );
        this.orientation = orientation;
        for ( final DockableElement element : elements )
        {
            add ( element );
        }
    }

    @Override
    public void added ( @Nullable final DockableContainer parent )
    {
        super.added ( parent );

        // Initializing children
        if ( elements != null )
        {
            for ( final DockableElement element : elements )
            {
                element.added ( this );
            }
        }
    }

    @Override
    public boolean isContent ()
    {
        boolean isContent = false;
        if ( elements != null )
        {
            for ( final DockableElement element : elements )
            {
                if ( element.isContent () )
                {
                    isContent = true;
                    break;
                }
            }
        }
        return isContent;
    }

    @NotNull
    @Override
    public Orientation getOrientation ()
    {
        return orientation;
    }

    @Override
    public void setOrientation ( @NotNull final Orientation orientation )
    {
        this.orientation = orientation;
    }

    @Override
    public int getElementCount ()
    {
        return elements != null ? elements.size () : 0;
    }

    @NotNull
    @Override
    public  E get ( @NotNull final String id )
    {
        final DockableElement element = find ( id );
        if ( element == null )
        {
            throw new RuntimeException ( "Unable to find element with identifier: " + id );
        }
        return ( E ) element;
    }

    @Nullable
    @Override
    public  E find ( @NotNull final String id )
    {
        DockableElement element = null;
        if ( elements != null )
        {
            for ( final DockableElement e : elements )
            {
                if ( Objects.equals ( id, e.getId () ) )
                {
                    element = e;
                    break;
                }
                if ( e instanceof DockableContainer )
                {
                    element = ( ( DockableContainer ) e ).find ( id );
                    if ( element != null )
                    {
                        break;
                    }
                }
            }
        }
        return ( E ) element;
    }

    @Override
    public boolean contains ( @NotNull final String id )
    {
        return find ( id ) != null;
    }

    @Override
    public int indexOf ( @NotNull final DockableElement element )
    {
        return elements != null ? elements.indexOf ( element ) : -1;
    }

    @NotNull
    @Override
    public DockableElement get ( final int index )
    {
        final DockableElement element = elements != null ? elements.get ( index ) : null;
        if ( element == null )
        {
            throw new RuntimeException ( "Unable to find element at index: " + index );
        }
        return element;
    }

    @Override
    public void add ( @NotNull final DockableElement element )
    {
        add ( getElementCount (), element );
    }

    @Override
    public void add ( final int index, @NotNull final DockableElement element )
    {
        // Ensure elements list is created
        if ( elements == null )
        {
            elements = new ArrayList ( 2 );
        }

        // Add element
        elements.add ( index, element );
        element.added ( this );
    }

    @Override
    public void remove ( final DockableElement element )
    {
        if ( elements != null )
        {
            elements.remove ( element );
            element.removed ( this );
        }
    }

    @Override
    public boolean isVisible ( @NotNull final WebDockablePane dockablePane )
    {
        boolean isVisible = false;
        if ( elements != null )
        {
            for ( final DockableElement element : elements )
            {
                if ( element.isVisible ( dockablePane ) )
                {
                    isVisible = true;
                    break;
                }
            }
        }
        return isVisible;
    }

    @Override
    public void layout ( @NotNull final WebDockablePane dockablePane, @NotNull final Rectangle bounds,
                         @NotNull final List resizeableAreas )
    {
        // Saving bounds
        setBounds ( bounds );

        // Placing elements
        if ( elements != null )
        {
            // Settings
            final boolean horizontal = orientation.isHorizontal ();

            // Calculating existing sizes
            int summ = 0;
            int minSumm = 0;
            int cindex = -1;
            final List visible = new ArrayList ( elements.size () );
            final List sizes = new ArrayList ( elements.size () );
            for ( final DockableElement element : elements )
            {
                if ( element.isVisible ( dockablePane ) )
                {
                    final int length;
                    if ( element.isContent () )
                    {
                        length = 0;
                        cindex = visible.size ();
                    }
                    else
                    {
                        final Dimension minimumSize = element.getMinimumSize ( dockablePane );
                        length = horizontal ?
                                Math.max ( element.getSize ().width, minimumSize.width ) :
                                Math.max ( element.getSize ().height, minimumSize.height );
                        final int minLength = horizontal ? minimumSize.width : minimumSize.height;
                        summ += Math.max ( length, minLength );
                        minSumm += minLength;
                    }
                    visible.add ( element );
                    sizes.add ( length );
                }
            }

            // Continue only if there are visible elements
            if ( visible.size () > 0 )
            {
                // Minimum content length
                final int minContentLength = cindex == -1 ? 0 : horizontal ?
                        visible.get ( cindex ).getMinimumSize ( dockablePane ).width :
                        visible.get ( cindex ).getMinimumSize ( dockablePane ).height;

                // Calculating required and minimum container length
                final int spacing = dockablePane.getContentSpacing ();
                final int spacings = spacing * ( visible.size () - 1 );
                final int requiredLength = summ + minContentLength + spacings;
                final int minimumLength = minSumm + minContentLength + spacings;

                // Calculating total available space
                // We do not want to scale below minimum required space, so we set it as minimum
                final int availableSpace = Math.max ( horizontal ? bounds.width : bounds.height, minimumLength );

                // Adjusting sizes if they do not fit into available area
                if ( requiredLength > availableSpace )
                {
                    // Available space minus spacings and minimum content size
                    final int available = availableSpace - minContentLength - spacings;

                    // Shrinking all elements according to their size
                    for ( int i = 0; i < visible.size (); i++ )
                    {
                        if ( i != cindex )
                        {
                            // Shrinked element size
                            // todo Spread possible remaining pixels
                            sizes.set ( i, ( int ) Math.floor ( ( float ) available * sizes.get ( i ) / summ ) );
                        }
                        else
                        {
                            // Minimum content size
                            sizes.set ( i, minContentLength );
                        }
                    }
                }
                else
                {
                    if ( cindex != -1 )
                    {
                        // Filling all available space with content
                        // We leave all the rest of the sizes intact as they fit into available space
                        sizes.set ( cindex, availableSpace - summ - spacings );
                    }
                    else
                    {
                        // Stretching elements across the area since there is no content in this container
                        final int available = availableSpace - spacings;
                        for ( int i = 0; i < visible.size (); i++ )
                        {
                            // todo Spread possible remaining pixels
                            sizes.set ( i, ( int ) Math.floor ( ( float ) available * sizes.get ( i ) / summ ) );
                        }
                    }
                }

                // Placing structure elements
                final boolean ltr = dockablePane.getComponentOrientation ().isLeftToRight ();
                int x = ltr ? bounds.x : bounds.x + bounds.width;
                int y = bounds.y;
                for ( int i = 0; i < visible.size (); i++ )
                {
                    final DockableElement element = visible.get ( i );

                    // Calculating resulting element sizes
                    final int width = horizontal ? sizes.get ( i ) : bounds.width;
                    final int height = horizontal ? bounds.height : sizes.get ( i );

                    // Updating sizes for non-content areas
                    if ( i != cindex )
                    {
                        // We only want to update width for horizontal orientation and height for vertical
                        // The opposite size is handled by the parent container, we don't want to record it's size changes
                        final Dimension oldSize = element.getSize ();
                        element.setSize ( new Dimension (
                                horizontal ? sizes.get ( i ) : oldSize.width,
                                horizontal ? oldSize.height : sizes.get ( i )
                        ) );
                    }

                    // Placing element
                    element.layout (
                            dockablePane,
                            new Rectangle (
                                    ltr ? x : x - width,
                                    y,
                                    width,
                                    height
                            ),
                            resizeableAreas
                    );

                    // Adding resize element bounds
                    if ( i < visible.size () - 1 )
                    {
                        // Resize gripper bounds
                        final int rg = dockablePane.getResizeGripperWidth ();
                        final int rgx = horizontal ? ltr ? x + width + spacing / 2 - rg / 2 : x - width - spacing / 2 - rg / 2 : x;
                        final int rgy = horizontal ? y : y + height + spacing / 2 - rg / 2;
                        final int rgw = horizontal ? rg : width;
                        final int rgh = horizontal ? height : rg;
                        final Rectangle rb = new Rectangle ( rgx, rgy, rgw, rgh );

                        // Resizeable elements
                        final String leftElementId = !horizontal || ltr ? element.getId () : visible.get ( i + 1 ).getId ();
                        final String rightElementId = !horizontal || ltr ? visible.get ( i + 1 ).getId () : element.getId ();

                        // ResizeData
                        resizeableAreas.add ( new ResizeData ( rb, orientation, leftElementId, rightElementId ) );
                    }

                    // Incrementing coordinate
                    if ( horizontal )
                    {
                        if ( ltr )
                        {
                            x += width + spacing;
                        }
                        else
                        {
                            x -= width - spacing;
                        }
                    }
                    else
                    {
                        y += height + spacing;
                    }
                }
            }
        }
    }

    @NotNull
    @Override
    public Dimension getMinimumSize ( @NotNull final WebDockablePane dockablePane )
    {
        // Base minimum size
        Dimension min = dockablePane.getMinimumElementSize ();

        // Children-dictated minimum size
        if ( dockablePane.isOccupyMinimumSizeForChildren () )
        {
            final Dimension mc = new Dimension ( 0, 0 );
            if ( elements != null )
            {
                final int spacing = dockablePane.getContentSpacing ();
                for ( final DockableElement element : elements )
                {
                    final Dimension minElement = element.getMinimumSize ( dockablePane );
                    if ( orientation.isHorizontal () )
                    {
                        mc.width += minElement.width + spacing;
                        mc.height = Math.max ( minElement.height, mc.height );
                    }
                    else
                    {
                        mc.width = Math.max ( minElement.width, mc.width );
                        mc.height += minElement.height + spacing;
                    }
                }
                if ( orientation.isHorizontal () )
                {
                    mc.width -= spacing;
                }
                else
                {
                    mc.height -= spacing;
                }
            }
            min = new Dimension ( Math.max ( min.width, mc.width ), Math.max ( min.height, mc.height ) );
        }

        // Validating size
        // This is made here to optimize performance
        if ( size.width < min.width || size.height < min.height )
        {
            setSize ( new Dimension ( Math.max ( size.width, min.width ), Math.max ( size.height, min.height ) ) );
        }

        return min;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy