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

How best to indent the first line of wrapped text? #840

Open
garybentley opened this issue Aug 15, 2019 · 11 comments
Open

How best to indent the first line of wrapped text? #840

garybentley opened this issue Aug 15, 2019 · 11 comments

Comments

@garybentley
Copy link

garybentley commented Aug 15, 2019

What is the best way to indent just the first line of a paragraph with wrapped text?

I'm wanting to have this type of thing:

indented-text

The text area would having wrapping on. The rest of the text would not have the indent.

If the StyledSegment had some information about it's position or paragraph then I could add a node in front of the segment when it's the first segment.

Any ideas on the best way to handle it?

@garybentley
Copy link
Author

I've been trying out some different techniques for this feature and the closest I've come is to add a custom shape to the ParagraphText via the ParagraphBox.

i.e. adding the following to ParagraphBox.

    public void setFirstLineIndent (Number v) {
        double d = (v != null ? v.doubleValue() : 0);
        if (d > 0)
        {
            if (firstLineIndentNode == null) {
                firstLineIndentNode = new Rectangle();
                firstLineIndentNode.setHeight(1);
                firstLineIndentNode.setFill(Color.TRANSPARENT);
            }
            firstLineIndentNode.setWidth(d);
            text.getChildren().add(0, this.firstLineIndentNode);
        } else {
            text.getChildren().remove(this.firstLineIndentNode);
        }
        firstLineIndent = d;
    }

This works and displays an indent (where the indent value is also styled via a custom css property -rtfx-first-line-indent) however when I try and edit the text at the start of the line characters disappear and the layout messes up.

What else do I need to take into account/modify to stop the editing from misbehaving?

@Jugen
Copy link
Collaborator

Jugen commented Aug 22, 2019

I think that the easiest and simplest way to achieve this is unfortunately by manipulating the text itself by either inserting spaces, a tab, or a Label.

This would require doing a search for ("\n\n") and replacing matches with (eg: "\n\n\t") on any text being added/inserted into the text area, which you'd also have to remove again when retrieving the contents of the text area.

You'd also need to add a key listener for Enter and insert a tab/spaces/Label when appropriate.

Doing all this for tab or spaces is straight forward, but for Label is more complicated.

@garybentley
Copy link
Author

On reflection I'm thinking of using the same sort of approach the RichTextDemo uses and instead of inserting an image, insert a shape when the Return key is pressed or a \n is detected. I've found in the past that messing with the text itself nearly always leads to problems further down the line.

Just one question though, when the user turns off the indent how do I repaint the view to remove the shape indents?

@garybentley
Copy link
Author

After trying numerous different ways, I've opted for overriding the "layoutChildren" method in TextFlowExt and copying the code from TextFlow.layoutChildren so that TextFlowExt lays out the text itself (something I think it should do anyway). While this works and I can then indent the text, the area then behaves strangely when you move the caret or type. The line of text will disappear then reappear and the area doesn't recognize that there aren't any characters in the indent.

What do I also need to change to make things behave correctly? Let me know if you want me to post the changes to TextFlowExt here, there's quite a lot.

@Jugen
Copy link
Collaborator

Jugen commented Sep 1, 2019

Sorry for the delay. The problem is that TextFlow is rectangular and all the positioning of text and caret is done relative to it. For things to behave correctly the simplest solution (I think) is for the model to have the indent to be part of it. Changing the positioning calculations based on a different geometry is in my mind complex and difficult.

I've created a basic demo (IndentSegmentDemo.java) where the model gets manipulated to produce the indentation of the first line of a paragraph. This is achieved in StyledSegmentTextArea which extends GenericStyledArea parameterized with AbstractSegment instead of String. See TextSegment (for ordinary text) and IndentSegment for the indentation.

To insert an indent manually at a specific point in the text you can invoke StyledSegmentTextArea .insert( int, AbstractSegment ) with an IndentSegment (which can be reused i.e. it doen't have to be new everytime).

Retrieving the text content with getText() or textProperty() doesn't contain any indentation.

@garybentley
Copy link
Author

Thanks for that. I'd already tried that approach though and while it will insert the indent the problem is that the indent is still treated as a real character in the text rather than an indent. So you can move the caret around it (before and after) whereas I want the indent to be transparent to the caret, i.e. you move the caret to be before the indent and it will move back to the previous line and vice versa.

How can I make the indent "transparent"? I've tried setting the length on the segment to 0 but the area then throws an exception.

@Jugen
Copy link
Collaborator

Jugen commented Sep 4, 2019

I've updated StyledSegmentTextArea to accommodate navigating around the Indents.

When manipulating the text externally bear in mind that because the indents are not part of the text there is a discrepancy between the reported position of the caret and the actual position in the text. Let me know if you encounter something like that ....

@garybentley
Copy link
Author

garybentley commented Sep 6, 2019

Unfortunately I do need an accurate caret position since I use the location to insert text and for a spell checker.

Also this solution has the problem that the user can select the indent by dragging the mouse which isn't desirable.

Personally I see this as a layout issue rather than a model one, TextFlow should support this feature itself in a similar way how JTextPane does. It's odd that they don't, it was supported in Java Swing and is supported in web CSS, it's a strange omission.

Can you point me in the right direction for how GenericStyledArea determines where to put the caret? I have a modified version of TextFlowExt that lays out the text itself and puts an indent in but when I move the caret around or type a character the line starts disappearing and behaving weirdly.

edit: Here's a new feature request from 2010 for this: https://bugs.openjdk.java.net/browse/JDK-8091174 (it's still open!)

@garybentley
Copy link
Author

Btw, I added a new feature request to openjfx, javafxports/openjdk-jfx#582 it might help if you could add your voice on that request as a maintainer of a major project that makes use of Text/TextFlow.

@garybentley
Copy link
Author

I've raised a new feature request in the java bug database: https://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-8231014

@tchudyk
Copy link

tchudyk commented Jun 23, 2021

I just arrived at the same problem, but in the opposite direction - I want the first line to start earlier than the others in the same paragraph.
Here, unfortunately, adding spaces/tabs/segments at the beginning of a paragraph won't help.

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

No branches or pull requests

3 participants