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

com.alee.painter.decoration.content.AbstractContent Maven / Gradle / Ivy

The 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.painter.decoration.content;

import com.alee.api.data.Rotation;
import com.alee.managers.style.BoundsType;
import com.alee.painter.decoration.IDecoration;
import com.alee.utils.GraphicsUtils;
import com.alee.utils.SwingUtils;
import com.thoughtworks.xstream.annotations.XStreamAsAttribute;

import javax.swing.*;
import java.awt.*;
import java.awt.geom.AffineTransform;

/**
 * Abstract content providing some general method implementations.
 *
 * @param  component type
 * @param  decoration type
 * @param  content type
 * @author Mikle Garin
 * @author Alexandr Zernov
 */
public abstract class AbstractContent, I extends AbstractContent>
        implements IContent
{
    /**
     * Content identifier.
     * It should be unique within its parent layout to keep this content separate.
     * In case identifier is already used by another content in the same layout this content will be merged on top of existing one.
     */
    @XStreamAsAttribute
    protected String id;

    /**
     * Whether or not this content should overwrite previous one when merged.
     */
    @XStreamAsAttribute
    protected Boolean overwrite;

    /**
     * Bounds this content should be restricted with.
     *
     * @see com.alee.managers.style.BoundsType
     */
    @XStreamAsAttribute
    protected BoundsType bounds;

    /**
     * Content constraints within {@link com.alee.painter.decoration.layout.IContentLayout}.
     */
    @XStreamAsAttribute
    protected String constraints;

    /**
     * Content padding.
     */
    @XStreamAsAttribute
    protected Insets padding;

    /**
     * Preferred content size.
     * Only specify it when you want to hardcode specific content size.
     */
    @XStreamAsAttribute
    protected Dimension size;

    /**
     * Content rotation.
     * It is applied to content {@link #bounds} and {@link #padding}.
     */
    @XStreamAsAttribute
    protected Rotation rotation;

    /**
     * Content opacity.
     */
    @XStreamAsAttribute
    protected Float opacity;

    @Override
    public String getId ()
    {
        return id != null ? id : "content";
    }

    @Override
    public boolean isOverwrite ()
    {
        return overwrite != null && overwrite;
    }

    @Override
    public void activate ( final C c, final D d )
    {
        // Do nothing by default
    }

    @Override
    public void deactivate ( final C c, final D d )
    {
        // Do nothing by default
    }

    @Override
    public BoundsType getBoundsType ()
    {
        return bounds != null ? bounds : BoundsType.padding;
    }

    @Override
    public String getConstraints ()
    {
        return constraints;
    }

    /**
     * Returns actual padding.
     *
     * @param c painted component
     * @param d painted decoration state
     * @return actual padding
     */
    protected Insets getPadding ( final C c, final D d )
    {
        if ( padding != null )
        {
            final boolean ltr = isLeftToRight ( c, d );
            return new Insets ( padding.top, ltr ? padding.left : padding.right, padding.bottom, ltr ? padding.right : padding.left );
        }
        else
        {
            return null;
        }
    }

    /**
     * Returns preferred content size.
     *
     * @param c painted component
     * @param d painted decoration state
     * @return preferred content size
     */
    protected Dimension getSize ( final C c, final D d )
    {
        return size;
    }

    /**
     * Returns content rotation.
     *
     * @param c painted component
     * @param d painted decoration state
     * @return content rotation
     */
    protected Rotation getRotation ( final C c, final D d )
    {
        return rotation != null ? rotation : Rotation.none;
    }

    /**
     * Returns content opacity.
     *
     * @param c painted component
     * @param d painted decoration state
     * @return content opacity
     */
    public float getOpacity ( final C c, final D d )
    {
        return opacity != null ? opacity : 1f;
    }

    @Override
    public boolean hasBaseline ( final C c, final D d )
    {
        final Rotation rotation = getActualRotation ( c, d );
        return ( rotation == Rotation.none || rotation == Rotation.upsideDown ) && hasContentBaseline ( c, d );
    }

    /**
     * Returns whether or not this content has a reasonable baseline.
     *
     * @param c aligned component
     * @param d aligned component decoration state
     * @return {@code true} if this content has a reasonable baseline, {@code false} otherwise
     */
    protected boolean hasContentBaseline ( final C c, final D d )
    {
        return false;
    }

    @Override
    public int getBaseline ( final C c, final D d, final Rectangle bounds )
    {
        final Rotation rotation = getActualRotation ( c, d );
        if ( rotation == Rotation.none || rotation == Rotation.upsideDown )
        {
            // Adjusting bounds
            final Rectangle rotated = rotation.transpose ( bounds );
            final Rectangle shrunk = SwingUtils.shrink ( rotated, getPadding ( c, d ) );

            // Retrieving baseline
            final int baseline = getContentBaseline ( c, d, shrunk );

            // Adjusting baseline according to initial bounds
            if ( rotation == Rotation.none )
            {
                // Return baseline "as is" since there is no rotation
                return baseline;
            }
            else
            {
                // Change baseline relative to the opposite side of the content bounds
                return bounds.y + bounds.height - ( baseline - rotated.y );
            }
        }
        return -1;
    }

    /**
     * Returns content baseline within the specified bounds, measured from the top of the bounds.
     * A return value less than {@code 0} indicates this content does not have a reasonable baseline.
     *
     * @param c      aligned component
     * @param d      aligned component decoration state
     * @param bounds bounds to get the baseline for
     * @return content baseline within the specified bounds, measured from the top of the bounds
     */
    protected int getContentBaseline ( final C c, final D d, final Rectangle bounds )
    {
        return -1;
    }

    @Override
    public Component.BaselineResizeBehavior getBaselineResizeBehavior ( final C c, final D d )
    {
        final Rotation rotation = getActualRotation ( c, d );
        if ( rotation == Rotation.none || rotation == Rotation.upsideDown )
        {
            final Component.BaselineResizeBehavior behavior = getContentBaselineResizeBehavior ( c, d );
            return rotation == Rotation.none ? behavior : rotation.adjust ( behavior );
        }
        return Component.BaselineResizeBehavior.OTHER;
    }

    /**
     * Returns enum indicating how the baseline of the content changes as the size changes.
     *
     * @param c aligned component
     * @param d aligned component decoration state
     * @return enum indicating how the baseline of the content changes as the size changes
     */
    public Component.BaselineResizeBehavior getContentBaselineResizeBehavior ( final C c, final D d )
    {
        return Component.BaselineResizeBehavior.OTHER;
    }

    @Override
    public void paint ( final Graphics2D g2d, final C c, final D d, final Rectangle bounds )
    {
        if ( bounds.width > 0 && bounds.height > 0 && !isEmpty ( c, d ) )
        {
            // Content opacity
            final float opacity = getOpacity ( c, d );
            final Composite oc = GraphicsUtils.setupAlphaComposite ( g2d, opacity, opacity < 1f );

            // Applying rotation
            final AffineTransform transform = g2d.getTransform ();
            final Rotation rotation = getActualRotation ( c, d );
            if ( rotation != Rotation.none )
            {
                double angle = 0;
                double rX = 0;
                double rY = 0;
                switch ( rotation )
                {
                    case clockwise:
                        angle = Math.PI / 2;
                        rX = bounds.width;
                        rY = bounds.width;
                        break;

                    case upsideDown:
                        angle = Math.PI;
                        rX = bounds.width;
                        rY = bounds.height;
                        break;

                    case counterClockwise:
                        angle = -Math.PI / 2;
                        rX = bounds.height;
                        rY = bounds.height;
                        break;
                }
                g2d.rotate ( angle, bounds.x + rX / 2, bounds.y + rY / 2 );
            }

            // Adjusting bounds
            final Rectangle rotated = rotation.transpose ( bounds );
            final Rectangle shrunk = SwingUtils.shrink ( rotated, getPadding ( c, d ) );

            // Proper content clipping
            final Shape os = GraphicsUtils.intersectClip ( g2d, shrunk );

            // Painting content
            paintContent ( g2d, c, d, shrunk );

            // Restoring clip area
            GraphicsUtils.restoreClip ( g2d, os );

            // Restoring graphics settings
            g2d.setTransform ( transform );

            // Restoring composite
            GraphicsUtils.restoreComposite ( g2d, oc );
        }
    }

    /**
     * Paints content.
     *
     * @param g2d    graphics context
     * @param c      painted component
     * @param d      painted decoration state
     * @param bounds painting bounds
     */
    protected abstract void paintContent ( Graphics2D g2d, C c, D d, Rectangle bounds );

    @Override
    public Dimension getPreferredSize ( final C c, final D d, final Dimension available )
    {
        // Actual padding
        final Insets padding = getPadding ( c, d );

        // Actial rotation
        final Rotation rotation = getActualRotation ( c, d );

        // Calculating proper available width
        // We have to take padding and rotation into account here
        final Dimension shrunk = SwingUtils.shrink ( available, padding );
        final Dimension transposed = rotation.transpose ( shrunk );

        // Calculating content preferred size
        final Dimension ps;
        final Dimension hardcoded = getSize ( c, d );
        if ( hardcoded != null )
        {
            // Make sure to apply rotation to the preferred size
            ps = rotation.transpose ( hardcoded );
        }
        else
        {
            // Calculate dynamic preferred size
            ps = getContentPreferredSize ( c, d, transposed );
        }

        // Adding content padding
        final Dimension stretched = SwingUtils.stretch ( ps, padding );
        return rotation.transpose ( stretched );
    }

    /**
     * Returns content preferred size.
     *
     * @param c         painted component
     * @param d         painted decoration state
     * @param available available space
     * @return content preferred size
     */
    protected abstract Dimension getContentPreferredSize ( C c, D d, Dimension available );

    /**
     * Returns actual content rotation.
     *
     * @param c painted component
     * @param d painted decoration state
     * @return actual content rotation
     */
    protected Rotation getActualRotation ( final C c, final D d )
    {
        final Rotation tr = getRotation ( c, d );
        return isLeftToRight ( c, d ) ? tr : tr.rightToLeft ();
    }

    /**
     * Returns whether or not component has LTR orientation.
     *
     * @param c painted component
     * @param d painted decoration state
     * @return {@code true} if component has LTR orientation, {@code false} if it has RTL orientation
     */
    protected boolean isLeftToRight ( final C c, final D d )
    {
        return c.getComponentOrientation ().isLeftToRight ();
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy