java - JPanel in JComboBox dropdown but not in the editor -


what want seems relatively simple, , i'm there. used extend tablecelleditor, size important. want this: mock-up of ideal jpanelcombobox

with combination of custom comboboxeditors , listcellrenderers i've been able this:

mock-up of best attempt @ jpanelcombobox

which has inconveniences of:

  • cutting off components beyond original width of jcombobox
  • forcing height of jcombobox height of jpanel in drop-down
  • allowing 1 (1) click modification of form before drop-down disappears.

i'd have drop-down stay visible until user clicks editor or jcombobox looses focus control, , have value in editor update. there ever 1 (1) jpanel in drop-down , don't want editor able edit string displayed.

my question similar @erkanhaspalut 's question here neither response satisfying. i'd made similar attempt embedding jpanel in jpopupmenu , adding jtextfield had similar issues popup disappearing prematurely.

i've tried forcing size of jcombobox both setting setmaximumsize height value (which has no effect) , with

    rectangle tmp = cbotest.getbounds();     tmp.height = 24;     cbotest.setbounds(tmp); 

which shows top 24 lines of jcombobox. minimum compilable example

/*     * program test having jpanel in jcombobox's drop-down not jcombobox's editor.     */     package testdropdownsubform;      import java.awt.component;     import java.awt.rectangle;     import java.awt.event.actionlistener;     import java.awt.event.focusevent;     import java.awt.event.focuslistener;     import javafx.application.application;     import javafx.stage.stage;     import javax.swing.comboboxeditor;     import javax.swing.jcombobox;     import javax.swing.jlist;     import javax.swing.listcellrenderer;     import javax.swing.event.popupmenuevent;     import javax.swing.event.popupmenulistener;     import javax.swing.jtextfield;     import javax.swing.plaf.basic.basiccomboboxeditor;      /**     * @author masked coder     */      public class dim {         public long dimwidth;         public long dimheight;          public dim () {             dimwidth = 1l;             dimheight = 1l;         }          @override         public string tostring() {             return dimwidth.tostring() + "\' x " + dimheight.tostring() + "\'";         }     }      public class dimpanel extends javax.swing.jpanel {          public dimpanel() {             spndimwidth = new javax.swing.jspinner();             spndimheight = new javax.swing.jspinner();              spndimwidth.setmodel(new javax.swing.spinnernumbermodel(long.valueof(1l), long.valueof(0l), null, long.valueof(1l)));             spndimwidth.setpreferredsize(new java.awt.dimension(50, 24));             addcomponent(spndimwidth);              lbltween.settext(" x ");             addcomponent(lbltween);              spndimheight.setmodel(new javax.swing.spinnernumbermodel(long.valueof(1l), long.valueof(0l), null, long.valueof(1l)));             spndimheight.setpreferredsize(new java.awt.dimension(50, 24));             addcomponent(spndimheight);         }          private javax.swing.jspinner spndimwidth;         private javax.swing.jlabel lbltween;         private javax.swing.jspinner spndimheight;     }       public class dimlistcellrenderer implements listcellrenderer {         private dimpanel dpdim;          public dimlistcellrenderer(dimpanel newdimpanel) {             dpdim = newdimpanel;          }          @override         public component getlistcellrenderercomponent(jlist list, object value, int index, boolean isselected, boolean cellhasfocus) {             dim dvalue = (dim) value;             dpdim.setdim(dvalue);             return dpdim;         }      }      public class dimcomboboxeditor extends basiccomboboxeditor implements comboboxeditor {         jtextfield txtdim = new jtextfield();         dim item = new dim();          public dimcomboboxeditor() {             txtdim.setenabled(false);             txtdim.setopaque(true);         }         @override         public component geteditorcomponent() {             txtdim.settext(item.tostring());             return txtdim;         }          @override         public void setitem(object anobject) {             item = (dim) anobject;         }          @override         public object getitem() {             return item;         }          @override         public void selectall() {             txtdim.selectall();         }          @override         public void addactionlistener(actionlistener l) {             txtdim.addactionlistener(l);         }          @override         public void removeactionlistener(actionlistener l) {             txtdim.removeactionlistener(l);         }      }      public class maintestform extends javax.swing.jframe {         public maintestform() {             lblprevcomponent = new javax.swing.jlabel();             chkprevcomponent = new javax.swing.jcheckbox();             lbltest = new javax.swing.jlabel();             cbotest = new jcombobox<testdropdownsubform.dicepanel>();             lblnextcomponent = new javax.swing.jlabel();             scpnextcomponent = new javax.swing.jscrollpane();             txanextcomponent = new javax.swing.jtextarea();             btnforceheight = new javax.swing.jbutton();              setdefaultcloseoperation(javax.swing.windowconstants.exit_on_close);              lblprevcomponent.settext("prev. component");              chkprevcomponent.settext("jcheckbox1");              lbltest.settext("dimension");              cbotest.seteditable(true);             cbotest.seteditor(new dimcomboboxeditor());             cbotest.setrenderer(new dimlistcellrenderer(new dimpanel()));             cbotest.additem(new dim());              lblnextcomponent.settext("next component");              txanextcomponent.setcolumns(20);             txanextcomponent.setrows(5);             scpnextcomponent.setviewportview(txanextcomponent);              btnforceheight.settext("force");             btnforceheight.settooltiptext("force test combobox height");             btnforceheight.addactionlistener(new java.awt.event.actionlistener() {                 public void actionperformed(java.awt.event.actionevent evt) {                     btnforceheightactionperformed(evt);                 }             });              .addcomponent(lblprevcomponent)             .addcomponent(chkprevcomponent))             .addcomponent(lbltest)             .addcomponent(cbotest)             .addcomponent(lblnextcomponent)             .addcomponent(scpnextcomponent)             .addcomponent(btnforceheight))          }      private void btnforceheightactionperformed(java.awt.event.actionevent evt) {                                                            rectangle tmp = cbotest.getbounds();             tmp.height = 24;             cbotest.setbounds(tmp);     }                                                        /**         * @param args command line arguments         */         public static void main(string args[]) {             /* create , display form */             java.awt.eventqueue.invokelater(new runnable() {                 public void run() {                     new maintestform().setvisible(true);                 }             });         }          private javax.swing.jbutton btnforceheight;         private javax.swing.jcombobox cbotest;         private javax.swing.jcheckbox chkprevcomponent;         private javax.swing.jlabel lblnextcomponent;         private javax.swing.jlabel lblprevcomponent;         private javax.swing.jlabel lbltest;         private javax.swing.jscrollpane scpnextcomponent;         private javax.swing.jtextarea txanextcomponent;     }      public class testdropdownsubform extends application {          @override         public void start(stage primarystage) {             maintestform mtfmain = new maintestform();              mtfmain.setvisible(true);         }          /**         * @param args command line arguments         */         public static void main(string[] args) {             launch(args);         }      } 

my question is, missing detail, or can see better way accomplish this? in advance advice.

edit: declaring jcombobox

jcombobox cbotest = new jcombobox<dimpanel>() {     private boolean layingout = false;       @override     public void dolayout(){          try{          layingout = true;          super.dolayout();          }finally{          layingout = false;          }      }           @override         public dimension getsize(){              dimension dim = super.getsize();              if(!layingout) {                 dim.width = math.max(dim.width, dpthis.getpreferredsize().width);              }             return dim;          }  }; 

fixes width of drop-down. declaring

jcombobox cbotest = new jcombobox<testdropdownsubform.dicepanel>() {     private boolean layingout = false;       @override     public void dolayout(){          try{          layingout = true;          super.dolayout();          }finally{          layingout = false;          }      }           @override         public dimension getsize(){              dimension dim = super.getsize();              if(!layingout) {                 dimension dim2 = dpthis.getpreferredsize();                  dim.width = math.max(dim.width, dim2.width);      //          dim.height = dim2.height;              }             return dim;          }       @override     public dimpanel getprototypedisplayvalue() {         dimpanel tmppanel = new dimpanel();         if(iscalledfromcombopopup()) {         //         }         else {         dimension r = dcbeeditor.getpreferredsize();         tmppanel.setpreferredsize(r);         }         return tmppanel;     }      /**     * hack method determine if called within combo popup ui.     */     public boolean iscalledfromcombopopup() {         try {         final throwable t = new throwable();         t.fillinstacktrace();         stacktraceelement[] st = t.getstacktrace();         // @ top 5 elements of call stack         int max = math.min(st.length, 5);         (int i=0; i<max; ++i) {             final string name = st[i].getclassname();             system.out.println(i + ")  " + name);             return ((name != null) && name.contains("combopopup"));         }         } catch (final exception e) {         // if there problem, assume not called combo popup         }         return false;     } }; 

fixes editor size but drop-down width , height of editor.

so, ended going on different track, creating own pseudo-combobox jtextfield , jbutton in jpanel. believe i've quite handily genericized result 4 related classes. first, need implementation of abstractpopuppanel (which quite handily loads netbeans designer if you're using it):

package complexcombobox;  /** * * @author maskedcoder * * @param e - object edited in panel */ public abstract class abstractpopuppanel<e> extends javax.swing.jpanel {      /**     * creates new form popuppanel     */     public abstractpopuppanel() {         initcomponents();     }      public abstract boolean ischanged();      public abstract e getvalue();      public abstract void setvalue(e newvalue);      private void initcomponents() {         javax.swing.grouplayout layout = new javax.swing.grouplayout(this);         this.setlayout(layout);         layout.sethorizontalgroup(             layout.createparallelgroup(javax.swing.grouplayout.alignment.leading)             .addgap(0, 400, short.max_value)         );         layout.setverticalgroup(             layout.createparallelgroup(javax.swing.grouplayout.alignment.leading)             .addgap(0, 300, short.max_value)         );     }                         } 

with created satisfaction, need place in pop-up menu:

package complexcombobox;  import javax.swing.jpopupmenu; import javax.swing.event.popupmenuevent; import javax.swing.event.popupmenulistener;  /** * * @author maskedcoder *  * @param e - object edited * @param p - extension of abstractpopuppanel display object wish */ public class complexpopup<e, p extends abstractpopuppanel<e>> extends jpopupmenu {      /*     * interface notify user of editing     */     public interface editlistener<e> {         // return object's value edited.         // if user returns null, existing dice used.         public e beforeedit();           // receives new value         // called *only* when object has changed.         public void afteredit(e newvalue);       }      /*     *internal variables     */     private p thispanel;     private editlistener<e> userlistener; //  private class<e> eclass; //  private class<p> pclass;     private popupmenulistener mypopuplistener = new popupmenulistener() {          @override         public void popupmenuwillbecomevisible(popupmenuevent e) {             if(userlistener != null) {                 e tmpsize = userlistener.beforeedit();                 if(tmpsize != null) thispanel.setvalue(tmpsize);             }         }          @override         public void popupmenuwillbecomeinvisible(popupmenuevent e) {             if(userlistener != null) { //              if(thispanel.ischanged())                     userlistener.afteredit((e) thispanel.getvalue());             }         }          @override         public void popupmenucanceled(popupmenuevent e) {             popupmenuwillbecomeinvisible(e);         }     };      /*      * constructors     */      public complexpopup(e inivalue, p inidropdownpanel, editlistener<e> inilistener) {         super();          init(inivalue, inidropdownpanel, inilistener);     }      private void init(e inivalue, p inidropdownpanel, editlistener<e> inilistener) {         thispanel = inidropdownpanel;         thispanel.setvalue(inivalue);         add(thispanel);         userlistener = inilistener;         this.addpopupmenulistener(mypopuplistener);     }      /*      * public properties      */     public e getvalue() {         return (e) thispanel.getvalue();     }     public void setvalue(e newobjectsize) {         thispanel.setvalue(newobjectsize);     }      public editlistener getuserlistener() {         return userlistener;     }      public void seteditlistener(editlistener newlistener) {         userlistener = newlistener;     }      /*     * functional extensions     */     public void afteredit(e newvalue) {         if(userlistener != null) {             if(thispanel.ischanged())                 userlistener.afteredit((e) thispanel.getvalue());         }     } } 

this places form in jpopupmenu , allows access object edited notification before , after editing. now, couldn't co-opt jcombobox interface without getting deeper mechanics wanted go, fudged combo box, mentioned:

package complexcombobox;  /** * * @author maskedcoder *  * @param <i> item (object) edited * @param <q> abstractpopuppanel used editing.  */ public class complexcombobox<i, q extends abstractpopuppanel<i>> extends javax.swing.jpanel {     private complexpopup<i, q> thepopup;     private complexpopup.editlistener<i> mylistener = new complexpopup.editlistener<i>() {          @override         public beforeedit() {             return null; // no changes so, let ride.         }          @override         public void afteredit(i newvalue) {             txteditor.settext(thepopup.getvalue().tostring());         }     };      /**     * creates new form objectsizecombobox     */     public complexcombobox(i inivalue, q inipanel) {         initcomponents();         thepopup = new complexpopup<i, q>(inivalue, inipanel, mylistener);     }      public getvalue() {         return thepopup.getvalue();     }      public void setvalue(i newvalue) {         thepopup.setvalue(newvalue);     }      private void initcomponents() {          txteditor = new javax.swing.jtextfield();         btnshowpanel = new javax.swing.jbutton();          setbackground(new java.awt.color(255, 255, 255));         setminimumsize(new java.awt.dimension(40, 19));          txteditor.seteditable(false);         txteditor.settooltiptext("");         txteditor.setopaque(false);          btnshowpanel.seticon(new javax.swing.imageicon(getclass().getresource("/testpopupsubform/art/dropdownarrow.png"))); // noi18n         btnshowpanel.addactionlistener(new java.awt.event.actionlistener() {         public void actionperformed(java.awt.event.actionevent evt) {             btnshowpanelactionperformed(evt);         }         });          javax.swing.grouplayout layout = new javax.swing.grouplayout(this);         this.setlayout(layout);         layout.sethorizontalgroup(         layout.createparallelgroup(javax.swing.grouplayout.alignment.leading)         .addgroup(layout.createsequentialgroup()             .addcomponent(txteditor, javax.swing.grouplayout.default_size, 115, short.max_value)             .addgap(1, 1, 1)             .addcomponent(btnshowpanel, javax.swing.grouplayout.preferred_size, 24, javax.swing.grouplayout.preferred_size))         );         layout.setverticalgroup(         layout.createparallelgroup(javax.swing.grouplayout.alignment.leading)         .addcomponent(btnshowpanel, javax.swing.grouplayout.default_size, javax.swing.grouplayout.default_size, short.max_value)         .addcomponent(txteditor)         );     }                              private void btnshowpanelactionperformed(java.awt.event.actionevent evt) {                                                          thepopup.show(txteditor, 0, txteditor.getheight());     }                                                  private javax.swing.jbutton btnshowpanel;     private javax.swing.jtextfield txteditor; } 

this seems work in cell editor table, provided make row heights tall enough. that's no different regular jcombobox editor, though:

package complexcombobox;  import java.awt.component; import javax.swing.abstractcelleditor; import javax.swing.jtable; import javax.swing.table.tablecelleditor;  /** * * @author maskedcoder * * @param <e> element (object) edited * @param <p> abstractpopuppanel used editing.  */ public class complexcelleditor<e, p extends abstractpopuppanel<e>> extends abstractcelleditor             implements tablecelleditor {     private complexcombobox<e, p> complexeditor;      /*     * constructors     */     public complexcelleditor(e inivalue, p inipanel) {          super();          complexeditor = new complexcombobox<e, p>(inivalue, inipanel);     }      /*     * abstractcelleditor extensions     */     @override     public boolean stopcellediting() {             return super.stopcellediting();     }      /*     * tablecelleditor implementation     */         @override     public e getcelleditorvalue() {             return complexeditor.getvalue();     }      @override     public component gettablecelleditorcomponent(jtable initable, object inivalue, boolean isselected, int row, int column) {             complexeditor.setvalue((e) inivalue);              return complexeditor;     } } 

three things i'm not entirely satisfied with: - i'd grab jcombobox down-arrow icon system rather relying on project resource - i've had pass through abstractpopuppanel instance means coder has create panel , hand constructor complexcombobox or complexcelleditor. i'd rather have created panel internally, in complexpopup , managed myself. ran difficulty instantiating generic constructors. pass-through allow coder more control don't think that's needed , i'd optional @ least.

my knowledge of generics isn't advanced seems trick.


Comments

Popular posts from this blog

android - Gradle sync Error:Configuration with name 'default' not found -

java - Andrioid studio start fail: Fatal error initializing 'null' -

html - jQuery UI Sortable - Remove placeholder after item is dropped -