From 506316b2be8be75f095bf08ef0d4cbc846cf8ea2 Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Wed, 18 Sep 2024 18:42:00 -0500 Subject: [PATCH] Fix input harvester scrolling bug When a dialog is large enough to create scroll bars, and the dialog contains a message widget (String with visibility=MESSAGE), the dialog's view would snap back to scroll the first such message widget into view. It happens when setText is called on the widget's JTextArea, even when the text being set is the same as it was previously. I'm guessing this is a bug in Java somehow. Not sure whether the snap-back is intended to occur when the text changes, but in the case of SciJava message parameters, the text typically never changes, so we can avoid the issue in the vast majority of scenarios by only calling setText when the text *has* actually changed. Unfortunately, a JTextPane in text/html mode is backed by some pretty fancy logic that normalizes the input HTML, such that the final assigned text does not precisely match the input text. For example: Hello becomes something like: Hello and then a naive string comparison fails. To work around this fact, this patch introduces a dummy JTextPane for the sole purpose of filtering the input text, so that it can be compared against the real JTextPane's current text before we attempt to assign it needlessly. Closes #74. --- .../ui/swing/widget/SwingMessageWidget.java | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/scijava/ui/swing/widget/SwingMessageWidget.java b/src/main/java/org/scijava/ui/swing/widget/SwingMessageWidget.java index a1adf91..ae90ec0 100644 --- a/src/main/java/org/scijava/ui/swing/widget/SwingMessageWidget.java +++ b/src/main/java/org/scijava/ui/swing/widget/SwingMessageWidget.java @@ -30,6 +30,7 @@ package org.scijava.ui.swing.widget; import java.io.IOException; +import java.util.Objects; import javax.swing.JEditorPane; import javax.swing.JPanel; @@ -124,6 +125,24 @@ public boolean supports(final WidgetModel model) { @Override public void doRefresh() { // maybe dialog owner changed message content - pane.setText(get().getText()); + String text = get().getText(); + if (!Objects.equals(htmlify(text), pane.getText())) { + // NB: Only change the text if it actually changed. + // This avoids triggering a scrollRectToVisible-type behavior where the + // containing scroll pane's view gets adjusted to include this text area. + // Not sure if it's a bug, strictly speaking, but it causes undesirable + // sudden scrolling, as reported in scijava/scijava-ui-swing#74. + pane.setText(text); + } + } + + // HACK: Normalize text to final HTML representation + // by feeding it through a dummy JEditorPane instance. + private JEditorPane dummy; + private String htmlify(String text) { + if (text == null) return null; + if (dummy == null) dummy = new JEditorPane("text/html", ""); + dummy.setText(text); + return dummy.getText(); } }