Add zoom support to VirtualMediaPanel
This commit is contained in:
@@ -19,27 +19,26 @@
|
|||||||
|
|
||||||
package com.threerings.media;
|
package com.threerings.media;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
|
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
|
import java.awt.Point;
|
||||||
import java.awt.Rectangle;
|
import java.awt.Rectangle;
|
||||||
import java.awt.event.MouseEvent;
|
import java.awt.event.MouseEvent;
|
||||||
import java.awt.event.MouseWheelEvent;
|
import java.awt.event.MouseWheelEvent;
|
||||||
|
import java.awt.geom.AffineTransform;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import javax.swing.SwingUtilities;
|
import javax.swing.SwingUtilities;
|
||||||
|
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
|
|
||||||
import com.samskivert.util.RunAnywhere;
|
import com.samskivert.util.RunAnywhere;
|
||||||
|
import static com.threerings.media.Log.log;
|
||||||
import com.threerings.media.image.ImageUtil;
|
import com.threerings.media.image.ImageUtil;
|
||||||
import com.threerings.media.image.Mirage;
|
import com.threerings.media.image.Mirage;
|
||||||
import com.threerings.media.util.MathUtil;
|
import com.threerings.media.util.MathUtil;
|
||||||
import com.threerings.media.util.Pathable;
|
import com.threerings.media.util.Pathable;
|
||||||
|
|
||||||
import static com.threerings.media.Log.log;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extends the base media panel with the notion of a virtual coordinate system. All entities in
|
* Extends the base media panel with the notion of a virtual coordinate system. All entities in
|
||||||
* the virtual media panel have virtual coordinates and the virtual media panel displays a window
|
* the virtual media panel have virtual coordinates and the virtual media panel displays a window
|
||||||
@@ -67,6 +66,11 @@ public class VirtualMediaPanel extends MediaPanel
|
|||||||
public VirtualMediaPanel (FrameManager framemgr)
|
public VirtualMediaPanel (FrameManager framemgr)
|
||||||
{
|
{
|
||||||
super(framemgr);
|
super(framemgr);
|
||||||
|
|
||||||
|
addZoomListener((oldZoom, newZoom) -> {
|
||||||
|
_vbounds = _zoomManager.rescaleBounds(_vbounds, getWidth(), getHeight());
|
||||||
|
_metamgr.getRegionManager().addDirtyRegion(_vbounds);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -149,7 +153,8 @@ public class VirtualMediaPanel extends MediaPanel
|
|||||||
@Override
|
@Override
|
||||||
protected void processMouseEvent (MouseEvent event)
|
protected void processMouseEvent (MouseEvent event)
|
||||||
{
|
{
|
||||||
event.translatePoint(_vbounds.x, _vbounds.y);
|
event.translatePoint(_nx, _ny);
|
||||||
|
_zoomManager.adjustMouseEvent(event, _vbounds);
|
||||||
super.processMouseEvent(event);
|
super.processMouseEvent(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -160,7 +165,8 @@ public class VirtualMediaPanel extends MediaPanel
|
|||||||
@Override
|
@Override
|
||||||
protected void processMouseMotionEvent (MouseEvent event)
|
protected void processMouseMotionEvent (MouseEvent event)
|
||||||
{
|
{
|
||||||
event.translatePoint(_vbounds.x, _vbounds.y);
|
event.translatePoint(_nx, _ny);
|
||||||
|
_zoomManager.adjustMouseEvent(event, _vbounds);
|
||||||
super.processMouseMotionEvent(event);
|
super.processMouseMotionEvent(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -171,7 +177,8 @@ public class VirtualMediaPanel extends MediaPanel
|
|||||||
@Override
|
@Override
|
||||||
protected void processMouseWheelEvent (MouseWheelEvent event)
|
protected void processMouseWheelEvent (MouseWheelEvent event)
|
||||||
{
|
{
|
||||||
event.translatePoint(_vbounds.x, _vbounds.y);
|
event.translatePoint(_nx, _ny);
|
||||||
|
_zoomManager.adjustMouseEvent(event, _vbounds);
|
||||||
super.processMouseWheelEvent(event);
|
super.processMouseWheelEvent(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -179,7 +186,7 @@ public class VirtualMediaPanel extends MediaPanel
|
|||||||
protected void dirtyScreenRect (Rectangle rect)
|
protected void dirtyScreenRect (Rectangle rect)
|
||||||
{
|
{
|
||||||
// translate the screen rect into happy coordinates
|
// translate the screen rect into happy coordinates
|
||||||
rect.translate(_vbounds.x, _vbounds.y);
|
rect.translate(_nx, _ny);
|
||||||
_metamgr.getRegionManager().addDirtyRegion(rect);
|
_metamgr.getRegionManager().addDirtyRegion(rect);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -201,6 +208,7 @@ public class VirtualMediaPanel extends MediaPanel
|
|||||||
// keep track of the size of the viewport
|
// keep track of the size of the viewport
|
||||||
_vbounds.width = getWidth();
|
_vbounds.width = getWidth();
|
||||||
_vbounds.height = getHeight();
|
_vbounds.height = getHeight();
|
||||||
|
_vbounds = _zoomManager.scaleOnCenter(_vbounds);
|
||||||
|
|
||||||
// we need to obtain our absolute screen coordinates to work
|
// we need to obtain our absolute screen coordinates to work
|
||||||
// around the Windows copyArea() bug
|
// around the Windows copyArea() bug
|
||||||
@@ -235,16 +243,23 @@ public class VirtualMediaPanel extends MediaPanel
|
|||||||
|
|
||||||
protected void adjustBoundsCenter ()
|
protected void adjustBoundsCenter ()
|
||||||
{
|
{
|
||||||
int width = getWidth(), height = getHeight();
|
int width = _vbounds.width, height = _vbounds.height;
|
||||||
|
|
||||||
// adjusts our view location to track any pathable we might be tracking
|
// adjusts our view location to track any pathable we might be tracking
|
||||||
trackPathable();
|
trackPathable();
|
||||||
|
|
||||||
|
// our offset is from the center of the view panel, so we need to adjust
|
||||||
|
// coordinates to account for zoom.
|
||||||
|
Point viewCenter = ZoomManager.center(_vbounds);
|
||||||
|
Point panelCenter = new Point(getWidth() / 2, getHeight() / 2);
|
||||||
|
int offsetX = viewCenter.x - panelCenter.x;
|
||||||
|
int offsetY = viewCenter.y - panelCenter.y;
|
||||||
|
|
||||||
// if we have a new target location, we'll need to generate dirty
|
// if we have a new target location, we'll need to generate dirty
|
||||||
// regions for the area exposed by the scrolling
|
// regions for the area exposed by the scrolling
|
||||||
if (_nx != _vbounds.x || _ny != _vbounds.y) {
|
if (_nx != offsetX || _ny != offsetY) {
|
||||||
// determine how far we'll be moving on this tick
|
// determine how far we'll be moving on this tick
|
||||||
int dx = _nx - _vbounds.x, dy = _ny - _vbounds.y;
|
int dx = _nx - offsetX, dy = _ny - offsetY;
|
||||||
|
|
||||||
// log.info("Scrolling into place [n=(" + _nx + ", " + _ny +
|
// log.info("Scrolling into place [n=(" + _nx + ", " + _ny +
|
||||||
// "), t=(" + _vbounds.x + ", " + _vbounds.y +
|
// "), t=(" + _vbounds.x + ", " + _vbounds.y +
|
||||||
@@ -254,29 +269,29 @@ public class VirtualMediaPanel extends MediaPanel
|
|||||||
_dx = dx;
|
_dx = dx;
|
||||||
_dy = dy;
|
_dy = dy;
|
||||||
|
|
||||||
|
// now go ahead and update our location so that changes in between here and the call
|
||||||
|
// to paint() for this tick don't booch everything
|
||||||
|
_vbounds.x += _dx;
|
||||||
|
_vbounds.y += _dy;
|
||||||
|
|
||||||
// these are used to prevent the vertical strip from
|
// these are used to prevent the vertical strip from
|
||||||
// overlapping the horizontal strip
|
// overlapping the horizontal strip
|
||||||
int sy = _ny, shei = height;
|
int sy = _vbounds.y, shei = height;
|
||||||
|
|
||||||
// and add invalid rectangles for the exposed areas
|
// and add invalid rectangles for the exposed areas
|
||||||
if (dy > 0) {
|
if (dy > 0) {
|
||||||
shei = Math.max(shei - dy, 0);
|
shei = Math.max(shei - dy, 0);
|
||||||
_metamgr.getRegionManager().invalidateRegion(_nx, _ny + height - dy, width, dy);
|
_metamgr.getRegionManager().invalidateRegion(_vbounds.x, _vbounds.y + height - dy, width, dy);
|
||||||
} else if (dy < 0) {
|
} else if (dy < 0) {
|
||||||
sy -= dy;
|
sy -= dy;
|
||||||
_metamgr.getRegionManager().invalidateRegion(_nx, _ny, width, -dy);
|
_metamgr.getRegionManager().invalidateRegion(_vbounds.x, _vbounds.y, width, -dy);
|
||||||
}
|
}
|
||||||
if (dx > 0) {
|
if (dx > 0) {
|
||||||
_metamgr.getRegionManager().invalidateRegion(_nx + width - dx, sy, dx, shei);
|
_metamgr.getRegionManager().invalidateRegion(_vbounds.x + width - dx, sy, dx, shei);
|
||||||
} else if (dx < 0) {
|
} else if (dx < 0) {
|
||||||
_metamgr.getRegionManager().invalidateRegion(_nx, sy, -dx, shei);
|
_metamgr.getRegionManager().invalidateRegion(_vbounds.x, sy, -dx, shei);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// now go ahead and update our location so that changes in between here and the call
|
|
||||||
// to paint() for this tick don't booch everything
|
|
||||||
_vbounds.x = _nx; _vbounds.y = _ny;
|
|
||||||
|
|
||||||
addObscurerDirtyRegions(false);
|
addObscurerDirtyRegions(false);
|
||||||
|
|
||||||
// let derived classes react if they so desire
|
// let derived classes react if they so desire
|
||||||
@@ -313,8 +328,8 @@ public class VirtualMediaPanel extends MediaPanel
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int width = getWidth(), height = getHeight();
|
int width = _vbounds.width, height = _vbounds.height;
|
||||||
int nx = _vbounds.x, ny = _vbounds.y;
|
int nx = _nx, ny = _ny;
|
||||||
|
|
||||||
// figure out where to move
|
// figure out where to move
|
||||||
switch (_fmode) {
|
switch (_fmode) {
|
||||||
@@ -356,8 +371,18 @@ public class VirtualMediaPanel extends MediaPanel
|
|||||||
@Override
|
@Override
|
||||||
protected void paint (Graphics2D gfx, Rectangle[] dirty)
|
protected void paint (Graphics2D gfx, Rectangle[] dirty)
|
||||||
{
|
{
|
||||||
// if we're scrolling, go ahead and do the business
|
// if we zoom too quickly, the zoomManager scale may not be in sync with the paint tick
|
||||||
if (_dx != 0 || _dy != 0) {
|
// so we check based on the actual view bounds to be safe
|
||||||
|
double scale = getWidth() / (double) _vbounds.width;
|
||||||
|
|
||||||
|
if (scale != 1.0) {
|
||||||
|
// if our scale is not 1.0, we can't rely on copyArea(). it doesn't support non integer
|
||||||
|
// movements, so it will cause an unpleasant stutter as the rounding errors accumulate.
|
||||||
|
// TODO?: keep track of movement over multiple frames to cancel out the rounding errors.
|
||||||
|
dirty = new Rectangle[]{_vbounds};
|
||||||
|
|
||||||
|
// if we're scrolling, go ahead and do the business
|
||||||
|
} else if (_dx != 0 || _dy != 0) {
|
||||||
int width = getWidth(), height = getHeight();
|
int width = getWidth(), height = getHeight();
|
||||||
int cx = (_dx > 0) ? _dx : 0;
|
int cx = (_dx > 0) ? _dx : 0;
|
||||||
int cy = (_dy > 0) ? _dy : 0;
|
int cy = (_dy > 0) ? _dy : 0;
|
||||||
@@ -396,21 +421,28 @@ public class VirtualMediaPanel extends MediaPanel
|
|||||||
_dx = 0; _dy = 0;
|
_dx = 0; _dy = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AffineTransform originalTransform = gfx.getTransform();
|
||||||
|
|
||||||
|
int centerX = _vbounds.x + _vbounds.width / 2;
|
||||||
|
int centerY = _vbounds.y + _vbounds.height / 2;
|
||||||
|
|
||||||
// translate into happy space
|
// translate into happy space
|
||||||
gfx.translate(-_vbounds.x, -_vbounds.y);
|
gfx.translate(getWidth() / 2, getHeight() / 2);
|
||||||
|
gfx.scale(scale, scale);
|
||||||
|
gfx.translate(-centerX, -centerY);
|
||||||
|
|
||||||
// now do the actual painting
|
// now do the actual painting
|
||||||
super.paint(gfx, dirty);
|
super.paint(gfx, dirty);
|
||||||
|
|
||||||
// translate back out of happy space
|
// translate back out of happy space
|
||||||
gfx.translate(_vbounds.x, _vbounds.y);
|
gfx.setTransform(originalTransform);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void constrainToBounds (Rectangle dirty)
|
protected void constrainToBounds (Rectangle dirty)
|
||||||
{
|
{
|
||||||
SwingUtilities.computeIntersection(
|
SwingUtilities.computeIntersection(
|
||||||
_vbounds.x, _vbounds.y, getWidth(), getHeight(), dirty);
|
_vbounds.x, _vbounds.y, _vbounds.width, _vbounds.height, dirty);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -429,6 +461,38 @@ public class VirtualMediaPanel extends MediaPanel
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void enableZoom() {
|
||||||
|
addMouseWheelListener(_zoomManager.createMouseWheelListener());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void enableZoom(double min, double max) {
|
||||||
|
enableZoom();
|
||||||
|
setZoomConstraints(min, max);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setZoomConstraints(double min, double max) {
|
||||||
|
_zoomManager.setMinZoomLevel(min);
|
||||||
|
_zoomManager.setMaxZoomLevel(max);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setZoomLevel(double zoom) {
|
||||||
|
_zoomManager.setZoomLevel(zoom);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected double getZoomLevel() {
|
||||||
|
return _zoomManager.getZoomLevel();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void addZoomListener(ZoomManager.ZoomListener listener) {
|
||||||
|
_zoomManager.addZoomListener(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void removeZoomListener(ZoomManager.ZoomListener listener) {
|
||||||
|
_zoomManager.removeZoomListener(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ZoomManager _zoomManager = new ZoomManager();
|
||||||
|
|
||||||
/** Our viewport bounds in virtual coordinates. */
|
/** Our viewport bounds in virtual coordinates. */
|
||||||
protected Rectangle _vbounds = new Rectangle();
|
protected Rectangle _vbounds = new Rectangle();
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,192 @@
|
|||||||
|
package com.threerings.media;
|
||||||
|
|
||||||
|
import java.awt.Point;
|
||||||
|
import java.awt.Rectangle;
|
||||||
|
import java.awt.event.MouseEvent;
|
||||||
|
import java.awt.event.MouseWheelListener;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
|
|
||||||
|
public class ZoomManager {
|
||||||
|
|
||||||
|
protected static final double ZOOM_SNAP_THRESHOLD = 0.05;
|
||||||
|
protected static final double ZOOM_OUT_FACTOR = 0.9;
|
||||||
|
protected static final double ZOOM_IN_FACTOR = 1.1;
|
||||||
|
|
||||||
|
protected double _zoomLevel = 1.0;
|
||||||
|
protected double _minZoomLevel = 0.25;
|
||||||
|
protected double _maxZoomLevel = 2.0;
|
||||||
|
|
||||||
|
public interface ZoomListener {
|
||||||
|
|
||||||
|
void zoomChanged(double oldZoom, double newZoom);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final List<ZoomListener> _listeners = new CopyOnWriteArrayList<>();
|
||||||
|
|
||||||
|
public ZoomManager() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public ZoomManager(double zoomLevel) {
|
||||||
|
_zoomLevel = zoomLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ZoomManager(double zoomLevel, double minZoomLevel, double maxZoomLevel) {
|
||||||
|
_zoomLevel = zoomLevel;
|
||||||
|
_minZoomLevel = minZoomLevel;
|
||||||
|
_maxZoomLevel = maxZoomLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the center point of the given rectangle.
|
||||||
|
*
|
||||||
|
* @param rect the rectangle to find the center of
|
||||||
|
* @return the center point of the rectangle
|
||||||
|
*/
|
||||||
|
public static Point center(Rectangle rect) {
|
||||||
|
int centerX = rect.x + rect.width / 2;
|
||||||
|
int centerY = rect.y + rect.height / 2;
|
||||||
|
return new Point(centerX, centerY);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rescales the given bounds rectangle based on the current zoom level.
|
||||||
|
* Useful when the zoom level changes to update the viewport.
|
||||||
|
*
|
||||||
|
* @param viewport the rectangle to rescale
|
||||||
|
* @param width the new width
|
||||||
|
* @param height the new height
|
||||||
|
* @return a new rectangle representing the rescaled bounds
|
||||||
|
*/
|
||||||
|
public Rectangle rescaleBounds(Rectangle viewPort, int width, int height) {
|
||||||
|
Point center = center(viewPort);
|
||||||
|
int newWidth = (int) (width / _zoomLevel);
|
||||||
|
int newHeight = (int) (height / _zoomLevel);
|
||||||
|
return new Rectangle(center.x - newWidth / 2, center.y - newHeight / 2, newWidth, newHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scales the given viewport rectangle based on the current zoom level,
|
||||||
|
* centering the scaling on the viewport's center.
|
||||||
|
*
|
||||||
|
* @param viewPort the viewport rectangle to scale
|
||||||
|
* @return a new rectangle representing the scaled viewport
|
||||||
|
*/
|
||||||
|
public Rectangle scaleOnCenter(Rectangle viewPort) {
|
||||||
|
Point center = center(viewPort);
|
||||||
|
int newWidth = (int) (viewPort.width / _zoomLevel);
|
||||||
|
int newHeight = (int) (viewPort.height / _zoomLevel);
|
||||||
|
return new Rectangle(center.x - newWidth / 2, center.y - newHeight / 2, newWidth, newHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adjusts the given mouse event for the current zoom level. Uses scaled
|
||||||
|
* distance from the center of the panel
|
||||||
|
*
|
||||||
|
* @param event the mouse event to adjust
|
||||||
|
*/
|
||||||
|
public void adjustMouseEvent(MouseEvent event, Rectangle viewPort) {
|
||||||
|
Point adjustedPoint = screenToVirtual(viewPort, event.getPoint());
|
||||||
|
event.translatePoint(
|
||||||
|
adjustedPoint.x - event.getX(),
|
||||||
|
adjustedPoint.y - event.getY());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adjusts the given point for the current zoom level. Zoom happens from the
|
||||||
|
* center of the viewport.
|
||||||
|
*
|
||||||
|
* @param viewPort the viewport rectangle
|
||||||
|
* @param toAdjust the point to adjust
|
||||||
|
* @return a new point representing the adjusted coordinates
|
||||||
|
*/
|
||||||
|
public Point screenToVirtual(Rectangle viewPort, Point screenPoint) {
|
||||||
|
Point center = center(viewPort);
|
||||||
|
double scale = _zoomLevel;
|
||||||
|
int virtX = (int) ((screenPoint.x - center.x) / scale + center.x);
|
||||||
|
int virtY = (int) ((screenPoint.y - center.y) / scale + center.y);
|
||||||
|
return new Point(virtX, virtY);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a mouse wheel listener that adjusts the zoom level.
|
||||||
|
*
|
||||||
|
* @return a mouse wheel listener
|
||||||
|
*/
|
||||||
|
public MouseWheelListener createMouseWheelListener() {
|
||||||
|
return e -> {
|
||||||
|
int notches = e.getWheelRotation();
|
||||||
|
scaleZoomLevel(notches > 0 ? ZOOM_OUT_FACTOR : ZOOM_IN_FACTOR);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getZoomLevel() {
|
||||||
|
return _zoomLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scales the current zoom level by the given factor.
|
||||||
|
*
|
||||||
|
* @param scale the scale factor
|
||||||
|
* @return the actual scale factor applied after clamping to min and max
|
||||||
|
*/
|
||||||
|
public double scaleZoomLevel(double scale) {
|
||||||
|
double oldZoom = _zoomLevel;
|
||||||
|
setZoomLevel(_zoomLevel * scale);
|
||||||
|
return _zoomLevel / oldZoom;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the current zoom level, clamping it to the min and max zoom levels.
|
||||||
|
*
|
||||||
|
* @param zoomLevel the new zoom level
|
||||||
|
*/
|
||||||
|
public void setZoomLevel(double zoomLevel) {
|
||||||
|
double oldZoom = _zoomLevel;
|
||||||
|
if (zoomLevel < _minZoomLevel) {
|
||||||
|
_zoomLevel = _minZoomLevel;
|
||||||
|
} else if (zoomLevel > _maxZoomLevel) {
|
||||||
|
_zoomLevel = _maxZoomLevel;
|
||||||
|
} else {
|
||||||
|
_zoomLevel = zoomLevel;
|
||||||
|
// if we're close to 1.0, we might as well snap to it
|
||||||
|
if (Math.abs(_zoomLevel - 1.0) < ZOOM_SNAP_THRESHOLD) {
|
||||||
|
_zoomLevel = 1.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (oldZoom != _zoomLevel) {
|
||||||
|
fireZoomChanged(oldZoom, _zoomLevel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getMaxZoomLevel() {
|
||||||
|
return _maxZoomLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMaxZoomLevel(double maxZoomLevel) {
|
||||||
|
_maxZoomLevel = maxZoomLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getMinZoomLevel() {
|
||||||
|
return _minZoomLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMinZoomLevel(double minZoomLevel) {
|
||||||
|
_minZoomLevel = minZoomLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addZoomListener(ZoomListener listener) {
|
||||||
|
_listeners.add(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeZoomListener(ZoomListener listener) {
|
||||||
|
_listeners.remove(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fireZoomChanged(double oldZoom, double newZoom) {
|
||||||
|
for (ZoomListener listener : _listeners) {
|
||||||
|
listener.zoomChanged(oldZoom, newZoom);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -118,6 +118,10 @@ public class MisoScenePanel extends VirtualMediaPanel
|
|||||||
_resolver.start();
|
_resolver.start();
|
||||||
_resolvers.put(_ctx, _resolver);
|
_resolvers.put(_ctx, _resolver);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addZoomListener((oldZoom, newZoom) -> {
|
||||||
|
rethink();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -716,7 +720,7 @@ public class MisoScenePanel extends VirtualMediaPanel
|
|||||||
|
|
||||||
// compute the tile coordinates of our upper left screen coordinate and request a rethink
|
// compute the tile coordinates of our upper left screen coordinate and request a rethink
|
||||||
// if they've changed
|
// if they've changed
|
||||||
MisoUtil.screenToTile(_metrics, _vbounds.x, _vbounds.y, _tcoords);
|
MisoUtil.screenToTile(_metrics, _nx, _ny, _tcoords);
|
||||||
if (_ulpos == null || !_tcoords.equals(_ulpos)) {
|
if (_ulpos == null || !_tcoords.equals(_ulpos)) {
|
||||||
// if this is a forced rethink (_ulpos is null), we might delay paint as a result of
|
// if this is a forced rethink (_ulpos is null), we might delay paint as a result of
|
||||||
// it, but only if we queue up blocks for resolution in our rethink
|
// it, but only if we queue up blocks for resolution in our rethink
|
||||||
|
|||||||
Reference in New Issue
Block a user