Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

StageTextTextEditor: input text goes invisible on Android when focused #1505

Open
timoisalive opened this issue Dec 2, 2016 · 15 comments
Open

Comments

@timoisalive
Copy link

timoisalive commented Dec 2, 2016

EDIT by @joshtynjala: This does not have anything to do with maintainTouchFocus. See comment below for code to reproduce.


Original post:

I feel bad ranting about these TextInput issues knowing how difficult it is to work with StageText in AIR...

However, I discovered that the maintainFocus behavior on Android (Nexus 5 Android 6.0.1) does not seem to be right.

Focusing on the input works. Unfocusing the input works also as expected (pressing outside the red button does not lose the focus whereas pressing the button does clear the focus).

But when re-focusing on the input field, the input text disappears, caret is not shown, and the soft keyboard does not come up. Seeing what's happening in the StageTextEditor, the focusIn and softKeyboardActivate events are dispatched by the StageText instance, so something's apparently going wrong in the StageTextTextEditor.

My app descriptor defines softKeyboardBehavior = none as I'm handling that by myself.

I have defined fullScreen = false because if it's enabled, the red button does not clear the focus at all, which is another issue...

Here's the example project.

package {

	import flash.display.Sprite;
	import flash.display.Stage;
	import flash.display.StageScaleMode;
	import flash.events.Event;
	import flash.geom.Rectangle;
	
	import starling.core.Starling;
	import starling.events.Event;
	
	[SWF(width="0", height="0", backgroundColor="#000000")]
	public class Main extends Sprite {
	
		public static var stage:Stage;
		
		public function Main() {
			Main.stage = this.stage;
			stage.scaleMode = StageScaleMode.NO_SCALE;
			stage.addEventListener(flash.events.Event.RESIZE, handleInitStageResize);
		}
		
		private function handleInitStageResize(event:flash.events.Event):void {
			if(stage.stageHeight == 0 || stage.stageWidth == 0) return;
			stage.removeEventListener(flash.events.Event.RESIZE, handleInitStageResize);
			
			new Starling(TextInputTest, stage);
			Starling.current.addEventListener(starling.events.Event.ROOT_CREATED, onStarlingRootCreated);
		}
		
		private function onStarlingRootCreated(event:starling.events.Event):void {
			Starling.current.removeEventListener(starling.events.Event.ROOT_CREATED, onStarlingRootCreated);
			Starling.current.start();
			
			Starling.current.viewPort = new Rectangle(0, 0, stage.stageWidth, stage.stageHeight);
			Starling.current.stage.stageWidth = stage.stageWidth;
			Starling.current.stage.stageHeight = stage.stageHeight;
			
			var starlingView:TextInputTest = Starling.current.root as TextInputTest;
			starlingView.initialize();
		}
	
	}
	
}

import feathers.controls.Button;
import feathers.controls.TextInput;
import feathers.core.FeathersControl;
import feathers.core.ITextEditor;

import starling.display.Quad;
import starling.display.Sprite;
import starling.events.Event;

internal class TextInputTest extends Sprite {
	
	public function TextInputTest() {}
	
	public function initialize():void {
		FeathersControl.defaultTextEditorFactory = function():ITextEditor {
			var editorMobile:StageTextTextEditor = new StageTextTextEditor();
			editorMobile.styleProvider = null;
			editorMobile.fontFamily = "Verdana";
			editorMobile.fontSize = 50;
			editorMobile.color = 0;
			editorMobile.maintainTouchFocus = true;
			return editorMobile;
		};
		
		var quad:Quad = new Quad(stage.stageWidth, stage.stageHeight, 0xffffff);
		addChild(quad);
		
		var button:Button = new Button();
		button.addEventListener(Event.TRIGGERED, onButtonTriggered);
		button.defaultSkin = new Quad(100, 100, 0xff0000);
		button.x = 100;
		button.y = 400;
		addChild(button);
		
		var textInput:TextInput = new TextInput();
		textInput.text = "Input text";
		textInput.width = Main.stage.stageWidth;
		textInput.height = 100;
		addChild(textInput);
		
		function onButtonTriggered(event:Event):void {
			textInput.clearFocus();
		}
	}
	
}
@joshtynjala
Copy link
Member

The way I intended maintainTouchFocus to work is that you should set it to false before you clear focus, even clearing focus programmatically. Give that a try, and let me know how it works.

@timoisalive
Copy link
Author

You mean something like this?

		function onButtonTriggered(event:Event):void {
			textInput.textEditorProperties.maintainTouchFocus = false;
			textInput.clearFocus();
		}

Didn't help, unfortunately. The mouseFocusChange doesn't get dispatched when the focus is cleared programmatically or when input is again focused in, so this setting shouldn't matter, I believe...

@timoisalive
Copy link
Author

I'm returning to this issue with reproduceable (on Nexus 5 (6.0.1) and an old Galaxy Tab (4.4.2), but NOT on Nexus S (4.1.2)) code.

It seems like the issue doesn't actually have anything to do with maintainTouchFocus, and as said, on Nexus S I experience no issues...

I'm using AIR 23 but AIR 19 seemed to behave alike.

Please let me know if you can reproduce the issue, thanks!

Steps to repro:

  • focus on textInput2
  • type something
  • press Next on the soft keyboard

What should happen:

  • textInput1 should focus and be visible

What actually happens:

  • textInput1 focuses but goes invisible
  • soft keyboard is visible and you can type, but nothing is seen until you focus out of textInput1

Here's the code, Main class is the same as before:

import feathers.controls.Button;
import feathers.controls.TextInput;
import feathers.controls.text.StageTextTextEditor;
import feathers.core.FeathersControl;
import feathers.core.ITextEditor;
import feathers.events.FeathersEventType;

import starling.display.Quad;
import starling.display.Sprite;
import starling.events.Event;

internal class TextInputTest extends Sprite {
	
	public function TextInputTest() {}
	
	public function initialize():void {
		FeathersControl.defaultTextEditorFactory = function():ITextEditor {
			var editorMobile:StageTextTextEditor = new StageTextTextEditor();
			editorMobile.fontSize = 50;
			return editorMobile;
		};
		
		var quad:Quad = new Quad(stage.stageWidth, stage.stageHeight, 0xffffff);
		addChild(quad);
		
		var textInput1:TextInput = new TextInput();
		textInput1.addEventListener(FeathersEventType.ENTER, onInputEnterPress);
		textInput1.text = "Input 1";
		textInput1.width = Main.stage.stageWidth;
		textInput1.height = 100;
		textInput1.y = 0;
		textInput1.textEditorProperties.returnKeyLabel = "next";
		addChild(textInput1);
		
		var textInput2:TextInput = new TextInput();
		textInput2.addEventListener(FeathersEventType.ENTER, onInputEnterPress);
		textInput2.text = "Input 2";
		textInput2.width = Main.stage.stageWidth;
		textInput2.height = 100;
		textInput2.y = 100;
		textInput2.textEditorProperties.returnKeyLabel = "next";
		addChild(textInput2);
		
		function onInputEnterPress(event:Event):void {
			var inputField:TextInput = event.target as TextInput;
			if(inputField == textInput1) {
				textInput2.setFocus();
			} else if(inputField == textInput2) {
				textInput1.setFocus();
			}
		}
	}
}

Here's my app descriptor:

<?xml version="1.0" encoding="utf-8" ?>

<application xmlns="http://ns.adobe.com/air/application/23.0">

    <id>***</id>
    <filename>Main</filename>
    <name>Main</name>
    <versionNumber>2.3.0</versionNumber>

    <initialWindow>
        <content></content>
        <visible>true</visible>
        <autoOrients>true</autoOrients>
        <fullScreen>false</fullScreen>
        <renderMode>direct</renderMode>
        <depthAndStencil>true</depthAndStencil>
        <softKeyboardBehavior>none</softKeyboardBehavior>
    </initialWindow>

    <android>
        <manifestAdditions>
            <![CDATA[
			<manifest android:installLocation="auto">
				<uses-sdk android:minSdkVersion="14" />
				<application android:hardwareAccelerated="false">
				</application>
			</manifest>
			]]>
        </manifestAdditions>
    </android>

</application>

@timoisalive timoisalive changed the title StageTextTextEditor maintainFocus behavior on Android StageTextTextEditor input text goes invisible on Android Dec 12, 2016
@timoisalive
Copy link
Author

Moving the input by a pixel when setting the focus is enough to make it visible again:

		function onInputEnterPress(event:Event):void {
			var inputField:TextInput = event.target as TextInput;
			if(inputField == textInput1) {
				textInput2.setFocus();
			} else if(inputField == textInput2) {
				textInput1.setFocus();
				textInput1.x += 1;
			}
		}

@joshtynjala joshtynjala changed the title StageTextTextEditor input text goes invisible on Android StageTextTextEditor: input text goes invisible on Android when focused Dec 12, 2016
@joshtynjala
Copy link
Member

joshtynjala commented Dec 12, 2016

Thank you, I can reproduce on my Nexus 5!

@joshtynjala joshtynjala added this to the 3.2 milestone Dec 12, 2016
@joshtynjala
Copy link
Member

Another workaround that seems to work is to clear the focus on the current input before setting focus on the new one:

if(inputField == textInput1)
{
	textInput1.clearFocus();
	textInput2.setFocus();
}
else if(inputField == textInput2)
{
	textInput2.clearFocus();
	textInput1.setFocus();
}

@timoisalive
Copy link
Author

Clearing the focus doesn't seem to help over here...

@timoisalive
Copy link
Author

Just mentioning if anyone else runs into the same issue. The previous idea of clearing the focus before re-assigning it seems to actually yield into another problem. In my actual project (cannot reproduce in the test project), the soft keyboard drops down when clearing the focus as suggested above. The newly appointed input field gets focused, I can see the caret, but the soft keyboard is gone...

After countless days (if not weeks altogether) of debugging the StageText, I think I finally have a working (although very much hacked) solution for both iOS and Android, hooray! :)

@joshtynjala
Copy link
Member

Looking at this again today. Interestingly, setting the font size smaller (20 instead of 50) seems to help too. How strange.

@joshtynjala
Copy link
Member

Another workaround that seems to help is to change how the vertical alignment is handled:

textInput1.verticalAlign = VerticalAlign.JUSTIFY;

If you still need to center the text vertically, you can do it on the starling.text.TextFormat (this might require Feathers 3.2, though... I don't remember exactly when I implemented support for vertical alignment with starling.text.TextFormat in text renderers/editors).

textInput1.fontStyles.verticalAlign = Align.CENTER;

StageText doesn't have a measurement API, so I use a TextField behind the scenes to measure. It's not perfectly accurate, and this might be part of why the text is disappearing (though it should only be clipped!)

Anyway, I'm still not sure exactly why this is happening. I can't reproduce with simple StageText outside of Starling. I'm forced to leave this open and check back from time to time if I get new ideas.

@joshtynjala joshtynjala removed this from the 3.2 milestone Jan 25, 2017
@timoisalive
Copy link
Author

I'm back with InputText/StageTextTextEditor issues... :(

As I mentioned earlier in my comment, if I use a button to clearFocus() on a TextInput and re-focus on it right after, the soft keyboard doesn't re-appear. Input focuses and SOFT_KEYBOARD_ACTIVATE is dispatched, but no keyboard appears.

The keyboard appears only on the first time I tap on a TextInput, or if I tap on the StageText another time, after the focus is set already.

This is reproduceable with the same example, just add a button which clears the focus.

Oh, and in this case it seems to be about maintainTouchFocus! When true, the problem exists, but when false, it doesn't seem to.

@joshtynjala
Copy link
Member

Try setting maintainTouchFocus to false before clearing focus, and set it back to true later.

@timoisalive
Copy link
Author

I did, no effect. :(

@timoisalive
Copy link
Author

Here's the changed code (otherwise same as before). Please let me know if you can't reproduce, thanks!

	public function initialize():void {
		FeathersControl.defaultTextEditorFactory = function():ITextEditor {
			var editorMobile:StageTextTextEditor = new StageTextTextEditor();
			editorMobile.fontSize = 50;
			editorMobile.maintainTouchFocus = true;
			return editorMobile;
		};
		
		var quad:Quad = new Quad(stage.stageWidth, stage.stageHeight, 0xffffff);
		addChild(quad);
		
		var textInput1:TextInput = new TextInput();
		textInput1.verticalAlign = VerticalAlign.JUSTIFY;
		textInput1.text = "Input 1";
		textInput1.width = Main.stage.stageWidth;
		textInput1.height = 100;
		textInput1.y = 0;
		textInput1.textEditorProperties.returnKeyLabel = "next";
		addChild(textInput1);
		
		var button:Sprite = new Sprite();
		button.addEventListener(TouchEvent.TOUCH, onButtonTouch);
		button.addChild(new Quad(100, 100, 0xff0000));
		button.y = 300;
		addChild(button);
		
		function onButtonTouch(event:TouchEvent):void {
			if(event.getTouch(button, TouchPhase.ENDED)) {
				textInput1.textEditorProperties.maintainTouchFocus = false;
				textInput1.clearFocus();
				textInput1.textEditorProperties.maintainTouchFocus = true;
			}
		}
	}

@urthling
Copy link

The workaround is to use an ANE to open the keyboard. Note you can only call your ANE within the SOFT_KEYBOARD_ACTIVATING handler -- if you wait until after the SOFT_KEYBOARD_ACTIVATED handler fires, a call to your ANE will be ignored.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants