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

Using "setStyle" two times in a row causes inexplicable scrolling #525

Closed
ITrun90 opened this issue Jun 17, 2017 · 7 comments
Closed

Using "setStyle" two times in a row causes inexplicable scrolling #525

ITrun90 opened this issue Jun 17, 2017 · 7 comments

Comments

@ITrun90
Copy link

ITrun90 commented Jun 17, 2017

Hey ;)

i upgraded from v6 to v7 and i noticed that there is an issue when using area.setStyle(0,1,"...") two times in a row with different ranges.

For example:
setStyle(0, 1, Arrays.asList("highlight")); setStyle(10000, 10001, Arrays.asList("highlight"));

Causes the following behavior:
After calling the method which includes the above code nothing special happen. The styles are changed like i expect. But after i begin typing the scrollingposition changes to position 0 (or 1) like set in the first line.

In v6 the scrollingposition did not change.

Maybe someone knows a workaround or something what could help me.

@JordanMartinez
Copy link
Contributor

Could you provide a reproducible demo? I'm not fully understanding the issue.

This may have something to do with #390

@JordanMartinez
Copy link
Contributor

Also, which milestone release are you using for 0.7?

@ITrun90
Copy link
Author

ITrun90 commented Jun 18, 2017

Many thx for your replay. I forget to tell you that i use setStyle TWO TIMES in a row (i edited my post). The first setStyle is changing a range at the beginning of the document and the second one ist changing the style of a character in the mid of the document.

For example changing the -rtfx-background-color of the opening <div> tag if the caretposition is after an ending tag </div>. The caretposition is still after the ending tag but the scrollposition jumps to the beginning tag. But it jumps not directly after changing the style but only after i click behind another ending tag.

I will give you an examplecode asap (maybe in a few hours) ;)

At first i started with version 6.10 and it worked. Now i am using v7 Milestone 5.

@ITrun90
Copy link
Author

ITrun90 commented Jun 18, 2017

Hi,

