Package tripleplay.ui

Source Code of tripleplay.ui.Field$MaxLength

//
// Triple Play - utilities for use in PlayN-based games
// Copyright (c) 2011-2014, Three Rings Design, Inc. - All rights reserved.
// http://github.com/threerings/tripleplay/blob/master/LICENSE

package tripleplay.ui;

import playn.core.Keyboard;
import playn.core.Keyboard.TextType;
import playn.core.Layer;
import playn.core.PlayN;
import playn.core.Pointer;
import playn.core.util.Callback;

import pythagoras.f.Point;
import pythagoras.f.Rectangle;

import react.Signal;
import react.SignalView;
import react.Slot;
import react.Value;

import tripleplay.platform.NativeTextField;
import tripleplay.platform.TPPlatform;
import tripleplay.ui.util.Insets;

/**
* Displays text which can be edited via the {@link Keyboard#getText} popup.
*/
public class Field extends TextWidget<Field>
{
    /** Creates a style binding for the given maximum length. */
    public static Style.Binding<Validator> maxLength (int max) {
        return VALIDATOR.is(new MaxLength(max));
    }

    /** Checks if the platform has native text fields. */
    public static boolean hasNative () {
        return TPPlatform.instance().hasNativeTextFields();
    }

    /** Exposes protected field information required for native fields. */
    public final class Native {
        /** Resolves the given style for the field. */
        public <T> T resolveStyle (Style<T> style) {
            return Field.this.resolveStyle(style);
        }

        /** Tests if the proposed text is valid. */
        public boolean isValid (String text) {
            return Field.this.textIsValid(text);
        }

        /** Transforms the given text. */
        public String transform (String text) {
            return Field.this.transformText(text);
        }

        /** A signal that is dispatched when the native text field has lost focus. Value is false if
         * editing was canceled */
        public Signal<Boolean> finishedEditing () {
            return _finishedEditing;
        }

        /** Refreshes the bounds of this field's native field. Used as a platform callback to
         * support some degree of animation for UI containing native fields. */
        public void refreshBounds () {
            updateNativeFieldBounds();
        }

        public Field field () {
            return Field.this;
        }
    }

    /** For native text fields, decides whether to block a keypress based on the proposed content
     * of the field. */
    public interface Validator {
        /** Return false if the keypress causing this text should be blocked. */
        boolean isValid (String text);
    }

    /** For native text fields, transforms text during typing. */
    public interface Transformer {
        /** Transform the specified text. */
        public String transform (String text);
    }

    /** Blocks keypresses for a native text field when the length is at a given maximum. */
    public static class MaxLength implements Validator {
        /** The maximum length accepted. */
        public final int max;
        public MaxLength (int max) {
            this.max = max;
        }
        @Override public boolean isValid (String text) {
            return text.length() <= max;
        }
    }

    /** If on a platform that utilizes native fields and this is true, the native field is
     * displayed whenever this Field is visible, and the native field is responsible for all text
     * rendering. If false, the native field is only displayed while actively editing (after a user
     * click). */
    public static final Style.Flag FULLTIME_NATIVE_FIELD = Style.newFlag(false, true);

    /** Controls the behavior of native text fields with respect to auto-capitalization on
     * platforms that support it. */
    // TODO: iOS supports multiple styles of autocap, support them here?
    public static final Style.Flag AUTOCAPITALIZATION = Style.newFlag(false, true);

    /** Controls the behavior of native text fields with respect to auto-correction on platforms
     * that support it. */
    public static final Style.Flag AUTOCORRECTION = Style.newFlag(false, true);

    /** Controls secure text entry on native text fields: typically this will mean dots or asterix
     * displayed instead of the typed character. */
    public static final Style.Flag SECURE_TEXT_ENTRY = Style.newFlag(false, false);

    /** Sets the Keyboard.TextType in use by this Field. */
    public static final Style<TextType> TEXT_TYPE = Style.newStyle(false, TextType.DEFAULT);

    /** Sets the validator to use when censoring keypresses into native text fields.
     * @see MaxLength */
    public static final Style<Validator> VALIDATOR = Style.newStyle(true, null);

    /** Sets the transformner to use when updating native text fields while being typed into. */
    public static final Style<Transformer> TRANSFORMER = Style.newStyle(true, null);

    /** Sets the label used on the "return" key of the virtual keyboard on native keyboards. Be
     * aware that some platforms (such as iOS) have a limited number of options. The underlying
     * native implementation is responsible for attempting to match this style, but may be unable
     * to do so. Defaults to null (uses platform default). */
    public static final Style<String> RETURN_KEY_LABEL = Style.newStyle(false, null);

    /** Sets the field to allow the return key to insert a line break in the text. */
    public static final Style.Flag MULTILINE = Style.newFlag(false, false);

    /** The text displayed by this widget. */
    public final Value<String> text;

    public Field () {
        this("");
    }

    public Field (String initialText) {
        this(initialText, Styles.none());
    }

    public Field (Styles styles) {
        this("", styles);
    }

    public Field (String initialText, Styles styles) {
        setStyles(styles);

        text = Value.create("");
        _finishedEditing = Signal.create();

        if (hasNative()) {
            _finishedEditing.connect(new Slot<Boolean>() {
                @Override public void onEmit (Boolean event) {
                    if (!_fullTimeNative) updateMode(false);
                }
            });
        }

        this.text.update(initialText);
        this.text.connect(textDidChange());
    }

