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

org.fujion.component.BaseUIComponent Maven / Gradle / Ivy

There is a newer version: 3.1.0
Show newest version
/*
 * #%L
 * fujion
 * %%
 * Copyright (C) 2018 Fujion Framework
 * %%
 * 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.
 *
 * #L%
 */
package org.fujion.component;

import org.fujion.ancillary.CssClasses;
import org.fujion.ancillary.CssStyles;
import org.fujion.ancillary.IDisable;
import org.fujion.ancillary.PrintOptions;
import org.fujion.annotation.Component.PropertyGetter;
import org.fujion.annotation.Component.PropertySetter;
import org.fujion.event.KeyCode;
import org.springframework.util.Assert;

/**
 * The base class from which all UI components derive.
 */
public abstract class BaseUIComponent extends BaseComponent implements IDisable {

    private final CssStyles styles = new CssStyles();

    private final CssClasses classes = new CssClasses();

    private String height;

    private String width;

    private String flex;

    private String hint;

    private String balloon;

    private boolean disabled;

    private boolean visible = true;

    private int tabindex;

    private String css;

    private String dragid;

    private String dropid;

    private Popup context;

    private Popup popup;

    private String keycapture;

    /**
     * Creates a shaded mask over the component, effectively preventing direct user interaction.
     */
    public void addMask() {
        addMask(null);
    }

    /**
     * Creates a shaded mask over the component, effectively preventing direct user interaction.
     *
     * @param label Text to display in the center of the mask.
     */
    public void addMask(String label) {
        invoke("addMask", label);
    }

    /**
     * Creates a shaded mask over the component, effectively preventing direct user interaction.
     *
     * @param label Text to display in the center of the mask.
     * @param popup Popup to display when right-clicking on the mask.
     */
    public void addMask(String label, Popup popup) {
        invoke("addMask", label, popup);
    }

    /**
     * Removes the mask, if present.
     */
    public void removeMask() {
        invoke("removeMask");
    }

    /**
     * Returns the styles set for this component.
     *
     * @return The styles set for this component.
     */
    @PropertyGetter(value = "style", description = "The active CSS styles for this component.")
    public String getStyles() {
        return styles.toString();
    }

    /**
     * Sets the styles for this component, replacing any existing styles.
     *
     * @param styles Styles to set, represented as a string in the same format as the HTML style
     *            attribute.
     */
    @PropertySetter(value = "style", description = "The active CSS styles for this component.")
    public void setStyles(String styles) {
        String oldStyles = this.styles.toString();
        this.styles.parse(styles, true);
        _syncStyles(oldStyles);
    }

    private String _syncStyle(String name, String dflt) {
        String current = styles.get(name);

        if (current != null) {
            return current;
        }

        styles.put(name, dflt);
        return dflt;
    }

    /**
     * Synchronizes style properties with stored styles and with the client.
     *
     * @param oldStyles Previous styles value.
     */
    protected void _syncStyles(String oldStyles) {
        height = _syncStyle("height", height);
        width = _syncStyle("width", width);
        flex = _syncStyle("flex", flex);
        propertyChange("style", oldStyles, styles.toString(), true);
    }

    /**
     * Returns the value of the named style.
     *
     * @param name The style name (e.g., "color").
     * @return The value of the style, or null if no value set for the style.
     */
    public String getStyle(String name) {
        return styles.get(name);
    }

    /**
     * Adds a style to existing styles. If that style already exists, it will be overwritten.
     *
     * @param name The style name (e.g., "color").
     * @param value The style value.
     * @return The previous value of the style, or null if none.
     */
    public String addStyle(String name, String value) {
        String oldStyles = this.styles.toString();
        String oldValue = styles.put(name, value);
        _syncStyles(oldStyles);
        return oldValue;
    }

    /**
     * Adds styles specified as a string.
     *
     * @param styles Styles to add, represented as a string in the same format as the HTML style
     *            attribute.
     */
    public void addStyles(String styles) {
        String oldStyles = this.styles.toString();
        this.styles.parse(styles, false);
        _syncStyles(oldStyles);
    }

    /**
     * Removes a style.
     *
     * @param name The style name (e.g., "color").
     * @return The value of the removed style, or null if none.
     */
    public String removeStyle(String name) {
        return addStyle(name, null);
    }

    /**
     * Returns the CSS classes set for this component.
     *
     * @return Space-delimited list of CSS classes.
     */
    @PropertyGetter(value = "class", description = "Space-delimited list of CSS classes.")
    public String getClasses() {
        return classes.toString();
    }

