com.vaadin.ui.AbstractComponentContainer Maven / Gradle / Ivy
/*
* Copyright (C) 2000-2024 Vaadin Ltd
*
* This program is available under Vaadin Commercial License and Service Terms.
*
* See for the full
* license.
*/
package com.vaadin.ui;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import com.vaadin.server.ComponentSizeValidator;
import com.vaadin.shared.Registration;
import com.vaadin.shared.ui.AbstractComponentContainerState;
/**
* Extension to {@link AbstractComponent} that defines the default
* implementation for the methods in {@link ComponentContainer}. Basic UI
* components that need to contain other components inherit this class to easily
* qualify as a component container.
*
* @author Vaadin Ltd
* @since 3.0
*/
@SuppressWarnings("serial")
public abstract class AbstractComponentContainer extends AbstractComponent
implements ComponentContainer {
/**
* Constructs a new component container.
*/
public AbstractComponentContainer() {
super();
}
/*
* (non-Javadoc)
*
* @see
* com.vaadin.ui.ComponentContainer#addComponents(com.vaadin.ui.Component[])
*/
@Override
public void addComponents(Component... components) {
for (Component c : components) {
addComponent(c);
}
}
/**
* Removes all components from the container. This should probably be
* re-implemented in extending classes for a more powerful implementation.
*/
@Override
public void removeAllComponents() {
final LinkedList l = new LinkedList<>();
// Adds all components
for (final Iterator i = getComponentIterator(); i
.hasNext();) {
l.add(i.next());
}
// Removes all component
for (Component aL : l) {
removeComponent(aL);
}
}
/*
* Moves all components from an another container into this container. Don't
* add a JavaDoc comment here, we use the default documentation from
* implemented interface.
*/
@Override
public void moveComponentsFrom(ComponentContainer source) {
final LinkedList components = new LinkedList<>();
for (final Iterator i = source.getComponentIterator(); i
.hasNext();) {
components.add(i.next());
}
for (final Component c : components) {
source.removeComponent(c);
addComponent(c);
}
}
/* documented in interface */
@Override
public Registration addComponentAttachListener(
ComponentAttachListener listener) {
return addListener(ComponentAttachEvent.class, listener,
ComponentAttachListener.attachMethod);
}
/* documented in interface */
@Override
@Deprecated
public void removeComponentAttachListener(
ComponentAttachListener listener) {
removeListener(ComponentAttachEvent.class, listener,
ComponentAttachListener.attachMethod);
}
/* documented in interface */
@Override
public Registration addComponentDetachListener(
ComponentDetachListener listener) {
return addListener(ComponentDetachEvent.class, listener,
ComponentDetachListener.detachMethod);
}
/* documented in interface */
@Override
@Deprecated
public void removeComponentDetachListener(
ComponentDetachListener listener) {
removeListener(ComponentDetachEvent.class, listener,
ComponentDetachListener.detachMethod);
}
/**
* Fires the component attached event. This should be called by the
* addComponent methods after the component have been added to this
* container.
*
* @param component
* the component that has been added to this container.
*/
protected void fireComponentAttachEvent(Component component) {
fireEvent(new ComponentAttachEvent(this, component));
}
/**
* Fires the component detached event. This should be called by the
* removeComponent methods after the component have been removed from this
* container.
*
* @param component
* the component that has been removed from this container.
*/
protected void fireComponentDetachEvent(Component component) {
fireEvent(new ComponentDetachEvent(this, component));
}
/**
* This only implements the events and component parent calls. The extending
* classes must implement component list maintenance and call this method
* after component list maintenance.
*
* @see com.vaadin.ui.ComponentContainer#addComponent(Component)
*/
@Override
public void addComponent(Component c) {
// Make sure we're not adding the component inside it's own content
if (isOrHasAncestor(c)) {
throw new IllegalArgumentException(
"Component cannot be added inside it's own content");
}
if (c.getParent() != null) {
// If the component already has a parent, try to remove it
AbstractSingleComponentContainer.removeFromParent(c);
}
c.setParent(this);
fireComponentAttachEvent(c);
markAsDirty();
}
/**
* This only implements the events and component parent calls. The extending
* classes must implement component list maintenance and call this method
* before component list maintenance.
*
* @see com.vaadin.ui.ComponentContainer#removeComponent(Component)
*/
@Override
public void removeComponent(Component c) {
if (equals(c.getParent())) {
c.setParent(null);
fireComponentDetachEvent(c);
markAsDirty();
}
}
@Override
public void setWidth(float width, Unit unit) {
/*
* child tree repaints may be needed, due to our fall back support for
* invalid relative sizes
*/
Collection dirtyChildren = null;
boolean childrenMayBecomeUndefined = false;
if (getWidth() == SIZE_UNDEFINED && width != SIZE_UNDEFINED) {
// children currently in invalid state may need repaint
dirtyChildren = getInvalidSizedChildren(false);
} else if ((width == SIZE_UNDEFINED && getWidth() != SIZE_UNDEFINED)
|| (unit == Unit.PERCENTAGE
&& getWidthUnits() != Unit.PERCENTAGE
&& !ComponentSizeValidator
.parentCanDefineWidth(this))) {
/*
* relative width children may get to invalid state if width becomes
* invalid. Width may also become invalid if units become percentage
* due to the fallback support
*/
childrenMayBecomeUndefined = true;
dirtyChildren = getInvalidSizedChildren(false);
}
super.setWidth(width, unit);
repaintChangedChildTrees(dirtyChildren, childrenMayBecomeUndefined,
false);
}
private void repaintChangedChildTrees(Collection invalidChildren,
boolean childrenMayBecomeUndefined, boolean vertical) {
if (childrenMayBecomeUndefined) {
Collection previouslyInvalidComponents = invalidChildren;
invalidChildren = getInvalidSizedChildren(vertical);
if (previouslyInvalidComponents != null
&& invalidChildren != null) {
for (Iterator iterator = invalidChildren
.iterator(); iterator.hasNext();) {
Component component = iterator.next();
if (previouslyInvalidComponents.contains(component)) {
// still invalid don't repaint
iterator.remove();
}
}
}
} else if (invalidChildren != null) {
Collection stillInvalidChildren = getInvalidSizedChildren(
vertical);
if (stillInvalidChildren != null) {
for (Component component : stillInvalidChildren) {
// didn't become valid
invalidChildren.remove(component);
}
}
}
if (invalidChildren != null) {
repaintChildTrees(invalidChildren);
}
}
private Collection getInvalidSizedChildren(
final boolean vertical) {
HashSet components = null;
for (Component component : this) {
boolean valid = vertical
? ComponentSizeValidator.checkHeights(component)
: ComponentSizeValidator.checkWidths(component);
if (!valid) {
if (components == null) {
components = new HashSet<>();
}
components.add(component);
}
}
return components;
}
private void repaintChildTrees(Collection dirtyChildren) {
for (Component c : dirtyChildren) {
c.markAsDirtyRecursive();
}
}
@Override
public void setHeight(float height, Unit unit) {
/*
* child tree repaints may be needed, due to our fall back support for
* invalid relative sizes
*/
Collection dirtyChildren = null;
boolean childrenMayBecomeUndefined = false;
if (getHeight() == SIZE_UNDEFINED && height != SIZE_UNDEFINED) {
// children currently in invalid state may need repaint
dirtyChildren = getInvalidSizedChildren(true);
} else if ((height == SIZE_UNDEFINED && getHeight() != SIZE_UNDEFINED)
|| (unit == Unit.PERCENTAGE
&& getHeightUnits() != Unit.PERCENTAGE
&& !ComponentSizeValidator
.parentCanDefineHeight(this))) {
/*
* relative height children may get to invalid state if height
* becomes invalid. Height may also become invalid if units become
* percentage due to the fallback support.
*/
childrenMayBecomeUndefined = true;
dirtyChildren = getInvalidSizedChildren(true);
}
super.setHeight(height, unit);
repaintChangedChildTrees(dirtyChildren, childrenMayBecomeUndefined,
true);
}
/**
* {@inheritDoc}
*
* @deprecated As of 7.0, use {@link #iterator()} instead.
*/
@Deprecated
@Override
public Iterator getComponentIterator() {
return iterator();
}
@Override
protected AbstractComponentContainerState getState() {
return (AbstractComponentContainerState) super.getState();
}
@Override
protected AbstractComponentContainerState getState(boolean markAsDirty) {
return (AbstractComponentContainerState) super.getState(markAsDirty);
}
}