The Source for Java Technology Collaboration
Webmaster Alert: Posting to Jive Forums is currently not working. Estimated time for fix is unknown.

Home » java.net Forums » Java Desktop Technologies » JXLayer

Thread: Multiple layers on one component

Welcome, Guest Help
Login Login
Guest Settings Guest Settings
This question is answered. Helpful answers available: 2. Correct answers available: 1.

Reply to this Thread Reply to this Thread Search Forum Search Forum Back to Thread List Back to Thread List

Permlink Replies: 10 - Last Post: Nov 1, 2008 11:46 PM by: alexfromsun Threads: [ Previous | Next ]
lebesnec

Posts: 17
Multiple layers on one component
Posted: Oct 24, 2008 1:11 AM
 
  Click to reply to this thread Reply

hello,

I want to write two LayerUIs, each one with a specific behavior, and then applied them on the same component. This solution : http://www.pbjar.org/blogs/jxlayer/JXLayer_one.html does not work in my case because it remove the generic and the second layer is not applied on the component but on the first layer.

I have wrote this class which seem to work, but you have to extend CompoundLayerUI.AbstractLayerUI instead of AbstractLayerUI for the "sub-layer". Also super.paintLayer(...) should not be called on the "sub-layer" (since it's already done in CompoundLayerUI).

The question is : is there a better way of doing this ?

package ilist.ui.generic;
 
import java.awt.Graphics2D;
import java.awt.event.FocusEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelEvent;
 
import javax.swing.JComponent;
 
import org.jdesktop.jxlayer.JXLayer;
import org.jdesktop.jxlayer.plaf.AbstractLayerUI;
 
public class CompoundLayerUI<V extends JComponent> extends AbstractLayerUI<V> {
	
	private AbstractLayerUI<V> layer1;
	private AbstractLayerUI<V> layer2;
	
	
	public CompoundLayerUI(AbstractLayerUI<V> layer1, AbstractLayerUI<V> layer2) {
		super();
		this.layer1 = layer1;
		this.layer2 = layer2;
	}
 
 
	@Override
	protected void paintLayer(Graphics2D g, JXLayer<V> l) {
		super.paintLayer(g, l);
		this.layer1.paintLayer(g, l);
		this.layer2.paintLayer(g, l);
	}
 
	@Override
	protected void processFocusEvent(FocusEvent e, JXLayer<V> l) {
		super.processFocusEvent(e, l);
		this.layer1.eventDispatched(e, l);
		this.layer2.eventDispatched(e, l);
	}
 
	@Override
	protected void processKeyEvent(KeyEvent e, JXLayer<V> l) {
		super.processKeyEvent(e, l);
		this.layer1.eventDispatched(e, l);
		this.layer2.eventDispatched(e, l);
	}
 
	@Override
	protected void processMouseEvent(MouseEvent e, JXLayer<V> l) {
		super.processMouseEvent(e, l);
		this.layer1.eventDispatched(e, l);
		this.layer2.eventDispatched(e, l);
	}
 
	@Override
	protected void processMouseMotionEvent(MouseEvent e, JXLayer<V> l) {
		super.processMouseMotionEvent(e, l);
		this.layer1.eventDispatched(e, l);
		this.layer2.eventDispatched(e, l);
	}
 
	@Override
	protected void processMouseWheelEvent(MouseWheelEvent e, JXLayer<V> l) {
		super.processMouseWheelEvent(e, l);
		this.layer1.eventDispatched(e, l);
		this.layer2.eventDispatched(e, l);
	}
	
	
	public static class AbstractLayerUI<T extends JComponent> extends org.jdesktop.jxlayer.plaf.AbstractLayerUI<T> {
		
		// paintLayer is now public :
		@Override
		public void paintLayer(Graphics2D g, JXLayer<T> l) {
			super.paintLayer(g, l);
		}
		
	}
 
}



How to use it :

JXLayer<JList> myLayer = new JXLayer<JList>(myList);
CompoundLayerUI<JList> compoundLayerUI = new CompoundLayerUI<JList>(layerUI1, layerUI2);
myLayer.setUI(compoundLayerUI);
JScrollPane scrollPane = new JScrollPane(myLayer);


pietblok

Posts: 291
Re: Multiple layers on one component
Posted: Oct 24, 2008 2:38 AM   in response to: lebesnec
 
  Click to reply to this thread Reply

Hi Lebesnec,

I don't understand exactly what you mean by:

This solution : http://www.pbjar.org/blogs/jxlayer/JXLayer_one.html does not work in my case because it remove the generic and the second layer is not applied on the component but on the first layer.

Could you provide some code example that shows what goes wrong?

Your code looks somewhat familiar to me, because I also made some attempt to write a generic MultiLayerUI. I failed because for virtually all methods I had to decide to either do the processing in the MultiLayerUI or delegate to the child LayerUIs. For me, that was very problematic.

Just as a hint. It seems that you implement your own AbstractLayerUI, extending from the original AbstractLayerUI. Your naming is very confusing. Shouldn't you name your class differently? Something like PublicPaintAbstractLayerUI?

Piet

lebesnec

Posts: 17
Re: Multiple layers on one component
Posted: Oct 24, 2008 2:44 AM   in response to: pietblok
 
  Click to reply to this thread Reply

Update : this work even better :

package ilist.ui.generic;
 
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.FocusEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelEvent;
 
import javax.swing.JComponent;
 
import org.jdesktop.jxlayer.JXLayer;
import org.jdesktop.jxlayer.plaf.AbstractLayerUI;
 
public class CompoundLayerUI<V extends JComponent> extends AbstractLayerUI<V> {
	
	private AbstractLayerUI<V> layer1;
	private AbstractLayerUI<V> layer2;
	
	
	public CompoundLayerUI(AbstractLayerUI<V> layer1, AbstractLayerUI<V> layer2) {
		super();
		this.layer1 = layer1;
		this.layer1.setCompoundLayer(this);
		this.layer2 = layer2;
		this.layer2.setCompoundLayer(this);
	}
 
 
	@Override
	protected void paintLayer(Graphics2D g, JXLayer<V> l) {
		super.paintLayer(g, l);
		this.layer1.paintLayer(g, l);
		this.layer2.paintLayer(g, l);
	}
 
	@Override
	protected void processFocusEvent(FocusEvent e, JXLayer<V> l) {
		super.processFocusEvent(e, l);
		this.layer1.eventDispatched(e, l);
		this.layer2.eventDispatched(e, l);
	}
 
	@Override
	protected void processKeyEvent(KeyEvent e, JXLayer<V> l) {
		super.processKeyEvent(e, l);
		this.layer1.eventDispatched(e, l);
		this.layer2.eventDispatched(e, l);
	}
 
	@Override
	protected void processMouseEvent(MouseEvent e, JXLayer<V> l) {
		super.processMouseEvent(e, l);
		this.layer1.eventDispatched(e, l);
		this.layer2.eventDispatched(e, l);
	}
 
	@Override
	protected void processMouseMotionEvent(MouseEvent e, JXLayer<V> l) {
		super.processMouseMotionEvent(e, l);
		this.layer1.eventDispatched(e, l);
		this.layer2.eventDispatched(e, l);
	}
 
	@Override
	protected void processMouseWheelEvent(MouseWheelEvent e, JXLayer<V> l) {
		super.processMouseWheelEvent(e, l);
		this.layer1.eventDispatched(e, l);
		this.layer2.eventDispatched(e, l);
	}
	
	@Override
	public Dimension getPreferredScrollableViewportSize(JXLayer<V> l) {
		return layer1.getPreferredScrollableViewportSize(l);
	}
 
	@Override
	public int getScrollableBlockIncrement(JXLayer<V> l, Rectangle r, int orientation, int direction) {
		return layer1.getScrollableBlockIncrement(l, r, orientation, direction);
	}
 
	@Override
	public boolean getScrollableTracksViewportHeight(JXLayer<V> l) {
		return layer1.getScrollableTracksViewportHeight(l);
	}
 
	@Override
	public boolean getScrollableTracksViewportWidth(JXLayer<V> l) {
		return layer1.getScrollableTracksViewportWidth(l);
	}
 
	@Override
	public int getScrollableUnitIncrement(JXLayer<V> l, Rectangle r,int orientation, int direction) {
		return layer1.getScrollableUnitIncrement(l, r, orientation, direction);
	}
	
	
	public static class AbstractLayerUI<T extends JComponent> extends org.jdesktop.jxlayer.plaf.AbstractLayerUI<T> {
		
		private CompoundLayerUI compoundLayerUI;
 
		// paintLayer is now public :
		@Override
		public void paintLayer(Graphics2D g, JXLayer<T> l) {
			super.paintLayer(g, l);
		}
		
		protected void setCompoundLayer(CompoundLayerUI compoundLayerUI) {
			this.compoundLayerUI = compoundLayerUI;			
		}
		
		@Override
		protected void setDirty(boolean dirty) {
			super.setDirty(dirty);
			this.compoundLayerUI.setDirty(dirty);
		}
		
	}
 
}


lebesnec

Posts: 17
Re: Multiple layers on one component
Posted: Oct 24, 2008 3:07 AM   in response to: pietblok
 
  Click to reply to this thread Reply

hello Piet,

in fact the problem is not that the solution at http://www.pbjar.org/blogs/jxlayer/JXLayer_one.html does not work, it is that I can't use it ...

For example let say I want to use 2 layers on a JList. If I used this solution the second layers will be on a JXlayer instead of a JList, and I won't be able to do :

public void paintLayer(Graphics2D g, JXLayer<JList> layer) {
      layer.getView().getSelectedIndex(); // getSelectedIndex is a method of JList
      ....
}




Just as a hint. It seems that you implement your own AbstractLayerUI, extending from the original AbstractLayerUI. Your naming is very confusing. Shouldn't you name your class differently? Something like PublicPaintAbstractLayerUI?

I keep the same name because I wanted it to be as invisible as possible for the users of CompoundLayerUI, but may be this too confusing :)

lebesnec

Posts: 17
Re: Multiple layers on one component
Posted: Oct 24, 2008 3:18 AM   in response to: lebesnec
 
  Click to reply to this thread Reply

another small change :

I have changed :
// paintLayer is now public :
@Override
public void paintLayer(Graphics2D g, JXLayer<T> l) {
		super.paintLayer(g, l);
}


into :
// paintLayer is now public :
@Override
public void paintLayer(Graphics2D g, JXLayer<T> l) {}


So it will work even if in the sub-layers you forget to remove the call to super.paintLayer(...)

pietblok

Posts: 291
Re: Multiple layers on one component
Posted: Oct 24, 2008 3:59 AM   in response to: lebesnec
 
  Click to reply to this thread Reply

I see.

In that case, define the outer JXLayer as follows:

JXLayer<JXLayer<JList>> outerLayer;


then you get at the index as follows:

public void paintLayer(Graphics2D g, JXLayer<JXLayer<JList>> layer) {
      layer.getView().getView().getSelectedIndex(); // getSelectedIndex is a method of JList
      ....
}


Piet

pietblok

Posts: 291
Re: Multiple layers on one component
Posted: Oct 24, 2008 5:03 AM   in response to: pietblok
 
  Click to reply to this thread Reply

Or something like this (to keep it more transparent)

