Hello, I've been trying to fix a bug I just found using Transitions and ComponentMouseListeners. You can take, for example, the ScaleTransition I just submitted to the dev mailing list, although this should apply to any transition.

For this example, I'll use the ScaleTransition in a ComponentMouseListener, as follows:


import java.util.Arrays;
import java.util.Collection;
import org.apache.pivot.wtk.Component;
import org.apache.pivot.wtk.ComponentMouseListener;

public class ZoomComponentMouseListener implements ComponentMouseListener {

    private ScaleTransition scaleTransition = new ScaleTransition();

    public ZoomComponentMouseListener(Component targetComponent) {
        scaleTransition.addComponent(targetComponent);
    }

    public boolean mouseMove(Component component, int x, int y) {
        return false;
    }

    public void mouseOver(Component component) {
        if (scaleTransition.isRunning()) {
            scaleTransition.reverse();
        } else {
            scaleTransition.setReversed(false);
            scaleTransition.start();
        }
    }

    public void mouseOut(Component component) {
        if (scaleTransition.isRunning()) {
            scaleTransition.reverse();
        } else {
            scaleTransition.setReversed(true);
            scaleTransition.start();
        }
    }

    public void setZoom(float zoom) {
        scaleTransition.setZoom(zoom);
    }
}


When this listener is attached to a Component, like an ImageView for example, if you get through the ImageView quickly with the mouse pointer and then cross it again quickly, the method Transition#getPercentComplete will get screwed, the second time the transition is started getPercentComplete will report 1 when it should report 0. I've tested this many times, before calling Transition#start, getPercentComplete reports 0, when calling start and checking the value of getPercentComplete from the Transition#update method it reports 1.

I've got no clue on what's happening here, placing a breakpoint inside the Transition#start method reports currentTime - startTime == 0, but checking again when it gets to the Transition#update method currentTime - startTime == 1.

Perhaps I've anticipated on saying this is a bug for Transitions, perhaps is my code what's causing problems, but I just don't have a clue on how to fix this.

Please, if anybody's able to reproduce this, let me know so we can find a solution. I've attached the ScaleTransition here in case there are users not subscribed to the dev mailing list.

Thanks in advance!
Edgar Merino
/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package org.devpower.pivot;

import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import org.apache.pivot.wtk.Component;
import org.apache.pivot.wtk.effects.Decorator;
import org.apache.pivot.wtk.effects.ScaleDecorator;

/**
 * This transition provides a "zoom" effect for components that use it.
 * 
 * @author emerino
 */
public class ScaleTransition extends Transition {

    private class CustomScaleDecorator extends ScaleDecorator {

        private float initialScaleX = 1.0f;
        private float initialScaleY = 1.0f;

        public void setInitialScales(ScaleDecorator decorator) {
            initialScaleX = decorator.getScaleX();
            initialScaleY = decorator.getScaleY();
        }

        public float getInitialScaleX() {
            return initialScaleX;
        }

        public float getInitialScaleY() {
            return initialScaleY;
        }
    }

    private static final int DEFAULT_DURATION = 200;
    private static final int DEFAULT_RATE = 28;

    private Map<Component, CustomScaleDecorator> componentDecoratorMap =
            Collections.synchronizedMap(new HashMap<Component, CustomScaleDecorator>());

    private boolean reversed;
    private float zoom = 0.3f;

    public ScaleTransition() {
        this(DEFAULT_DURATION, DEFAULT_RATE);
    }

    public ScaleTransition(int duration, int rate) {
        super(duration, rate);
    }

    public Set<Component> getComponents() {
        return componentDecoratorMap.keySet();
    }

    public void addComponent(Component component) {
        if (component == null) {
            throw new IllegalArgumentException("Component cannot be null");
        }

        if (!componentDecoratorMap.containsKey(component)) {
            CustomScaleDecorator scaleDecorator = new CustomScaleDecorator();

            for (Decorator decorator : component.getDecorators()) {
                if (decorator instanceof ScaleDecorator) {
                    scaleDecorator.setInitialScales(scaleDecorator);

                    break;
                }
            }

            componentDecoratorMap.put(component, scaleDecorator);
            component.getDecorators().add(scaleDecorator);
        }
    }

    public void addComponents(Collection<Component> components) {
        for (Component c : components) {
            addComponent(c);
        }
    }

    public void setZoom(float zoom) {
        if (isRunning()) {
            throw new IllegalStateException("Transition in progress");
        }

        this.zoom = zoom;
    }

    public void setReversed(boolean reversed) {
        if (isRunning()) {
            throw new IllegalStateException("Transition in progress");
        }

        this.reversed = reversed;
    }

    @Override
    protected void update() {
        float scaleX = 0;
        float scaleY = 0;
        CustomScaleDecorator scaleDecorator = null;

        for (Component component : componentDecoratorMap.keySet()) {
            scaleDecorator = componentDecoratorMap.get(component);

            if (!reversed) {
                scaleX = scaleDecorator.getInitialScaleX() + (zoom * getPercentComplete());
                scaleY = scaleDecorator.getInitialScaleY() + (zoom * getPercentComplete());
            } else {
                scaleX = (scaleDecorator.getInitialScaleX() + zoom) - (zoom * getPercentComplete());
                scaleY = (scaleDecorator.getInitialScaleY() + zoom) - (zoom * getPercentComplete());
            }

            scaleDecorator.setScaleX(scaleX);
            scaleDecorator.setScaleY(scaleY);

            component.repaint();
        }
    }

    public void removeComponent(Component component) {
        if (componentDecoratorMap.containsKey(component)) {
            component.getDecorators().remove(componentDecoratorMap.remove(component));
        }
    }

    public void clearComponents() {
        for (Component c : componentDecoratorMap.keySet()) {
            removeComponent(c);
        }
    }
}

Reply via email to