    /**
     * Sets the CSS classes for this component, replacing any existing classes.
     *
     * @param classes Space-delimited list of CSS classes. Extended syntax is supported (see
     *            {@link org.fujion.ancillary.CssClasses});
     */
    public void setClasses(String classes) {
        String oldClasses = classes.toString();
        this.classes.parse(classes);
        _syncClasses(oldClasses);
    }

    /**
     * Synchronize class settings with the client.
     *
     * @param oldClasses Previous value for classes.
     */
    protected void _syncClasses(String oldClasses) {
        propertyChange("clazz", oldClasses, classes.toString(true), true);
    }

    /**
     * Adds one or more classes to existing classes.
     *
     * @param value Space-delimited list of CSS classes. Extended syntax is supported (see
     *            {@link org.fujion.ancillary.CssClasses});
     */
    @PropertySetter(value = "class", description = "Space-delimited list of CSS classes.")
    public void addClass(String value) {
        String oldClasses = classes.toString();

        if (classes.add(value)) {
            _syncClasses(oldClasses);
        }
    }

    /**
     * Removes one or more classes from existing classes.
     *
     * @param value Space-delimited list of CSS classes. Extended syntax is supported (see
     *            {@link org.fujion.ancillary.CssClasses});
     */
    public void removeClass(String value) {
        String oldClasses = classes.toString();

        if (classes.remove(value)) {
            _syncClasses(oldClasses);
        }
    }

    /**
     * Toggles the presence of two mutually exclusive classes based on a condition. Extended syntax
     * is supported for each of the class specifiers (see {@link org.fujion.ancillary.CssClasses})
     *
     * @param yesValue Classes to be added if the condition is true, or removed if false.
     * @param noValue Classes to be added if the condition is false, or removed if true.
     * @param condition The condition value.
     */
    public void toggleClass(String yesValue, String noValue, boolean condition) {
        String oldClasses = classes.toString();
        
        if (classes.toggle(yesValue, noValue, condition)) {
            _syncClasses(oldClasses);
        }
    }

    /**
     * Hides the component - a shortcut for {@link #setVisible setVisible(false)}.
     */
    public void hide() {
        setVisible(false);
    }

    /**
     * Shows the component - a shortcut for {@link #setVisible setVisible(true)}.
     */
    public void show() {
        setVisible(true);
    }

    /**
     * Returns the height. This is synchronized with the height style setting.
     *
     * @return The height.
     */
    @PropertyGetter(value = "height", description = "The component's height.")
    public String getHeight() {
        return height;
    }

    /**
     * Sets the height. This is synchronized with the height style setting.
     *
     * @param height The height.
     */
    @PropertySetter(value = "height", description = "The component's height.")
    public void setHeight(String height) {
        height = trimify(height);

        if (!areEqual(height, this.height)) {
            this.height = height;
            addStyle("height", height);
        }
    }

    /**
     * Returns the width. This is synchronized with the width style setting.
     *
     * @return The width.
     */
    @PropertyGetter(value = "width", description = "The component's width.")
    public String getWidth() {
        return width;
    }

    /**
     * Sets the width. This is synchronized with the width style setting.
     *
     * @param width The width.
     */
    @PropertySetter(value = "width", description = "The component's width.")
    public void setWidth(String width) {
        width = trimify(width);

        if (!areEqual(width, this.width)) {
            this.width = width;
            addStyle("width", width);
        }
    }

    /**
     * Returns the flex setting. This is synchronized with the flex style setting.
     *
     * @return The flex setting.
     */
    @PropertyGetter(value = "flex", description = "The component's flex style setting.")
    public String getFlex() {
        return flex;
    }

    /**
     * Sets the flex setting. This is synchronized with the flex style setting.
     *
     * @param flex The flex setting.
     */
    @PropertySetter(value = "flex", description = "The component's flex style setting.")
    public void setFlex(String flex) {
        flex = trimify(flex);

        if (!areEqual(flex, this.flex)) {
            this.flex = flex;
            addStyle("flex", flex);
        }
    }

    /**
     * Sets/removes the input focus to/from this component. If the component cannot receive the
     * input focus, the request is ignored.
     *
     * @param focus If true, the component receives the input focus if possible. If false and the
     *            component currently has the input focus, the focus is relinquished.
     */
    @PropertySetter(value = "focus", defaultValue = "false", defer = true, description = "Sets or removes focus for this component.")
    public void setFocus(boolean focus) {
        invoke("focus", focus);
    }

    /**
     * Sets the input focus to this component. If the component cannot receive the input focus, the
     * request is ignored. This is a shortcut for setFocus(true).
     */
    public void focus() {
        setFocus(true);
    }

