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

8335547: Support multi-line prompt text for TextArea #1716

Open
wants to merge 9 commits into
base: master
Choose a base branch
from

Conversation

Ziad-Mid
Copy link
Contributor

@Ziad-Mid Ziad-Mid commented Feb 19, 2025

Added multi line prompt support for TextArea this will provide the ability to have multiple lines in textArea as expected,
Also fixed tests to meet the new changes


Progress

  • Change must not contain extraneous whitespace
  • Commit message must refer to an issue
  • Change must be properly reviewed (2 reviews required, with at least 1 Reviewer, 1 Author)
  • Change requires CSR request JDK-8352564 to be approved

Issues

  • JDK-8335547: Support multi-line prompt text for TextArea (Enhancement - P4)
  • JDK-8352564: Support multi-line prompt text for TextArea (CSR)

Reviewers

Reviewing

Using git

Checkout this PR locally:
$ git fetch https://git.openjdk.org/jfx.git pull/1716/head:pull/1716
$ git checkout pull/1716

Update a local copy of the PR:
$ git checkout pull/1716
$ git pull https://git.openjdk.org/jfx.git pull/1716/head

Using Skara CLI tools

Checkout this PR locally:
$ git pr checkout 1716

View PR using the GUI difftool:
$ git pr show -t 1716

Using diff file

Download this PR as a diff file:
https://git.openjdk.org/jfx/pull/1716.diff

Using Webrev

Link to Webrev Comment

Sorry, something went wrong.

@bridgekeeper
Copy link

bridgekeeper bot commented Feb 19, 2025

👋 Welcome back Ziad-Mid! A progress list of the required criteria for merging this PR into master will be added to the body of your pull request. There are additional pull request commands available for use with this pull request.

@openjdk
Copy link

openjdk bot commented Feb 19, 2025

❗ This change is not yet ready to be integrated.
See the Progress checklist in the description for automated requirements.

if (isBound()) {
unbind();
}
if (isBound()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we might want to bypass the whole thing if it is a TextArea.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes I did change the code a bit and now the tests do not fail

if (isBound()) {
unbind();
}
if (TextInputControl.this instanceof TextArea) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this the right approach? One can always ask whether we should rely on the instanceof TextArea here, for example in the context of a custom TextInputControl.

Also think of:

  • Can a custom control override this behavior?
  • Do we want to give this capability to the application?
  • Should we create a public API instead? And if so, which kind?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The purpose of this is to remove the stripping of new lines in TextArea only as that is normally how it should be,
but the default behavior will stay as it was before for other classes that extends TextInputControl.
And to give the capability to applications to modify this behavior is not necessary.

@andy-goryachev-oracle
Copy link
Contributor

Also notice the github actions (GHA) are failing, meaning this PR breaks some tests

TextAreaTest > checkPromptTextPropertyBind() FAILED
TextFieldTest > checkPromptTextPropertyBind() FAILED

@Ziad-Mid
Copy link
Contributor Author

Ziad-Mid commented Feb 20, 2025

Also notice the github actions (GHA) are failing, meaning this PR breaks some tests

TextAreaTest > checkPromptTextPropertyBind() FAILED
TextFieldTest > checkPromptTextPropertyBind() FAILED

I have fixed the code for this , but there is still another fail

@Ziad-Mid Ziad-Mid closed this Feb 20, 2025
@Ziad-Mid Ziad-Mid reopened this Feb 20, 2025
@andy-goryachev-oracle
Copy link
Contributor

ServiceLifecycleTest > cancelCalledFromOnFailed() FAILED
this failure happens intermittently, see https://bugs.openjdk.org/browse/JDK-8088343 and is unrelated to the issue.

also, you can try re-running failed tests (look for a button in the top right corner).

@andy-goryachev-oracle
Copy link
Contributor

What do you think of an alternative solution: instead of trying to fix the property value, we could leave it alone but change the way it's interpreted by the skin.

Look at TextFieldSkin::createPromptNode method. We could bind the Text.text property to the StringBinding created by Bindings::createStringBinding, something along the lines

        promptNode.textProperty().bind(Bindings.createStringBinding(() -> {
            String s = getSkinnable().getPromptText();
            // TODO replace newlines here
            return s;
        }, getSkinnable().promptTextProperty());

This will remove all the logic from the control and place it where it needs to happen (TextFieldSkin). The TextAreaSkin is unchanged and will display newlines when all the cruft is removed from TextInputControl::promptText.

(We could also change the said property to be lazily initialized since in most cases it's not used).

What do you think?

Copy link
Contributor

@andy-goryachev-oracle andy-goryachev-oracle left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The idea is solid - you could make it ready for review (RFR) now.

I've left a number of general formatting suggestions, but the main concern is the decision to convert the tests to be specific to the TextField and the TextArea (omitting the PasswordField). Since the only difference is the expected values, maybe we can keep the existing tests and introduce a function that computes the expected value based on the (parameter) type.

s = "";
s = s.replace("\n", "");
return s;
}, getSkinnable().promptTextProperty()));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

two minor problems:

  1. missing { } after if. (I would highly recommend always using { })
  2. when s is null you call replace() unnecessarily

suggestion:

            String s = getSkinnable().getPromptText();
            if (s == null) {
                return "";
            }
            return s.replace("\n", "");

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should consider using a fluent binding here, which is a more modern solution compared to the Bindings class. It is also simpler because you don't need to check for null:

promptNode.textProperty().bind(getSkinnable().promptTextProperty().map(s -> s.replace("\n", "")));

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you considered the suggestion?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes I will push the new changes thanks for the suggestion @mstr2

txtArea.promptTextProperty().bind(promptProperty);
root.getChildren().add(txtArea);
Text promptNode = TextInputSkinShim.getPromptNode(txtArea);
assertEquals("Prompt\nwith\nLineBreaks", promptNode.getText());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here you can use promptWithLineBreaks string declared earlier
(also applies to other tests)

txtArea.setPromptText(promptWithLineBreaks);
root.getChildren().add(txtArea);
Text promptNode = TextInputSkinShim.getPromptNode(txtArea);
System.out.println(promptNode.getText());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no need to print to stdout. please remove all System.out.println()'s

assertEquals("Prompt without LineBreaks", promptNode.getText());
txtField.setPromptText(promptWithLineBreaks);
assertEquals("PromptwithLineBreaks", promptNode.getText());

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please remove extra newline

@andy-goryachev-oracle
Copy link
Contributor

you can make this PR ready for review (RFR) now.

@Ziad-Mid Ziad-Mid marked this pull request as ready for review March 15, 2025 04:58
@openjdk openjdk bot added the rfr Ready for review label Mar 15, 2025
@mlbridge
Copy link

mlbridge bot commented Mar 15, 2025

Webrevs

@mstr2
Copy link
Collaborator

mstr2 commented Mar 16, 2025

What happens if the text contains line breaks other than \n, such as \r or \r\n? There's System.lineSeparator(), but we might want to remove all kinds of line separators regardless.

@andy-goryachev-oracle
Copy link
Contributor

andy-goryachev-oracle commented Mar 17, 2025

What happens if the text contains line breaks other than \n

This is a good question.
I guess it depends on what other characters cause line break in JavaFX.
Wikipedia (https://en.wikipedia.org/wiki/Newline) specifies a few:

1. vert tab \u000b ---
2. form feed \u000c ---
3. cr \u000d ---
4. crlf \u000d\u000a ---
5. lf \u000a ---
6. nel \u0085 ---
7. ls \u2028 ---
8. ps \u2029 ---

@Ziad-Mid could you investigate please?

@Ziad-Mid
Copy link
Contributor Author

  1. cr \u000d ---

Yes , this approach do not handle all the line Seperators only "\n" , I will change it to handle it all

@andy-goryachev-oracle
Copy link
Contributor

@Ziad-Mid please make sure we need to change - does the code (with no filtering) render other newline characters?

@Ziad-Mid
Copy link
Contributor Author

Ziad-Mid commented Mar 18, 2025

I did test the mentioned line separators without filtering and the line separators are not rendered as newlines, so there is no need to add filtering for other line separators as they are already handled by default.

Copy link
Contributor

@andy-goryachev-oracle andy-goryachev-oracle left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it currently looks good, even though PasswordField lost its portion of tests. Since we know the PasswordField implementation does not add any functionality in the area being tested, the TextFieldTest should be sufficient.

Left some minor comments.

I would like to have another pair of eyes though.

/reviewers 2

@openjdk
Copy link

openjdk bot commented Mar 20, 2025

@andy-goryachev-oracle
The total number of required reviews for this PR (including the jcheck configuration and the last /reviewers command) is now set to 2 (with at least 1 Reviewer, 1 Author).

@openjdk openjdk bot added the csr Need approved CSR to integrate pull request label Mar 20, 2025
@mstr2
Copy link
Collaborator

mstr2 commented Mar 21, 2025

I think that we should treat all usual line separators in the same way (that is, \n, \r, and \r\n), as I don’t know if we have a specification that only one kind of line separator visually breaks lines in JavaFX.

@andy-goryachev-oracle
Copy link
Contributor

Confirming that no other character (see earlier comment) other than LF render as newlines, on macOS and Windows.

The ticket mentions a possible "workaround" of using \r (CR) on Windows, but it is not true.

Screenshot 2025-03-21 112855

Copy link
Contributor

@andy-goryachev-oracle andy-goryachev-oracle left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Ziad-Mid :
please do not resolve the conversations without the original commenter confirming that the conversation is resolved (it's really a bug in guthub, as only the originator of the comment can say if the conversation has been resolved). I think there are still some questions left unanswered, please check.

(I've started to use thumbs-up emoji to mark the comment as addressed, if you want my opinion).

Other than that, the changes look good to me.

@Ziad-Mid
Copy link
Contributor Author

@Ziad-Mid : please do not resolve the conversations without the original commenter confirming that the conversation is resolved (it's really a bug in guthub, as only the originator of the comment can say if the conversation has been resolved). I think there are still some questions left unanswered, please check.

(I've started to use thumbs-up emoji to mark the comment as addressed, if you want my opinion).

Other than that, the changes look good to me.

I did unresolved them, and I will check the suggestions whenever possible and answer,
thanks

@@ -733,7 +732,7 @@ private void createPromptNode() {
promptNode.visibleProperty().bind(usePromptText);
promptNode.fontProperty().bind(getSkinnable().fontProperty());

promptNode.textProperty().bind(getSkinnable().promptTextProperty());
promptNode.textProperty().bind(getSkinnable().promptTextProperty().map(s -> s.replace("\n", "")));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I recommend to replace all usual newline characters with replaceAll("[\r\n]", "") as I can't think of a compelling reason to remove only some, but leave others in the string.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The tests show that only LF "\n" is rendered as a new line, there is no need to add more restrictions that is not needed
and the same was tested by @andy-goryachev-oracle previously in the comments and it confirms the same.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That doesn't sound like a compelling reason to me. In fact, it makes it seems like a bug in JavaFX that a line break is only rendered with \n, but not with \r\n or \r.

In any case, the goal here is to (semantically) transform a string such that it doesn't contain line breaks, and line breaks come in three different usual forms. Our goal should always be to do the right thing, and not stop half-way and rely on unspecified rendering quirks for the rest.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We seem to have arrived at these two options:

  1. acknowledge and defend the status quo in which the text layout considers only '\n' as newlines
  2. enhance the text layout to handle other paragraph separators (as well as maybe add support for other symbols that impact layout, such as soft hyphen)

@kevinrushforth what do you think?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
csr Need approved CSR to integrate pull request rfr Ready for review
Development

Successfully merging this pull request may close these issues.

None yet

3 participants