All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.piccolo2d.PRoot Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2008-2019, Piccolo2D project, http://piccolo2d.org
 * Copyright (c) 1998-2008, University of Maryland
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, are permitted provided
 * that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this list of conditions
 * and the following disclaimer.
 *
 * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
 * and the following disclaimer in the documentation and/or other materials provided with the
 * distribution.
 *
 * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its
 * contributors may be used to endorse or promote products derived from this software without specific
 * prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package org.piccolo2d;

import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import javax.swing.SwingUtilities;
import javax.swing.Timer;

import org.piccolo2d.activities.PActivity;
import org.piccolo2d.activities.PActivityScheduler;
import org.piccolo2d.util.PDebug;
import org.piccolo2d.util.PNodeFilter;


/**
 * PRoot serves as the top node in Piccolo2D's runtime structure. The
 * PRoot responsible for running the main UI loop that processes input from
 * activities and external events.
 * 

* * @version 1.1 * @author Jesse Grosjean */ public class PRoot extends PNode { /** * Allows for future serialization code to understand versioned binary * formats. */ private static final long serialVersionUID = 1L; /** * The property name that identifies a change in the set of this root's * input sources (see {@link InputSource InputSource}). In any property * change event the new value will be a reference to the list of this root's * input sources, but old value will always be null. */ public static final String PROPERTY_INPUT_SOURCES = "inputSources"; /** * The property code that identifies a change in the set of this root's * input sources (see {@link InputSource InputSource}). In any property * change event the new value will be a reference to the list of this root's * input sources, but old value will always be null. */ public static final int PROPERTY_CODE_INPUT_SOURCES = 1 << 14; /** * The property name that identifies a change in this node's interacting * state. * * @since 1.3 */ public static final String PROPERTY_INTERACTING_CHANGED = "INTERACTING_CHANGED_NOTIFICATION"; /** * The property code that identifies a change in this node's interacting * state. * * @since 1.3 */ public static final int PROPERTY_CODE_INTERACTING_CHANGED = 1 << 13; /** Whether this not is currently processing inputs. */ protected transient boolean processingInputs; /** Whether this node needs to have its inputs processed. */ protected transient boolean processInputsScheduled; /** The number of interactions this node is currently participating in. */ private transient int interacting; /** * The singleton instance of the default input manager. */ private transient PInputManager defaultInputManager; /** The Input Sources that are registered to this node. */ private final transient List inputSources; /** * Used to provide a consistent clock time to activities as they are being * processed. * * Should it happen that an activity step take longer than a millisecond, * the next step will be unaffected by the change in clock had it used * System.currentMillis(). */ private transient long globalTime; /** * Object responsible for scheduling activities, regardless of where in the * scene they take place. */ private final PActivityScheduler activityScheduler; /** * Construct a new PRoot(). Note the PCanvas already creates a basic scene * graph for you so often you will not need to construct your own roots. */ public PRoot() { super(); inputSources = new ArrayList(); globalTime = System.currentTimeMillis(); activityScheduler = new PActivityScheduler(this); } // **************************************************************** // Activities // **************************************************************** /** * Add an activity to the activity scheduler associated with this root. * Activities are given a chance to run during each call to the roots * processInputs method. When the activity has finished running * it will automatically get removed. * * @param activity Activity that should be scheduled * @return whether it has been scheduled (always true) */ public boolean addActivity(final PActivity activity) { getActivityScheduler().addActivity(activity); return true; } /** * Get the activity scheduler associated with this root. * * @return associated scheduler */ public PActivityScheduler getActivityScheduler() { return activityScheduler; } /** * Wait for all scheduled activities to finish before returning from this * method. This will freeze out user input, and so it is generally * recommended that you use PActivities.setTriggerTime() to offset * activities instead of using this method. */ public void waitForActivities() { final PNodeFilter cameraWithCanvas = new CameraWithCanvasFilter(); while (activityScheduler.getActivitiesReference().size() > 0) { processInputs(); final Iterator i = getAllNodes(cameraWithCanvas, null).iterator(); while (i.hasNext()) { final PCamera each = (PCamera) i.next(); each.getComponent().paintImmediately(); } } } /** * Since getRoot is handled recursively, and root is the lowest point in the * hierarchy, simply returns itself. * * @return itself */ public PRoot getRoot() { return this; } /** * Get the default input manager to be used when processing input events. * PCanvas's use this method when they forward new swing input events to the * PInputManager. * * @return a singleton instance of PInputManager */ public PInputManager getDefaultInputManager() { if (defaultInputManager == null) { defaultInputManager = new PInputManager(); addInputSource(defaultInputManager); } return defaultInputManager; } /** * Return true if this root has been marked as interacting. If so the root * will normally render at a lower quality that is faster. * * @since 1.3 * @return true if this root has user interaction taking place */ public boolean getInteracting() { return interacting > 0; } /** * Set if this root is interacting. If so the root will normally render at a * lower quality that is faster. Also repaints the root if the the * interaction has ended. *

* This has similar functionality to the setInteracting method on Canvas, * but this is the appropriate place to mark interactions that may occur in * multiple canvases if this Root is shared. * * @since 1.3 * @param isInteracting True if this root has user interaction taking place * @see PCanvas#setInteracting(boolean) */ public void setInteracting(final boolean isInteracting) { final boolean wasInteracting = getInteracting(); if (isInteracting) { interacting++; } else { interacting--; } if (!isInteracting && !getInteracting()) { // force all the child cameras to repaint for (int i = 0; i < getChildrenCount(); i++) { final PNode child = getChild(i); if (child instanceof PCamera) { child.repaint(); } } } if (wasInteracting != isInteracting) { firePropertyChange(PROPERTY_CODE_INTERACTING_CHANGED, PROPERTY_INTERACTING_CHANGED, Boolean .valueOf(wasInteracting), Boolean.valueOf(isInteracting)); } } /** * Advanced. If you want to add additional input sources to the roots UI * process you can do that here. You will seldom do this unless you are * making additions to the Piccolo2D framework. * * @param inputSource An input source that should be added */ public void addInputSource(final InputSource inputSource) { inputSources.add(inputSource); firePropertyChange(PROPERTY_CODE_INPUT_SOURCES, PROPERTY_INPUT_SOURCES, null, inputSources); } /** * Advanced. If you want to remove the default input source from the roots * UI process you can do that here. You will seldom do this unless you are * making additions to the Piccolo2D framework. * * @param inputSource input source that should no longer be asked about * input events */ public void removeInputSource(final InputSource inputSource) { if (inputSources.remove(inputSource)) { firePropertyChange(PROPERTY_CODE_INPUT_SOURCES, PROPERTY_INPUT_SOURCES, null, inputSources); } } /** * Returns a new timer. This method allows subclasses, such as PSWTRoot to * create custom timers that will be used transparently by the Piccolo2D * framework. * * @param delay # of milliseconds before action listener is invoked * @param listener listener to be invoked after delay * * @return A new Timer */ public Timer createTimer(final int delay, final ActionListener listener) { return new Timer(delay, listener); } // **************************************************************** // UI Loop - Methods for running the main UI loop of Piccolo2D. // **************************************************************** /** * Get the global Piccolo2D time. This is set to System.currentTimeMillis() * at the beginning of the roots processInputs method. * Activities should usually use this global time instead of System. * currentTimeMillis() so that multiple activities will be synchronized. * * @return time as recorded at the beginning of activity scheduling */ public long getGlobalTime() { return globalTime; } /** * This is the heartbeat of the Piccolo2D framework. Pending input events * are processed. Activities are given a chance to run, and the bounds * caches and any paint damage is validated. */ public void processInputs() { PDebug.startProcessingInput(); processingInputs = true; globalTime = System.currentTimeMillis(); if (inputSources.size() > 0) { final Iterator inputSourceIterator = inputSources.iterator(); while (inputSourceIterator.hasNext()) { final InputSource each = (InputSource) inputSourceIterator.next(); each.processInput(); } } activityScheduler.processActivities(globalTime); validateFullBounds(); validateFullPaint(); processingInputs = false; PDebug.endProcessingInput(); } /** {@inheritDoc} */ public void setFullBoundsInvalid(final boolean fullLayoutInvalid) { super.setFullBoundsInvalid(fullLayoutInvalid); scheduleProcessInputsIfNeeded(); } /** {@inheritDoc} */ public void setChildBoundsInvalid(final boolean childLayoutInvalid) { super.setChildBoundsInvalid(childLayoutInvalid); scheduleProcessInputsIfNeeded(); } /** {@inheritDoc} */ public void setPaintInvalid(final boolean paintInvalid) { super.setPaintInvalid(paintInvalid); scheduleProcessInputsIfNeeded(); } /** {@inheritDoc} */ public void setChildPaintInvalid(final boolean childPaintInvalid) { super.setChildPaintInvalid(childPaintInvalid); scheduleProcessInputsIfNeeded(); } /** * Schedule process inputs if needed. */ public void scheduleProcessInputsIfNeeded() { /* * The reason for the special case here (when not in the event dispatch * thread) is that the SwingUtilitiles.invokeLater code below only * invokes later with respect to the event dispatch thread, it will * invoke concurrently with other threads. */ if (!SwingUtilities.isEventDispatchThread()) { /* * Piccolo2D is not thread safe and should almost always be called * from the Swing event dispatch thread. It should only reach this * point when a new canvas is being created. */ return; } PDebug.scheduleProcessInputs(); if (!processInputsScheduled && !processingInputs && (getFullBoundsInvalid() || getChildBoundsInvalid() || getPaintInvalid() || getChildPaintInvalid())) { processInputsScheduled = true; SwingUtilities.invokeLater(new Runnable() { public void run() { processInputs(); processInputsScheduled = false; } }); } } private static final class CameraWithCanvasFilter implements PNodeFilter { public boolean accept(final PNode aNode) { return aNode instanceof PCamera && ((PCamera) aNode).getComponent() != null; } public boolean acceptChildrenOf(final PNode aNode) { return true; } } /** * This interfaces is for advanced use only. If you want to implement a * different kind of input framework then Piccolo2D provides you can hook it * in here. */ public static interface InputSource { /** Causes the system to process any pending Input Events. */ void processInput(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy