Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-4476714

DefaultCaret causes spurious scrolling in multi-line text components

XMLWordPrintable

    • Icon: Bug Bug
    • Resolution: Duplicate
    • Icon: P4 P4
    • None
    • 1.3.0
    • client-libs



      Name: bsC130419 Date: 06/29/2001


      C:\>java -version
      java version "1.3.0"
      Java(TM) 2 Runtime Environment, Standard Edition (build 1.3.0-C)
      Java HotSpot(TM) Client VM (build 1.3.0-C, mixed mode)

      Due to the way in which DefaultCaret attempts to scroll itself into view, there
      are some unusual behaviors that occur when updating viewports that contain
      multi-line text components (such as JTextArea). The protected method
      adjustVisibility(Rectangle nloc) is called when the text area is modified,
      which causes the scroll pane to scroll to each text area in the viewport. This
      behavior is especially noticeable when information in the text area changes
      asynchronously or programmatically.

      The following code illustrates this behavior:
      ------------------------------8<-------------------------------
      import java.awt.*;
      import java.awt.event.*;
      import javax.swing.*;
      import javax.swing.event.*;
      import javax.swing.text.*;

      public class ScrollTest extends JFrame implements ChangeListener
      {
          JTextArea area1 = new JTextArea("1", 8, 20);
          JTextArea area2 = new JTextArea("2", 8, 20);
          JTextArea area3 = new JTextArea("3", 8, 20);
          JScrollPane scrollPane = new JScrollPane();

          public ScrollTest()
          {
              this.setLocation(100, 100);
              this.setSize(300, 300);

              JPanel panel = new JPanel(new GridBagLayout());
              GridBagConstraints gbc = new GridBagConstraints();
              gbc.gridx = 0;
              gbc.gridy = GridBagConstraints.RELATIVE;
              panel.add(area1, gbc);
              panel.add(area2, gbc);
              panel.add(area3, gbc);

              scrollPane.setViewportView(panel);
              scrollPane.getVerticalScrollBar().getModel().addChangeListener(this);

              this.getContentPane().add(scrollPane, BorderLayout.CENTER);

              JButton button = new JButton("update");
              this.getContentPane().add(button, BorderLayout.SOUTH);
              button.addActionListener(new ActionListener()
              {
                  public void actionPerformed(ActionEvent event)
                  {
                      area1.setText("1\n\n\n\n\n\n\n\n\n\n\n\nWahhoooo!");
                      area2.setText("2\n\n\n\n\n\n\n\n\n\n\n\nWahhoooo!");
                      area3.setText("3\n\n\n\n\n\n\n\n\n\n\n\nWahhoooo!");
                  }
              });
              this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
          }

          public void stateChanged(ChangeEvent event)
          {
              System.out.println(scrollPane.getVerticalScrollBar().getModel());
          }

          public static void main(String[] args)
          {
              new ScrollTest().setVisible(true);
          }
      }
      ------------------------------8<-------------------------------
      Clicking the button will cause the viewport to scroll such that the last line
      of the bottom-most text area is visible. Note that the individual JTextArea
      components are NOT in JScrollPane containers.

      The output from the console is this:
      javax.swing.DefaultBoundedRangeModel[value=0, extent=243, min=0, max=408,
      adj=false]
      javax.swing.DefaultBoundedRangeModel[value=0, extent=243, min=0, max=663,
      adj=false]
      javax.swing.DefaultBoundedRangeModel[value=199, extent=243, min=0, max=663,
      adj=false]
      javax.swing.DefaultBoundedRangeModel[value=420, extent=243, min=0, max=663,
      adj=false]

      This shows the problem in its essence. The last three model changes are caused
      by the DefaultCaret class when it detects a change in the text component and
      attempts to scroll the viewport so the changed component is visible. This
      wreaks havoc since it will cause the viewport to scroll even if someone is
      doing data entry in another field. The adjustments are asynchronous and
      controlled by inaccessible methods in DefaultCaret.

      I have come up with two workarounds. Simplest is to enclose all of the
      JTextAreas in a JScrollPane. This seemed a little draconian, so I tried
      another route. By extending DefaultCaret, I was able to skirt the problem
      sufficiently:

      ------------------------------8<-------------------------------
              area1.setCaret(new NoScrollCaret());
              area2.setCaret(new NoScrollCaret());
              area3.setCaret(new NoScrollCaret());
              .
              .
          class NoScrollCaret extends DefaultCaret
          {
              protected void adjustVisibility(Rectangle rect)
              {
                  JTextComponent component = this.getComponent();
                  if (component.getParent().getClass() == JViewport.class)
                      super.adjustVisibility(rect);
              }
          }
      ------------------------------8<-------------------------------
      In this manner, if the immediate parent of a text component is not a viewport,
      the adjustment is not made.
      (Review ID: 127325)
      ======================================================================

            naasunw Naa Naa (Inactive)
            bstrathesunw Bill Strathearn (Inactive)
            Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

              Created:
              Updated:
              Resolved:
              Imported:
              Indexed: