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

[Problem] richChanges is called by setStyle #719

Closed
iamgio opened this issue Apr 3, 2018 · 13 comments
Closed

[Problem] richChanges is called by setStyle #719

iamgio opened this issue Apr 3, 2018 · 13 comments

Comments

@iamgio
Copy link
Contributor

iamgio commented Apr 3, 2018

Hi,
since I'm having issues with richChanges() (setStyle calls it), I wonder if it could be possible to add an enum that specifies the source of the change (TYPE (user typing), STYLE (setStyle, setStyleClass, etc), UNDO (undo), REDO (redo), etc).

@JordanMartinez
Copy link
Contributor

I tried something like that, but ran into some issues. See #486 for a quick overview of what happened.

What issues specifically are you having with richChanges()?

@iamgio
Copy link
Contributor Author

iamgio commented Apr 3, 2018

What issues specifically are you having with richChanges()?

Basically, if I type 'x', which follows a green 'y' (style class .green), 'x' gets the .green style class too (like every text editor does).

The problem is that if I apply a non-color property (bold, italic...) the richChanges() method is called and sets the style class to .green because it treats the bold text as a typed text.

Example

@JordanMartinez
Copy link
Contributor

Can you provide a reproducible demo? I'm not exactly sure what you're doing in your code.

@iamgio
Copy link
Contributor Author

iamgio commented Apr 3, 2018

Can you provide a reproducible demo? I'm not exactly sure what you're doing in your code.

This is Kotlin:

richChanges().filter {change -> !getText().isEmpty() && change.inserted != change.removed}
                .subscribe {change ->
                    if(change.inserted.length() > 0) {
                        change.inserted.text.toCharArray().forEachIndexed {index, _ ->
                            setStyle(change.position + index, change.position + index + 1, if(change.position != 0) {
                                getStyleOfChar(change.position - 1)
                            } else listOf("white"))
                        }
                    }
                }

In Java, it would be like this:

richChanges().filter(change -> !getText().isEmpty() && change.getInserted() != change.getRemoved())
                .subscribe(change -> {
                    if(change.getInserted().length() > 0) {
                        String text = change.getInserted().getText();
                        for(int i = 0; i < text.length(); i++) {
                            List<String> style = null;
                            if(change.getPosition() != 0) {
                                style = getStyleOfChar(change.position - 1)
                            } else style = Collections.singletonList("white");
                            setStyle(change.getPosition() + i, change.getPosition() + i + 1, style);
                        }
                    }
                });

EDIT: This is the part that manages the auto-styling of typed text

When bold/italic is applied, it's just:

List<String> style = getStyleOfChar(getSelection().getStart());
style.add("bold");
setStyle(getSelection().getStart(), getSelection().getEnd(), style);

@JordanMartinez
Copy link
Contributor

By reproducible demo, I mean a JavaFX Application that I can copy-and-paste-and-run.

@iamgio
Copy link
Contributor Author

iamgio commented Apr 6, 2018

public class Main extends Application {

    @Override
    public void start(Stage primaryStage) {
        Button bold = new Button("Make selection bold");
        CodeArea area = new CodeArea("RichTextFX");
        area.setStyleClass(0, area.getText().length(), "green");
        area.richChanges().filter(change -> !change.getInserted().equals(change.getRemoved())).subscribe(change -> {
            Collection<String> style = area.getStyleOfChar(change.getPosition() - 1);
            area.setStyle(change.getPosition(), change.getPosition() + 1, style);
            System.out.println("Set " + change.getInserted() + " to " + style);
        });
        bold.setOnAction(e -> {
            IndexRange selection = area.getSelection();
            if(selection.getLength() > 0) {
                for(int i = selection.getStart(); i < selection.getEnd(); i++) {
                    Collection<String> style = area.getStyleOfChar(i);
                    List<String> copy = new ArrayList<>(style); // Adding elements to 'style' throws UnsupportedOperation
                    copy.add("bold");
                    area.setStyle(i, i + 1, copy);
                }
                System.out.println("Bold has been set");
            }
        });
        VBox root = new VBox(bold, area);
        root.setPrefWidth(400);
        Scene scene = new Scene(root);
        scene.getStylesheets().add("style.css");
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    public static void main(String... args) {
        launch(args);
    }
}

style.css:

.bold {
    -fx-font-weight: bold;
}

.green {
    -fx-fill: green
}

It works if !change.getInserted().getText().equals(change.getRemoved().getText()) is added in filters, but it doesn't style when you select a character and re-write it without backspacing.

@iamgio
Copy link
Contributor Author

iamgio commented Apr 7, 2018

Do you think this could be implemented?

@JordanMartinez
Copy link
Contributor

I haven't had a chance to look at this yet (but thank you for supplying the full demo!). I should get to it by this upcoming Monday.

@JordanMartinez
Copy link
Contributor

It sounds like you want to insert some character independent of the style of the character that precedes it. Is that correct?

@JordanMartinez
Copy link
Contributor

I think you mean to write this:

public class Main extends Application {

    @Override
    public void start(Stage primaryStage) {
        Button bold = new Button("Make selection bold");
        CodeArea area = new CodeArea("RichTextFX");
        // set text to .green
        area.setStyleClass(0, area.getText().length(), "green");

        // when text is modified (ignore style changes),
        area.plainTextChanges().subscribe(change -> {
            Collection<String> style = area.getStyleOfChar(change.getPosition() - 1);
            area.setStyle(change.getPosition(), change.getPosition() + 1, style);
        });
        bold.setOnAction(e -> {
            IndexRange selection = area.getSelection();
            StyleSpans<Collection<String>> stylesInSelection = area.getStyleSpans(selection);
            
            // merge the original spans and the added spans together into a new StyleSpans object
            StyleSpans<Collection<String>> updatedSpans = stylesInSelection.overlay(
                    StyleSpans.singleton(Collections.singleton("bold"), selection.getLength()),
                    (oldList, addedList) -> {
                        List<String> copy = new ArrayList<>(oldList);
                        copy.addAll(addedList);
                        return copy;
                    });
            area.setStyleSpans(selection.getStart(), updatedSpans);
        });
        VBox root = new VBox(bold, area);
        root.setPrefWidth(400);
        Scene scene = new Scene(root);
        scene.getStylesheets().add(this.getClass().getResource("style.css").toExternalForm());
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    public static void main(String... args) {
        launch(args);
    }
}

@iamgio
Copy link
Contributor Author

iamgio commented Apr 11, 2018

It sounds like you want to insert some character independent of the style of the character that precedes it. Is that correct?

I want to apply the style of the previous character to the new one.

Anyways, I'll try your code. Thank you

@iamgio
Copy link
Contributor Author

iamgio commented Apr 14, 2018

The problem was about using richChanges() instead of plainTextChanges(), now it seems like it's working.
I'd still enjoy if you add that feature though.

Keep up the great work, and thanks a lot for your support.

@JordanMartinez
Copy link
Contributor

Glad you figured it out!

I'd still enjoy if you add that feature though.

Well, I know you already opened this issue for that, but the content of this issue turned to focus on fixing your specific problem. I initially did not want to add that feature because of how it went previously (hence why I tried to figure out your specific problem), but I'm willing to reconsider it. Could you change the title of this issue to something that adheres to more of this issue's content and open a new issue for that feature request. We can continue the discussion there. Perhaps there is a better way to implement it without the problems I encountered previously.

@iamgio iamgio changed the title [Feature request] Adding RichTextChange<...>.getSource() [Problem] richChanges is called by setStyle Apr 24, 2018
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

2 participants