com.vaadin.ui.CustomField 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.Collections;
import java.util.Iterator;
import com.vaadin.data.HasValue;
import com.vaadin.shared.ui.customfield.CustomFieldState;
/**
* A {@link HasValue} whose UI content can be constructed by the user, enabling
* the creation of e.g. form fields by composing Vaadin components.
* Customization of both the visual presentation and the logic of the field is
* possible.
*
* Subclasses must implement {@link #initContent()}.
*
* Most custom fields can simply compose a user interface that calls the methods
* {@link #doSetValue(Object)} and {@link #getValue()} when necessary.
*
* @param
* field value type
*
* @since 8.0
*/
public abstract class CustomField extends AbstractField
implements HasComponents {
/**
* The root component implementing the custom component.
*/
private Component root = null;
/**
* Constructs a new custom field.
*
*
* The component is implemented by wrapping the methods of the composition
* root component given as parameter. The composition root must be set
* before the component can be used.
*
*/
public CustomField() {
// expand horizontally by default
setWidth(100, Unit.PERCENTAGE);
}
/**
* Constructs the content and notifies it that the {@link CustomField} is
* attached to a window.
*
* @see com.vaadin.ui.Component#attach()
*/
@Override
public void attach() {
// First call super attach to notify all children (none if content has
// not yet been created)
super.attach();
// If the content has not yet been created, create and attach it at
// this point by calling getContent()
getContent();
}
/**
* Returns the content (UI) of the custom component.
*
* @return Component
*/
protected Component getContent() {
if (null == root) {
root = initContent();
root.setParent(this);
}
return root;
}
/**
* Create the content component or layout for the field. Subclasses of
* {@link CustomField} should implement this method.
*
* Note that this method is called when the CustomField is attached to a
* layout or when {@link #getContent()} is called explicitly for the first
* time. It is only called once for a {@link CustomField}.
*
* @return {@link Component} representing the UI of the CustomField
*/
protected abstract Component initContent();
// Size related methods
// TODO might not be necessary to override but following the pattern from
// AbstractComponentContainer
@Override
public void setHeight(float height, Unit unit) {
super.setHeight(height, unit);
markAsDirtyRecursive();
}
@Override
public void setWidth(float width, Unit unit) {
super.setWidth(width, unit);
markAsDirtyRecursive();
}
@Override
protected CustomFieldState getState() {
return (CustomFieldState) super.getState();
}
@Override
protected CustomFieldState getState(boolean markAsDirty) {
return (CustomFieldState) super.getState(markAsDirty);
}
// ComponentContainer methods
@Override
public Iterator iterator() {
// Can't use getContent() here as this will cause an infinite loop if
// initContent happens to all iterator(). This happens if you do
// setWidth...
if (root != null) {
return Collections.singletonList(root).iterator();
} else {
return Collections. emptyList().iterator();
}
}
/**
* Sets the component to which all methods from the {@link Focusable}
* interface should be delegated.
*
* Set this to a wrapped field to include that field in the tabbing order,
* to make it receive focus when {@link #focus()} is called and to make it
* be correctly focused when used as a Grid editor component.
*
* By default, {@link Focusable} events are handled by the super class and
* ultimately ignored.
*
* @param focusDelegate
* the focusable component to which focus events are redirected
*/
public void setFocusDelegate(Focusable focusDelegate) {
getState().focusDelegate = focusDelegate;
}
private Focusable getFocusable() {
return (Focusable) getState(false).focusDelegate;
}
@Override
public void focus() {
if (getFocusable() != null) {
getFocusable().focus();
} else {
super.focus();
}
}
@Override
public int getTabIndex() {
if (getFocusable() != null) {
return getFocusable().getTabIndex();
} else {
return super.getTabIndex();
}
}
@Override
public void setTabIndex(int tabIndex) {
if (getFocusable() != null) {
getFocusable().setTabIndex(tabIndex);
} else {
super.setTabIndex(tabIndex);
}
}
}