com.google.appinventor.client.editor.simple.components.MockComponentsUtil.java Source code

Java tutorial

Introduction

Here is the source code for com.google.appinventor.client.editor.simple.components.MockComponentsUtil.java

Source

// -*- mode: java; c-basic-offset: 2; -*-
// Copyright 2009-2011 Google, All Rights reserved
// Copyright 2011-2012 MIT, All rights reserved
// Released under the Apache License, Version 2.0
// http://www.apache.org/licenses/LICENSE-2.0

package com.google.appinventor.client.editor.simple.components;

// import com.google.gwt.event.dom.client.LoadEvent;
// import com.google.gwt.event.dom.client.LoadHandler;
import com.google.gwt.event.shared.GwtEvent.Type;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.widgetideas.graphics.client.Color;

/**
 * Helper methods for working with mock components.
 *
 * @author lizlooney@google.com (Liz Looney)
 */
public final class MockComponentsUtil {

    private MockComponentsUtil() {
    }

    /**
     * Sets the background color for the given widget.
     *
     * @param widget  widget to change background color for
     * @param color  new color (RGB value)
     */
    static void setWidgetBackgroundColor(Widget widget, String color) {
        if (isNoneColor(color)) {
            DOM.setStyleAttribute(widget.getElement(), "backgroundColor", "transparent");
        } else {
            DOM.setStyleAttribute(widget.getElement(), "backgroundColor", "#" + getHexString(color, 6));
        }
    }

    /**
     * Sets the background image for the given widget.
     *
     * @param widget  widget to change background image for
     * @param image  URL
     */
    static void setWidgetBackgroundImage(Widget widget, String image) {
        DOM.setStyleAttribute(widget.getElement(), "backgroundImage", "url(" + image + ')');
        DOM.setStyleAttribute(widget.getElement(), "backgroundRepeat", "no-repeat");
        DOM.setStyleAttribute(widget.getElement(), "backgroundPosition", "center");
        DOM.setStyleAttribute(widget.getElement(), "backgroundSize", "100% 100%");
    }

    /**
     * Sets the background image for the given widget.
     *
     * @param container the MockContainer to refresh when image is loaded
     * @param widget  widget to change background image for
     * @param image  URL
     */
    static void setWidgetBackgroundImage(final MockContainer container, Widget widget, String image) {
        // Problem: When we change the background image via a Style referencing a "url"
        // the browser doesn't initially know the size of the image. We need to know it
        // when the container layout height and/or width is "Automatic." If we query right
        // away, we will be told the image is 0 x 0 because it isn't loaded yet.
        // I have not been able to figure out how to get the browser to give us a onLoad (or
        // similar event) when the image is loaded. If we could get such an event, we can
        // call refreshForm in the container and win.
        //
        // The code below fudges this by setting up a time to fire after 1 second with the
        // hope that the image will have been loaded by then and its dimensions known.
        // The code commented out immediately below this code is what I would like to use,
        // but it doesn't seem to work!   -JIS
        Timer t = new Timer() {
            @Override
            public void run() {
                container.refreshForm();
            }
        };
        //    widget.addHandler(new LoadHandler() {
        //        @Override
        //        public void onLoad(LoadEvent event) {
        //          container.refreshForm();
        //        }
        //      }, new Type<LoadHandler>());
        setWidgetBackgroundImage(widget, image);
        t.schedule(1000); // Fire in one second
    }

    /**
     * Sets the font weight for the given widget (bold or normal).
     *
     * @param widget  widget to change font weight for
     * @param value  {@code true} for bold font and {@code false} for normal font
     */
    static void setWidgetFontBold(Widget widget, String value) {
        DOM.setStyleAttribute(widget.getElement(), "fontWeight", Boolean.parseBoolean(value) ? "bold" : "normal");
    }

    /**
     * Sets the text color for the given widget.
     *
     * @param widget  widget to change text color for
     * @param color  new color (RGB value)
     */
    static void setWidgetTextColor(Widget widget, String color) {
        if (isNoneColor(color)) {
            DOM.setStyleAttribute(widget.getElement(), "color", "transparent");
        } else {
            DOM.setStyleAttribute(widget.getElement(), "color", "#" + getHexString(color, 6));
        }
    }

    /**
     * Sets the font style for the given widget (italic or normal).
     *
     * @param widget  widget to change font style for
     * @param value  {@code true} for italic font and {@code false} for normal font
     */
    static void setWidgetFontItalic(Widget widget, String value) {
        DOM.setStyleAttribute(widget.getElement(), "fontStyle", Boolean.parseBoolean(value) ? "italic" : "normal");
    }

    /**
     * Sets the font size for the given widget.
     *
     * @param widget  widget to change font size for
     * @param size  new font size (in scaled px)
     */
    static void setWidgetFontSize(Widget widget, String size) {
        // Fonts on Android are in scaled pixels...
        try {
            DOM.setStyleAttribute(widget.getElement(), "fontSize", (int) (Float.parseFloat(size) * 0.9) + "px");
        } catch (NumberFormatException e) {
            // Ignore this. If we throw an exception here, the project is unrecoverable.
        }
    }