    /** Returns a signal that is dispatched when text editing is complete. */
    public SignalView<Boolean> finishedEditing () {
        return _finishedEditing;
    }

    /**
     * Configures the label to be displayed when text is requested via a popup.
     */
    public Field setPopupLabel (String label) {
        _popupLabel = label;
        return this;
    }

    /**
     * Forcibly notify the NativeTextField backing this field that its screen position has changed.
     *
     * @return this for call chaining.
     */
    public Field updateNativeFieldBounds () {
        if (_nativeField != null) _nativeField.setBounds(getNativeFieldBounds());
        return this;
    }

    /** Attempt to focus on this field, if it is backed by a native field. If the platform
     * uses a virtual keyboard, this will cause it slide up, just as though the use had tapped
     * the field. For hardware keyboard, a blinking caret will appear in the field. */
    public void focus () {
        if (_nativeField != null) _nativeField.focus();
    }

    @Override public Field setVisible (boolean visible) {
        if (_nativeField != null) {
            if (visible) {
                _nativeField.add();
            } else {
                _nativeField.remove();
            }
        }
        return super.setVisible(visible);
    }

    /** Returns this field's native text field, if it has one, otherwise null. */
    public NativeTextField exposeNativeField () {
        return _nativeField;
    }

    /**
     * Main entry point for deciding whether to reject keypresses on a native field. By default,
     * consults the current validator instance, set up by {@link #VALIDATOR}.
     */
    protected boolean textIsValid (String text) {
        return _validator == null || _validator.isValid(text);
    }

    /**
     * Called when the native field's value is changed. Override and return a modified value to
     * perform text transformation while the user is editing the field. By default, consults
     * the current transformer instance, set up by {@link #TRANSFORMER}.
     */
    protected String transformText (String text) {
        return _transformer == null ? text : _transformer.transform(text);
    }

    @Override protected Class<?> getStyleClass () {
        return Field.class;
    }

    @Override protected String text () {
        String ctext = text.get();
        // we always want non-empty text so that we force ourselves to always have a text layer and
        // sane dimensions even if the text field contains no text
        return (ctext == null || ctext.length() == 0) ? " " : ctext;
    }

    @Override protected Icon icon () {
        return null; // fields never have an icon
    }

    @Override protected void wasRemoved () {
        super.wasRemoved();
        // make sure the field is gone
        updateMode(false);
    }

    @Override protected Behavior<Field> createBehavior () {
        return new Behavior.Select<Field>(this) {
            @Override public void onClick (Pointer.Event event) {
                if (!_fullTimeNative) startEdit();
            }
        };
    }

    @Override protected LayoutData createLayoutData (float hintX, float hintY) {
        return new FieldLayoutData(hintX, hintY);
    }

    protected void startEdit () {
        if (hasNative()) {
            updateMode(true);
            _nativeField.focus();

        } else {
            // TODO: multi-line keyboard.getText
            PlayN.keyboard().getText(_textType, _popupLabel, text.get(), new Callback<String>() {
                @Override public void onSuccess (String result) {
                    // null result is a canceled entry dialog
                    if (result != null) text.update(result);
                    _finishedEditing.emit(result != null);
                }
                @Override public void onFailure (Throwable cause) { /* noop */ }
            });
        }
    }

    protected Rectangle getNativeFieldBounds () {
        Insets insets = resolveStyle(Style.BACKGROUND).insets;
        Point screenCoords = Layer.Util.layerToScreen(layer, insets.left(), insets.top());
        return new Rectangle(screenCoords.x, screenCoords.y,
                             _size.width - insets.width(), _size.height - insets.height());
    }

    protected void updateMode (boolean nativeField) {
        if (!hasNative()) return;
        if (nativeField) {
            _nativeField = _nativeField == null ?
                TPPlatform.instance().createNativeTextField(new Native()) :
                TPPlatform.instance().refresh(_nativeField);

            _nativeField.setEnabled(isEnabled());
            updateNativeFieldBounds();
            _nativeField.add();
            setGlyphLayerVisible(false);
        } else if (_nativeField != null) {
            _nativeField.remove();
            setGlyphLayerVisible(true);
        }
    }

    protected void setGlyphLayerVisible (boolean visible) {
        if (_tglyph.layer() != null) _tglyph.layer().setVisible(visible);
    }

    protected class FieldLayoutData extends TextLayoutData {
        public FieldLayoutData (float hintX, float hintY) {
            super(hintX, hintY);
        }

        @Override public void layout (float left, float top, float width, float height) {
            super.layout(left, top, width, height);
            _fullTimeNative = hasNative() && resolveStyle(FULLTIME_NATIVE_FIELD);
            if (_fullTimeNative || _nativeField != null) updateMode(true);

            // make sure our cached bits are up to date
            _validator = resolveStyle(VALIDATOR);
            _transformer = resolveStyle(TRANSFORMER);
            _textType = resolveStyle(TEXT_TYPE);
        }
    }

    protected NativeTextField _nativeField;
    protected Validator _validator;
    protected Transformer _transformer;
    protected TextType _textType;
    protected boolean _fullTimeNative;
    protected final Signal<Boolean> _finishedEditing;

    // used when popping up a text entry interface on mobile platforms
    protected String _popupLabel;
}
TOP

Related Classes of tripleplay.ui.Field$MaxLength

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.