Package tripleplay.platform

Source Code of tripleplay.platform.IOSTextFieldHandler

//
// 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.platform;

import java.util.HashMap;
import java.util.Map;

import cli.MonoTouch.CoreGraphics.CGAffineTransform;
import cli.MonoTouch.Foundation.NSNotification;
import cli.MonoTouch.Foundation.NSNotificationCenter;
import cli.MonoTouch.Foundation.NSSet;
import cli.MonoTouch.Foundation.NSValue;
import cli.MonoTouch.UIKit.UIDevice;
import cli.MonoTouch.UIKit.UIDeviceOrientation;
import cli.MonoTouch.UIKit.UIEvent;
import cli.MonoTouch.UIKit.UIFont;
import cli.MonoTouch.UIKit.UIKeyboard;
import cli.MonoTouch.UIKit.UITextField;
import cli.MonoTouch.UIKit.UITextView;
import cli.MonoTouch.UIKit.UIView;
import cli.System.Drawing.PointF;
import cli.System.Drawing.RectangleF;
import cli.System.Drawing.SizeF;

import playn.core.Font;
import playn.core.PlayN;
import playn.ios.IOSFont;
import playn.ios.IOSPlatform;
import pythagoras.f.Point;

import static tripleplay.platform.Log.log;

/**
* Handles shared bits for native text fields.
*/
public class IOSTextFieldHandler
{
    public IOSTextFieldHandler (IOSTPPlatform platform) {
        _platform = platform;
        _overlay = platform.overlay();
        _touchDetector = new TouchDetector(_overlay.get_Bounds());

        cli.System.Action$$00601_$$$_Lcli__MonoTouch__Foundation__NSNotification_$$$$_
            // dispatches text changes
            change = new cli.System.Action$$00601_$$$_Lcli__MonoTouch__Foundation__NSNotification_$$$$_(new cli.System.Action$$00601_$$$_Lcli__MonoTouch__Foundation__NSNotification_$$$$_.Method() {
                @Override public void Invoke (NSNotification nf) {
                    // we get notifications about all text fields, whether they're under our
                    // control or not
                    IOSNativeTextField field = _activeFields.get(nf.get_Object());
                    if (field != null) field.handleNewValue();
                }}),
            // dispatches text starting edit
            didBegin = new cli.System.Action$$00601_$$$_Lcli__MonoTouch__Foundation__NSNotification_$$$$_(new cli.System.Action$$00601_$$$_Lcli__MonoTouch__Foundation__NSNotification_$$$$_.Method() {
                @Override public void Invoke (NSNotification nf) {
                    IOSNativeTextField field = _activeFields.get(nf.get_Object());
                    if (field != null) field.didStart();
                }}),
            // dispatches text end notifications
            didEnd = new cli.System.Action$$00601_$$$_Lcli__MonoTouch__Foundation__NSNotification_$$$$_(new cli.System.Action$$00601_$$$_Lcli__MonoTouch__Foundation__NSNotification_$$$$_.Method() {
                @Override public void Invoke (NSNotification nf) {
                    IOSNativeTextField field = _activeFields.get(nf.get_Object());
                    if (field != null) field.didFinish();
                }});

        NSNotificationCenter center = NSNotificationCenter.get_DefaultCenter();

        // observe UITextField
        center.AddObserver(UITextField.get_TextDidBeginEditingNotification(), didBegin);
        center.AddObserver(UITextField.get_TextFieldTextDidChangeNotification(), change);
        center.AddObserver(UITextField.get_TextDidEndEditingNotification(), didEnd);

        // observe UITextView
        center.AddObserver(UITextView.get_TextDidBeginEditingNotification(), didBegin);
        center.AddObserver(UITextView.get_TextDidChangeNotification(), change);
        center.AddObserver(UITextView.get_TextDidEndEditingNotification(), didEnd);

        // slide the game view up when the keyboard is displayed
        center.AddObserver(UIKeyboard.get_DidShowNotification(),
            new cli.System.Action$$00601_$$$_Lcli__MonoTouch__Foundation__NSNotification_$$$$_(new cli.System.Action$$00601_$$$_Lcli__MonoTouch__Foundation__NSNotification_$$$$_.Method() {
                @Override public void Invoke (NSNotification nf) {
                    if (_gameViewTransformed) {
                        // already transformed, bail with a warning
                        log.warning("Keyboard shown when already showing?", "viewTransform",
                                    _gameViewTransform);
                        return;
                    }

                    // find the first responder
                    IOSNativeTextField firstResponder = findFirstResponder();
                    if (firstResponder == null) return; // it's not a field we're managing, bail

                    // figure out how we need to transform the game view
                    SizeF size = ((NSValue) nf.get_UserInfo().get_Item(
                        UIKeyboard.get_FrameBeginUserInfoKey())).get_RectangleFValue().get_Size();
                    RectangleF fieldFrame = firstResponder.getView().get_Frame();
                    // oddly, the size given for keyboard dimensions is portrait, always.
                    float targetOffset = -size.get_Width() +
                        _overlay.get_Bounds().get_Height() - fieldFrame.get_Bottom();
                    // give it a little padding, and make sure we never move the game view down,
                    // also make sure we never move the bottom of the game view past the top of the
                    // keyboard
                    targetOffset = Math.max(Math.min(targetOffset - 10, 0), -size.get_Width());
                    PointF target = new PointF(0, targetOffset);
                    target = _overlay.get_Transform().TransformPoint(target);

                    // update and set the transform on the game view
                    UIView gameView = _overlay.get_Superview();
                    CGAffineTransform trans = gameView.get_Transform();
                    _gameViewTransform = trans.Invert().Invert(); // clone
                    trans.Translate(target.get_X(), target.get_Y());
                    gameView.set_Transform(trans);
                    _gameViewTransformed = true;

                    // touches outside of the keyboard will close the keyboard
                    _overlay.Add(_touchDetector);
                }}));

        center.AddObserver(UIKeyboard.get_WillHideNotification(),
            new cli.System.Action$$00601_$$$_Lcli__MonoTouch__Foundation__NSNotification_$$$$_(new cli.System.Action$$00601_$$$_Lcli__MonoTouch__Foundation__NSNotification_$$$$_.Method() {
                @Override public void Invoke (NSNotification nf) {
                    // bail if not transformed; this might be ok, if the keyboard was shown outside
                    // of our purview
                    if (!_gameViewTransformed) return;

                    UIView gameView = _overlay.get_Superview();
                    gameView.set_Transform(_gameViewTransform);
                    _gameViewTransform = null;
                    _gameViewTransformed = false;
                    _touchDetector.RemoveFromSuperview();
                    _platform._focus.update(null);
                }}));

        _currentOrientation = UIDevice.get_CurrentDevice().get_Orientation().Value;
        center.AddObserver(UIDevice.get_OrientationDidChangeNotification(),
            new cli.System.Action$$00601_$$$_Lcli__MonoTouch__Foundation__NSNotification_$$$$_(new cli.System.Action$$00601_$$$_Lcli__MonoTouch__Foundation__NSNotification_$$$$_.Method() {
                @Override
                public void Invoke (NSNotification nf) {
                    UIDeviceOrientation orient = UIDevice.get_CurrentDevice().get_Orientation();
                    if (orient.Value == _currentOrientation) return; // NOOP
                    if (!((IOSPlatform)PlayN.platform()).supportedOrients().isSupported(orient)) {
                        return; // unsupported orientation, no rotation
                    }
                    _currentOrientation = orient.Value;

                    if (_gameViewTransformed) {
                        // the game rotated, and we've transformed it, kill the keyboard so it can
                        // get back to normal
                        IOSNativeTextField firstResponder = findFirstResponder();
                        if (firstResponder != null) firstResponder.getView().ResignFirstResponder();
                    }
                }}));
    }

