0
votes

I have a JTextPane inside JScrollPane (with horizontal policy NEVER and vertical policy ALWAYS). I have written a method to append text in this JTextPane. Now there is a button and on click of the button, action listener is running doing some data manipulation and updating the data on JTextPane. I am using JScrollPane.update() method to refresh the appended text. Text is updating correctly until the height of the JTextPane is reached. As more text is added, it should show vertical scrollbar and update the text. But this is not happening. Instead, the whole text appears as the flow is out from action listener method.

I have tried with updating both the JTextPane and JScrollPane. But it doesn't seem to be working.

JTextPane txt_message = new JTextPane();
txt_message.setPreferredSize(new Dimension(300,300));
JScrollPane sPane = new JScrollPane(txt_message);
sPane.setHorizontalScrollBarPolicy
           (JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
sPane.setVerticalScrollBarPolicy
           (JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
sPane.setPreferredSize(new Dimension(320,320));
pnl_messagePane.add(sPane);
txt_message.setEditable(false);
DefaultCaret caret = (DefaultCaret) txt_message.getCaret();
caret.setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE);

//some other GUI elements
..... 

btn_Run.addActionListener(e->{
  for(int i=1; i<100; i++){
       appendToPanel(this.txt_message, "dummy text "+i, TextType.SUCCESS);
  }

//TextType is an enum defined in code whose value can be SUCCESS, ERROR

});



// Method for appending JTextPanel Text
private void appendToPane(JTextPane tp, String msg, TextType type)
   {
       Color c = null;
       switch(type) {
       case ERROR:
           c = Color.RED;
           break;

       case SUCCESS:
           c=Color.GREEN;
           break;
       default:
           c = Color.BLACK;
       }


       StyledDocument doc = tp.getStyledDocument();

       SimpleAttributeSet keyWord = new SimpleAttributeSet();
       StyleConstants.setForeground(keyWord, c);
       StyleConstants.setBold(keyWord, true);

       try
       {
           doc.insertString(doc.getLength(), "\n"+msg, keyWord );
       }

       catch(Exception e) { 
           System.out.println(e); 
       }

       tp.update(tp.getGraphics());

       this.sPane.update(this.sPane.getGraphics());
   }


As the loop is running, it should update the text in JTextPane as
dummy text 1
dummy text 2
dummy text 3
. . .

Let's suppose after dummy text 10, it reached to the JScrollPane height.
Now expected behaviour is --
it should keep on updating new text like
dummy text 11
dummy text 12
.. and vertical scrollbar should appear.

But actual behaviour is --
it works fine until dummy text 10 (assuming the height of JScrollPane could hold 10 lines). After that the JTextPane flickers but nothing happens and as the loop completes, it shows the remaining 90 line at once with vertical scrollbar.

dummy text 11
dummy text 12
.
.
.

I guess the problem is somewhere in visibility of JScrollBar when I am appending more and more lines.

Any help would be appreciated.

1
In your case, following improvement could help: SwingUtilities.invokeLater(() -> appendToPanel(this.txt_message, "dummy text "+i, TextType.SUCCESS)). But better read about concurrency in Swing. - Sergiy Medvynskyy
Tried the above, but it is not working. - Chirag Bansal
In this case you must read about Concurrency in Swing and follow the examples from that page. - Sergiy Medvynskyy

1 Answers

1
votes

Instead, the whole text appears as the flow is out from action listener method.

Code invoked from a listener executes on the Event Dispatch Thread (EDT).

Therefore, as has already been mentioned, your looping code is executing on the EDT. The EDT is responsible for repainting the GUI but it can't do so until the main loop has finished executing.

it works fine until dummy text 10 (assuming the height of JScrollPane could hold 10 lines). After that the JTextPane flickers but nothing happens

It only appear to work because you are using Swing incorrectly:

tp.update(tp.getGraphics());
this.sPane.update(this.sPane.getGraphics());

You should NOT be invoking the update(...) method directly. Swing will invoke this method internally when a component needs to be repainted. Forcing the update causes the flickering. Don't do this!

Instead to update the text area you need to execute your code on a separate Thread so you don't block the EDT.

One way to do this is to use a SwingWorker. It created the Thread for you and allows you to publish() results to your text pane as required so that the text pane is then updated correctly on the EDT.

The Concurrency link you have been given in the comments section has a section on How to Use a SwingWorker with working examples to get you started. The tutorial also explains how the EDT works.