Custom swing component...



  • I want to create a JComponent that displays its child components as a Graph (Vertices/Nodes and Edges)
    You can add any number of Nodes and Edges.  The Nodes are basically just an (double x, double y) tuple and java.awt.Component.  Edges are just a (Node from, Node to) tuple.

    So, my main question is... Should I create my own layout manager to manage this, or extend JPanel to do so?  In order to "draw" the edges, I would need to extend a JComponent, but the placement of the "Nodes" is an iterative (animated) process.

    I have something that "Works". Currently it extends JPanel, and sets the Layout to null. Then it manages the size/location of the Nodes itself.




  • @danielpitts said:

    I want to create a JComponent that displays its child components as a Graph (Vertices/Nodes and Edges)
    You can add any number of Nodes and Edges.  The Nodes are basically just an (double x, double y) tuple and java.awt.Component.  Edges are just a (Node from, Node to) tuple.

    So, my main question is... Should I create my own layout manager to manage this, or extend JPanel to do so?  In order to "draw" the edges, I would need to extend a JComponent, but the placement of the "Nodes" is an iterative (animated) process.

    I have something that "Works". Currently it extends JPanel, and sets the Layout to null. Then it manages the size/location of the Nodes itself.




    might I suggest extending Java.awt.Canvas instead.  I've actually made something similar.  that sounds like about a 30 minute job if you extend canvas.



  • @tster said:

    might I suggest extending Java.awt.Canvas instead.  I've actually made something similar.  that sounds like about a 30 minute job if you extend canvas.


    Well, first off. I'm writing a Swing app, and mixing Canvas (a heavy weight component) with JComponent will cause performance issues.

    Second off. The "Nodes" are actual Components. Such as JLabel, JPanel, etc...



  • @danielpitts said:

    I want to create a JComponent that displays its child components as a Graph (Vertices/Nodes and Edges)
    You can add any number of Nodes and Edges.  The Nodes are basically just an (double x, double y) tuple and java.awt.Component.  Edges are just a (Node from, Node to) tuple.

    So, my main question is... Should I create my own layout manager to manage this, or extend JPanel to do so?  In order to "draw" the edges, I would need to extend a JComponent, but the placement of the "Nodes" is an iterative (animated) process.

    I have something that "Works". Currently it extends JPanel, and sets the Layout to null. Then it manages the size/location of the Nodes itself.


    I've writte quite a lot custom components which did their own drawing. Layoutmanagers are a real pain in the ***. I suggest you extend an JPanel and overwrite  paintComponent(Graphics g). Then just read the Graph-class (or whatever you have) and paint Nnodes and edges manually.

    As for the animated part I'm not quite sure what you mean. If you have cyclic animation then just use one Thread/Swingworker/TimerTask.
    But if you intend to let's say expand your graph by user interaction or timed events and want the nodes "move" to their appropriate location then I suggest you write Wrappers for the nodes (or even the whole Graph). The Wrapper would not only contain the x,y coordinates of the position where the node is supposed to be, but you might also want to add x2,y2-coordinates that store the current location of the nodes on the screen. Then everytime your timer repaint the components, you can alter the x2,y2-values to come stepwise closer to the x,y coordinates. Thus you will get a nice animation. If you are really into a stunning animation you may even want to add some sort of inertia for the node (for example deltaX, deltaY which is added to x2,y2 in every iteration). So let's say you want to implement a rubber-band like behaviour, you can set the absolute value of the vector the deltaX,deltaY coordinates represent the higher the further away they are from the destination (for example the distance itself). The nodes will then come "flying" towards the destination coordinates and bounce back and forth a little while. In order to decrease hectic movement you may want to add some sort of friction to the delta-values (for example multiply them by 0.98 every iteration).

    As for the nodes you need to find the right size. If yo have text with arbitrary lenght you will run into the problem that the node wont encapsule the text (the strings that has been drawn) properly. I wrote this method to determine the bounding box of the text:

    <font face="Courier New" size="2">public static Rectangle2D getBounds(String s, Font font, Graphics g) {</font>
    <font face="Courier New" size="2">        Graphics2D g2 = (Graphics2D) g;</font>
    <font face="Courier New" size="2">        FontRenderContext frc = g2.getFontRenderContext();</font>
    <font face="Courier New" size="2">        TextLayout layout = new TextLayout(s, font, frc);</font>
    <font face="Courier New" size="2">        layout.draw(g2, 0, 0);</font>
    <font face="Courier New" size="2">        Rectangle2D bounds = layout.getBounds();</font>
    <font face="Courier New" size="2">        return bounds;</font>
    <font face="Courier New" size="2">}

    </font>This method does not really visibly draw on the screen but you will need an instance of a graphics object for it to work. So now you do not need to rely on components for the nodes but instead draw the right size according to the bounding box and drawString the text over it for example. I prefer it that way instead of mixing custom painting and components and try to align them. It also looks better. Implementing custom behavior like mouseover events can simply be determined by the comparison of the mouse position and the positions and size of the nodes. Thus you could drag your nodes to new positions (or just let them bounce back like described above for the fun of it).
    Then again if you rely on textfields or radio/check buttons or any rather complex components in the nodes you might be better off using components instead. It does not make sense to reimplement what is already there. But if you only use JPanels and JLabels as nodes I suggest custom drawn ones instead.

    Hope I didn't overdo it. enjoy... :)



  • @Phalphalak said:


    As for the animated part I'm not quite sure what you mean. If you have cyclic animation then just use one Thread/Swingworker/TimerTask.
    But if you intend to let's say expand your graph by user interaction or timed events and want the nodes "move" to their appropriate location then I suggest you write Wrappers for the nodes (or even the whole Graph). The Wrapper would not only contain the x,y coordinates of the position where the node is supposed to be, but you might also want to add x2,y2-coordinates that store the current location of the nodes on the screen. Then everytime your timer repaint the components, you can alter the x2,y2-values to come stepwise closer to the x,y coordinates. Thus you will get a nice animation. If you are really into a stunning animation you may even want to add some sort of inertia for the node (for example deltaX, deltaY which is added to x2,y2 in every iteration). So let's say you want to implement a rubber-band like behaviour, you can set the absolute value of the vector the deltaX,deltaY coordinates represent the higher the further away they are from the destination (for example the distance itself). The nodes will then come "flying" towards the destination coordinates and bounce back and forth a little while. In order to decrease hectic movement you may want to add some sort of friction to the delta-values (for example multiply them by 0.98 every iteration).


    Thanks for the lengthy post :-)
    Actually I have (many times) implemented a "physics"' engine that does what you've described. It includes attractive and repulsive forces with both kd^2 and k/d^2 acceleration, as well as "friction" etc... 

    My question was more of a "What is the proper way" type.  I can make it happen any number of ways, I just wanted to know what people thought was the more elegant of them.  With more reflection on the matter, I've come to realize that a custom component UI delegate is also a possibility, and that my research needs to be expanded.

    In any case, thanks for the time you took to reply :-)



  • I also recommend making the whole graph drawer a single component, on which you draw the nodes and connections yourself. That's much easier than fighting a layout manager!



  • @Bob Janova said:

    I also recommend making the whole graph drawer a single component, on which you draw the nodes and connections yourself. That's much easier than fighting a layout manager!

    Exactly! Just extend java.awt.Canvas and overwrite the paint method. Works like a charm for me.



  • A bit like this component I've just written in fact!

    public class TreeDrawer extends Canvas {
    	Tree tree;
    	// ...	
    	public TreeDrawer(){
    		this.enableEvents(AWTEvent.MOUSE_MOTION_EVENT_MASK | AWTEvent.MOUSE_EVENT_MASK | AWTEvent.COMPONENT_EVENT_MASK);
    	}
    	
    	public void paint(Graphics g){
    		if(tree == null) return;
    		
    		// Now draw the tree
    		for(TreeNode node : tree.nodes){
    			if(g.getClipBounds().intersects(node.getBounds(node.highlighted))){
    				doNode(g,inset,fs,node);
    			}
    		}
    	}
    	
    	// ...
    }

    If it's a Swing app then JCanvas or whatever it's called would be better though, because as the OP says Swing and AWT don't quite use the same rulebook.



  • @Bob Janova said:


    If it's a Swing app then JCanvas or whatever it's called would be better though, because as the OP says Swing and AWT don't quite use the same rulebook.


    Canvas plays well in Swing apps; in my application, the custom widget (a 2D drawing area, read: "SVG for the very poor") is the only AWT widget while the rest is Swing. No problem. In fact, I don't think Swing could add too much to such a custom widget.



  • @ammoQ said:

    @Bob Janova said:

    If it's a Swing app
    then JCanvas or whatever it's called would be better though, because as
    the OP says Swing and AWT don't quite use the same rulebook.


    Canvas
    plays well in Swing apps; in my application, the custom widget (a 2D
    drawing area, read: "SVG for the very poor") is the only AWT widget
    while the rest is Swing. No problem. In fact, I don't think Swing could
    add too much to such a custom widget.


    It might not play so well if you wanted it to be in a
    JScrollPane.  It also will obscure JToolTips, JPopupMenus, and the
    drop-down components of JComboBoxes, unless you forced them to be
    heavyweight.  Since you say you didn't have any problems, I'm
    guessing you didn't have a JMenuBar or any iconic controls with
    tooltips above your Canvas.



    You're right of course that drawing on a JComponent or JPanel won't be any better or worse than drawing on a Canvas.



  • @VGR said:

    @ammoQ said:

    Canvas
    plays well in Swing apps; in my application, the custom widget (a 2D
    drawing area, read: "SVG for the very poor") is the only AWT widget
    while the rest is Swing. No problem. In fact, I don't think Swing could
    add too much to such a custom widget.


    It might not play so well if you wanted it to be in a
    JScrollPane.  It also will obscure JToolTips, JPopupMenus, and the
    drop-down components of JComboBoxes, unless you forced them to be
    heavyweight.  Since you say you didn't have any problems, I'm
    guessing you didn't have a JMenuBar or any iconic controls with
    tooltips above your Canvas.



    You're right of course that drawing on a JComponent or JPanel won't be any better or worse than drawing on a Canvas.

    You are right, I don't have any of the mentioned controls above my Canvas. Thanks for the hint, I will take care of those possible issues should there ever be a such constellation.



  • First I wanted to write a comment how double buffering is not implemented in Canvas. I had a quick look at the API and figured that it actually does. Last time I checked was around the time that Swing came out. Just learned something new... Well, I know a lot of people that say "I've been doing this for ten years, trust me I know how to do it right.". They may as well have done it wrong for ten years. Seems I was one of them. :)

    But I still have a question concerning Canvas versus JPanel. JPanel does support Tooltips PopUpMenus and such. So, can anyone please confirm or deny that the only significant difference between JPanel and Canvas lies in the possibility to use ToolTips PopUps etc...? Is there any (even slight) advantage over JPanels? I figured you can customize the MultiBuffering with Canvases and such. Does anyone have experience with that and knows if it actually comes with any notable performance gain when using triple instead of double buffering or any of these additional features? As far as I see you cannot customize buffering strategies on JPanels or other Swing components.



  • [quote user="Phalphalak"]

    First I wanted to write a comment how double buffering is not implemented in Canvas. I had a quick look at the API and figured that it actually does. Last time I checked was around the time that Swing came out. Just learned something new... Well, I know a lot of people that say "I've been doing this for ten years, trust me I know how to do it right.". They may as well have done it wrong for ten years. Seems I was one of them. :)

    But I still have a question concerning Canvas versus JPanel. JPanel does support Tooltips PopUpMenus and such. So, can anyone please confirm or deny that the only significant difference between JPanel and Canvas lies in the possibility to use ToolTips PopUps etc...? Is there any (even slight) advantage over JPanels? I figured you can customize the MultiBuffering with Canvases and such. Does anyone have experience with that and knows if it actually comes with any notable performance gain when using triple instead of double buffering or any of these additional features? As far as I see you cannot customize buffering strategies on JPanels or other Swing components.

    [/quote]

    The main difference between Canvas and JPanel is that Canvas is an AWT concept, where JPanel is Swing.  It is considered bad form to mix the two unless you REALLY need to, as they have different workflows for painting children/etc...

    All AWT Components are Heavyweight, where as many Swing are lightweight (including JPanel I believe).  As for double vs tripple buffering, you can implement either one in a JPanel using BufferedImage objects.

    I haven't used triple buffering before, but I think I know where it helps:
    You can be writing to the third buffer without interfering with the flip from the first to the second. Where as with double buffering, you have to have two complete buffers (about-to-be-shown and currently-shown), and have to therefore time your updates with the monitor refresh.
     



  • I'm also about to do something like this and have a question which might be of interest to the original poster as well. Which GUI technology do you recommend for this type of project? AWT, Swing or SWT? Personally I'm interested in doing my project in SWT. Which one would be easiest to use? Best performance wise? And are there any license issues with creating a commercial application using any of these three?



  • [quote user="kentyl"]I'm also about to do something like this and have a question which might be of interest to the original poster as well. Which GUI technology do you recommend for this type of project? AWT, Swing or SWT? Personally I'm interested in doing my project in SWT. Which one would be easiest to use? Best performance wise? And are there any license issues with creating a commercial application using any of these three?
    [/quote]

    Unless you have very special reasons (like creating a java applet that runs on old java version that came with IE), AWT is in my opinion not really an option. So the question is: SWT or Swing? It depends on. SWT promises native look and feel, Swing a plattform-independend look and feel. SWT is supposed to be faster, but looking at Eclipse, I dare to doubt.
     



  • I second that. AWT is out of date. I have no experince with SWT whatsoever but I dare to say that you might want to go for swing unless you have your reasons to do otherwise. At least that's my impression from what I read.

    Not that I believe everything I read but may I quote the wikipedia article about SWT, which says: 

    The purpose of SWT is to provide a common API for accessing native widgets across a spectrum of platforms. The primary design goals are high performance, native look and feel, and deep platform integration. Swing, on the other hand, is designed to allow for a highly customizable look and feel that is common across all platforms.

    ...

    It has also been alleged that SWT's performance on platforms other than Windows is noticeably less efficient or less complete.

    Since SWT uses a different native library for each platform, SWT developers may be exposed to platform specific bugs, thus neutralizing the Java "Write Once, Run Everywhere" advantage, by requiring them to write platform specific code. SWT has been referred to as a "Write Once, Test Everywhere" solution, similar to those provided by languages such as C.

    ... 

    Since SWT uses different libraries for different platforms, a platform specific library must be distributed to application users. In short, SWT introduces an additional dependency that the developers and client must both keep track of.

     

     Not that after having read this makes me an expert, but I assume that you might want to use SWT if you have platform dependent reasons etc.
     


Log in to reply