import java.awt.Graphics2D;
 
import javax.swing.JComponent;
import javax.swing.JList;
 
import org.jdesktop.jxlayer.JXLayer;
import org.jdesktop.jxlayer.plaf.AbstractLayerUI;
 
public class JListLayerUI extends AbstractLayerUI<JComponent> {
 
    @Override
    protected void paintLayer(Graphics2D g2, JXLayer<JComponent> layer) {
	super.paintLayer(g2, layer);
	JComponent view = layer.getView();
	while (!(view instanceof JList)) {
	    if (view instanceof JXLayer) {
		view = ((JXLayer<?>) view).getView();
	    } else if (view == null) {
		break;
	    } else {
		throw new RuntimeException("Unexpected view encountered");
	    }
	}
	if (view != null) {
	    JList list = (JList) view;
	    int selectedIndex = list.getSelectedIndex();
	    // etc.
	}
    }
 
}


Not tested

Piet

psychostud

Posts: 43
Re: Multiple layers on one component
Posted: Oct 24, 2008 7:53 PM   in response to: pietblok
 
  Click to reply to this thread Reply

I suppose the api should be extended to add multiple layers , maybe having a LayeredUIContainer whose layout supports adding multiple layers

pietblok

Posts: 291
Re: Multiple layers on one component
Posted: Oct 29, 2008 2:59 AM   in response to: psychostud
 
  Click to reply to this thread Reply

Hi,

That's what I initially thought also. For a second time I tried to wite a MultiLayerUI (probably it will have many flaws, but it serves as a demo).

I found the results very disapponting, but now I think I understand why this never will work.

Imagine two or three LayerUI's that all do some customized painting.

1) Paints curves following the mouse
2) Paints dots where the mouse is clicked
3) Some magnifying glass

When applying those LayerUI's upon wrapped JXLayer's, they work so to speak recursive:

A) LayerUI 3 will, by invoking super.paintLayer(..) have LayerUI 2 do its work
B) Then LayerUI 2 will, by invoking super.paintLayer(..) have LayerUI 1 do its work
C) Then LayerUI1 will, by invoking super.paintLayer(..) have the view component be painted
D) LayerUI 1 paints its curves
E) LayerUI 2 paints its dots
F) LayerUI 3 paints it magnifying glass

Now we see on the screen the views painting and the paintings of the three LayerUI's.

Now try this with a MultiLayerUI (use the code below for the implementation)

You will see that all LayerUI's will erase the painting of their peers, leaving the painting of thelast one only.

You may vary this experiment by commenting out the invocation of super.paintLayer(..) in the MultiLayerUI and/or in LayerUI 1, 2 and 3.

You will see that acting of those LayerUI's as peers, instead of a wrapped hierarchy, is the problematic part.

Well, this was just my experiment. I am very curious if anyone can setup some implementation of some multi LayerUI where the LayerUI's work in harmony together.

import java.awt.AWTEvent;
import java.awt.Graphics;
import java.util.LinkedHashSet;
 
import javax.swing.JComponent;
 
import org.jdesktop.jxlayer.JXLayer;
import org.jdesktop.jxlayer.plaf.AbstractLayerUI;
import org.jdesktop.jxlayer.plaf.LayerUI;
import org.jdesktop.jxlayer.plaf.item.LayerItemChangeEvent;
import org.jdesktop.jxlayer.plaf.item.LayerItemListener;
 
public class MultiLayerUI<V extends JComponent> extends AbstractLayerUI<V> {
 
    private LayerItemListener layerItemListener = new LayerItemListener() {
 
	@SuppressWarnings("unchecked")
	@Override
	public void layerItemChanged(LayerItemChangeEvent event) {
	    /*
	     * Getting the originating LayerUI. But what to do with it?
	     */
	    @SuppressWarnings("unused")
	    LayerUI<V> layerUI = (LayerUI<V>) event.getSource();
	    /*
	     * When a child LayerUI is changed, then this LayerUI is changed as
	     * well.
	     */
	    MultiLayerUI.this.fireLayerItemChanged();
	}
    };
 
    private LinkedHashSet<LayerUI<V>> uis = new LinkedHashSet<LayerUI<V>>();
 
    public void addLayerUI(LayerUI<V> layerUI) {
	layerUI.addLayerItemListener(layerItemListener);
	uis.add(layerUI);
    }
 
    @Override
    public void eventDispatched(AWTEvent e, JXLayer<V> layer) {
	for (LayerUI<V> layerUI : this.getLayerUIs()) {
	    if (layerUI.isEnabled()) {
		layerUI.eventDispatched(e, layer);
	    }
	}
    }
 
    @SuppressWarnings("unchecked")
    public LayerUI<V>[] getLayerUIs() {
	return (LayerUI<V>[]) uis.toArray(new LayerUI<?>[uis.size()]);
    }
 
    @Override
    public void installUI(JComponent c) {
	super.installUI(c);
	for (LayerUI<V> layerUI : this.getLayerUIs()) {
	    layerUI.installUI(c);
	}
    }
 
    @Override
    public void paint(Graphics g, JComponent component) {
	super.paint(g, component);
	for (LayerUI<V> layerUI : this.getLayerUIs()) {
	    if (layerUI.isEnabled()) {
		layerUI.paint(g.create(), component);
	    }
	}
    }
 
    public void removeAllLayerUIs() {
	for (LayerUI<V> layerUI : getLayerUIs()) {
	    removeLayerUI(layerUI);
	}
	uis.clear();
    }
 
    public void removeLayerUI(LayerUI<V> layerUI) {
	layerUI.removeLayerItemListener(layerItemListener);
	uis.remove(layerUI);
    }
 
    @Override
    public void uninstallUI(JComponent c) {
	super.uninstallUI(c);
	for (LayerUI<V> layerUI : this.getLayerUIs()) {
	    layerUI.uninstallUI(c);
	}
    }
 
}


Piet

alexfromsun

Posts: 416
Re: Multiple layers on one component
Posted: Nov 1, 2008 11:46 PM   in response to: pietblok
 
  Click to reply to this thread Reply

(Writing from Bangkok, at the end of vacation)

Hello Piet

I like your evaluation about MultiLayerUI
I also believe that it can never be implemented correctly

the ui ordering is another problem,
(adding one more ui on top of the others or in between of them)
it can also be solved by prioritizing the ui's
but it makes the result API not easy to use and maintain

I remember the experiment with MultiplePainter implementation from SwingX team
it made me decide to not follow this way
(don't tell Richard about that :-))

Thanks
alexp

lebesnec

Posts: 17
Re: Multiple layers on one component
Posted: Oct 27, 2008 1:30 AM   in response to: pietblok
 
  Click to reply to this thread Reply

yes, both methods seem correct !




 XML java.net RSS