// -*- 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"); } } }