java - Mapping mouse event coordinates -
i've implemented jlayer<jpanel>
component paint zoomed graphics
of itself, descending components zoomed too. jlayer applied contentpane jframe component.
the main problem zoom applies, indeed, graphic , actual size , position of components remain same. implies mouse events happen in wrong position respectively user see.
i've king of tweaked defining glasspane
jcomponent
@ top of frame has it's own mouseinputadapter
redispatch mouseevent
s underlying components using swingutilities.getdeepestcomponentat()
. accomplished creating new mouseevent
mouse coordinates mapped depending on zoom value. (obtained modifying how use rootpanes tutorial)
this method not satisfying because lot of events can't fired (for example mouse_entered
events fired descending components). on other hand using layerui.paint()
override implies have have remap mouse coordinates.
is there way map mouse coordinates without breaking inner
mouseevent
processing?or
is there way zoom in bitmap of components while modifying real position , size? i've kind of tried calling
setsize()
of inner component seems someway calllayerui.paint()
second time bigger graphic disconnected actual widget position
this i've done far madprogrammer , pbar extensions, working jlayer in java 8.
the transformui
include in pbar's org.pbjar.jxlayer.plaf.ext.mouseeventui
, org.pbjar.jxlayer.plaf.ext.transformui
:
package jzoom.transform; import java.awt.awtevent; import java.awt.component; import java.awt.container; import java.awt.graphics; import java.awt.graphics2d; import java.awt.point; import java.awt.event.mouseevent; import java.awt.event.mousewheelevent; import java.awt.geom.affinetransform; import java.awt.geom.noninvertibletransformexception; import java.util.hashset; import java.util.set; import javax.swing.jcomponent; import javax.swing.jlayer; import javax.swing.jpanel; import javax.swing.swingutilities; import javax.swing.plaf.layerui; /** * * ui apply {@link affinetransform} visible objects, , apply inversion * of same transform mouse event's coordinates * * @author andrea.maracci based on pbjar jxlayer extension * */ public class transformui extends layerui<jcomponent> { private static final long serialversionuid = 1l; private component lastenteredtarget, lastpressedtarget; private final set<jcomponent> originaldoublebuffered = new hashset<jcomponent>(); private affinetransform transform = new affinetransform(); private jlayer<jcomponent> installedlayer; private boolean dispatchingmode = false; /** * process mouse events , map mouse coordinates inverting internal affine transformation. * * @param * event event dispatched * layer layer layerui set * */ @override public void eventdispatched(awtevent event, final jlayer<? extends jcomponent> layer) { if (event instanceof mouseevent) { mouseevent mouseevent = (mouseevent) event; /* * if discriminate between generated , original event. * removing cause stack overflow caused event being redispatched class. */ if (!dispatchingmode) { // process original mouse event dispatchingmode = true; try { redispatchmouseevent(mouseevent, layer); } { dispatchingmode = false; } } else { /* * process generated mouse events * added check, because on mouse entered or exited, cursor * may set specific dragging cursors. */ if (mouseevent.mouse_entered == mouseevent.getid() || mouseevent.mouse_exited == mouseevent.getid()) { layer.getglasspane().setcursor(null); } else { component component = mouseevent.getcomponent(); layer.getglasspane().setcursor(component.getcursor()); } } } else { super.eventdispatched(event, layer); } layer.repaint(); } /** * set affine transformation applied graphics * @param transform transformation */ public void settransform(affinetransform transform) { if (transform != null) { this.transform = transform; } } /** * return affine transformation applied graphics * @return transformation */ public affinetransform gettransform() { return transform; } /** * paint specified component {@code c} applying transformation on it's graphic * * @param * g - graphics context in paint * c - component being painted */ @override public void paint(graphics g, jcomponent c) { if (g instanceof graphics2d) { graphics2d g2 = (graphics2d) g.create(); jlayer<? extends jcomponent> l = (jlayer<? extends jcomponent>) c; g2.transform(transform); paintlayer(g2, l); g2.dispose(); } } /** * paint view decorated jlayer {@code layer} , jlayer * * @param g2 * @param layer layer layerui set */ private final void paintlayer(graphics2d g2, jlayer<? extends jcomponent> layer) { jcomponent view = layer.getview(); if (view != null) { if (view.getx() < 0 || view.gety() < 0) { settonodoublebuffering(view); g2.translate(view.getx(), view.gety()); view.paint(g2); (jcomponent jcomp : originaldoublebuffered) { jcomp.setdoublebuffered(true); } originaldoublebuffered.clear(); return; } } layer.paint(g2); } /** * disable double buffering {@code component} , of it's children * * @param component */ private void settonodoublebuffering(component component) { if (component instanceof jcomponent) { jcomponent jcomp = (jcomponent) component; if (jcomp.isdoublebuffered()) { originaldoublebuffered.add(jcomp); jcomp.setdoublebuffered(false); } } if (component instanceof container) { container container = (container) component; (int index = 0; index < container.getcomponentcount(); index++) { settonodoublebuffering(container.getcomponent(index)); } } } /** * {@inheritdoc} */ @override public void uninstallui(jcomponent component) { if (!(component instanceof jlayer<?>)) { throw new illegalargumentexception( this.getclass().getname() + " invalid class, must jlayer component"); } jlayer<jcomponent> jlayer = (jlayer<jcomponent>) component; jlayer.setlayereventmask(0); super.uninstallui(component); } /** * {@inheritdoc} */ @override public void installui(jcomponent component) throws illegalstateexception { super.installui(component); if (installedlayer != null) { throw new illegalstateexception(this.getclass().getname() + " cannot shared between multiple layers"); } if (!(component instanceof jlayer<?>)) { throw new illegalargumentexception( this.getclass().getname() + " invalid class, must jlayer component"); } // component.getclass().getdeclaringclass(); installedlayer = (jlayer<jcomponent>) component; installedlayer.setlayereventmask(awtevent.mouse_event_mask | awtevent.mouse_motion_event_mask | awtevent.mouse_wheel_event_mask | awtevent.key_event_mask | awtevent.focus_event_mask); } /** * process mouse events , map mouse coordinates inverting internal affine transformation. * consume original event, calculates mapped mouse coordinates , find real target of mouse event. * create new event correct informations in , redispatch target event * * @param originalevent event dispatched * @param layer layer layerui set */ private void redispatchmouseevent(mouseevent originalevent, jlayer<? extends jcomponent> layer) { if (layer.getview() != null) { if (originalevent.getcomponent() != layer.getglasspane()) { originalevent.consume(); } mouseevent newevent = null; point realpoint = calculatetargetpoint(layer, originalevent); component realtarget = gettarget(layer, realpoint); // component realtarget = // swingutilities.getdeepestcomponentat(layer.getview(), // realpoint.x, realpoint.y); if (realtarget != null) { //system.out.println(realtarget.getclass().getname()); realtarget = getlisteningcomponent(originalevent, realtarget); } switch (originalevent.getid()) { case mouseevent.mouse_pressed: newevent = transformmouseevent(layer, originalevent, realtarget, realpoint); if (newevent != null) { lastpressedtarget = newevent.getcomponent(); } break; case mouseevent.mouse_released: newevent = transformmouseevent(layer, originalevent, lastpressedtarget, realpoint); lastpressedtarget = null; break; case mouseevent.mouse_clicked: newevent = transformmouseevent(layer, originalevent, realtarget, realpoint); lastpressedtarget = null; break; case mouseevent.mouse_moved: newevent = transformmouseevent(layer, originalevent, realtarget, realpoint); generateenterexitevents(layer, originalevent, realtarget, realpoint); break; case mouseevent.mouse_entered: generateenterexitevents(layer, originalevent, realtarget, realpoint); break; case mouseevent.mouse_exited: generateenterexitevents(layer, originalevent, realtarget, realpoint); break; case mouseevent.mouse_dragged: newevent = transformmouseevent(layer, originalevent, lastpressedtarget, realpoint); generateenterexitevents(layer, originalevent, realtarget, realpoint); break; case (mouseevent.mouse_wheel): // redispatchmousewheelevent((mousewheelevent) originalevent, // realtarget, realpoint); newevent = transformmousewheelevent(layer, (mousewheelevent) originalevent, realtarget, realpoint); break;/**/ } dispatchmouseevent(newevent); } } /** * apply inverse transformation {@code point} * * @param layer layer layerui set * @param point starting point * @return transformed point */ private point transformpoint(jlayer<? extends jcomponent> layer, point point) { if (transform != null) { try { transform.inversetransform(point, point); } catch (noninvertibletransformexception e) { e.printstacktrace(); } } return point; } /** * find deepest component in awt hierarchy * * @param layer layer ui installed * @param targetpoint point in layer's coordinates * @return component in specified point */ private component gettarget(jlayer<? extends jcomponent> layer, point targetpoint) { component view = layer.getview(); if (view == null) { return null; } else { point viewpoint = swingutilities.convertpoint(layer, targetpoint, view); return swingutilities.getdeepestcomponentat(view, viewpoint.x, viewpoint.y); } } /** * convert {@code mouseevent}'s coordinates {@code layer}'s space * @param layer layer layerui set * @param mouseevent original mouse event * @return {@code mouseevent}'s point transformed {@code layer}'s coordinate space */ private point calculatetargetpoint(jlayer<? extends jcomponent> layer, mouseevent mouseevent) { point point = mouseevent.getpoint(); //swingutilities.convertpointtoscreen(point, mouseevent.getcomponent()); //swingutilities.convertpointfromscreen(point, layer); point = swingutilities.convertpoint(mouseevent.getcomponent(), point, layer); return transformpoint(layer, point); } private mouseevent transformmouseevent(jlayer<? extends jcomponent> layer, mouseevent mouseevent, component target, point realpoint) { return transformmouseevent( layer, mouseevent, target, realpoint, mouseevent.getid()); } /** * create new event being dispatched */ private mouseevent transformmouseevent(jlayer<? extends jcomponent> layer, mouseevent mouseevent, component target, point targetpoint, int id) { if (target == null) { return null; } else { point newpoint = swingutilities.convertpoint(layer, targetpoint, target); return new mouseevent(target, // id, // mouseevent.getwhen(), // mouseevent.getmodifiers(), // newpoint.x, // newpoint.y, // mouseevent.getclickcount(), // mouseevent.ispopuptrigger(), // mouseevent.getbutton()); } } /** * create new mouse wheel event being dispached */ private mousewheelevent transformmousewheelevent( jlayer<? extends jcomponent> layer, mousewheelevent mousewheelevent, component target, point targetpoint) { if (target == null) { return null; } else { point newpoint = swingutilities.convertpoint(layer, targetpoint, target); return new mousewheelevent(target, // mousewheelevent.getid(), // mousewheelevent.getwhen(), // mousewheelevent.getmodifiers(), // newpoint.x, // newpoint.y, // mousewheelevent.getclickcount(), // mousewheelevent.ispopuptrigger(), // mousewheelevent.getscrolltype(), // mousewheelevent.getscrollamount(), // mousewheelevent.getwheelrotation() // ); } } /** * dispatch {@code mouseevent} * @param mouseevent event dispatched */ private void dispatchmouseevent(mouseevent mouseevent) { if (mouseevent != null) { component target = mouseevent.getcomponent(); target.dispatchevent(mouseevent); } } /** * listening component associated {@code component}'s {@code event} */ private component getlisteningcomponent(mouseevent event, component component) { switch (event.getid()) { case (mouseevent.mouse_clicked): case (mouseevent.mouse_entered): case (mouseevent.mouse_exited): case (mouseevent.mouse_pressed): case (mouseevent.mouse_released): return getmouselisteningcomponent(component); case (mouseevent.mouse_dragged): case (mouseevent.mouse_moved): return getmousemotionlisteningcomponent(component); case (mouseevent.mouse_wheel): return getmousewheellisteningcomponent(component); } return null; } /** * cycles through {@code component}'s parents find {@link component} associated {@link mouselistener} */ private component getmouselisteningcomponent(component component) { if (component.getmouselisteners().length > 0) { return component; } else { container parent = component.getparent(); if (parent != null) { return getmouselisteningcomponent(parent); } else { return null; } } } /** * cycles through {@code component}'s parents find {@link component} associated {@link mousemotionlistener} */ private component getmousemotionlisteningcomponent(component component) { /* * mouse motion events may result in mouse_entered , mouse_exited. * * therefore, components mouselisteners registered should * returned well. */ if (component.getmousemotionlisteners().length > 0 || component.getmouselisteners().length > 0) { return component; } else { container parent = component.getparent(); if (parent != null) { return getmousemotionlisteningcomponent(parent); } else { return null; } } } /** * cycles through {@code component}'s parents find {@link component} associated {@link mousewheellistener} */ private component getmousewheellisteningcomponent(component component) { if (component.getmousewheellisteners().length > 0) { return component; } else { container parent = component.getparent(); if (parent != null) { return getmousewheellisteningcomponent(parent); } else { return null; } } } /** * generate {@code mouse_entered} , {@code mouse_exited} event when target component changed */ private void generateenterexitevents( jlayer<? extends jcomponent> layer,mouseevent originalevent, component newtarget, point realpoint) { if (lastenteredtarget != newtarget) { dispatchmouseevent( transformmouseevent(layer, originalevent, lastenteredtarget, realpoint, mouseevent.mouse_exited)); lastenteredtarget = newtarget; //system.out.println("last " + lastenteredtarget.getclass().getname()); dispatchmouseevent( transformmouseevent(layer, originalevent, lastenteredtarget, realpoint, mouseevent.mouse_entered)); } } }
zoompanel
jpanel
include jlayer
transformui
in it. jlayer
contains jpanel
springlayout
in constraints updated scale factor layout correctly components (jxlayer
had it's own layoutmanager
in jlayer
can't set it)
package jzoom.transform; import ... public class zoompanel extends jpanel { private static final long serialversionuid = 1l; private affinetransform transform; private transformui layerui; private jlayer<jcomponent> layer; private springlayout layout; private jpanel springpanel; private container view = null; public zoompanel() { this(null); } public zoompanel(container view) { setlayout(new borderlayout()); this.view = view; transform = new affinetransform(); layout = new springlayout(); springpanel = new jpanel(layout); if (view != null) { updateconstraints(); springpanel.add(view); } layerui = new transformui(); layerui.settransform(transform); layer = new jlayer<jcomponent>(springpanel, layerui); super.add(layer); } private void updateconstraints() { spring width = layout.getconstraint(springlayout.width, springpanel); spring height = layout.getconstraint(springlayout.height, springpanel); springlayout.constraints constraints = layout.getconstraints(view); constraints.setx(spring.constant(0)); constraints.sety(spring.constant(0)); constraints.setwidth(spring.scale(width, (float) (1 / transform.getscalex()))); constraints.setheight(spring.scale(height, (float) (1 / transform.getscalex()))); } public void setview(container view) { if (this.view != null) { throw new illegalstateexception( this.getclass().getname() + " cannot shared between multiple containers"); } if (view != null) { this.view = view; updateconstraints(); springpanel.add(view); } else { throw new illegalargumentexception("can't set null view"); } } public double getscale() { return transform.getscalex(); } public void zoomin() { setscale(transform.getscalex() + 0.1); } public void zoomout() { setscale(transform.getscalex() - 0.1); } public void setscale(double scale) { if (!(scale < 1)) { transform.settoidentity(); transform.scale(scale, scale); updateconstraints(); springpanel.updateui(); } } protected component addtoview(component comp, object constraints, int index) { if (view != null) { view.add(comp, constraints, index); return comp; } if (comp instanceof container) { setview((container) comp); return view; } throw new illegalstateexception("you need add or set container view before adding components"); } @override public component add(component comp) { // todo auto-generated method stub return addtoview(comp, null, this.getcomponentcount()); } @override public component add(component comp, int index) { // todo auto-generated method stub return addtoview(comp, null, index); } @override public void add(component comp, object constraints) { // todo auto-generated method stub addtoview(comp, constraints, this.getcomponentcount()); } @override public void add(component comp, object constraints, int index) { // todo auto-generated method stub addtoview(comp, constraints, index); } private void inspectview(container view) { printstream ps = null; try { ps = new printstream("c:\\users\\andrea.maracci\\documents\\sicrareflectiontemp.txt"); inspectview(view, 0, ps); } catch (filenotfoundexception e) { e.printstacktrace(); } { if (ps != null) { ps.close(); } } } private static void inspectview(component component, integer level, printstream ps) { (integer = 0; < level; i++) { ps.print("\t"); } ps.print(level + ")"); ps.println("inspecting " + component.getclass().getname()); int accessiblecount = 0; if (component.getaccessiblecontext() != null) { accessiblecount = component.getaccessiblecontext().getaccessiblechildrencount(); if (accessiblecount > 0) { ps.println("*********************************************accessible context*********************************************"); (int = 0; < accessiblecount; i++) { ps.println(i + ") " + component.getaccessiblecontext().getaccessiblechild(i).getclass().getname()); } ps.println("************************************************************************************************************"); } } if (component instanceof jcomponent) { jcomponent jcomponent = ((jcomponent)component); if (jcomponent.getcomponentcount() > 0) { component[] children = jcomponent.getcomponents(); (component child : children) { inspectview(child, ++level, ps); } } } --level; } }
and here horrible test program
package jzoom.test; import ... public class testfinal { public static void main(string[] args) { new testfinal(); } public testfinal() { eventqueue.invokelater(new runnable() { @override public void run() { try { uimanager.setlookandfeel(uimanager.getsystemlookandfeelclassname()); } catch (classnotfoundexception e) { // todo auto-generated catch block e.printstacktrace(); } catch (instantiationexception e) { // todo auto-generated catch block e.printstacktrace(); } catch (illegalaccessexception e) { // todo auto-generated catch block e.printstacktrace(); } catch (unsupportedlookandfeelexception e) { // todo auto-generated catch block e.printstacktrace(); } jframe frame = new jframe("testing"); frame.setdefaultcloseoperation(jframe.exit_on_close); frame.setlayout(new borderlayout()); frame.add(new testpane()); frame.pack(); frame.setminimumsize(new dimension(400,500)); frame.setlocationrelativeto(null); frame.setvisible(true); } }); } public class testpane extends jpanel { private jlayer<jcomponent> layer; private transformui layerui; private jpanel content; private affinetransform transform = new affinetransform(); private zoompanel zoompanel; public testpane() { content = new jpanel(new gridbaglayout()); // content = new jpanel(new xylayout()); gridbagconstraints gbc = new gridbagconstraints(); gbc.gridy = 0; gbc.weighty = 0; gbc.weightx = 0; gbc.fill = gridbagconstraints.horizontal; jlabel label = new jlabel("hello"); jtextfield field = new jtextfield("world", 20); content.add(label, gbc); gbc.weightx = 1; content.add(field, gbc); // content.add(label, new xyconstraints(50, 20, 50, 22)); // content.add(field, new xyconstraints(100, 20, 200, 22)); gbc.gridy++; gbc.gridwidth = 2; final jslider slider = new jslider(100, 200); slider.setvalue(100); slider.addchangelistener(new changelistener() { @override public void statechanged(changeevent e) { int value = slider.getvalue(); double scale = value / 100d; zoompanel.setscale(scale); } }); content.add(slider, gbc); // content.add(slider, new xyconstraints(75, 50, 200, 50)); gbc.gridy++; gbc.gridwidth = 2; gbc.weighty = 1; gbc.fill = gridbagconstraints.both; jtextarea textarea = new jtextarea(); textarea.seteditable(true); textarea.settext( "pollofritto\npalma\npalmipedone\ncaccoletta\namammata\na\nasd\nasdgfag\nasdafa\nasdfasf\nadsfasdf\nadfadsf\nadsfdasf\nasdfdas\npollofritto\npalma\npalmipedone\ncaccoletta\namammata\na\nasd\nasdgfag\nasdafa\nasdfasf\nadsfasdf\nadfadsf\nadsfdasf\nasdfdas"); // textarea.setpreferredsize(new dimensions()); jscrollpane scrollpane = new jscrollpane(textarea); scrollpane.setverticalscrollbarpolicy(jscrollpane.vertical_scrollbar_always); scrollpane.setpreferredsize(new dimension(200, 75)); content.add(scrollpane, gbc); gbc.gridy++; gbc.gridwidth = 2; gbc.weighty = 0; gbc.weightx = 1; gbc.fill = gridbagconstraints.horizontal; string[] petstrings = { "bird", "cat", "dog", "rabbit", "pig" }; jcombobox petlist = new jcombobox(petstrings); content.add(petlist, gbc); jbutton zoomin = new jbutton("zoom in"); // zoomin.addmouselistener(new zoommouselistener()); zoomin.addactionlistener(new actionlistener() { public void actionperformed(actionevent e) { // system.out.println("first"); // layerui.zoomin(); //double zoom = transform.getscalex(); //transform.settoidentity(); //transform.scale(zoom + 0.1, zoom + 0.1); zoompanel.zoomin(); // jlayer.repaint(); } }); jbutton zoomout = new jbutton("zoom out"); zoomout.addactionlistener(new actionlistener() { public void actionperformed(actionevent e) { zoompanel.zoomout(); //double zoom = transform.getscalex(); //transform.settoidentity(); //transform.scale(zoom - 0.1, zoom - 0.1); // jlayer.repaint(); } }); gbc.gridy++; gbc.gridx = 0; gbc.gridwidth = 0; gbc.anchor = gridbagconstraints.line_end; // content.add(zoomout, new xyconstraints(50, 120, 100, 25)); // content.add(zoomin, new xyconstraints(170, 120, 100, 25)); jpanel button = new jpanel(); button.setlayout(new boxlayout(button, boxlayout.line_axis)); button.add(zoomout); button.add(zoomin); gbc.fill = gridbagconstraints.none; content.add(button, gbc); setlayout(new borderlayout()); setborder(borderfactory.createemptyborder(10, 10, 10, 10)); zoompanel = new zoompanel(); zoompanel.setview(content); add(zoompanel); } } }
Comments
Post a Comment