Package pivot.wtk.skin.terra

Source Code of pivot.wtk.skin.terra.TerraRollupSkin

/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements.  See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except in
* compliance with the License.  You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package pivot.wtk.skin.terra;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.RoundRectangle2D;

import pivot.collections.Sequence;
import pivot.util.Vote;
import pivot.wtk.Button;
import pivot.wtk.ButtonPressListener;
import pivot.wtk.Component;
import pivot.wtk.ComponentMouseButtonListener;
import pivot.wtk.Container;
import pivot.wtk.Cursor;
import pivot.wtk.Dimensions;
import pivot.wtk.Mouse;
import pivot.wtk.PushButton;
import pivot.wtk.Rollup;
import pivot.wtk.RollupListener;
import pivot.wtk.Theme;
import pivot.wtk.effects.Transition;
import pivot.wtk.effects.TransitionListener;
import pivot.wtk.effects.easing.Easing;
import pivot.wtk.effects.easing.Quadratic;
import pivot.wtk.media.Image;
import pivot.wtk.skin.ButtonSkin;
import pivot.wtk.skin.ContainerSkin;

/**
* Rollup skin.
* <p>
* TODO Optimize this class by performing preferred size calculation in one
* pass.
*
* @author tvolkert
*/
public class TerraRollupSkin extends ContainerSkin
    implements RollupListener, ButtonPressListener {
    private class ExpansionTransition extends Transition {
        private int height1;
        private int height2;
        private boolean reverse;

        private int originalPreferredHeight;
        private int height;

        private Easing easing = new Quadratic();

        public ExpansionTransition(int height1, int height2, boolean reverse, int duration, int rate) {
            super(duration, rate, false);

            this.height1 = height1;
            this.height2 = height2;
            this.reverse = reverse;
        }

        public int getHeight() {
            return height;
        }

        @Override
        public void start(TransitionListener transitionListener) {
            Rollup rollup = (Rollup)getComponent();
            originalPreferredHeight = rollup.isPreferredHeightSet() ?
                rollup.getPreferredHeight() : -1;

            super.start(transitionListener);
        }

        @Override
        public void stop() {
            Rollup rollup = (Rollup)getComponent();
            rollup.setPreferredHeight(originalPreferredHeight);

            super.stop();
        }

        @Override
        protected void update() {
            float percentComplete = getPercentComplete();

            if (percentComplete < 1f) {
                int elapsedTime = getElapsedTime();
                int duration = getDuration();

                height = (int)(height1 + (height2 - height1) * percentComplete);
                if (reverse) {
                    height = (int)easing.easeIn(elapsedTime, height1, height2 - height1, duration);
                } else {
                    height = (int)easing.easeOut(elapsedTime, height1, height2 - height1, duration);
                }

                Rollup rollup = (Rollup)getComponent();
                rollup.setPreferredHeight(height);
            }
        }
    }

    protected class RollupButton extends PushButton {
        public RollupButton() {
            super(null);
            setSkin(new RollupButtonSkin());
        }
    }

    protected class RollupButtonSkin extends ButtonSkin {
        @Override
        public boolean isFocusable() {
            return false;
        }

        public int getPreferredWidth(int height) {
            RollupButton rollupButton = (RollupButton)getComponent();
            Button.DataRenderer dataRenderer = rollupButton.getDataRenderer();
            dataRenderer.render(rollupButton.getButtonData(), rollupButton, false);
            return dataRenderer.getPreferredWidth(height);
        }

        public int getPreferredHeight(int width) {
            RollupButton rollupButton = (RollupButton)getComponent();
            Button.DataRenderer dataRenderer = rollupButton.getDataRenderer();
            dataRenderer.render(rollupButton.getButtonData(), rollupButton, false);
            return dataRenderer.getPreferredHeight(width);
        }

        public Dimensions getPreferredSize() {
            RollupButton rollupButton = (RollupButton)getComponent();
            Button.DataRenderer dataRenderer = rollupButton.getDataRenderer();
            dataRenderer.render(rollupButton.getButtonData(), rollupButton, false);
            Dimensions contentSize = dataRenderer.getPreferredSize();
            return new Dimensions(contentSize.width, contentSize.height);
        }

        public void paint(Graphics2D graphics) {
            RollupButton rollupButton = (RollupButton)getComponent();

            // Paint the content
            Button.DataRenderer dataRenderer = rollupButton.getDataRenderer();
            dataRenderer.render(rollupButton.getButtonData(), rollupButton, false);

            Dimensions contentSize = dataRenderer.getPreferredSize();
            dataRenderer.setSize(contentSize.width, contentSize.height);
            dataRenderer.paint(graphics);
        }

        @Override
        public boolean mouseClick(Component component, Mouse.Button button, int x, int y, int count) {
            PushButton pushButton = (PushButton)getComponent();
            pushButton.press();
            return true;
        }
    }

    protected abstract class ButtonImage extends Image {
        public int getWidth() {
            return 7;
        }

        public int getHeight() {
            return 7;
        }
    }

    protected class ExpandImage extends ButtonImage {
        public void paint(Graphics2D graphics) {
            graphics.setStroke(new BasicStroke(0));
            graphics.setPaint(buttonColor);

            graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                    RenderingHints.VALUE_ANTIALIAS_ON);

            int[] xPoints = {0, 6, 0};
            int[] yPoints = {0, 3, 6};
            graphics.fillPolygon(xPoints, yPoints, 3);
            graphics.drawPolygon(xPoints, yPoints, 3);
        }
    }

    protected class CollapseImage extends ButtonImage {
        public void paint(Graphics2D graphics) {
            graphics.setStroke(new BasicStroke(0));
            graphics.setPaint(buttonColor);

            graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                    RenderingHints.VALUE_ANTIALIAS_ON);

            int[] xPoints = {0, 3, 6};
            int[] yPoints = {0, 6, 0};
            graphics.fillPolygon(xPoints, yPoints, 3);
            graphics.drawPolygon(xPoints, yPoints, 3);
        }
    }

    protected class BulletImage extends ButtonImage {
        public void paint(Graphics2D graphics) {
            graphics.setStroke(new BasicStroke(0));
            graphics.setPaint(buttonColor);

            RoundRectangle2D.Double shape = new RoundRectangle2D.Double(1, 1, 4, 4, 2, 2);

            graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);

            graphics.draw(shape);
            graphics.fill(shape);
        }
    }

    private class ToggleComponentMouseHandler
        implements ComponentMouseButtonListener {
        public boolean mouseDown(Component component, Mouse.Button button, int x, int y) {
            return false;
        }

        public boolean mouseUp(Component component, Mouse.Button button, int x, int y) {
            return false;
        }

        public boolean mouseClick(Component component, Mouse.Button button, int x, int y, int count) {
            Rollup rollup = (Rollup)getComponent();
            rollup.setExpanded(!rollup.isExpanded());
            return true;
        }
    }

    private RollupButton rollupButton = null;
    private Component toggleComponent = null;
    private ToggleComponentMouseHandler toggleComponentMouseHandler =
        new ToggleComponentMouseHandler();
    private ExpandImage expandImage = new ExpandImage();
    private CollapseImage collapseImage = new CollapseImage();
    private BulletImage bulletImage = new BulletImage();

    private ExpansionTransition expandTransition = null;
    private ExpansionTransition collapseTransition = null;

    private Color buttonColor;
    private int spacing;
    private int buffer;
    private boolean justify;
    private boolean firstChildToggles;

    private static final int EXPANSION_DURATION = 250;
    private static final int EXPANSION_RATE = 30;

    public TerraRollupSkin() {
        TerraTheme theme = (TerraTheme)Theme.getTheme();
        buttonColor = theme.getColor(9);
        spacing = 4;
        buffer = 4;
        justify = false;
        firstChildToggles = true;
    }

    @Override
    public void install(Component component) {
        super.install(component);

        Rollup rollup = (Rollup)component;
        rollup.getRollupListeners().add(this);

        updateToggleComponent();

        rollupButton = new RollupButton();
        updateRollupButton();
        rollup.add(rollupButton);
        rollupButton.getButtonPressListeners().add(this);
    }

    @Override
    public void uninstall() {
        Rollup rollup = (Rollup)getComponent();
        rollup.getRollupListeners().remove(this);

        rollupButton.getButtonPressListeners().remove(this);
        rollup.remove(rollupButton);
        rollupButton = null;

        if (toggleComponent != null) {
            toggleComponent.getComponentMouseButtonListeners().remove(toggleComponentMouseHandler);
            toggleComponent = null;
        }

        super.uninstall();
    }

    @Override
    public int getPreferredWidth(int height) {
        Rollup rollup = (Rollup)getComponent();

        int preferredWidth = 0;

        // Preferred width is the max of our childrens' preferred widths, plus
        // the button width, buffer, and padding. If we're collapsed, we only
        // look at the first child.
        for (int i = 0, n = rollup.getLength(); i < n; i++) {
            Component component = rollup.get(i);

            if (component == rollupButton) {
                // Ignore "private" component
                continue;
            }

            if (component.isDisplayable()) {
                int componentPreferredWidth = component.getPreferredWidth(-1);
                preferredWidth = Math.max(preferredWidth, componentPreferredWidth);
            }

            if (!rollup.isExpanded()) {
                // If we're collapsed, we only look at the first child.
                break;
            }
        }

        preferredWidth += rollupButton.getPreferredWidth(-1) + buffer;

        return preferredWidth;
    }

    @Override
    public int getPreferredHeight(int width) {
        Rollup rollup = (Rollup)getComponent();
        return getPreferredHeight(width, rollup.isExpanded());
    }

    private int getPreferredHeight(int width, boolean expanded) {
        Rollup rollup = (Rollup)getComponent();

        // Preferred height is the sum of our childrens' preferred heights,
        // plus spacing and padding.
        Dimensions rollupButtonPreferredSize = rollupButton.getPreferredSize();

        if (justify
            && width != -1) {
            width = Math.max(width - rollupButtonPreferredSize.width - buffer, 0);
        } else {
            width = -1;
        }

        int preferredHeight = 0;

        int displayableComponentCount = 0;
        for (int i = 0, n = rollup.getLength(); i < n; i++) {
            Component component = rollup.get(i);

            if (component == rollupButton) {
                // Ignore "private" component
                continue;
            }

            if (component.isDisplayable()) {
                preferredHeight += component.getPreferredHeight(width);
                displayableComponentCount++;
            }

            if (!expanded) {
                // If we're collapsed, we only look at the first child.
                break;
            }
        }

        if (displayableComponentCount > 0) {
            preferredHeight += (displayableComponentCount - 1) * spacing;
        }

        preferredHeight = Math.max(preferredHeight,
            rollupButtonPreferredSize.height);

        return preferredHeight;
    }

    public void layout() {
        Rollup rollup = (Rollup)getComponent();
        Dimensions rollupButtonSize = rollupButton.getPreferredSize();
        rollupButton.setSize(rollupButtonSize);

        int x = rollupButtonSize.width + buffer;
        int y = 0;
        int justifiedWidth = Math.max(getWidth() - rollupButtonSize.width - buffer, 0);

        Component firstComponent = null;

        for (int i = 0, n = rollup.getLength(); i < n; i++) {
            Component component = rollup.get(i);

            if (component == rollupButton) {
                // Ignore "private" component
                continue;
            }

            if (firstComponent == null) {
                firstComponent = component;
            }

            if ((component == firstComponent
                || rollup.isExpanded())
                && component.isDisplayable()) {
                // We lay this child out and make sure it's painted.
                component.setVisible(true);

                int componentWidth, componentHeight;
                if (justify) {
                    componentWidth = justifiedWidth;
                    componentHeight = component.getPreferredHeight(componentWidth);
                } else {
                    Dimensions componentPreferredSize = component.getPreferredSize();
                    componentWidth = componentPreferredSize.width;
                    componentHeight = componentPreferredSize.height;
                }

                component.setLocation(x, y);
                component.setSize(componentWidth, componentHeight);

                y += componentHeight + spacing;
            } else {
                // We make sure this child doesn't get painted.  There's also
                // no need to lay the child out.
                component.setVisible(false);
            }
        }

        int rollupButtonY = (firstComponent == null) ?
            0 : (firstComponent.getHeight() - rollupButtonSize.height) / 2 + 1;

        rollupButton.setLocation(0, rollupButtonY);
    }

    public Color getButtonColor() {
        return buttonColor;
    }

    public void setButtonColor(Color buttonColor) {
        this.buttonColor = buttonColor;
        repaintComponent();
    }

    public final void setButtonColor(String buttonColor) {
        if (buttonColor == null) {
            throw new IllegalArgumentException("buttonColor is null.");
        }

        setButtonColor(decodeColor(buttonColor));
    }

    public int getSpacing() {
        return spacing;
    }

    public void setSpacing(int spacing) {
        this.spacing = spacing;
        invalidateComponent();
    }

    public int getBuffer() {
        return buffer;
    }

    public void setBuffer(int buffer) {
        this.buffer = buffer;
        invalidateComponent();
    }

    public boolean getJustify() {
        return justify;
    }

    public void setJustify(boolean justify) {
        this.justify = justify;
        invalidateComponent();
    }

    public boolean getFirstChildToggles() {
        return firstChildToggles;
    }

    public void setFirstChildToggles(boolean firstChildToggles) {
        this.firstChildToggles = firstChildToggles;
        updateToggleComponent();
    }

    private void updateRollupButton() {
        Rollup rollup = (Rollup)getComponent();

        Image buttonData = null;
        Cursor cursor = Cursor.HAND;

        // Make sure to account for rollupButton
        if (rollup.getLength() == 2) {
            buttonData = bulletImage;
            cursor = Cursor.DEFAULT;
        } else if (rollup.isExpanded()) {
            buttonData = collapseImage;
        } else {
            buttonData = expandImage;
        }

        rollupButton.setButtonData(buttonData);
        rollupButton.setCursor(cursor);
    }

    private void updateToggleComponent() {
        Rollup rollup = (Rollup)getComponent();
        Component previousToggleComponent = toggleComponent;

        toggleComponent = null;
        if (firstChildToggles) {
            for (int i = 0, n = rollup.getLength(); i < n; i++) {
                Component child = rollup.get(i);
                if (child != rollupButton) {
                    toggleComponent = child;
                    break;
                }
            }
        }

        if (toggleComponent != null
            && rollup.getLength() > 2) {
            // TODO Record original cursor
            toggleComponent.setCursor(Cursor.HAND);
        }

        if (toggleComponent != previousToggleComponent) {
            if (previousToggleComponent != null) {
                // TODO Restore original cursor
                previousToggleComponent.setCursor(Cursor.DEFAULT);

                previousToggleComponent.getComponentMouseButtonListeners().remove(toggleComponentMouseHandler);
            }

            if (toggleComponent != null) {
                toggleComponent.getComponentMouseButtonListeners().add(toggleComponentMouseHandler);
            }
        }
    }

    // Container events
    @Override
    public void componentInserted(Container container, int index) {
        super.componentInserted(container, index);

        updateRollupButton();
        updateToggleComponent();
    }

    @Override
    public void componentsRemoved(Container container, int index, Sequence<Component> components) {
        super.componentsRemoved(container, index, components);

        updateRollupButton();
        updateToggleComponent();
    }

    // RollupListener methods

    public Vote previewExpandedChange(final Rollup rollup) {
        Vote vote = Vote.APPROVE;

        if (rollup.getDisplay() != null) {
            if (rollup.isExpanded()) {
                // Start a collapse transition, return false, and set the
                // expanded state when the transition is complete
                if (collapseTransition == null) {
                    int duration = EXPANSION_DURATION;
                    int height1 = getHeight();

                    if (expandTransition != null) {
                        // Stop the expand transition
                        expandTransition.stop();

                        // Record its progress so we can reverse it at the right point
                        duration = expandTransition.getElapsedTime();
                        height1 = expandTransition.getHeight();

                        expandTransition = null;
                    }

                    if (duration > 0) {
                        int height2 = getPreferredHeight(-1, false);

                        collapseTransition = new ExpansionTransition(height1, height2,
                            true, duration, EXPANSION_RATE);
                        collapseTransition.start(new TransitionListener() {
                            public void transitionCompleted(Transition transition) {
                                rollup.setExpanded(false);
                                collapseTransition = null;
                            }
                        });

                        vote = Vote.DEFER;
                    }
                } else {
                    vote = collapseTransition.isRunning() ? Vote.DEFER : Vote.APPROVE;
                }
            }
        }

        return vote;
    }

    public void expandedChangeVetoed(Rollup rollup, Vote reason) {
        if (reason == Vote.DENY
            && collapseTransition != null) {
            collapseTransition.stop();
            collapseTransition = null;
        }
    }

    public void expandedChanged(Rollup rollup) {
        updateRollupButton();
        invalidateComponent();

        if (rollup.getDisplay() != null) {
            if (rollup.isExpanded()) {
                // Start an expansion transition
                int height1 = getHeight();
                int height2 = getPreferredHeight(-1, true);

                expandTransition = new ExpansionTransition(height1, height2,
                    false, EXPANSION_DURATION, EXPANSION_RATE);
                expandTransition.start(new TransitionListener() {
                    public void transitionCompleted(Transition transition) {
                        expandTransition = null;
                    }
                });
            }
        }
    }

    // ButtonPressListener methods

    public void buttonPressed(Button button) {
        Rollup rollup = (Rollup)getComponent();
        rollup.setExpanded(!rollup.isExpanded());
    }
}
TOP

Related Classes of pivot.wtk.skin.terra.TerraRollupSkin

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.