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:
with combination of custom comboboxeditor
s , listcellrenderer
s i've been able this:
which has inconveniences of:
- cutting off components beyond original width of
jcombobox
- forcing height of
jcombobox
height ofjpanel
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
Post a Comment