java - JScrollBar + JTextPane with HTML not properly scrolling to maximum value -
i have following problem in project of mine, took me while figure out whats causing problem, , can reproduce simple code attached.
i dynamically adding content jtextpane htmleditorkit. set autoscroll off because want control manually (when user scrolled up, stop, , when event triggered activated again).
the problem is, when set value of jscrollbar it's maximum value, it's different one, moment, after having content inserted htmldocument. when trigger setvalue again second time manually, scrolls correct maximum value.
it seems jscrollbar not aware correct maximumvalue right after adding htmldocument, , (delayed) time later.
using
caret.setupdatepolicy(defaultcaret.always_update);
is not solution, because doesn't work properly. doesn't scroll maximum value too, leaving view pixel below, don't want.
here full code reproducing issue. if click on right button (add & scroll), inserts div element body. moment last visible line reached, doesn't scroll correctly last maximum value, last line hidden. when click on left button manually trigger second scrolltoend(), scrolls correctly maximum value.
code:
/* * change license header, choose license headers in project properties. * change template file, choose tools | templates * , open template in editor. */ package javaapplication26; import java.io.ioexception; import java.math.biginteger; import java.security.securerandom; import java.util.logging.level; import java.util.logging.logger; import javax.swing.text.badlocationexception; import javax.swing.text.defaultcaret; import javax.swing.text.element; import javax.swing.text.html.htmldocument; import javax.swing.text.html.htmleditorkit; public class newjframe extends javax.swing.jframe { /** * creates new form newjframe */ public newjframe() { initcomponents(); this.setsize(500, 200); this.setlocationrelativeto(null); this.jtextpane1.seteditorkit(new htmleditorkit()); this.jtextpane1.setcontenttype("text/html"); this.jtextpane1.settext("<html><body><div id=\"globaldiv\"></div></body></html>"); this.jscrollpane1.sethorizontalscrollbarpolicy(javax.swing.scrollpaneconstants.horizontal_scrollbar_never); this.jscrollpane1.setverticalscrollbarpolicy(javax.swing.scrollpaneconstants.vertical_scrollbar_as_needed); defaultcaret caret = (defaultcaret) this.jtextpane1.getcaret(); caret.setupdatepolicy(defaultcaret.never_update); this.jscrollpane1.setautoscrolls(false); this.jtextpane1.setautoscrolls(false); } private void scrolltoend() { this.jscrollpane1.getverticalscrollbar().setvalue(this.jscrollpane1.getverticalscrollbar().getmaximum()); //this.jtextpane1.setcaretposition(this.jtextpane1.getdocument().getlength()); } /** * method called within constructor initialize form. * warning: not modify code. content of method * regenerated form editor. */ @suppresswarnings("unchecked") // <editor-fold defaultstate="collapsed" desc="generated code"> private void initcomponents() { jpanel1 = new javax.swing.jpanel(); jscrollpane1 = new javax.swing.jscrollpane(); jtextpane1 = new javax.swing.jtextpane(); jpanel2 = new javax.swing.jpanel(); jbutton1 = new javax.swing.jbutton(); jbutton2 = new javax.swing.jbutton(); setdefaultcloseoperation(javax.swing.windowconstants.exit_on_close); jpanel1.setlayout(new java.awt.borderlayout()); jscrollpane1.setviewportview(jtextpane1); jpanel1.add(jscrollpane1, java.awt.borderlayout.center); getcontentpane().add(jpanel1, java.awt.borderlayout.center); jbutton1.settext("scroll end"); jbutton1.addactionlistener(new java.awt.event.actionlistener() { public void actionperformed(java.awt.event.actionevent evt) { jbutton1actionperformed(evt); } }); jpanel2.add(jbutton1); jbutton2.settext("add & scroll"); jbutton2.addactionlistener(new java.awt.event.actionlistener() { public void actionperformed(java.awt.event.actionevent evt) { jbutton2actionperformed(evt); } }); jpanel2.add(jbutton2); getcontentpane().add(jpanel2, java.awt.borderlayout.page_end); pack(); }// </editor-fold> private void jbutton2actionperformed(java.awt.event.actionevent evt) { try { htmldocument doc = (htmldocument) this.jtextpane1.getdocument(); htmleditorkit editorkit = (htmleditorkit) this.jtextpane1.geteditorkit(); securerandom random = new securerandom(); string htmlcode = "<div style=\"background-color: #ffff22; height: 12px; font-size: 12;\">"+new biginteger(64, random).tostring(64)+"</div>"; //editorkit.inserthtml(doc, doc.getlength(), htmlcode, 0, 0, null); element element = doc.getelement("globaldiv"); if (element != null) { doc.insertbeforeend(element, htmlcode); } this.scrolltoend(); } catch (badlocationexception ex) { logger.getlogger(newjframe.class.getname()).log(level.severe, null, ex); } catch (ioexception ex) { logger.getlogger(newjframe.class.getname()).log(level.severe, null, ex); } } private void jbutton1actionperformed(java.awt.event.actionevent evt) { this.scrolltoend(); } /** * @param args command line arguments */ public static void main(string args[]) { /* set nimbus , feel */ //<editor-fold defaultstate="collapsed" desc=" , feel setting code (optional) "> /* if nimbus (introduced in java se 6) not available, stay default , feel. * details see http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html */ try { (javax.swing.uimanager.lookandfeelinfo info : javax.swing.uimanager.getinstalledlookandfeels()) { if ("nimbus".equals(info.getname())) { javax.swing.uimanager.setlookandfeel(info.getclassname()); break; } } } catch (classnotfoundexception ex) { java.util.logging.logger.getlogger(newjframe.class.getname()).log(java.util.logging.level.severe, null, ex); } catch (instantiationexception ex) { java.util.logging.logger.getlogger(newjframe.class.getname()).log(java.util.logging.level.severe, null, ex); } catch (illegalaccessexception ex) { java.util.logging.logger.getlogger(newjframe.class.getname()).log(java.util.logging.level.severe, null, ex); } catch (javax.swing.unsupportedlookandfeelexception ex) { java.util.logging.logger.getlogger(newjframe.class.getname()).log(java.util.logging.level.severe, null, ex); } //</editor-fold> /* create , display form */ java.awt.eventqueue.invokelater(new runnable() { public void run() { new newjframe().setvisible(true); } }); } // variables declaration - not modify private javax.swing.jbutton jbutton1; private javax.swing.jbutton jbutton2; private javax.swing.jpanel jpanel1; private javax.swing.jpanel jpanel2; private javax.swing.jscrollpane jscrollpane1; private javax.swing.jtextpane jtextpane1; // end of variables declaration }
this code replacement works though, leaves small gap, not scrolling maximum value:
this.jtextpane1.setcaretposition(0); this.jtextpane1.setcaretposition(this.jtextpane1.getdocument().getlength());
when insert div document, document model being updated immediately. however, jtextpane
receives notification invalid , needs laid out. notification creates event on edt processed after current event (triggered button clicked) has finished.
thus, @ moment when invoke scrolltoend()
, revalidation of jtextpane
still pending, , height of text pane still small.
in order sequence of events right, need schedule invokation of scrolltoend() in edt, using invokelater:
swingutilities.invokelater(new runnable(){ public void run(){ scrolltoend(); } });
Comments
Post a Comment