    /**
     * Returns the CSS specifier for this component. This is similar to an embedded style sheet, but
     * is associated with this component and is added to or removed from the client as this
     * component is added or removed. Moreover, EL expressions can be used in the selector to
     * reference this component or one of its subcomponents by their id. For example,
     *
     * 
     *      ##{id}>.inline{font-style:italic; background: white}
     * 
* * @return The CSS specifier. */ @PropertyGetter(value = "css", description = "The CSS specifier for this component.") public String getCss() { return css; } /** * Sets the CSS specifier for this component. This is similar to an embedded style sheet, but is * associated with this component and is added to or removed from the client as this component * is added or removed. Moreover, EL expressions can be used in the selector to reference this * component or one of its subcomponents by their id. For example, * *
     *      ##{id}>.inline{font-style:italic; background: white}
     * 
* * @param css The CSS specifier. */ @PropertySetter(value = "css", description = "The CSS specifier for this component.") public void setCss(String css) { propertyChange("css", this.css, this.css = nullify(css), true); } /** * Causes the component to report its current dimensions and relative position via a * {@link org.fujion.event.ResizeEvent resize event}. */ public void reportSize() { invoke("reportSize"); } /** * Returns the popup text to be displayed when hovering over this component. * * @return The hint text. */ @PropertyGetter(value = "hint", description = "The popup text to be displayed when hovering over this component.") public String getHint() { return hint; } /** * Sets the popup text to be displayed when hovering over this component. * * @param hint The hint text. */ @PropertySetter(value = "hint", description = "The popup text to be displayed when hovering over this component.") public void setHint(String hint) { propertyChange("hint", this.hint, this.hint = nullify(hint), true); } /** * Returns the balloon text to be displayed adjacent to the component. * * @return The balloon text. */ @PropertyGetter(value = "balloon", description = "The balloon text to be displayed adjacent to the component.") public String getBalloon() { return balloon; } /** * Sets the balloon text to be displayed adjacent to the component. * * @param balloon The balloon text. */ @PropertySetter(value = "balloon", description = "The balloon text to be displayed adjacent to the component.") public void setBalloon(String balloon) { propertyChange("balloon", this.balloon, this.balloon = nullify(balloon), true); } @Override @PropertyGetter(value = "disabled", description = "True if the component is disabled.") public boolean isDisabled() { return disabled; } @Override @PropertySetter(value = "disabled", defaultValue = "false", description = "True if the component is disabled.") public void setDisabled(boolean disabled) { propertyChange("disabled", this.disabled, this.disabled = disabled, true); } /** * Returns the visibility state of the component. * * @return The visibility state of the component. */ @PropertyGetter(value = "visible", description = "The visibility state of the component.") public boolean isVisible() { return visible; } /** * Sets the visibility state of the component. * * @param visible The visibility state of the component. */ @PropertySetter(value = "visible", defaultValue = "true", description = "The visibility state of the component.") public void setVisible(boolean visible) { propertyChange("visible", this.visible, this.visible = visible, true); } /** * Returns the tab index for the component. * * @return The tab index. */ @PropertyGetter(value = "tabindex", description = "The tab index for the component.") public int getTabindex() { return tabindex; } /** * Sets the tab index for the component. * * @param tabindex The tab index. */ @PropertySetter(value = "tabindex", defaultValue = "0", description = "The tab index for the component.") public void setTabindex(int tabindex) { propertyChange("tabindex", this.tabindex, this.tabindex = tabindex < 0 ? 0 : tabindex, true); } /** * Returns a space-delimited list of drag id's associated with this component. A non-empty value * enables dragging of the component. A dragged component may be dropped upon a target component * whose drop id(s) matches at least one of the dragged component's drag id's. A drag id value * of "*" will match any drop id. * * @return The drag id(s). */ @PropertyGetter(value = "dragid", description = "A space-delimited list of drag id's associated with this component.") public String getDragid() { return dragid; } /** * Sets the drag id(s) to be associated with this component. * * @param dragid A space-delimited list of drag id's to be associated with this component. A * non-empty value enables dragging of the component. A dragged component may be * dropped upon a target component whose drop id(s) matches at least one of the drag * id's. A drag id value of "*" will match any drop id. */ @PropertySetter(value = "dragid", description = "A space-delimited list of drag id's associated with this component.") public void setDragid(String dragid) { propertyChange("dragid", this.dragid, this.dragid = trimify(dragid), true); } /** * Returns a space-delimited list of drop id's associated with this component. A non-empty value * enables this component to act as a drop target. A dragged component may be dropped upon a * target component whose drop id(s) matches at least one of the dragged component's drag id's. * A drop id value of "*" will match any drag id. * * @return The drag id(s). */ @PropertyGetter(value = "dropid", description = "A space-delimited list of drop id's associated with this component.") public String getDropid() { return dropid; } /** * Sets the drag id(s) to be associated with this component. * * @param dropid A space-delimited list of drop id's to be associated with this component. A * non-empty value enables this component to act as a drop target. A dragged * component may be dropped upon a target component whose drop id(s) matches at least * one of the dragged component's drag id's. A drop id value of "*" will match any * drag id. */ @PropertySetter(value = "dropid", description = "A space-delimited list of drop id's associated with this component.") public void setDropid(String dropid) { propertyChange("dropid", this.dropid, this.dropid = trimify(dropid), true); } /** * Returns the popup component that will appear when right-clicking on this component. * * @return The popup component that will appear when right-clicking on this component. */ @PropertyGetter(value = "context", description = "The popup component that will appear when right-clicking on this component.") public Popup getContext() { return context; } /** * Sets the popup component that will appear when right-clicking on this component. * * @param context The popup component that will appear when right-clicking on this component. */ @PropertySetter(value = "context", defer = true, description = "The popup component that will appear when right-clicking on this component.") public void setContext(Popup context) { if (context != this.context) { swapTrackedComponents(context, this.context); propertyChange("context", this.context, this.context = context, true); } } /** * Returns the popup component that will appear when hovering over this component. * * @return The popup component that will appear when hovering over this component. */ @PropertyGetter(value = "popup", description = "The popup component that will appear when hovering over this component.") public Popup getPopup() { return popup; } /** * Sets the popup component that will appear when hovering over this component. * * @param popup The popup component that will appear when hovering over this component. */ @PropertySetter(value = "popup", defer = true, description = "The popup component that will appear when hovering over this component.") public void setPopup(Popup popup) { if (popup != this.popup) { swapTrackedComponents(popup, this.popup); propertyChange("popup", this.popup, this.popup = popup, true); } } /** * If the child being removed is the popup, set the popup to null. * * @see org.fujion.component.BaseComponent#afterRemoveChild(org.fujion.component.BaseComponent) */ @Override protected void afterRemoveChild(BaseComponent child) { super.afterRemoveChild(child); if (child == popup) { setPopup(null); } } /** * Returns the list of key codes to be captured. When a key in this list is typed, a * {@value org.fujion.event.KeycaptureEvent#TYPE} event will be sent to this component. * * @return List of key codes to be captured (see * {@link org.fujion.event.KeyCode#normalizeKeyCapture} for formatting details). */ @PropertyGetter(value = "keycapture", description = "The list of key codes to be captured.") public String getKeycapture() { return keycapture; } /** * Sets the list of key codes to be captured. When a key in this list is typed, a * {@value org.fujion.event.KeycaptureEvent#TYPE} event will be sent to this component. * * @param keycapture List of key codes to be captured (see * {@link org.fujion.event.KeyCode#normalizeKeyCapture} for formatting details). */ @PropertySetter(value = "keycapture", description = "The list of key codes to be captured.") public void setKeycapture(String keycapture) { if (propertyChange("keycapture", this.keycapture, this.keycapture = nullify(keycapture), false)) { sync("keycapture", KeyCode.normalizeKeyCapture(this.keycapture)); } } /** * Ensures that this component is visible within the view port by scrolling if necessary. */ public void scrollIntoView() { invoke("scrollIntoView"); } /** * Returns the first visible child, if any. * * @param recurse If true, all descendant levels are also searched using a breadth first * strategy. * @return The first visible child encountered, or null if not found. */ public BaseUIComponent getFirstVisibleChild(boolean recurse) { return getFirstVisibleChild(BaseUIComponent.class, recurse); } /** * Returns the first visible child of a given class, if any. * * @param The child class. * @param clazz The child class to consider. * @param recurse If true, all descendant levels are also searched using a breadth first * strategy. * @return The first visible child encountered, or null if not found. */ public T getFirstVisibleChild(Class clazz, boolean recurse) { for (T child : getChildren(clazz)) { if (child.isVisible()) { return child; } } if (recurse) { for (T child : getChildren(clazz)) { T comp = child.getFirstVisibleChild(clazz, recurse); if (comp != null) { return comp; } } } return null; } /** * Remove popup reference when it is destroyed. * * @see org.fujion.component.BaseComponent#onDestroyTracked(org.fujion.component.BaseComponent) */ @Override protected void onDestroyTracked(BaseComponent comp) { if (comp == popup) { setPopup(null); } if (comp == context) { setContext(null); } super.onDestroyTracked(comp); } /** * Print this element with default options. */ public void print() { print(null); } /** * Print this element with the specified options. * * @param options Print options. */ public void print(PrintOptions options) { Assert.state(isRendered(), () -> "A component may not be printed if it has not yet been rendered"); invoke("print", options); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy