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

com.vaadin.flow.component.contextmenu.MenuManager Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2000-2024 Vaadin Ltd.
 *
 * 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 com.vaadin.flow.component.contextmenu;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import com.vaadin.flow.component.ClickEvent;
import com.vaadin.flow.component.Component;
import com.vaadin.flow.component.ComponentEventListener;
import com.vaadin.flow.component.ComponentUtil;
import com.vaadin.flow.function.SerializableBiFunction;
import com.vaadin.flow.function.SerializableRunnable;

/**
 * Common management logic for context menus and sub menus. Maintains the list
 * of components to stamp into one overlay.
 *
 * @param 
 *            the context menu type
 * @param 
 *            the menu item type
 * @param 
 *            the sub menu type
 *
 * @author Vaadin Ltd.
 */
public class MenuManager, S extends SubMenuBase>
        implements Serializable {

    private final C menu;
    private final SerializableBiFunction itemGenerator;
    private final Class itemType;
    private final I parentMenuItem;
    private final SerializableRunnable contentReset;

    private final List children = new ArrayList<>();

    /**
     * Creates a new manager instance.
     *
     * @param menu
     *            the context menu
     * @param contentReset
     *            callback to reset the context menu
     * @param itemGenerator
     *            the item generator/factory
     * @param itemType
     *            the item type
     * @param parentMenuItem
     *            the parent menu item of the submenu
     */
    public MenuManager(C menu, SerializableRunnable contentReset,
            SerializableBiFunction itemGenerator,
            Class itemType, I parentMenuItem) {
        this.menu = menu;
        this.contentReset = contentReset;
        this.itemGenerator = itemGenerator;
        this.itemType = itemType;
        this.parentMenuItem = parentMenuItem;
    }

    /**
     * Adds a text as a menu item.
     *
     * @param text
     *            the text for the menu item
     * @return a new menu item
     */
    public I addItem(String text) {
        I menuItem = itemGenerator.apply(menu, contentReset);
        menuItem.setText(text);
        add(menuItem);
        return menuItem;
    }

    /**
     * Adds a component as a menu item.
     *
     * @param component
     *            the component for the menu item
     * @return a new menu item
     */
    public I addItem(Component component) {
        I menuItem = itemGenerator.apply(menu, contentReset);
        add(menuItem);
        menuItem.add(component);
        return menuItem;
    }

    /**
     * Adds a text as a menu item with a click listener.
     *
     * @param text
     *            the text for the menu item
     * @param clickListener
     *            a click listener
     * @return a new menu item
     */
    @SuppressWarnings({ "unchecked", "rawtypes" })
    public I addItem(String text,
            ComponentEventListener> clickListener) {
        I menuItem = addItem(text);
        if (clickListener != null) {
            ComponentUtil.addListener(menuItem, ClickEvent.class,
                    (ComponentEventListener) clickListener);
        }
        return menuItem;
    }

    /**
     * Adds a component as a menu item with a click listener.
     *
     * @param component
     *            the component for the menu item
     * @param clickListener
     *            a click listener
     * @return a new menu item
     */
    @SuppressWarnings({ "unchecked", "rawtypes" })
    public I addItem(Component component,
            ComponentEventListener> clickListener) {
        I menuItem = addItem(component);
        if (clickListener != null) {
            ComponentUtil.addListener(menuItem, ClickEvent.class,
                    (ComponentEventListener) clickListener);
        }
        return menuItem;
    }

    /**
     * Adds components to the (sub)menu.
     * 

* The components are added into the content as is, they are not wrapped as * menu items. * * @param components * components to add * @see #remove(Component...) * @see #addComponentAtIndex(int, Component) */ public void add(Component... components) { if (parentMenuItem != null && parentMenuItem.isCheckable()) { throw new IllegalStateException( "A checkable item cannot have a sub menu"); } Objects.requireNonNull(components, "Components to add cannot be null"); for (Component component : components) { Objects.requireNonNull(component, "Component to add cannot be null"); children.add(component); } if (components.length > 0) { updateChildren(); } } /** * Removes components to the (sub)menu. * * @param components * components to remove * @see #add(Component...) */ public void remove(Component... components) { Objects.requireNonNull(components, "Components to remove cannot be null"); boolean needUpdate = false; for (Component component : components) { Objects.requireNonNull(component, "Component to remove cannot be null"); if (children.remove(component)) { needUpdate = true; } else { throw new IllegalArgumentException("The given component (" + component + ") is not a child of this component"); } } if (needUpdate) { updateChildren(); } } /** * Remove all components and items from (sub)menu. * * @see #remove(Component...) */ public void removeAll() { children.clear(); updateChildren(); } /** * Inserts component to the (sub)menu using the {@code index}. *

* The component is inserted into the content as is, it is not wrapped as a * menu item. * * @param index * index to insert, not negative * @param component * component to insert * * @see #add(Component...) * @see #remove(Component...) */ public void addComponentAtIndex(int index, Component component) { if (parentMenuItem != null && parentMenuItem.isCheckable()) { throw new IllegalStateException( "A checkable item cannot have a sub menu"); } Objects.requireNonNull(component, "Component should not be null"); if (index < 0) { throw new IllegalArgumentException( "Cannot add a component with a negative index"); } children.add(index, component); updateChildren(); } /** * Gets all (sub)menu children. *

* Children consist of components and items. * * @see #add(Component...) * @see #addItem(Component) * * @see #getItems() * * @return the children components */ public Stream getChildren() { return children.stream(); } /** * Gets all children items. *

* The items are filtered using the provided item type in the constructor. * * @see #getChildren() * * @return all children items */ public List getItems() { return getChildren().filter(itemType::isInstance).map(itemType::cast) .collect(Collectors.toList()); } private void updateChildren() { contentReset.run(); } }