CodenameOne icon indicating copy to clipboard operation
CodenameOne copied to clipboard

Improve MultiLine TextField Autogrow on iPad

Open javieranton-zz opened this issue 5 years ago • 7 comments

Having a MultiLine TextField set with autogrow on iPad that starts off from having 1 or 2 rows results in the top rows being hidden when a new line is entered. No combination of padding fixes this. Currently, the only workaround is to capture changes with addDataChangedListener and do the below (add 2 extra rows than is required)

    setRows(getLines() + 2);

This will ensure that no rows are hidden.

here is an example of how the top line gets hidden:

image

Then consequence of this is that no autogrow TextField can start with just one row (minimum must be 3). This obviously presents problems for chat inputs etc

Here is some sample test code that shows the issue:

TextField msgField = new TextField();
msgField.setSingleLineTextArea(false);
msgField.msgField.setGrowByContent(true);

And here is a sample code that prevents the issue (by making sure minimum rows are always 3)

TextField msgField = new TextField();
msgField.setSingleLineTextArea(false);
msgField.msgField.setGrowByContent(true);
msgField.addDataChangedListener((i1, i2) -> { 
    msgField.setRows(msgField.getLines() + 2);
    msgField.revalidate();
});

Something in the native code must be causing this. I open this issue so that we are aware and hopefully can find a solution at some point

javieranton-zz avatar Nov 11 '20 12:11 javieranton-zz

In the past, I solved a problem similar to yours (or the same problem) with this method, give it a try:

    /**
     * The TextArea.setGrowByContent method works only after closing VKB; this
     * method is similar, but it works while typing text.
     *
     * @param textArea
     */
    public static void setGrowByContent(TextArea textArea) {
        textArea.setGrowByContent(true);

        textArea.addDataChangedListener((int type, int index) -> {
            if (textArea.getParent() != null) {
                textArea.getParent().revalidateWithAnimationSafety();
            }
        });
    }

Example of usage:

TextArea textArea = new TextArea(1, 80, TextArea.INITIAL_CAPS_SENTENCE);
setGrowByContent(textArea);

I hope this can help, Francesco

jsfan3 avatar Nov 21 '20 14:11 jsfan3

Hi Francesco, thanks for your input. I just tried exactly what you suggested and unfortunately it did not work. This is the code I used:

public void Test()
{
    Form hi = new Form("Test", new BorderLayout());
    Container cnt = new Container(BoxLayout.y());
    TextArea textArea = new TextArea(1, 80, TextArea.INITIAL_CAPS_SENTENCE);
    setGrowByContent(textArea);
    cnt.add(textArea);
    hi.add(BorderLayout.CENTER, cnt);
    hi.show();
}
public static void setGrowByContent(TextArea textArea) {
    textArea.setGrowByContent(true);

    textArea.addDataChangedListener((int type, int index) -> {
        if (textArea.getParent() != null) {
            textArea.getParent().revalidateWithAnimationSafety();
        }
    });

Here is a little video: (notice how first line goes out of view after first enter): https://drive.google.com/file/d/1qeyq8_iOFD8nE5-DPFNXoPnvQXnG0Y8o/view?usp=sharing

javieranton-zz avatar Nov 21 '20 23:11 javieranton-zz

Today I dedicated a few hours to this problem, because it also concerns the app I'm developing. I have not come to a solution, almost all my attempts have been unsuccessful (in my real app), but in simple test cases things seem to work.

I have a little modified my workaround. This is a minimal test case in which my workaround works enough well on my iPhone (it's not perfect, but acceptable):

    public void start() {
        if (current != null) {
            current.show();
            return;
        }
        Form hi = new Form("TextArea Autogrow", BoxLayout.y());
        TextArea textArea = new TextArea(1, 80, TextArea.INITIAL_CAPS_SENTENCE);
        textArea.setGrowByContent(true);
        setGrowByContent(textArea);
        hi.add(textArea);
        hi.show();
    }

    /**
     * The TextArea.setGrowByContent method works only after closing VKB; this
     * method is similar, but it works while typing text (the container of the
     * textArea should be scrollable on the Y axis).
     *
     * @param textArea
     */
    public static void setGrowByContent(TextArea textArea) {
        if (!textArea.isGrowByContent()) {
            textArea.setGrowByContent(true);
        }

        textArea.addDataChangedListener((int type, int index) -> {
            if (textArea.getParent() != null) {
                if (!textArea.getParent().isScrollableY()) {
                    Log.p("setGrowByContent -> Please make the container of the TextArea scrollable", Log.WARNING);
                }
                textArea.getParent().revalidate();
            }
        });
    }

Things still work if I insert the BoxLayout inside a BorderLayout, like in your example:

        Form hi = new Form("TextArea Autogrow", new BorderLayout());
        Container cnt = new Container(BoxLayout.y());
        cnt.setScrollableY(true);
        TextArea textArea = new TextArea(1, 80, TextArea.INITIAL_CAPS_SENTENCE);
        textArea.setGrowByContent(true);
        setGrowByContent(textArea);
        cnt.add(textArea);
        hi.add(BorderLayout.CENTER, cnt);
        hi.show();

So please try this slightly modified code. In this minimal example, if you comment cnt.setScrollableY(true); it still works, but I suppose that it's a good idea that the container must be scrollable.

In my real world app, my workaround sometimes works and sometimes doesn't. It's frustrating. I hope for a proper fix.

jsfan3 avatar Nov 22 '20 17:11 jsfan3

So I tested a bit more, here are my conclusions: Your workaround just revalidates, which helps, but was something I was already doing before so it doesn't present any improvement (the top most line still disappears) I had been testing on an iPad but decided to also test on an iPhone. There is a significant difference. The first line completely disappears on iPad as soon as we have a 2nd line. On iPhone, however, it only becomes partially hidden. So not all hope is lost, but still need to improve things specifically on the iPad side

javieranton-zz avatar Nov 22 '20 19:11 javieranton-zz

Your previous title was better: "Improve MultiLine TextField Autogrow on iOS", since the autogrow on iPhone works only in simple test cases, as I said.

If you can find a solution, I can only be pleased. Anyway, since I spent an afternoon experimenting with all the possible code combinations that came to my mind, it is clear to me that the problem is not in the use of the Codename One API, but in the iOS porting of TextArea.

In theory, a single call to setGrowByContent should work under all circumstances and without any special precautions.

@shannah What do you think about this?

jsfan3 avatar Nov 22 '20 20:11 jsfan3

Yes, maybe the previous title was better because TextField can be improved in many ways not just for iPad. But the specific description of this issue as told in the original post is related to the first hidden line which only fully happens on iPad. On iPhone, the first line is still 70% visible (this could be improved of course). Just trying to be more detailed so this issue's screenshots make sense

Multiline TextField has many issues. I've seen others too where I've had to apply "workarounds", also on Android

javieranton-zz avatar Nov 22 '20 20:11 javieranton-zz

Up

javieranton-zz avatar May 12 '21 09:05 javieranton-zz