Add zoom support to the designer infrastructure [fix-29696]

This commit is contained in:
Alexander Lobas
2013-03-21 12:19:08 +04:00
parent b57ce1b0e4
commit f4e910a02b
9 changed files with 446 additions and 13 deletions
@@ -280,6 +280,26 @@ public abstract class DesignerEditorPanel extends JPanel implements DataProvider
public void showError(@NonNls String message, Throwable e) {
DesignerEditorPanel.this.showError(message, e);
}
@Override
public boolean isZoomSupported() {
return DesignerEditorPanel.this.isZoomSupported();
}
@Override
public void zoom(@NotNull ZoomType type) {
DesignerEditorPanel.this.zoom(type);
}
@Override
public void setZoom(double zoom) {
DesignerEditorPanel.this.setZoom(zoom);
}
@Override
public double getZoom() {
return DesignerEditorPanel.this.getZoom();
}
};
myGlassLayer = new GlassLayer(myToolProvider, mySurfaceArea);
@@ -835,6 +855,26 @@ public abstract class DesignerEditorPanel extends JPanel implements DataProvider
return null;
}
//////////////////////////////////////////////////////////////////////////////////////////
//
// Zoom
//
//////////////////////////////////////////////////////////////////////////////////////////
public boolean isZoomSupported() {
return false;
}
public void zoom(@NotNull ZoomType type) {
}
public void setZoom(double zoom) {
}
public double getZoom() {
return 1;
}
//////////////////////////////////////////////////////////////////////////////////////////
//
// Inspection
@@ -23,8 +23,8 @@ import java.awt.image.BufferedImage;
* @author Alexander Lobas
*/
public class RootView extends JComponent {
private final int myX;
private final int myY;
protected final int myX;
protected final int myY;
protected BufferedImage myImage;
public RootView(int x, int y, BufferedImage image) {
@@ -0,0 +1,26 @@
/*
* Copyright 2000-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.designer.designSurface;
/**
* Interface implemented by a design view native component which implements zooming.
*/
public interface ScalableComponent {
/**
* Returns the scale to apply to coordinates in this view.
*/
double getScale();
}
@@ -0,0 +1,40 @@
/*
* Copyright 2000-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.designer.designSurface;
import org.jetbrains.annotations.NotNull;
public interface ZoomProvider {
/**
* Returns true if zooming is supported by this designer.
*/
boolean isZoomSupported();
/**
* Zoom the editable area.
*/
void zoom(@NotNull ZoomType type);
/**
* Sets the zoom level. Note that this should be 1, not 100 (percent), for an image at its actual size.
*/
void setZoom(double zoom);
/**
* Returns the current zoom level. Note that this is 1, not 100 (percent) for an image at its actual size.
*/
double getZoom();
}
@@ -0,0 +1,48 @@
/*
* Copyright 2000-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.designer.designSurface;
public enum ZoomType {
/**
* Zoom in
*/
IN,
/**
* Zoom out
*/
OUT,
/**
* Zoom to actual size (100%)
*/
ACTUAL,
/**
* Zoom to fit (the screen view port)
*/
FIT,
/**
* Zoom to fit, but do not zoom more than 100%
*/
FIT_INTO,
/**
* Zoom to match the exact device size (depends on the monitor dpi)
*/
SCREEN
}
@@ -16,6 +16,7 @@
package com.intellij.designer.designSurface.tools;
import com.intellij.designer.designSurface.EditableArea;
import com.intellij.designer.designSurface.ZoomType;
import com.intellij.designer.model.RadComponent;
import com.intellij.designer.propertyTable.InplaceContext;
import com.intellij.openapi.actionSystem.ActionManager;
@@ -221,10 +222,39 @@ public class SelectionTool extends InputTool {
if (myTracker != null) {
myTracker.keyTyped(event, area);
}
else if (myToolProvider != null && !area.isTree() &&
Character.isLetterOrDigit(event.getKeyChar()) &&
(event.getModifiers() & (InputEvent.ALT_MASK | InputEvent.CTRL_MASK | InputEvent.META_MASK)) == 0) {
myToolProvider.startInplaceEditing(new InplaceContext(event.getKeyChar()));
else if (myToolProvider != null && !area.isTree()) {
char keyChar = event.getKeyChar();
switch (keyChar) {
// Zoom
case '-':
case '+':
case '0':
case '1':
ZoomType type;
if (keyChar == '-') {
type = ZoomType.OUT;
}
else if (keyChar == '+') {
type = ZoomType.IN;
}
else if (keyChar == '0') {
type = ZoomType.FIT;
}
else { // '1'
type = ZoomType.ACTUAL;
}
if (myToolProvider.isZoomSupported()) {
myToolProvider.zoom(type);
event.consume();
return;
}
// else: fall through
default:
if (Character.isLetterOrDigit(keyChar) &&
(event.getModifiers() & (InputEvent.ALT_MASK | InputEvent.CTRL_MASK | InputEvent.META_MASK)) == 0) {
myToolProvider.startInplaceEditing(new InplaceContext(keyChar));
}
}
}
}
@@ -17,6 +17,7 @@ package com.intellij.designer.designSurface.tools;
import com.intellij.designer.designSurface.EditOperation;
import com.intellij.designer.designSurface.EditableArea;
import com.intellij.designer.designSurface.ZoomProvider;
import com.intellij.designer.propertyTable.InplaceContext;
import com.intellij.util.ThrowableRunnable;
import org.jetbrains.annotations.NonNls;
@@ -29,7 +30,7 @@ import java.util.List;
/**
* @author Alexander Lobas
*/
public abstract class ToolProvider {
public abstract class ToolProvider implements ZoomProvider {
private InputTool myTool;
private EditableArea myArea;
private MouseEvent myEvent;
@@ -147,14 +147,139 @@ public abstract class RadComponent extends PropertiesContainer {
//
//////////////////////////////////////////////////////////////////////////////////////////
/**
* Returns the bounds of this {@linkplain RadComponent} in the model.
* <p/>
* Caller should <b>not</b> modify this rectangle.
*
* @return the bounds of this {@linkplain RadComponent} in the model coordinate system
* (e.g. unaffected by a view zoom for example)
*/
public Rectangle getBounds() {
return null;
}
/**
* Returns the bounds of this {@linkplain RadComponent} in the coordinate system of
* the given Swing component. This will scale the coordinates if the view is zoomed
* (see {@link DesignerEditorPanel#zoom(com.intellij.designer.designSurface.ZoomType)})
* and will apply any relative position offsets of views in the hierarchy between the
* two components.
* <p/>
* Returns a new {@link Rectangle}, so callers are free to modify the result.
*
* @param relativeTo the component whose coordinate system the model bounds should
* be shifted and scaled into
* @return the bounds of this {@linkplain RadComponent} in the given coordinate system
*/
public Rectangle getBounds(Component relativeTo) {
return null;
}
/**
* Converts the given rectangle (in model coordinates) to coordinates in the given
* target component's coordinate system. The model coordinate system refers to
* the same coordinate system as the bounds returned by {@link #getBounds()}.
* <p/>
* This means that calling {@link #getBounds(java.awt.Component)} is equivalent
* to calling this method and passing in {@link #getBounds()}.
* <p/>
* Returns a new {@link Rectangle}, so callers are free to modify the result.
*
* @param target the component whose coordinate system the rectangle should be
* translated into
* @param rectangle the model rectangle to convert
* @return the rectangle converted to the coordinate system of the target
*/
public Rectangle fromModel(@NotNull Component target, @NotNull Rectangle rectangle) {
return null;
}
/**
* Converts the given rectangle (in coordinates relative to the given component)
* into the equivalent rectangle in model coordinates.
* <p/>
* Returns a new {@link Rectangle}, so callers are free to modify the result.
*
* @param source the component which defines the coordinate system of the rectangle
* @param rectangle the rectangle to be converted into model coordinates
* @return the rectangle converted to the model coordinate system
*/
public Rectangle toModel(@NotNull Component source, @NotNull Rectangle rectangle) {
return null;
}
/**
* Converts the given point (in model coordinates) to coordinates in the given
* target component's coordinate system. The model coordinate system refers to
* the same coordinate system as the bounds returned by {@link #getBounds()}.
* <p/>
* Returns a new {@link Point}, so callers are free to modify the result.
*
* @param target the component whose coordinate system the point should be
* translated into
* @param point the model point to convert
* @return the point converted to the coordinate system of the target
*/
public Point fromModel(@NotNull Component target, @NotNull Point point) {
return null;
}
/**
* Converts the given point (in coordinates relative to the given component)
* into the equivalent point in model coordinates.
* <p/>
* Returns a new {@link Point}, so callers are free to modify the result.
*
* @param source the component which defines the coordinate system of the point
* @param point the point to be converted into model coordinates
* @return the point converted to the model coordinate system
*/
public Point toModel(@NotNull Component source, @NotNull Point point) {
return null;
}
/**
* Converts the given rectangle (in model coordinates) to coordinates in the given
* target component's coordinate system. The model coordinate system refers to
* the same coordinate system as the bounds returned by {@link #getBounds()}.
* <p/>
* Returns a new {@link Dimension}, so callers are free to modify the result.
*
* @param target the component whose coordinate system the dimension should be
* translated into
* @param size the model dimension to convert
* @return the size converted to the coordinate system of the target
*/
public Dimension fromModel(@NotNull Component target, @NotNull Dimension size) {
return null;
}
/**
* Converts the given size (in coordinates relative to the given component)
* into the equivalent size in model coordinates.
* <p/>
* Returns a new {@link Dimension}, so callers are free to modify the result.
*
* @param source the component which defines the coordinate system of the size
* @param size the dimension to be converted into model coordinates
* @return the size converted to the model coordinate system
*/
public Dimension toModel(@NotNull Component source, @NotNull Dimension size) {
return null;
}
/**
* Returns the point in the model coordinate space (see {@link #getBounds()}) given
* a coordinate {@code x, y} in the given Swing component.
* <p/>
* Returns a new {@link Point}, so callers are free to modify the result.
*
* @param relativeFrom the component whose coordinate system defines the rectangle
* @param x the x coordinate of the point
* @param y the y coordinate of the point
* @return a corresponding {@link Point} in the model coordinate system
*/
public Point convertPoint(Component relativeFrom, int x, int y) {
return null;
}
@@ -15,6 +15,9 @@
*/
package com.intellij.designer.model;
import com.intellij.designer.designSurface.ScalableComponent;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import java.awt.*;
@@ -32,9 +35,119 @@ public abstract class RadVisualComponent extends RadComponent {
@Override
public Rectangle getBounds(Component relativeTo) {
return myNativeComponent == relativeTo
? new Rectangle(getBounds()) :
SwingUtilities.convertRectangle(myNativeComponent, getBounds(), relativeTo);
return fromModel(relativeTo, getBounds());
}
@Override
public Rectangle fromModel(@NotNull Component target, @NotNull Rectangle bounds) {
if (target != myNativeComponent && myNativeComponent instanceof ScalableComponent) {
ScalableComponent scalableComponent = (ScalableComponent)myNativeComponent;
double zoom = scalableComponent.getScale();
if (zoom != 1) {
bounds = new Rectangle(bounds);
bounds.x *= zoom;
bounds.y *= zoom;
bounds.width *= zoom;
bounds.height *= zoom;
}
}
return myNativeComponent == target
? new Rectangle(bounds) :
SwingUtilities.convertRectangle(myNativeComponent, bounds, target);
}
@Override
public Rectangle toModel(@NotNull Component source, @NotNull Rectangle rectangle) {
Rectangle bounds = myNativeComponent == source
? new Rectangle(rectangle) : SwingUtilities.convertRectangle(source, rectangle, myNativeComponent);
if (myNativeComponent != source && myNativeComponent instanceof ScalableComponent) {
ScalableComponent scalableComponent = (ScalableComponent)myNativeComponent;
double zoom = scalableComponent.getScale();
if (zoom != 1) {
bounds = new Rectangle(bounds);
bounds.x /= zoom;
bounds.y /= zoom;
bounds.width /= zoom;
bounds.height /= zoom;
}
}
return bounds;
}
@Override
public Point fromModel(@NotNull Component target, @NotNull Point point) {
if (target != myNativeComponent && myNativeComponent instanceof ScalableComponent) {
ScalableComponent scalableComponent = (ScalableComponent)myNativeComponent;
double zoom = scalableComponent.getScale();
if (zoom != 1) {
point = new Point(point);
point.x *= zoom;
point.y *= zoom;
}
}
return myNativeComponent == target
? new Point(point) :
SwingUtilities.convertPoint(myNativeComponent, point, target);
}
@Override
public Point toModel(@NotNull Component source, @NotNull Point point) {
Point p = myNativeComponent == source
? new Point(point) : SwingUtilities.convertPoint(source, point, myNativeComponent);
if (myNativeComponent != source && myNativeComponent instanceof ScalableComponent) {
ScalableComponent scalableComponent = (ScalableComponent)myNativeComponent;
double zoom = scalableComponent.getScale();
if (zoom != 1) {
p = new Point(p);
p.x /= zoom;
p.y /= zoom;
}
}
return p;
}
@Override
public Dimension fromModel(@NotNull Component target, @NotNull Dimension size) {
size = new Dimension(size);
if (target != myNativeComponent && myNativeComponent instanceof ScalableComponent) {
ScalableComponent scalableComponent = (ScalableComponent)myNativeComponent;
double zoom = scalableComponent.getScale();
if (zoom != 1) {
size.width *= zoom;
size.height *= zoom;
}
}
return size;
}
@Override
public Dimension toModel(@NotNull Component source, @NotNull Dimension size) {
size = new Dimension(size);
if (myNativeComponent != source && myNativeComponent instanceof ScalableComponent) {
ScalableComponent scalableComponent = (ScalableComponent)myNativeComponent;
double zoom = scalableComponent.getScale();
if (zoom != 1) {
size.width /= zoom;
size.height /= zoom;
}
}
return size;
}
public void setBounds(int x, int y, int width, int height) {
@@ -43,9 +156,19 @@ public abstract class RadVisualComponent extends RadComponent {
@Override
public Point convertPoint(Component relativeFrom, int x, int y) {
return myNativeComponent == relativeFrom
? new Point(x, y) :
SwingUtilities.convertPoint(relativeFrom, x, y, myNativeComponent);
Point p = myNativeComponent == relativeFrom ? new Point(x, y) : SwingUtilities.convertPoint(relativeFrom, x, y, myNativeComponent);
if (myNativeComponent != relativeFrom && myNativeComponent instanceof ScalableComponent) {
ScalableComponent scalableComponent = (ScalableComponent)myNativeComponent;
double zoom = scalableComponent.getScale();
if (zoom != 1) {
p.x /= zoom;
p.y /= zoom;
}
}
return p;
}
public Component getNativeComponent() {