// -*- 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.components.runtime; import com.google.appinventor.components.annotations.DesignerProperty; import com.google.appinventor.components.annotations.PropertyCategory; import com.google.appinventor.components.annotations.SimpleEvent; import com.google.appinventor.components.annotations.SimpleFunction; import com.google.appinventor.components.annotations.SimpleObject; import com.google.appinventor.components.annotations.SimpleProperty; import com.google.appinventor.components.common.ComponentConstants; import com.google.appinventor.components.common.PropertyTypeConstants; import com.google.appinventor.components.runtime.util.EclairUtil; import com.google.appinventor.components.runtime.util.TextViewUtil; import com.google.appinventor.components.runtime.util.ViewUtil; //import com.google.appinventor.components.runtime.parameters.BooleanReferenceParameter; import android.graphics.drawable.Drawable; import android.os.Build; import android.view.View; import android.view.View.OnFocusChangeListener; import android.widget.EditText; /** * Underlying base class for TextBox, not directly accessible to Simple * programmers. * * @author sharon@google.com (Sharon Perl) */ @SimpleObject public abstract class TextBoxBase extends AndroidViewComponent implements OnFocusChangeListener { protected final EditText view; // Backing for text alignment private int textAlignment; // Backing for background color private int backgroundColor; // Backing for font typeface private int fontTypeface; // Backing for font bold private boolean bold; // Backing for font italic private boolean italic; // Backing for hint text private String hint; // Backing for text color private int textColor; // This is our handle on Android's nice 3-d default textbox. private Drawable defaultTextBoxDrawable; /** * Creates a new TextBoxBase component * * @param container container that the component will be placed in * @param textview the underlying EditText object that maintains the text */ public TextBoxBase(ComponentContainer container, EditText textview) { super(container); view = textview; // There appears to be an issue where, by default, Android 7+ // wants to provide suggestions in text boxes. However, we do not // compile the necessary layouts for this to work correctly, which // results in an application crash. This disables that feature // until we include newer Android layouts. if (Build.VERSION.SDK_INT >= 24 /* Nougat */ ) { EclairUtil.disableSuggestions(textview); } // Listen to focus changes view.setOnFocusChangeListener(this); defaultTextBoxDrawable = view.getBackground(); // Add a transformation method to provide input validation /* TODO(user): see comment above) setTransformationMethod(new ValidationTransformationMethod()); */ // Adds the component to its designated container container.$add(this); container.setChildWidth(this, ComponentConstants.TEXTBOX_PREFERRED_WIDTH); TextAlignment(Component.ALIGNMENT_NORMAL); // Leave the nice default background color. Users can change it to "none" if they like // // TODO(user): if we make a change here we also need to change the default property value. // Eventually I hope to simplify this so it has to be changed in one location // only). Maybe we need another color value which would be 'SYSTEM_DEFAULT' which // will not attempt to explicitly initialize with any of the properties with any // particular value. // BackgroundColor(Component.COLOR_NONE); Enabled(true); fontTypeface = Component.TYPEFACE_DEFAULT; TextViewUtil.setFontTypeface(view, fontTypeface, bold, italic); FontSize(Component.FONT_DEFAULT_SIZE); Hint(""); Text(""); TextColor(Component.COLOR_BLACK); } @Override public View getView() { return view; } /** * Event raised when this component is selected for input, such as by * the user touching it. */ @SimpleEvent public void GotFocus() { EventDispatcher.dispatchEvent(this, "GotFocus"); } /** * Event raised when this component is no longer selected for input, such * as if the user touches a different text box. */ @SimpleEvent public void LostFocus() { EventDispatcher.dispatchEvent(this, "LostFocus"); } /** * Default Validate event handler. */ /* TODO(markf): Restore event if needed. @SimpleEvent public void Validate(String text, BooleanReferenceParameter accept) { EventDispatcher.dispatchEvent(this, "Validate", text, accept); } */ /** * Returns the alignment of the textbox's text: center, normal * (e.g., left-justified if text is written left to right), or * opposite (e.g., right-justified if text is written left to right). * * @return one of {@link Component#ALIGNMENT_NORMAL}, * {@link Component#ALIGNMENT_CENTER} or * {@link Component#ALIGNMENT_OPPOSITE} */ @SimpleProperty( category = PropertyCategory.APPEARANCE, description = "Whether the text should be left justified, centered, " + "or right justified. By default, text is left justified.", userVisible = false) public int TextAlignment() { return textAlignment; } /** * Specifies the alignment of the textbox's text: center, normal * (e.g., left-justified if text is written left to right), or * opposite (e.g., right-justified if text is written left to right). * * @param alignment one of {@link Component#ALIGNMENT_NORMAL}, * {@link Component#ALIGNMENT_CENTER} or * {@link Component#ALIGNMENT_OPPOSITE} */ @DesignerProperty(editorType = PropertyTypeConstants.PROPERTY_TYPE_TEXTALIGNMENT, defaultValue = Component.ALIGNMENT_NORMAL + "") @SimpleProperty( userVisible = false) public void TextAlignment(int alignment) { this.textAlignment = alignment; TextViewUtil.setAlignment(view, alignment, false); } /** * Returns the textbox's background color as an alpha-red-green-blue * integer. * * @return background RGB color with alpha */ @SimpleProperty( category = PropertyCategory.APPEARANCE, description = "The background color of the input box. You can choose " + "a color by name in the Designer or in the Blocks Editor. The " + "default background color is 'default' (shaded 3-D look).") public int BackgroundColor() { return backgroundColor; } /** * Specifies the textbox's background color as an alpha-red-green-blue * integer. * * @param argb background RGB color with alpha */ @DesignerProperty(editorType = PropertyTypeConstants.PROPERTY_TYPE_COLOR, defaultValue = Component.DEFAULT_VALUE_COLOR_DEFAULT) @SimpleProperty public void BackgroundColor(int argb) { backgroundColor = argb; if (argb != Component.COLOR_DEFAULT) { TextViewUtil.setBackgroundColor(view, argb); } else { ViewUtil.setBackgroundDrawable(view, defaultTextBoxDrawable); } } /** * Returns true if the textbox is active and useable. * * @return {@code true} indicates enabled, {@code false} disabled */ @SimpleProperty( category = PropertyCategory.BEHAVIOR, description = "Whether the user can enter text into this input box. " + "By default, this is true.") public boolean Enabled() { return TextViewUtil.isEnabled(view); } /** * Specifies whether the textbox should be active and useable. * * @param enabled {@code true} for enabled, {@code false} disabled */ @DesignerProperty(editorType = PropertyTypeConstants.PROPERTY_TYPE_BOOLEAN, defaultValue = "True") @SimpleProperty public void Enabled(boolean enabled) { TextViewUtil.setEnabled(view, enabled); } /** * Returns true if the textbox's text should be bold. * If bold has been requested, this property will return true, even if the * font does not support bold. * * @return {@code true} indicates bold, {@code false} normal */ @SimpleProperty( category = PropertyCategory.APPEARANCE, userVisible = false, description = "Whether the font for the text should be bold. By " + "default, it is not.") public boolean FontBold() { return bold; } /** * Specifies whether the textbox's text should be bold. * Some fonts do not support bold. * * @param bold {@code true} indicates bold, {@code false} normal */ @DesignerProperty(editorType = PropertyTypeConstants.PROPERTY_TYPE_BOOLEAN, defaultValue = "False") @SimpleProperty( userVisible = false) public void FontBold(boolean bold) { this.bold = bold; TextViewUtil.setFontTypeface(view, fontTypeface, bold, italic); } /** * Returns true if the textbox's text should be italic. * If italic has been requested, this property will return true, even if the * font does not support italic. * * @return {@code true} indicates italic, {@code false} normal */ @SimpleProperty( category = PropertyCategory.APPEARANCE, description = "Whether the text should appear in italics. By " + "default, it does not.", userVisible = false) public boolean FontItalic() { return italic; } /** * Specifies whether the textbox's text should be italic. * Some fonts do not support italic. * * @param italic {@code true} indicates italic, {@code false} normal */ @DesignerProperty(editorType = PropertyTypeConstants.PROPERTY_TYPE_BOOLEAN, defaultValue = "False") @SimpleProperty(userVisible = false) public void FontItalic(boolean italic) { this.italic = italic; TextViewUtil.setFontTypeface(view, fontTypeface, bold, italic); } /** * Returns the textbox's text's font size, measured in sp(scale-independent pixels). * * @return font size in sp(scale-independent pixels). */ @SimpleProperty( category = PropertyCategory.APPEARANCE, description = "The font size for the text. By default, it is " + Component.FONT_DEFAULT_SIZE + " points.") public float FontSize() { return TextViewUtil.getFontSize(view, container.$context()); } /** * Specifies the textbox's text's font size, measured in sp(scale-independent pixels). * * @param size font size in pixel */ @DesignerProperty(editorType = PropertyTypeConstants.PROPERTY_TYPE_NON_NEGATIVE_FLOAT, defaultValue = Component.FONT_DEFAULT_SIZE + "") @SimpleProperty public void FontSize(float size) { TextViewUtil.setFontSize(view, size); } /** * Returns the textbox's text's font face as default, serif, sans * serif, or monospace. * * @return one of {@link Component#TYPEFACE_DEFAULT}, * {@link Component#TYPEFACE_SERIF}, * {@link Component#TYPEFACE_SANSSERIF} or * {@link Component#TYPEFACE_MONOSPACE} */ @SimpleProperty( category = PropertyCategory.APPEARANCE, description = "The font for the text. The value can be changed in " + "the Designer.", userVisible = false) public int FontTypeface() { return fontTypeface; } /** * Specifies the textbox's text's font face as default, serif, sans * serif, or monospace. * * @param typeface one of {@link Component#TYPEFACE_DEFAULT}, * {@link Component#TYPEFACE_SERIF}, * {@link Component#TYPEFACE_SANSSERIF} or * {@link Component#TYPEFACE_MONOSPACE} */ @DesignerProperty(editorType = PropertyTypeConstants.PROPERTY_TYPE_TYPEFACE, defaultValue = Component.TYPEFACE_DEFAULT + "") @SimpleProperty( userVisible = false) public void FontTypeface(int typeface) { fontTypeface = typeface; TextViewUtil.setFontTypeface(view, fontTypeface, bold, italic); } /** * Hint property getter method. * * @return hint text */ @SimpleProperty( category = PropertyCategory.APPEARANCE, description = "Text that should appear faintly in the input box to " + "provide a hint as to what the user should enter. This can only be " + "seen if the <code>Text</code> property is empty.") public String Hint() { return hint; } /** * Hint property setter method. * * @param hint hint text */ @DesignerProperty(editorType = PropertyTypeConstants.PROPERTY_TYPE_STRING, defaultValue = "") @SimpleProperty public void Hint(String hint) { this.hint = hint; view.setHint(hint); view.invalidate(); } /** * Returns the textbox contents. * * @return text box contents */ @SimpleProperty(category = PropertyCategory.BEHAVIOR) public String Text() { return TextViewUtil.getText(view); } /** * Specifies the textbox contents. * * @param text new text in text box */ @DesignerProperty(editorType = PropertyTypeConstants.PROPERTY_TYPE_STRING, defaultValue = "") @SimpleProperty( // This kind of breaks the appearance/behavior dichotomy category = PropertyCategory.BEHAVIOR, description = "The text in the input box, which can be set by the " + "programmer in the Designer or Blocks Editor, or it can be entered by " + "the user (unless the <code>Enabled</code> property is false).") public void Text(String text) { TextViewUtil.setText(view, text); } /** * Returns the textbox's text color as an alpha-red-green-blue * integer. * * @return text RGB color with alpha */ @SimpleProperty( category = PropertyCategory.APPEARANCE, description = "The color for the text. You can choose a color by name " + "in the Designer or in the Blocks Editor. The default text color is " + "black.") public int TextColor() { return textColor; } /** * Specifies the textbox's text color as an alpha-red-green-blue * integer. * * @param argb text RGB color with alpha */ @DesignerProperty(editorType = PropertyTypeConstants.PROPERTY_TYPE_COLOR, defaultValue = Component.DEFAULT_VALUE_COLOR_BLACK) @SimpleProperty public void TextColor(int argb) { textColor = argb; if (argb != Component.COLOR_DEFAULT) { TextViewUtil.setTextColor(view, argb); } else { TextViewUtil.setTextColor(view, Component.COLOR_BLACK); } } /** * Request focus to current textbox. */ @SimpleFunction( description = "Sets the textbox active.") public void RequestFocus() { view.requestFocus(); } // OnFocusChangeListener implementation @Override public void onFocusChange(View previouslyFocused, boolean gainFocus) { if (gainFocus) { // Initialize content backing for input validation // TODO(sharon): this field stayed in TextBox. It isn't being used yet, // and I'm not sure what to do with this assignment. // text = TextViewUtil.getText(view); GotFocus(); } else { LostFocus(); } } }