    public UIFont getUIFont (Font font) {
        if (font == null) font = IOSFont.defaultFont();

        String iosName = ((IOSFont)font).iosName();
        UIFont uiFont = UIFont.FromName(iosName, font.size());
        if (uiFont != null) return uiFont;

        if (iosName.equals(IOSFont.defaultFont().iosName())) {
            log.warning("Font shenanigans, default font not found!", "font", font);
            return null;
        }

        // font not found, use the default font at the given size, and style
        return getUIFont(new IOSFont(null, IOSFont.defaultFont().iosName(),
                                     font.style(), font.size()));
    }

    public void activate (IOSNativeTextField field) {
        _activeFields.put(field.getView(), field);
        _overlay.Add(field.getView());
    }

    public void deactivate (IOSNativeTextField field) {
        _activeFields.remove(field.getView());
        field.getView().RemoveFromSuperview();
    }

    public void refreshNativeBounds () {
        for (IOSNativeTextField field : _activeFields.values()) {
            field._element.refreshBounds();
        }
    }

    protected IOSNativeTextField findFirstResponder () {
        for (Map.Entry<UIView, IOSNativeTextField> entry : _activeFields.entrySet()) {
            if (entry.getKey().get_IsFirstResponder()) return entry.getValue();
        }
        return null;
    }

    protected class TouchDetector extends UIView {
        public TouchDetector (RectangleF bounds) {
            super(bounds);
        }

        @Override public void TouchesBegan (NSSet touches, UIEvent uiEvent) {
            IOSNativeTextField firstResponder = findFirstResponder();
            if (firstResponder != null) firstResponder.getView().ResignFirstResponder();
            // call super, otherwise the TouchesEnded event for this touch are never dispatched
            super.TouchesBegan(touches, uiEvent);
        }

        @Override public boolean PointInside (PointF pointF, UIEvent uiEvent) {
            // let through any touch that the virtual keyboard controller wants to allow.
            if (!hideVirtualKeyboardAt(pointF)) return false;
            // allow through touches that hit text fields we manage
            for (IOSNativeTextField field : _activeFields.values()) {
                if (field.getView().PointInside(
                        ConvertPointToView(pointF, field.getView()), uiEvent)) {
                    return false;
                }
            }
            // else absorb the hit at this point so that we can hide the keyboard in TouchesBegan
            return true;
        }

        protected boolean hideVirtualKeyboardAt (PointF pointF) {
            PointF overlay = ConvertPointToView(pointF, _overlay);
            Point pythagOverlay = new Point(overlay.get_X(), overlay.get_Y());
            return _platform._kfc == null ||
                    _platform._kfc.unfocusForLocation(pythagOverlay);
        }
    }

    protected IOSTPPlatform _platform;
    protected final UIView _overlay;
    protected final Map<UIView, IOSNativeTextField> _activeFields =
        new HashMap<UIView, IOSNativeTextField>();

    // we specifically track whether we've transformed the game view in a boolean because
    // CGAffineTransform is a value class and cannot be null
    protected boolean _gameViewTransformed;
    protected CGAffineTransform _gameViewTransform;
    protected int _currentOrientation;

    protected TouchDetector _touchDetector;
}
TOP

Related Classes of tripleplay.platform.IOSTextFieldHandler

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.
ww.google-analytics.com/analytics.js','ga'); ga('create', 'UA-20639858-1', 'auto'); ga('send', 'pageview');