Revamped the frame manager to support operating on non-JFrames where our Window

and our RootPaneContainer might be different things (the Window would naturally
contain the RootPaneContainer but wouldn't necessarily be the same component).


git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@4181 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
Michael Bayne
2006-06-07 21:54:12 +00:00
parent 04af1ed28a
commit 7090277d99
10 changed files with 108 additions and 114 deletions
@@ -24,7 +24,6 @@ package com.threerings.media;
import java.applet.Applet;
import java.awt.Component;
import java.awt.Frame;
import java.awt.Graphics2D;
import java.awt.Graphics;
import java.awt.Image;
@@ -53,17 +52,17 @@ import com.samskivert.util.StringUtil;
*
* @see FrameManager
*/
public class FrameRepaintManager extends RepaintManager
public class ActiveRepaintManager extends RepaintManager
{
/**
* Components that are rooted in this frame will be rendered into the
* offscreen buffer managed by the frame manager. Other components
* will be rendered into separate offscreen buffers and repainted in
* the normal Swing manner.
* Components that are rooted in this component (which must be a {@link
* Window} or an {@link Applet}) will be rendered into the offscreen buffer
* managed by the frame manager. Other components will be rendered into
* separate offscreen buffers and repainted in the normal Swing manner.
*/
public FrameRepaintManager (Frame frame)
public ActiveRepaintManager (Component root)
{
_rootFrame = frame;
_root = root;
}
// documentation inherited
@@ -393,7 +392,7 @@ public class FrameRepaintManager extends RepaintManager
// if this component is rooted in our frame, repaint it into
// the supplied graphics instance
if (root == _rootFrame) {
if (root == _root) {
if (DEBUG) {
Log.info("Repainting [comp=" + toString(comp) +
StringUtil.toString(_cbounds) +
@@ -478,8 +477,8 @@ public class FrameRepaintManager extends RepaintManager
}
}
/** The managed frame. */
protected Frame _rootFrame;
/** The root of our interface. */
protected Component _root;
/** A list of invalid components. */
protected Object[] _invalid;
@@ -1,5 +1,5 @@
//
// $Id: BackFrameManager.java,v 1.8 2004/08/27 02:12:37 mdb Exp $
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
@@ -39,11 +39,11 @@ public class BackFrameManager extends FrameManager
boolean incremental = true;
do {
GraphicsConfiguration gc = _frame.getGraphicsConfiguration();
GraphicsConfiguration gc = _window.getGraphicsConfiguration();
// create our off-screen buffer if necessary
if (_backimg == null || _backimg.getWidth() != _frame.getWidth() ||
_backimg.getHeight() != _frame.getHeight()) {
if (_backimg == null || _backimg.getWidth() != _window.getWidth() ||
_backimg.getHeight() != _window.getHeight()) {
createBackBuffer(gc);
}
@@ -73,8 +73,8 @@ public class BackFrameManager extends FrameManager
// dirty everything if we're not incrementally rendering
if (!incremental) {
_frame.getRootPane().revalidate();
_frame.getRootPane().repaint();
_root.getRootPane().revalidate();
_root.getRootPane().repaint();
}
if (!paint(_bgfx)) {
@@ -84,7 +84,7 @@ public class BackFrameManager extends FrameManager
// we cache our frame's graphics object so that we can avoid
// instantiating a new one on every tick
if (_fgfx == null) {
_fgfx = (Graphics2D)_frame.getGraphics();
_fgfx = (Graphics2D)_window.getGraphics();
}
_fgfx.drawImage(_backimg, 0, 0, null);
@@ -120,7 +120,7 @@ public class BackFrameManager extends FrameManager
}
// create the offscreen buffer
int width = _frame.getWidth(), height = _frame.getHeight();
int width = _window.getWidth(), height = _window.getHeight();
_backimg = gc.createCompatibleVolatileImage(width, height);
// fill the back buffer with white
@@ -1,5 +1,5 @@
//
// $Id: FlipFrameManager.java,v 1.6 2004/08/27 02:12:37 mdb Exp $
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
@@ -43,13 +43,13 @@ public class FlipFrameManager extends FrameManager
new ImageCapabilities(true), new ImageCapabilities(true),
BufferCapabilities.FlipContents.COPIED);
try {
_frame.createBufferStrategy(2, cap);
_window.createBufferStrategy(2, cap);
} catch (AWTException ae) {
Log.warning("Failed creating flip bufstrat: " + ae + ".");
// fall back to one without custom capabilities
_frame.createBufferStrategy(2);
_window.createBufferStrategy(2);
}
_bufstrat = _frame.getBufferStrategy();
_bufstrat = _window.getBufferStrategy();
}
// start out assuming we can do an incremental render
@@ -65,8 +65,8 @@ public class FlipFrameManager extends FrameManager
Log.info("Doing non-incremental render; contents lost " +
"[lost=" + _bufstrat.contentsLost() +
", rest=" + _bufstrat.contentsRestored() + "].");
_frame.getRootPane().revalidate();
_frame.getRootPane().repaint();
_root.getRootPane().revalidate();
_root.getRootPane().repaint();
}
// request to paint our participants and components and bail
+52 -54
View File
@@ -39,9 +39,10 @@ import java.awt.event.WindowListener;
import java.awt.EventQueue;
import javax.swing.JFrame;
import javax.swing.JLayeredPane;
import javax.swing.RepaintManager;
import javax.swing.JRootPane;
import javax.swing.RootPaneContainer;
import javax.swing.event.AncestorEvent;
import javax.swing.event.AncestorListener;
@@ -56,35 +57,33 @@ import com.threerings.media.util.TrailingAverage;
import com.threerings.util.unsafe.Unsafe;
/**
* Provides a central point from which the computation for each "frame" or
* tick can be dispatched. This assumed that the application structures
* its activity around the rendering of each frame, which is a common
* architecture for games. The animation and sprite support provided by
* other classes in this package are structured for use in an application
* that uses a frame manager to tick everything once per frame.
* Provides a central point from which the computation for each "frame" or tick
* can be dispatched. This assumed that the application structures its activity
* around the rendering of each frame, which is a common architecture for
* games. The animation and sprite support provided by other classes in this
* package are structured for use in an application that uses a frame manager
* to tick everything once per frame.
*
* <p> The frame manager goes through a simple two part procedure every
* frame:
* <p> The frame manager goes through a simple two part procedure every frame:
*
* <ul>
* <li> Ticking all of the frame participants: in {@link
* FrameParticipant#tick}, any processing that need be performed during
* this frame should be performed. Care should be taken not to execute
* code that will take unduly long, instead such processing should be
* broken up so that it can be performed in small pieces every frame (or
* performed on a separate thread with the results safely communicated
* back to the frame participants for incorporation into the rendering
* loop).
* FrameParticipant#tick}, any processing that need be performed during this
* frame should be performed. Care should be taken not to execute code that
* will take unduly long, instead such processing should be broken up so that
* it can be performed in small pieces every frame (or performed on a separate
* thread with the results safely communicated back to the frame participants
* for incorporation into the rendering loop).
*
* <li> Painting the user interface hierarchy: the top-level component
* (the frame) is painted (via a call to {@link JFrame#paint}) into a flip
* buffer (if supported, an off-screen buffer if not). Updates that were
* computed during the tick should be rendered in this call to paint. The
* paint call will propagate down to all components in the UI hierarchy,
* some of which may be {@link FrameParticipant}s and will have prepared
* themselves for their upcoming painting in the previous call to {@link
* FrameParticipant#tick}. When the call to paint completes, the flip
* buffer is flipped and the process starts all over again. </ul>
* <li> Painting the user interface hierarchy: the top-level component (the
* frame) is painted (via a call to {@link JRootPane#paint}) into a flip buffer
* (if supported, an off-screen buffer if not). Updates that were computed
* during the tick should be rendered in this call to paint. The paint call
* will propagate down to all components in the UI hierarchy, some of which may
* be {@link FrameParticipant}s and will have prepared themselves for their
* upcoming painting in the previous call to {@link
* FrameParticipant#tick}. When the call to paint completes, the flip buffer is
* flipped and the process starts all over again. </ul>
*
* <p> The ticking and rendering takes place on the AWT thread so as to
* avoid the need for complicated coordination between AWT event handler
@@ -125,12 +124,13 @@ public abstract class FrameManager
/**
* Creates a frame manager that will use a {@link SystemMediaTimer} to
* obtain timing information, which is available on every platform,
* but returns inaccurate time stamps on many platforms.
* obtain timing information, which is available on every platform, but
* returns inaccurate time stamps on many platforms.
*
* @see #newInstance(JFrame, MediaTimer)
* @see #newInstance(Window, RootPaneContainer, MediaTimer)
*/
public static FrameManager newInstance (JFrame frame)
public static FrameManager newInstance (
Window window, RootPaneContainer root)
{
// first try creating a PerfTimer which is the best if we're using
// JDK1.4.2
@@ -142,7 +142,7 @@ public abstract class FrameManager
"System.currentTimeMillis() based timer.");
timer = new SystemMediaTimer();
}
return newInstance(frame, timer);
return newInstance(window, root, timer);
}
/**
@@ -154,7 +154,8 @@ public abstract class FrameManager
*
* @see GraphicsDevice#setFullScreenWindow
*/
public static FrameManager newInstance (JFrame frame, MediaTimer timer)
public static FrameManager newInstance (
Window window, RootPaneContainer root, MediaTimer timer)
{
FrameManager fmgr;
if (_useFlip.getValue()) {
@@ -164,23 +165,25 @@ public abstract class FrameManager
Log.info("Creating back frame manager.");
fmgr = new BackFrameManager();
}
fmgr.init(frame, timer);
fmgr.init(window, root, timer);
return fmgr;
}
/**
* Initializes this frame manager and prepares it for operation.
*/
protected void init (JFrame frame, MediaTimer timer)
protected void init (
Window window, RootPaneContainer root, MediaTimer timer)
{
_frame = frame;
if (frame instanceof ManagedJFrame) {
((ManagedJFrame)_frame).init(this);
_window = window;
_root = root;
if (window instanceof ManagedJFrame) {
((ManagedJFrame)window).init(this);
}
_timer = timer;
// set up our custom repaint manager
_remgr = new FrameRepaintManager(_frame);
_remgr = new ActiveRepaintManager(_window);
RepaintManager.setCurrentManager(_remgr);
// turn off double buffering for the whole business because we
@@ -199,7 +202,7 @@ public abstract class FrameManager
protected void addTestListeners ()
{
// add a test window listener
_frame.addWindowListener(new WindowListener() {
_window.addWindowListener(new WindowListener() {
public void windowActivated (WindowEvent e) {
Log.info("Window activated [evt=" + e + "].");
}
@@ -230,7 +233,7 @@ public abstract class FrameManager
});
// add a component listener
_frame.addComponentListener(new ComponentListener() {
_window.addComponentListener(new ComponentListener() {
public void componentHidden (ComponentEvent e) {
Log.info("Window component hidden [evt=" + e + "].");
}
@@ -249,7 +252,7 @@ public abstract class FrameManager
});
// add test ancestor focus listener
_frame.getRootPane().addAncestorListener(
_root.getRootPane().addAncestorListener(
new AncestorListener() {
public void ancestorAdded (AncestorEvent e) {
Log.info("Root pane ancestor added [e=" + e + "].");
@@ -328,14 +331,6 @@ public abstract class FrameManager
ListUtil.clearRef(_participants, participant);
}
/**
* Returns the frame being managed.
*/
public JFrame getFrame ()
{
return _frame;
}
/**
* Returns a millisecond granularity time stamp using the {@link
* MediaTimer} with which this frame manager was configured.
@@ -419,8 +414,8 @@ public abstract class FrameManager
}
// if our frame is not showing (or is impossibly sized), don't try
// rendering anything
if (_frame.isShowing() &&
_frame.getWidth() > 0 && _frame.getHeight() > 0) {
if (_window.isShowing() &&
_window.getWidth() > 0 && _window.getHeight() > 0) {
// tick our participants
tickParticipants(tickStamp);
paint = _timer.getElapsedMicros();
@@ -488,7 +483,7 @@ public abstract class FrameManager
/**
* Called once per frame to invoke {@link Component#paint} on all of
* our frame participants' components and all dirty components managed
* by our {@link FrameRepaintManager}.
* by our {@link ActiveRepaintManager}.
*/
protected abstract void paint (long tickStamp);
@@ -774,14 +769,17 @@ public abstract class FrameManager
protected long _lastTick;
};
/** The frame into which we do our rendering. */
protected JFrame _frame;
/** The window into which we do our rendering. */
protected Window _window;
/** Provides access to our Swing bits. */
protected RootPaneContainer _root;
/** Used to obtain timing measurements. */
protected MediaTimer _timer;
/** Our custom repaint manager. */
protected FrameRepaintManager _remgr;
protected ActiveRepaintManager _remgr;
/** The number of milliseconds per frame (14 by default, which gives
* an fps of ~71). */
@@ -89,7 +89,7 @@ public class EditorApp implements Runnable
_frame = createEditorFrame();
// create our frame manager
_framemgr = FrameManager.newInstance(_frame);
_framemgr = FrameManager.newInstance(_frame, _frame);
// create our myriad managers, repositories, etc.
_rmgr = new ResourceManager("rsrc");
@@ -76,7 +76,7 @@ public class ViewerApp
_mesgmgr = new MessageManager("rsrc.i18n");
_frame = new ViewerFrame(gc);
_framemgr = FrameManager.newInstance(_frame);
_framemgr = FrameManager.newInstance(_frame, _frame);
StageContext ctx = new ContextImpl();
_frame.init(ctx, new CharacterManager(_imgr, _crepo));
+26 -29
View File
@@ -22,7 +22,7 @@
package com.threerings.util;
import java.awt.Component;
import java.awt.Frame;
import java.awt.Window;
import java.awt.KeyEventDispatcher;
import java.awt.KeyboardFocusManager;
@@ -61,14 +61,14 @@ public class KeyDispatcher
/**
* Constructs a key dispatcher.
*/
public KeyDispatcher (Frame frame)
public KeyDispatcher (Window window)
{
// save things off
_frame = frame;
_window = window;
// listen to window events on our main frame so that we can
// release keys when the mouse leaves the frame
_frame.addWindowFocusListener(this);
// listen to window events on our main window so that we can release
// keys when the mouse leaves the window
_window.addWindowFocusListener(this);
// monitor key events from the central dispatch mechanism
KeyboardFocusManager.getCurrentKeyboardFocusManager().
@@ -76,19 +76,17 @@ public class KeyDispatcher
}
/**
* Shuts down the key dispatcher. We currently have no plans to ever
* shut this little guy down, and so we leave the following dead code
* here, commented forevermore.
* Shuts down the key dispatcher.
*/
// public void shutdown ()
// {
// // cease monitoring key events
// KeyboardFocusManager.getCurrentKeyboardFocusManager().
// removeKeyEventDispatcher(this);
public void shutdown ()
{
// cease monitoring key events
KeyboardFocusManager.getCurrentKeyboardFocusManager().
removeKeyEventDispatcher(this);
// // cease observing our frame
// _frame.removeWindowFocusListener(this);
// }
// cease observing our window
_window.removeWindowFocusListener(this);
}
/**
* Makes the specified component the new grabber of key typed events
@@ -121,7 +119,7 @@ public class KeyDispatcher
// update the current chat grabbing component
_curChatGrabber = _chatGrabbers.isEmpty() ? null :
(JTextComponent) _chatGrabbers.getLast();
_chatGrabbers.getLast();
}
/**
@@ -149,7 +147,7 @@ public class KeyDispatcher
case KeyEvent.KEY_TYPED:
// dispatch to all the global listeners
for (int ii = 0; ii < lsize; ii++) {
((KeyListener) _listeners.get(ii)).keyTyped(e);
_listeners.get(ii).keyTyped(e);
}
// see if a chat grabber needs to grab it
@@ -172,7 +170,7 @@ public class KeyDispatcher
case KeyEvent.KEY_PRESSED:
if (lsize > 0) {
for (int ii = 0; ii < lsize; ii++) {
((KeyListener) _listeners.get(ii)).keyPressed(e);
_listeners.get(ii).keyPressed(e);
}
// remember the key event..
_downKeys.put(e.getKeyCode(), e);
@@ -227,15 +225,13 @@ public class KeyDispatcher
// un-press any keys that were left down
if (!_downKeys.isEmpty()) {
long now = System.currentTimeMillis();
for (Iterator iter = _downKeys.elements(); iter.hasNext(); ) {
KeyEvent down = (KeyEvent) iter.next();
for (KeyEvent down : _downKeys.values()) {
KeyEvent up = new KeyEvent(
down.getComponent(), KeyEvent.KEY_RELEASED, now,
down.getModifiers(), down.getKeyCode(), down.getKeyChar(),
down.getKeyLocation());
for (int ii = 0, nn = _listeners.size(); ii < nn; ii++) {
((KeyListener) _listeners.get(ii)).keyReleased(up);
_listeners.get(ii).keyReleased(up);
}
}
_downKeys.clear();
@@ -260,19 +256,20 @@ public class KeyDispatcher
removeChatGrabber((JTextComponent) ae.getComponent());
}
/** The main frame for which we're observing key events. */
protected Frame _frame;
/** The main window for which we're observing key events. */
protected Window _window;
/** The current most-recently pushed component that wants to grab
* alphanumeric key presses. */
protected JTextComponent _curChatGrabber;
/** The stack of grabbers. */
protected LinkedList _chatGrabbers = new LinkedList();
protected LinkedList<JTextComponent> _chatGrabbers =
new LinkedList<JTextComponent>();
/** Global key listeners. */
protected ArrayList _listeners = new ArrayList();
protected ArrayList<KeyListener> _listeners = new ArrayList<KeyListener>();
/** Keys that are currently held down. */
protected HashIntMap _downKeys = new HashIntMap();
protected HashIntMap<KeyEvent> _downKeys = new HashIntMap<KeyEvent>();
}
@@ -1,5 +1,5 @@
//
// $Id: PathViz.java,v 1.5 2004/08/27 02:21:00 mdb Exp $
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
@@ -74,7 +74,7 @@ public class PathViz extends MediaPanel
public static void main (String[] args)
{
ManagedJFrame frame = new ManagedJFrame("Path viz");
FrameManager fmgr = FrameManager.newInstance(frame);
FrameManager fmgr = FrameManager.newInstance(frame, frame);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setContentPane(new PathViz(fmgr));
@@ -88,7 +88,7 @@ public class ScrollingTestApp
_frame = new ScrollingFrame(gc);
// set up our frame manager
_framemgr = FrameManager.newInstance(_frame);
_framemgr = FrameManager.newInstance(_frame, _frame);
// we don't need to configure anything
ResourceManager rmgr = new ResourceManager("rsrc");
@@ -81,7 +81,7 @@ public class ViewerApp
// create the window
_frame = new ViewerFrame(gc);
_framemgr = FrameManager.newInstance(_frame);
_framemgr = FrameManager.newInstance(_frame, _frame);
// we don't need to configure anything
ResourceManager rmgr = new ResourceManager("rsrc");