diff --git a/plugins/ui-designer-core/src/com/intellij/designer/designSurface/DesignerEditorPanel.java b/plugins/ui-designer-core/src/com/intellij/designer/designSurface/DesignerEditorPanel.java index aa7354de4282..2e6a898c695d 100644 --- a/plugins/ui-designer-core/src/com/intellij/designer/designSurface/DesignerEditorPanel.java +++ b/plugins/ui-designer-core/src/com/intellij/designer/designSurface/DesignerEditorPanel.java @@ -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 diff --git a/plugins/ui-designer-core/src/com/intellij/designer/designSurface/RootView.java b/plugins/ui-designer-core/src/com/intellij/designer/designSurface/RootView.java index 7669967fb553..837cbab094ce 100644 --- a/plugins/ui-designer-core/src/com/intellij/designer/designSurface/RootView.java +++ b/plugins/ui-designer-core/src/com/intellij/designer/designSurface/RootView.java @@ -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) { diff --git a/plugins/ui-designer-core/src/com/intellij/designer/designSurface/ScalableComponent.java b/plugins/ui-designer-core/src/com/intellij/designer/designSurface/ScalableComponent.java new file mode 100644 index 000000000000..56942ab19d4d --- /dev/null +++ b/plugins/ui-designer-core/src/com/intellij/designer/designSurface/ScalableComponent.java @@ -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(); +} \ No newline at end of file diff --git a/plugins/ui-designer-core/src/com/intellij/designer/designSurface/ZoomProvider.java b/plugins/ui-designer-core/src/com/intellij/designer/designSurface/ZoomProvider.java new file mode 100644 index 000000000000..ca25fe5b902b --- /dev/null +++ b/plugins/ui-designer-core/src/com/intellij/designer/designSurface/ZoomProvider.java @@ -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(); +} \ No newline at end of file diff --git a/plugins/ui-designer-core/src/com/intellij/designer/designSurface/ZoomType.java b/plugins/ui-designer-core/src/com/intellij/designer/designSurface/ZoomType.java new file mode 100644 index 000000000000..eadaaafdc218 --- /dev/null +++ b/plugins/ui-designer-core/src/com/intellij/designer/designSurface/ZoomType.java @@ -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 +} \ No newline at end of file diff --git a/plugins/ui-designer-core/src/com/intellij/designer/designSurface/tools/SelectionTool.java b/plugins/ui-designer-core/src/com/intellij/designer/designSurface/tools/SelectionTool.java index 462537e9bc85..35d94a02f523 100644 --- a/plugins/ui-designer-core/src/com/intellij/designer/designSurface/tools/SelectionTool.java +++ b/plugins/ui-designer-core/src/com/intellij/designer/designSurface/tools/SelectionTool.java @@ -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)); + } + } } } diff --git a/plugins/ui-designer-core/src/com/intellij/designer/designSurface/tools/ToolProvider.java b/plugins/ui-designer-core/src/com/intellij/designer/designSurface/tools/ToolProvider.java index e72dc25b3f05..bcbc4f1aebe4 100644 --- a/plugins/ui-designer-core/src/com/intellij/designer/designSurface/tools/ToolProvider.java +++ b/plugins/ui-designer-core/src/com/intellij/designer/designSurface/tools/ToolProvider.java @@ -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; diff --git a/plugins/ui-designer-core/src/com/intellij/designer/model/RadComponent.java b/plugins/ui-designer-core/src/com/intellij/designer/model/RadComponent.java index a48014c67fc9..fbc4c321148b 100644 --- a/plugins/ui-designer-core/src/com/intellij/designer/model/RadComponent.java +++ b/plugins/ui-designer-core/src/com/intellij/designer/model/RadComponent.java @@ -147,14 +147,139 @@ public abstract class RadComponent extends PropertiesContainer { // ////////////////////////////////////////////////////////////////////////////////////////// + /** + * Returns the bounds of this {@linkplain RadComponent} in the model. + *

+ * Caller should not 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. + *

+ * 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()}. + *

+ * This means that calling {@link #getBounds(java.awt.Component)} is equivalent + * to calling this method and passing in {@link #getBounds()}. + *

+ * 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. + *

+ * 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()}. + *

+ * 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. + *

+ * 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()}. + *

+ * 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. + *

+ * 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. + *

+ * 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; } diff --git a/plugins/ui-designer-core/src/com/intellij/designer/model/RadVisualComponent.java b/plugins/ui-designer-core/src/com/intellij/designer/model/RadVisualComponent.java index e9ff52595d45..c06402bb77b1 100644 --- a/plugins/ui-designer-core/src/com/intellij/designer/model/RadVisualComponent.java +++ b/plugins/ui-designer-core/src/com/intellij/designer/model/RadVisualComponent.java @@ -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() {