    /**
     * Sets the font typeface for the given widget.
     *
     * @param widget  widget to change font typeface for
     * @param typeface  "0" for normal, "1" for sans serif, "2" for serif and
     *                  "3" for monospace
     */
    static void setWidgetFontTypeface(Widget widget, String typeface) {
        switch (Integer.parseInt(typeface)) {
        default:
            // This should never happen
            throw new IllegalArgumentException("Typeface:" + typeface);

        case 0:
        case 1:
            typeface = "sans-serif";
            break;

        case 2:
            typeface = "serif";
            break;

        case 3:
            typeface = "monospace";
            break;
        }
        DOM.setStyleAttribute(widget.getElement(), "fontFamily", typeface);
    }

    /**
     * Sets the text alignment for the given widget.
     *
     * @param widget  widget to change text alignment for
     * @param align  one of "0" for left, "1" for center or "2" for right
     */
    static void setWidgetTextAlign(Widget widget, String align) {
        switch (Integer.parseInt(align)) {
        default:
            // This should never happen
            throw new IllegalArgumentException("align:" + align);

        case 0:
            align = "left";
            break;

        case 1:
            align = "center";
            break;

        case 2:
            align = "right";
            break;
        }
        DOM.setStyleAttribute(widget.getElement(), "textAlign", align);
    }

    /**
     * Indicates whether the given color value describes the "None" color.
     */
    static boolean isNoneColor(String color) {
        return getHexString(color, 8).equals(MockVisibleComponent.COLOR_NONE);
    }

    /**
     * Indicates whether the given color value describes the "Default" color.
     */
    static boolean isDefaultColor(String color) {
        return getHexString(color, 8).equals(MockVisibleComponent.COLOR_DEFAULT);
    }

    /**
     * Returns a new Color object for the given color value.
     */
    static Color getColor(String color) {
        String hex = getHexString(color, 8);
        int alpha = Integer.parseInt(hex.substring(0, 2), 16);
        int red = Integer.parseInt(hex.substring(2, 4), 16);
        int green = Integer.parseInt(hex.substring(4, 6), 16);
        int blue = Integer.parseInt(hex.substring(6, 8), 16);
        return new Color(red, green, blue, alpha / 255.0F);
    }

    /*
     * Converts a string containing a number to a string containing the requested amount of least\
     * significant digits of the equivalent hex number.
     */
    private static String getHexString(String color, int digits) {
        // When receiving the property values from the server hex numbers were converted to decimal
        // numbers
        color = color.startsWith("&H") ? color.substring(2) : Long.toHexString(Long.parseLong(color));
        int len = color.length();
        if (len < digits) {
            do {
                color = '0' + color;
            } while (++len < digits);

            return color;
        }

        return color.substring(len - digits);
    }

    /*
     * Retrieves the size style attributes of the given widgets and then clears
     * them.
     *
     * @param w the widget
     * @return the previous size style attributes as an array with width at index
     *         0 and height at index 1.
     */
    static String[] clearSizeStyle(Widget w) {
        Element element = w.getElement();
        String widthStyle = DOM.getStyleAttribute(element, "width");
        String heightStyle = DOM.getStyleAttribute(element, "height");
        if (widthStyle != null) {
            DOM.setStyleAttribute(element, "width", null);
        }
        if (heightStyle != null) {
            DOM.setStyleAttribute(element, "height", null);
        }
        return new String[] { widthStyle, heightStyle };
    }

    /*
     * Restores the given size style attributes for the widget.
     *
     * @param w the widget
     * @param style the size style attributes as an array with width at index 0
     *        and height at index 1.
     */
    static void restoreSizeStyle(Widget w, String[] style) {
        Element element = w.getElement();
        if (style[0] != null) {
            DOM.setStyleAttribute(element, "width", style[0]);
        }
        if (style[1] != null) {
            DOM.setStyleAttribute(element, "height", style[1]);
        }
    }

    /**
     * Returns the width of the given MockComponent after temporarily setting its
     * width and height styles to null.
     */
    static int getPreferredWidth(MockComponent component) {
        String[] style = clearSizeStyle(component);
        int width = component.getOffsetWidth() + 4;
        restoreSizeStyle(component, style);
        // We want the size without the MockComponent's CSS border.
        return width - MockComponent.BORDER_SIZE;
    }

    /**
     * Returns the height of the given MockComponent after temporarily setting its
     * width and height styles to null.
     */
    static int getPreferredHeight(MockComponent component) {
        String[] style = clearSizeStyle(component);
        int height = component.getOffsetHeight();
        restoreSizeStyle(component, style);
        // We want the size without the MockComponent's CSS border.
        return height - MockComponent.BORDER_SIZE;
    }

    /**
     * Returns the preferred size of the specified widget,
     * in an array of the form {@code [width, height]}.
     * <p>
     * It is assumed that:
     * <ul>
     *   <li>{@code w} has no parent</li>
     *   <li>{@code w} has not been configured to be invisible</li>
     * </ul>
     */
    public static int[] getPreferredSizeOfDetachedWidget(Widget w) {
        // Attach the widget to the DOM, so that its preferred size is calculated correctly
        RootPanel.get().add(w);

        String[] style = clearSizeStyle(w);
        int width = w.getOffsetWidth() + 4;
        int height = w.getOffsetHeight();
        restoreSizeStyle(w, style);

        // Detach the widget from the DOM before returning
        RootPanel.get().remove(w);

        return new int[] { width, height };
    }

    static void setEnabled(MockComponent component, String value) {
        // Can't disable GWT control because then you wouldn't be able to select it anymore because it
        // would not receive any browser events.
        if (Boolean.parseBoolean(value)) {
            component.removeStyleDependentName("disabled");
        } else {
            component.addStyleDependentName("disabled");
        }
    }
}