org.vaadin.alump.masonry.MasonryLayout.java Source code

Java tutorial

Introduction

Here is the source code for org.vaadin.alump.masonry.MasonryLayout.java

Source

/**
 * MasonryLayout.java (Masonry)
 *
 * Copyright 2014 Vaadin Ltd, Sami Viitanen <sami.viitanen@vaadin.org>
 *
 * Licensed 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 org.vaadin.alump.masonry;

import com.vaadin.annotations.JavaScript;
import com.vaadin.event.LayoutEvents;
import com.vaadin.shared.Connector;
import com.vaadin.shared.EventId;
import com.vaadin.shared.MouseEventDetails;
import com.vaadin.ui.AbstractLayout;
import com.vaadin.ui.Component;
import org.vaadin.alump.masonry.client.MasonryLayoutClientRpc;
import org.vaadin.alump.masonry.client.MasonryLayoutServerRpc;
import org.vaadin.alump.masonry.client.MasonryLayoutState;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/** Layout that uses Masonry JavaScript library to layout components
 * Masonry by David DeSandro: http://masonry.desandro.com/
 */
@JavaScript({ "masonry.pkgd.min.js" })
public class MasonryLayout extends AbstractLayout implements LayoutEvents.LayoutClickNotifier {

    /**
     * Wrapper style name for components added. Will tell wrapper to take double width.
     */
    public static final String DOUBLE_WIDE_STYLENAME = "masonry-double-wide";

    /**
     * Wrapper style name for components added. Will tell wrapper to take triple width.
     */
    public static final String TRIPLE_WIDE_STYLENAME = "masonry-triple-wide";

    /**
     * Wrapper style name for components added. Will tell wrapper to take quadruple width.
     */
    public static final String QUADRUPLE_WIDE_STYLENAME = "masonry-quadruple-wide";

    /**
     * Add this style name to MasonryLayout component to get nicer paper shadow effect. This is more complex CSS that
     * affects to multiple layers of DOM tree component creates.
     */
    public static final String MASONRY_PAPER_SHADOW_STYLENAME = "masonry-paper-shadow";

    protected List<Component> components = new ArrayList<Component>();

    private final MasonryLayoutServerRpc serverRpc = new MasonryLayoutServerRpc() {

        @Override
        public void layoutClick(MouseEventDetails mouseDetails, Connector clickedConnector) {
            fireEvent(
                    LayoutEvents.LayoutClickEvent.createEvent(MasonryLayout.this, mouseDetails, clickedConnector));
        }
    };

    /**
     * Create masonry layout with default column width (300px)
     */
    public MasonryLayout() {
        super();
        this.registerRpc(serverRpc, MasonryLayoutServerRpc.class);
    }

    /**
     * Create new masonry layout with defined column width
     * @param columnWidth Column width used to calculate left positions of items. Can not be changed after start.
     *                    Remember to update horizontal values of CSS rules to match with this.
     */
    public MasonryLayout(int columnWidth) {
        this();
        getState().columnWidth = columnWidth;
    }

    // We must override getState() to cast the state to MyComponentState
    @Override
    public MasonryLayoutState getState() {
        return (MasonryLayoutState) super.getState();
    }

    @Override
    public void replaceComponent(Component oldComponent, Component newComponent) {

        if (oldComponent.getParent() != this) {
            throw new IllegalArgumentException("Old component has different parent");
        }

        if (oldComponent == newComponent) {
            return;
        }

        if (newComponent.getParent() == this) {
            removeComponent(newComponent);
        }

        int oldIndex = components.indexOf(oldComponent);
        removeComponent(oldComponent);
        addComponent(newComponent, oldIndex);
    }

    @Override
    public void addComponent(Component component) {
        addComponent(component, getComponentCount());
    }

    /**
     * Add component to given index
     * @param component Component added
     * @param index Index where component is added
     */
    public void addComponent(Component component, int index) {

        if (component.getParent() == this) {
            int currentIndex = components.indexOf(component);
            if (currentIndex == index) {
                return;
            }
            removeComponent(component);
            if (index < components.size()) {
                components.add(index, component);
            } else {
                components.add(component);
            }
        } else {
            components.add(component);
        }

        try {
            super.addComponent(component);
            markAsDirty();
        } catch (IllegalArgumentException e) {
            components.remove(component);
            throw e;
        }
    }

    /**
     * Get child component at given index
     * @param index Index number of child
     * @return Child at index
     * @throws java.lang.IndexOutOfBoundsException When given index is invalid
     */
    public Component getComponent(int index) {
        if (index < 0 || index >= components.size()) {
            throw new IndexOutOfBoundsException(
                    "Given index (" + index + ") is out of bounds (0.." + (components.size() - 1) + ")");
        }

        return components.get(index);
    }

    /**
     * Add component with style name added to wrapping element
     * @param component Component added
     * @param wrapperStyleName Style name added to wrapping element, or null if no extra style names. Can be eq. used
     *                         to have double wide items.
     */
    public void addComponent(Component component, String wrapperStyleName) {
        addComponent(component, wrapperStyleName, getComponentCount());
    }

    /**
     * Add component with style name added to wrapping element to given index
     * @param component Component added
     * @param wrapperStyleName Style name added to wrapping element, or null if no extra style names. Can be eq. used to have
     *                    double wide items. To update this you need to re add component.
     * @param index Index where component is added
     */
    public void addComponent(Component component, String wrapperStyleName, int index) {
        // Correct index if already child of this layout
        int indexCorrection = 0;
        if (component.getParent() == this) {
            if (components.indexOf(component) < index) {
                indexCorrection = -1;
            }
        }

        addComponent(component, index + indexCorrection);

        if (wrapperStyleName != null) {
            getState().itemStyleNames.put(component, wrapperStyleName);
        } else {
            getState().itemStyleNames.remove(component);
        }
    }

    /**
     * Add component to first position
     * @param added Component added
     * @param wrapperStyleName Wrapper style name of component
     */
    public void addComponentFirst(Component added, String wrapperStyleName) {
        addComponent(added, wrapperStyleName, 0);
    }

    /**
     * Add component before given component
     * @param added Component added
     * @param wrapperStyleName Wrapper style name of component
     * @param before Component will be added before this component
     */
    public void addComponentBefore(Component added, String wrapperStyleName, Component before) {
        if (before.getParent() != this) {
            throw new IllegalArgumentException("Given target component is not child of this MasonryLayout");
        }
        if (added == before) {
            return;
        }
        addComponent(added, wrapperStyleName, components.indexOf(before));
    }

    /**
     * Add component after given component
     * @param added Component added
     * @param wrapperStyleName Wrapper stylename of component
     * @param after Component will be added after this component
     */
    public void addComponentAfter(Component added, String wrapperStyleName, Component after) {
        if (after.getParent() != this) {
            throw new IllegalArgumentException("Given target component is not child of this MasonryLayout");
        }
        if (added == after) {
            return;
        }
        addComponent(added, wrapperStyleName, components.indexOf(after) + 1);
    }

    /**
     * Get optional wrapper style name of component
     * @param component Component added earlier
     * @return Wrapper style name given for component, null if not defined
     */
    public String getComponentWrapperStyleName(Component component) {
        if (component.getParent() != this) {
            throw new IllegalArgumentException("Given component is not child of this MasonryLayout");
        }
        return getState().itemStyleNames.get(component);
    }

    @Override
    public void removeComponent(Component component) {

        getState().itemStyleNames.remove(component);

        if (components.remove(component)) {
            super.removeComponent(component);
            markAsDirty();
        }
    }

    @Override
    public int getComponentCount() {
        return components.size();
    }

    @Override
    public Iterator<Component> iterator() {
        return components.iterator();
    }

    /**
     * Request client side to re-layout. Useful if component sizes have changed.
     */
    public void requestLayout() {
        getRpcProxy(MasonryLayoutClientRpc.class).layout();
    }

    /**
     * Get column width used with this masonry layout
     * @return Column width in pixels
     */
    public int getColumnWidth() {
        return getState().columnWidth;
    }

    @Override
    public void addLayoutClickListener(LayoutEvents.LayoutClickListener listener) {
        addListener(EventId.LAYOUT_CLICK_EVENT_IDENTIFIER, LayoutEvents.LayoutClickEvent.class, listener,
                LayoutEvents.LayoutClickListener.clickMethod);
    }

    @Override
    public void addListener(LayoutEvents.LayoutClickListener layoutClickListener) {
        addLayoutClickListener(layoutClickListener);
    }

    @Override
    public void removeLayoutClickListener(LayoutEvents.LayoutClickListener listener) {
        removeListener(EventId.LAYOUT_CLICK_EVENT_IDENTIFIER, LayoutEvents.LayoutClickEvent.class, listener);
    }

    @Override
    public void removeListener(LayoutEvents.LayoutClickListener layoutClickListener) {
        removeLayoutClickListener(layoutClickListener);
    }
}