Pick up and drop with image moving with the mouse

Discussion in 'Java' started by Icarus, Jul 29, 2008.

  1. Icarus

    Icarus Guest

    I am currently working on creating a game platform where one can pick
    up tokens of various shapes and place them on a game board. For
    precise placement, I need an image of these tokens in their original
    size to be moving around with the mouse cursor until the token is
    placed on the board.

    My initial approach was to convert the token into an original-sized
    picture and setting this picture as mouse cursor. However, my system
    determines a maximum size for custom cursors which is far too small
    for my needs.

    Now I am at a loss. My idea now is to draw the picture on the
    GlassPane of my current JFrame and move it according to
    MouseMotionEvents. But that seems to be the most performance-heavy
    solution there is.
    I have found an example on http://weblogs.java.net/blog/gfx/archive/2005/10/drag_and_drop_e.html,
    but it comes with no direct explanation of the steps involved and is
    published under a license I don't want to have in my program. However,
    without understanding the steps involved, I can only copy the code,
    thus violating the license agreements.

    So I want to ask you:
    - What would be the most effective way of (visibly) moving objects
    around with the mouse cursor on the GlassPane?
    - Or: Can you think of any other way how this could be accomplished?

    I would be really grateful for any pointers in the right direction. As
    this problem creeped up right at the end of my project, my timetable
    is messed, I have 3 weeks for solving the problem, testing it and
    fixing up the documentation, I am quite on edge at the moment.
    Icarus, Jul 29, 2008
    #1
    1. Advertising

  2. Icarus

    Icarus Guest

    Forgot to mention: the actual data of the token is stored and
    processed elsewhere, so I don't have to deal with transfering actual
    data from one component to the next. The functionality I need is
    solely for presenting the changes to the user.
    Icarus, Jul 29, 2008
    #2
    1. Advertising

  3. On 29/07/2008 11:29, Icarus allegedly wrote:
    > I am currently working on creating a game platform where one can pick
    > up tokens of various shapes and place them on a game board. For
    > precise placement, I need an image of these tokens in their original
    > size to be moving around with the mouse cursor until the token is
    > placed on the board.
    >
    > My initial approach was to convert the token into an original-sized
    > picture and setting this picture as mouse cursor. However, my system
    > determines a maximum size for custom cursors which is far too small
    > for my needs.
    >
    > Now I am at a loss. My idea now is to draw the picture on the
    > GlassPane of my current JFrame and move it according to
    > MouseMotionEvents.


    Don't think using the GlassPane for this is the best idea. It should be
    reserved for other uses, IMHO.

    > But that seems to be the most performance-heavy
    > solution there is.


    Don't know about that.


    Here's a way to do it. As you can see, I've used the ContentPane instead
    of the GlassPane.

    <code>
    package scratch;

    import java.awt.*;
    import javax.swing.*;
    import java.awt.event.*;
    import javax.imageio.ImageIO;

    /**
    *
    * @author da.futt
    */
    public class DragPaneTest {

    private static Icon createIcon() throws Exception {
    return new ImageIcon(
    ImageIO.read(
    DragPaneTest.class.getResourceAsStream("withAMelon.png")
    )
    );
    }

    public static void main(String[] s){
    EventQueue.invokeLater(new Runnable(){
    public void run(){
    try {
    run0();
    }
    catch (Exception x){
    x.printStackTrace();
    }
    }

    private void run0() throws Exception {

    JFrame f = new JFrame("DragPaneTest");

    final DraggingPane cgp = new DraggingPane();

    cgp.setIcon( createIcon() );

    f.setContentPane(cgp);

    ControllerInterface ci = new ControllerInterface(){
    public void setIconLocation(Point p) {
    cgp.setIconLocation(p);
    }

    public void setIconVisible(boolean b) {
    cgp.setIconShowing(b);
    }

    public boolean getIconVisible() {
    return cgp.getIconShowing();
    }
    };

    MouseTracker mt = new MouseTracker(ci);
    cgp.addMouseListener( mt );
    cgp.addMouseMotionListener( mt );

    f.setSize(new Dimension(500, 400));
    f.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );

    f.setLocationRelativeTo(null);

    f.setVisible(true);
    }
    });

    }



    private static interface ControllerInterface {

    void setIconLocation(Point p);

    void setIconVisible(boolean b);

    boolean getIconVisible();
    }

    private static class MouseTracker
    extends MouseAdapter
    {
    private final
    ControllerInterface controller
    ;

    private boolean
    showing = false
    ;

    public MouseTracker(ControllerInterface ci){
    controller = ci;
    }

    @Override
    public void mouseClicked(MouseEvent e) {
    showing = ! showing;
    controller.setIconVisible(showing);
    }

    @Override
    public void mouseMoved(MouseEvent e) {
    controller.setIconLocation(e.getPoint());
    }
    }

    private static class DraggingPane
    extends JPanel
    {
    private Icon
    currentIcon
    ;

    private Rectangle
    iconBounds
    ;

    private boolean
    showIcon = false
    ;

    public DraggingPane(){
    setOpaque(true);
    }

    protected Rectangle getCurrentIconBounds(){
    return iconBounds;
    }

    protected void setCurrentIconBounds(Rectangle r){
    iconBounds = r;
    }

    public Icon getIcon(){
    return currentIcon;
    }

    public boolean getIconShowing(){
    return showIcon;
    }

    public void setIcon(Icon i){
    if( i == null )
    throw new NullPointerException();

    currentIcon = i;

    Rectangle r = getCurrentIconBounds();

    if( getIcon() != null && getIconShowing() ){

    RepaintManager.currentManager(this).addDirtyRegion(this, r.x, r.y,
    r.width, r.height);
    }

    Point p = r == null ?
    new Point( getWidth() >> 1, getHeight() >> 1 )
    : r.getLocation();

    r = new Rectangle( p, new Dimension(i.getIconWidth(),
    i.getIconHeight()) );
    setCurrentIconBounds( r );

    if( getIconShowing() ){

    RepaintManager.currentManager(this).addDirtyRegion(this, r.x, r.y,
    r.width, r.height);
    }
    }

    public void setIconShowing(boolean b){
    if( ! (b ^ showIcon) ){
    return ;
    }

    showIcon = b;

    if( getIcon() != null ){
    Rectangle r = getCurrentIconBounds();

    RepaintManager.currentManager(this).addDirtyRegion(this, r.x, r.y,
    r.width, r.height);
    }
    }

    public void setIconLocation(Point p){
    if( p.equals( getCurrentIconBounds().getLocation() ) ){
    return ;
    }

    Rectangle r = getCurrentIconBounds();

    if( getIconShowing() ){

    RepaintManager.currentManager(this).addDirtyRegion(this, r.x, r.y,
    r.width, r.height);
    }

    r.setLocation(p);

    if( getIconShowing() ){

    RepaintManager.currentManager(this).addDirtyRegion(this, r.x, r.y,
    r.width, r.height);
    }
    }

    @Override
    public void paint(Graphics g){
    super.paint(g);

    if( getIconShowing() && getIcon() != null ){
    Point p = getCurrentIconBounds().getLocation();
    getIcon().paintIcon(this, g, p.x, p.y);
    }
    }
    }
    }
    </code>
    --
    DF.
    Daniele Futtorovic, Jul 29, 2008
    #3
  4. Icarus

    Icarus Guest

    > Here's a way to do it. As you can see, I've used the ContentPane instead
    > of the GlassPane.


    I am using the ContentPane for my content already, i. e. the game
    board which must remain visible and able to react to MouseEvents
    itself, or else the placement won't work. Hence why I can't substitute
    the ContentPane in my program.

    It might be possible to merge this code with my own, but wouldn't that
    mean that the content below the icon won't react to MouseEvents? And
    as I am already using the ContentPane with a LayoutManager, is it such
    a good idea to paint over it, especially through overwriting the
    paint()-method?
    Icarus, Jul 30, 2008
    #4
  5. Icarus

    Icarus Guest

    There might even be a quite different approach in creating a JLabel
    from the image, putting it on the GlassPane and moving it around via
    setLocation(). That way, I don't even have to bother with overriding
    the paint methods. Putting it directly on the contentPane creates
    weird-looking results thanks to the LayoutManagers. This now has the
    problem that the GlassPane somehow catches the MouseEvents the other
    Components need, even if it's disabled.

    I have prepared a sample code replicating the problem. The goal is
    that the listener registered on the JPanel fires, i. e. displays the
    line "Received mouse event!".

    Please keep in mind that my original program consists of multiple
    JPanels, each containing other panels. So if I have to redirect the
    event manually to each component would result in a truckload of code
    that's very hard to service.


    import java.awt.Point;
    import java.awt.event.MouseAdapter;
    import java.awt.event.MouseEvent;
    import java.awt.event.MouseMotionAdapter;

    import javax.swing.ImageIcon;
    import javax.swing.JFrame;
    import javax.swing.JLabel;
    import javax.swing.JPanel;
    import javax.swing.WindowConstants;


    public class PickUpAndDrop extends JFrame{

    // Use Singleton design pattern to provide easy access of the
    relevant object
    // to the listeners
    private static PickUpAndDrop instance = new PickUpAndDrop();

    // The label to be moved around
    JLabel label;

    private PickUpAndDrop(){
    this.setSize(200, 200);
    this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);

    // Create a panel on the ContentPane that will react to user input
    JPanel listenerPanel = new JPanel();
    listenerPanel.addMouseListener(this.createListener());
    this.add(listenerPanel);

    // Create a JLabel to be moved around on the GlassPane
    java.net.URL url = this.getClass().getResource("image.png");
    ImageIcon image = new ImageIcon(url);
    this.label = new JLabel(image);

    // Get the GlassPane and prepare it for display
    JPanel glasspane = (JPanel)this.getGlassPane();
    glasspane.setVisible(true);
    glasspane.setEnabled(false);
    glasspane.setOpaque(false);
    glasspane.addMouseMotionListener(this.createMotionListener());

    // Put the label on the GlassPane
    glasspane.add(this.label);
    }

    // Move the label around
    protected void moveLabel(Point position){
    this.label.setLocation(position);
    }

    // Create a listener for giving out a printline
    // once the listener receives an event
    private MouseAdapter createListener(){

    MouseAdapter listener = new MouseAdapter(){

    public void mouseClicked(MouseEvent event) {
    System.out.println("Received mouse event!");
    }
    };

    return listener;
    }

    // Create a listener that orders the label to be moved
    // to the position of the cursor
    private MouseMotionAdapter createMotionListener(){

    MouseMotionAdapter listener = new MouseMotionAdapter(){

    public void mouseMoved(MouseEvent event) {

    Point aktPosition = event.getPoint();
    PickUpAndDrop.instance.moveLabel(aktPosition);
    }
    };

    return listener;
    }

    public static void main(String[] args){
    PickUpAndDrop frame = PickUpAndDrop.instance;
    frame.setVisible(true);
    }
    }
    Icarus, Jul 30, 2008
    #5
  6. On 30/07/2008 11:11, Icarus allegedly wrote:
    >> Here's a way to do it. As you can see, I've used the ContentPane instead
    >> of the GlassPane.

    >
    > I am using the ContentPane for my content already, i. e. the game
    > board which must remain visible and able to react to MouseEvents
    > itself, or else the placement won't work. Hence why I can't substitute
    > the ContentPane in my program.


    That's an illicit conclusion. Nothing in the code I wrote prevents mouse
    events from being fired. Neither is there anything keeping you from
    replacing the content pane in the beginning of UI building with one like
    the one I wrote and populating it just as you did before.

    > It might be possible to merge this code with my own, but wouldn't that
    > mean that the content below the icon won't react to MouseEvents?


    No, it does *not* mean that. On the contrary, you will as a matter of
    fact have trouble *getting* the mouse event if there's a child Component
    of the content pane below the cursor. So it would be better to use the
    GlassPane, after all (see below).

    > And
    > as I am already using the ContentPane with a LayoutManager, is it such
    > a good idea to paint over it, especially through overwriting the
    > paint()-method?


    No problem whatsoever /overriding/ it -- as long as super.paint is
    called beforehand.

    -.-

    I merely thought it would be better not to use the GlassPane out of
    gusto. Plus there's the problem that you would have to set the
    replacement GlassPane *visible* (for PaintEvents to be honoured), which
    it normally isn't.
    But given the issue of MouseEvents, it may be better to use the
    GlassPane. You'd set it visible at the beginning of the drag and
    invisible at the end of it.
    Otherwise, you'd have to override the content pane's processMouseEvent
    method to intercept the relevant events (which would otherwise be
    delegated to child Components). That's not all that difficult, but a tid
    bit more so.

    Furthermore, let me note that there are a few bugs and glitches, and
    some uncomelinesses, in the code I wrote. It shouldn't be simply
    incorporated as is. It was meant to show the idea of how to do it. Which
    I think it does.

    --
    DF.
    Daniele Futtorovic, Jul 30, 2008
    #6
  7. On 30/07/2008 14:41, Icarus allegedly wrote:
    > There might even be a quite different approach in creating a JLabel
    > from the image, putting it on the GlassPane and moving it around via
    > setLocation().


    I think that's a bad idea. It will surely result in more processor work,
    apart from being rather inappropriate design, IMO.


    > That way, I don't even have to bother with overriding
    > the paint methods. > Putting it directly on the contentPane creates
    > weird-looking results thanks to the LayoutManagers.


    Overriding the paint method isn't a bother. It's a basic accessory in
    advanced UI code design.

    Your code should work *perfectly*, LayoutManagers, LayoutManager2s
    notwithstanding, if you do it properly.

    > This now has the
    > problem that the GlassPane somehow catches the MouseEvents the other
    > Components need, even if it's disabled.


    Yes, if the GlassPase is visible, it will get the MouseEvent, and the
    Components below won't. That's the point of the GlassPane. It's not
    disabled if it's *visible*.

    But wouldn't you want the Components below *not* to receive events while
    the dragging occurs? Seems to me this would be beneficial. Just remember
    to hide the GlassPane when you're done ( setVisible(false) ).

    --
    DF.
    Daniele Futtorovic, Jul 30, 2008
    #7
  8. Icarus

    Icarus Guest

    > I think that's a bad idea. It will surely result in more processor work,
    > apart from being rather inappropriate design, IMO.


    The problem I faced using any other approach was that the drawn image
    on the GlassPane isn't properly deleted when the image is moved. I
    tried quite a lot here - using clearRect from Graphics2D, draw an
    invisible image on top of it, trying to repaint the old area etc.
    Using setLocation(), on the other hand, clears the old location
    automatically.

    > But wouldn't you want the Components below *not* to receive events while
    > the dragging occurs? Seems to me this would be beneficial.


    Actually, the components below need to receive an event in order to
    properly calculate the Panel in which to place the component. This is
    nothing I can change anymore at this point.

    I managed to let the events through using an AWTEventListener for
    getting the image moved on the GlassPane. And using the label
    approach, it is mostly working at the moment. But if I go back to the
    approach using paint: how do I delete the old image from view?
    Icarus, Jul 30, 2008
    #8
  9. On 30/07/2008 17:08, Icarus allegedly wrote:
    >> I think that's a bad idea. It will surely result in more processor work,
    >> apart from being rather inappropriate design, IMO.

    >
    > The problem I faced using any other approach was that the drawn image
    > on the GlassPane isn't properly deleted when the image is moved. I
    > tried quite a lot here - using clearRect from Graphics2D, draw an
    > invisible image on top of it, trying to repaint the old area etc.
    > Using setLocation(), on the other hand, clears the old location
    > automatically.
    >
    >> But wouldn't you want the Components below *not* to receive events while
    >> the dragging occurs? Seems to me this would be beneficial.

    >
    > Actually, the components below need to receive an event in order to
    > properly calculate the Panel in which to place the component. This is
    > nothing I can change anymore at this point.
    >
    > I managed to let the events through using an AWTEventListener for
    > getting the image moved on the GlassPane. And using the label
    > approach, it is mostly working at the moment.


    Glad you managed to find a working solution.

    --
    DF.
    Daniele Futtorovic, Jul 30, 2008
    #9
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. tom c
    Replies:
    5
    Views:
    376
    tom c
    Nov 1, 2006
  2. Replies:
    2
    Views:
    553
    Gernot Frisch
    Dec 15, 2005
  3. Narcolessico

    C++ and moving the mouse...

    Narcolessico, Feb 29, 2008, in forum: C++
    Replies:
    1
    Views:
    507
    Victor Bazarov
    Feb 29, 2008
  4. Hvid Hat
    Replies:
    6
    Views:
    102
    Nick Fletcher
    Mar 6, 2008
  5. Replies:
    6
    Views:
    172
    Dave Angel
    Feb 8, 2013
Loading...

Share This Page