i was able to reproduce the issue. setStyle - or setStyleClass seems not to be the problem. I wrote a method which shall reset the styles of every character to default and this caused the jumps. Look at the last method in my following code example (you can test it by clicking after the last </table>

    // The CodeArea is child of this VBox
    @FXML VBox richtext;

    // Patterns to identify TAGS like <div> or <table>
    private final Pattern PATTERN = Pattern.compile("(?<TAG><[^\\?](.*?)[ >]|/>)", Pattern.DOTALL);

    // Lets build a simple CodeArea and insert it into the above mentioned VBox
    public void initialize() {
        // The CodeArea
        CodeArea ca = new CodeArea();

        // Some Sampletext (its neccessary cause the used caretPositions are hardcoded)
        ca.replaceText(
                "<div class=\"alert alert-danger\">\n" +
                        "    <table>\n" +
                        "        <tr>\n" +
                        "            <td><span class=\"fa fa-close ft_size_27 mg_right_20\"></span></td>\n" +
                        "            <td>\n" +
                        "            {*\n" +
                        "                1: Username (> 255 || < 5)\n" +
                        "                2: Firstname (>255 || < 5)\n" +
                        "                3: Lastname (>255 || < 5)\n" +
                        "                4: EMail not valid\n" +
                        "                5: EMail already exists\n" +
                        "                6: Username already exists\n" +
                        "                7: Password too short or too long\n" +
                        "                8: Passwords doesnt match\n" +
                        "            *}\n" +
                        "            {$76be98f9171351b8444a6287fb7f31b3a6c28802}: \n" +
                        "            {if $error eq 1}\n" +
                        "                {$7884fc242eaf64d217d37262b0548582047b29b1}\n" +
                        "            {/if}\n" +
                        "            {if $error eq 2}\n" +
                        "                {$bdfb20b48b66009e556c6437dc020a34a8c6fbf7}\n" +
                        "            {/if}\n" +
                        "            {if $error eq 3}\n" +
                        "                {$d49a9d3450b7852da8932886b6e748a49c11621c}\n" +
                        "            {/if}\n" +
                        "            {if $error eq 4}\n" +
                        "                {$1f91fe7c8c2c73855fcd86bc09c3c769f31ad245}\n" +
                        "            {/if}\n" +
                        "            {if $error eq 5}\n" +
                        "                {$5e7ebc57cef21ce27246943fc69b4edd14f77f34}\n" +
                        "            {/if}\n" +
                        "            {if $error eq 6}\n" +
                        "                {$4e13086230c1f4a3e537a6897f75118a25c544ae}\n" +
                        "            {/if}\n" +
                        "            {if $error eq 7}\n" +
                        "                {$d8b61303910a0b8181828955fa57228821c4a94d}\n" +
                        "            {/if}\n" +
                        "            {if $error eq 8}\n" +
                        "                {$d8b61303910a0b8181828955fa57228821c4a94d}\n" +
                        "            {/if}\n" +
                        "            <br/>\n" +
                        "                <a href=\"\" onClick=\"history.go(-1);return true;\">\n" +
                        "                    {$5282443314cba22f63d935a02958e64895f0053f}\n" +
                        "                </a>\n" +
                        "            </td>\n" +
                        "        </tr>\n" +
                        "    </table>\n" +
                        "</div><!-- Hinweisbox: danger -->"
        );

        // here we insert the CodeArea into the VBox and setting the prefHeight
        richtext.getChildren().add(new VirtualizedScrollPane<>(ca));
        ca.setPrefHeight(800.0);

        // Now its going to be interesting...
        // Whenever the user clicks with the mouse it checks, if the caretPosition is after 1648
        // If true -> setStyleClass of caret 1647-1648 and 37-38 to "highlight"
        ca.setOnMouseClicked(new EventHandler<MouseEvent>() {
            @Override
            public void handle(MouseEvent event) {
                // in the following line comes the issue
                // this line shall reset the whole text to default style
                ca.setStyleSpans(0, computeHighlighting(ca.getText())); // <-- ISSUE

                // after text is set to default i want to change some specific chars
                // this works fine, but if i click again somewhere the line above makes now a jump
                // to Position 37
                if(ca.getCaretPosition() == 1648)
                {
                    ca.setStyleClass(37, 38, "highlight");
                    ca.setStyleClass(ca.getCaretPosition()-1, ca.getCaretPosition(), "highlight");
                }
            }
        });
    }

    // This method is the probleme i guess.
    // It shall reset every stylechanges i made before to the defined regular expressions
    public StyleSpans<Collection<String>> computeHighlighting(String text) {
        Matcher matcher = PATTERN.matcher(text);
        int lastKwEnd = 0;
        StyleSpansBuilder<Collection<String>> spansBuilder = new StyleSpansBuilder<>();
        while (matcher.find()) {
            String styleClass = matcher.group("TAG") != null ? "tag" : null;
            assert styleClass != null;
            spansBuilder.add(Collections.emptyList(), matcher.start() - lastKwEnd);
            spansBuilder.add(Collections.singleton(styleClass), matcher.end() - matcher.start());
            lastKwEnd = matcher.end();
        }

        spansBuilder.add(Collections.emptyList(), text.length() - lastKwEnd);
        return spansBuilder.create();
    }

@JordanMartinez
Copy link
Contributor

I think your problem might be caused by using setOnMouseClicked. Technically, two handlers are running: the one you have defined and the default mouse behavior that you have not overridden. You can see an example of how to properly override it via the Override Behavior Demo.

Try changing that and see it if makes any difference.

@ITrun90
Copy link
Author

ITrun90 commented Jun 19, 2017

Hi Jordan,

i tried the following:

InputMap<MouseEvent> testEvent = InputMap.consume(
    mousePressed()
);
// Now its going to be interesting...
// Whenever the user clicks with the mouse it checks, if the caretPosition is after 1648
// If true -> setStyleClass of caret 1647-1648 and 37-38 to "highlight"
EventHandler<MouseEvent> mouseClick = e -> {
    // in the following line comes the issue
    // this line shall reset the whole text to default style
    ca.setStyleSpans(0, computeHighlighting(ca.getText())); // <-- ISSUE
    // after text is set to default i want to change some specific chars
    // this works fine, but if i click again somewhere the line above makes now a jump
    // to Position 37
    if(ca.getCaretPosition() == 1648)
    {
        ca.setStyleClass(37, 38, "highlight");
        ca.setStyleClass(ca.getCaretPosition()-1, ca.getCaretPosition(), "highlight");
    }

};
Nodes.addInputMap(ca, testEvent);
ca.setOnMouseClicked(mouseClick);

I navigate to the position 1648 by using the Keyboard (cause i consumed the default MousePressed event) and then i clicked with the mouse so my EventHandler is doing its job but its the same result.

If i delete one of the (doesnt matter which one) ca.setStyleClass() lines the problem is gone but if both lines are activated the scrolljump is real.

@JordanMartinez
Copy link
Contributor

I ran the following code and clicked the mouse in the area. The scroll jump was seen without the two setStyleClass calls, indicating that this issue is a duplicate of #390. (Maybe you didn't see my first comment?)

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import org.fxmisc.flowless.VirtualizedScrollPane;
import org.fxmisc.richtext.CodeArea;
import org.fxmisc.richtext.model.StyleSpans;
import org.fxmisc.richtext.model.StyleSpansBuilder;
import org.fxmisc.wellbehaved.event.EventPattern;
import org.fxmisc.wellbehaved.event.InputMap;
import org.fxmisc.wellbehaved.event.Nodes;

import java.util.Collection;
import java.util.Collections;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class TwiceBug extends Application {

    // Patterns to identify TAGS like <div> or <table>
    private final Pattern PATTERN = Pattern.compile("(?<TAG><[^\\?](.*?)[ >]|/>)", Pattern.DOTALL);
    
    @Override
    public void start(Stage primaryStage) throws Exception {
        CodeArea ca = new CodeArea();

        ca.setPrefHeight(200);
        VBox box = new VBox(new VirtualizedScrollPane<>(ca));
        primaryStage.setScene(new Scene(box));
        primaryStage.show();

        ca.replaceText("<div class=\"alert alert-danger\">\n" +
                "    <table>\n" +
                "        <tr>\n" +
                "            <td><span class=\"fa fa-close ft_size_27 mg_right_20\"></span></td>\n" +
                "            <td>\n" +
                "            {*\n" +
                "                1: Username (> 255 || < 5)\n" +
                "                2: Firstname (>255 || < 5)\n" +
                "                3: Lastname (>255 || < 5)\n" +
                "                4: EMail not valid\n" +
                "                5: EMail already exists\n" +
                "                6: Username already exists\n" +
                "                7: Password too short or too long\n" +
                "                8: Passwords doesnt match\n" +
                "            *}\n" +
                "            {$76be98f9171351b8444a6287fb7f31b3a6c28802}: \n" +
                "            {if $error eq 1}\n" +
                "                {$7884fc242eaf64d217d37262b0548582047b29b1}\n" +
                "            {/if}\n" +
                "            {if $error eq 2}\n" +
                "                {$bdfb20b48b66009e556c6437dc020a34a8c6fbf7}\n" +
                "            {/if}\n" +
                "            {if $error eq 3}\n" +
                "                {$d49a9d3450b7852da8932886b6e748a49c11621c}\n" +
                "            {/if}\n" +
                "            {if $error eq 4}\n" +
                "                {$1f91fe7c8c2c73855fcd86bc09c3c769f31ad245}\n" +
                "            {/if}\n" +
                "            {if $error eq 5}\n" +
                "                {$5e7ebc57cef21ce27246943fc69b4edd14f77f34}\n" +
                "            {/if}\n" +
                "            {if $error eq 6}\n" +
                "                {$4e13086230c1f4a3e537a6897f75118a25c544ae}\n" +
                "            {/if}\n" +
                "            {if $error eq 7}\n" +
                "                {$d8b61303910a0b8181828955fa57228821c4a94d}\n" +
                "            {/if}\n" +
                "            {if $error eq 8}\n" +
                "                {$d8b61303910a0b8181828955fa57228821c4a94d}\n" +
                "            {/if}\n" +
                "            <br/>\n" +
                "                <a href=\"\" onClick=\"history.go(-1);return true;\">\n" +
                "                    {$5282443314cba22f63d935a02958e64895f0053f}\n" +
                "                </a>\n" +
                "            </td>\n" +
                "        </tr>\n" +
                "    </table>\n" +
                "</div><!-- Hinweisbox: danger -->");

        // ignore any default behavior
        Nodes.addInputMap(ca, InputMap.consume(EventPattern.mousePressed()));

        ca.setOnMouseClicked((event) -> {
            // simulate correct user click
            ca.moveTo(1648);

            ca.setStyleSpans(0, computeHighlighting(ca.getText()));

            if(ca.getCaretPosition() == 1648) {
//                    ca.setStyleClass(37, 38, "highlight");
//                    ca.setStyleClass(ca.getCaretPosition()-1, ca.getCaretPosition(), "highlight");
                }
        });

        // simulate scroll to the bottom
        ca.showParagraphAtBottom(ca.getParagraphs().size() - 1);
        
        // now user needs to manually click in the area
    }

    public StyleSpans<Collection<String>> computeHighlighting(String text) {
        Matcher matcher = PATTERN.matcher(text);
        int lastKwEnd = 0;
        StyleSpansBuilder<Collection<String>> spansBuilder = new StyleSpansBuilder<>();
        while (matcher.find()) {
            String styleClass = matcher.group("TAG") != null ? "tag" : null;
            assert styleClass != null;
            spansBuilder.add(Collections.emptyList(), matcher.start() - lastKwEnd);
            spansBuilder.add(Collections.singleton(styleClass), matcher.end() - matcher.start());
            lastKwEnd = matcher.end();
        }

        spansBuilder.add(Collections.emptyList(), text.length() - lastKwEnd);
        return spansBuilder.create();
    }
}

Also, just to be clearer, the above code is what I mean by a reproducible demo. Yours doesn't quite fit it because I can't copy and paste into my IDE and immediately run it.

Please reopen if that wasn't the issue and I somehow misunderstood you.

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