Animated pinch zoom.

This commit is contained in:
Maxim Shafirov
2011-12-15 19:00:25 +04:00
parent a9f848b35a
commit 1715759b3b
7 changed files with 281 additions and 55 deletions
@@ -18,14 +18,16 @@ package com.intellij.ui.components;
import com.intellij.ui.table.JBTable;
import com.intellij.util.ui.ComponentWithEmptyText;
import com.intellij.util.ui.StatusText;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ContainerEvent;
import java.awt.event.ContainerListener;
public class JBViewport extends JViewport {
public class JBViewport extends JViewport implements ZoomableViewport {
private StatusText myEmptyText;
private ZoomingDelegate myZoomer;
public JBViewport() {
addContainerListener(new ContainerListener() {
@@ -50,16 +52,40 @@ public class JBViewport extends JViewport {
}
@Override
public Dimension getPreferredSize() {
return super.getPreferredSize(); //To change body of overridden methods use File | Settings | File Templates.
public void paint(Graphics g) {
if (myZoomer != null && myZoomer.isActive()) {
myZoomer.paint(g);
}
else {
super.paint(g);
if (myEmptyText != null) {
myEmptyText.paint(this, g);
}
}
}
@Nullable
@Override
public Magnificator getMagnificator() {
JComponent view = (JComponent)getView();
return view != null ? (Magnificator)view.getClientProperty(Magnificator.CLIENT_PROPERTY_KEY) : null;
}
@Override
public void paint(Graphics g) {
super.paint(g);
public void magnificationStarted(Point at) {
myZoomer = new ZoomingDelegate((JComponent)getView(), this);
myZoomer.magnificationStarted(at);
}
if (myEmptyText != null) {
myEmptyText.paint(this, g);
}
@Override
public void magnificationFinished(double magnification) {
myZoomer.magnificationFinished(magnification);
myZoomer = null;
}
@Override
public void magnify(double magnification) {
myZoomer.magnify(magnification);
}
}
@@ -0,0 +1,29 @@
/*
* Copyright 2000-2011 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.
*/
/*
* @author max
*/
package com.intellij.ui.components;
import com.intellij.openapi.util.Key;
import java.awt.*;
public interface Magnificator {
Key CLIENT_PROPERTY_KEY = Key.create("MagnifiableComponent");
Point magnify(double scale, Point at);
}
@@ -0,0 +1,32 @@
/*
* Copyright 2000-2011 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.
*/
/*
* @author max
*/
package com.intellij.ui.components;
import org.jetbrains.annotations.Nullable;
import java.awt.*;
public interface ZoomableViewport {
@Nullable
Magnificator getMagnificator();
void magnificationStarted(Point at);
void magnificationFinished(double magnification);
void magnify(double magnification);
}
@@ -0,0 +1,125 @@
/*
* Copyright 2000-2011 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.
*/
/*
* @author max
*/
package com.intellij.ui.components;
import com.intellij.ui.Gray;
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
public class ZoomingDelegate {
private final JComponent myContentComponent;
private final JComponent myViewportComponent;
private BufferedImage myCachedImage;
private Point myMagnificationPoint;
private double myMagnification;
public ZoomingDelegate(JComponent contentComponent, JComponent viewportComponent) {
myContentComponent = contentComponent;
myViewportComponent = viewportComponent;
}
public void paint(Graphics g) {
if (myCachedImage != null && myMagnificationPoint != null && myMagnification != 0) {
double scale = magnificationToScale(myMagnification);
int xoffset = (int)(myMagnificationPoint.x - myMagnificationPoint.x * scale);
int yoffset = (int)(myMagnificationPoint.y - myMagnificationPoint.y * scale);
Rectangle clip = g.getClipBounds();
g.setColor(Gray._120);
g.fillRect(clip.x, clip.y, clip.width, clip.height);
Graphics2D translated = (Graphics2D)g.create();
translated.translate(xoffset, yoffset);
translated.scale(scale, scale);
translated.drawImage(myCachedImage, 0, 0, null);
}
}
public void magnificationStarted(Point at) {
myMagnificationPoint = at;
}
public void magnificationFinished(double magnification) {
if (myMagnification != 0) {
Magnificator magnificator = ((ZoomableViewport)myViewportComponent).getMagnificator();
if (magnificator != null) {
Point inContent = convertToContentCoordinates(myMagnificationPoint);
final Point inContentScaled = magnificator.magnify(magnificationToScale(magnification), inContent);
int voffset = inContentScaled.y - myMagnificationPoint.y;
int hoffset = inContentScaled.x - myMagnificationPoint.x;
myViewportComponent.repaint();
myViewportComponent.validate();
scrollTo(voffset, hoffset);
}
}
myMagnificationPoint = null;
myMagnification = 0;
myCachedImage = null;
}
protected void scrollTo(int voffset, int hoffset) {
JViewport viewport = (JViewport)myViewportComponent;
JScrollPane pane = (JScrollPane)viewport.getParent();
JScrollBar vsb = pane.getVerticalScrollBar();
vsb.setValue(voffset);
JScrollBar hsb = pane.getHorizontalScrollBar();
hsb.setValue(hoffset);
}
protected Point convertToContentCoordinates(Point point) {
return SwingUtilities.convertPoint(myViewportComponent, point, myContentComponent);
}
public boolean isActive() {
return myCachedImage != null;
}
private static double magnificationToScale(double magnification) {
return magnification < 0 ? 1f / (1 - magnification) : (1 + magnification);
}
public void magnify(double magnification) {
if (myMagnification != magnification) {
myMagnification = magnification;
if (myCachedImage == null) {
Rectangle bounds = myViewportComponent.getBounds();
BufferedImage image = new BufferedImage(bounds.width, bounds.height, BufferedImage.TYPE_INT_RGB);
Graphics graphics = image.getGraphics();
graphics.setClip(0, 0, bounds.width, bounds.height);
myViewportComponent.paint(graphics);
myCachedImage = image;
}
}
myViewportComponent.repaint();
}
}
@@ -16,9 +16,16 @@
package com.intellij.openapi.actionSystem.impl;
import com.apple.eawt.event.*;
import com.intellij.openapi.actionSystem.ActionManager;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.wm.IdeFrame;
import com.intellij.ui.components.Magnificator;
import com.intellij.ui.components.ZoomableViewport;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.MouseEvent;
/**
* User: anna
@@ -28,6 +35,7 @@ class MacGestureAdapter extends GestureAdapter {
double magnification;
private final IdeFrame myFrame;
private MouseGestureManager myManager;
private ZoomableViewport myMagnifyingViewport;
public MacGestureAdapter(MouseGestureManager manager, IdeFrame frame) {
myFrame = frame;
@@ -38,35 +46,63 @@ class MacGestureAdapter extends GestureAdapter {
@Override
public void gestureBegan(GesturePhaseEvent event) {
myManager.activateTrackpad();
magnification = 0;
Point mouse = MouseInfo.getPointerInfo().getLocation();
SwingUtilities.convertPointFromScreen(mouse, myFrame.getComponent());
Component deepest = SwingUtilities.getDeepestComponentAt(myFrame.getComponent(), mouse.x, mouse.y);
ZoomableViewport viewport = (ZoomableViewport) SwingUtilities.getAncestorOfClass(ZoomableViewport.class, deepest);
if (viewport != null) {
Magnificator magnificator = viewport.getMagnificator();
if (magnificator != null) {
Point at = MouseInfo.getPointerInfo().getLocation();
SwingUtilities.convertPointFromScreen(at, (JComponent)viewport);
viewport.magnificationStarted(at);
myMagnifyingViewport = viewport;
}
}
}
@Override
public void gestureEnded(GesturePhaseEvent event) {
myManager.activateTrackpad();
if (magnification != 0) {
MouseGestureManager.processMagnification(myFrame, magnification);
if (myMagnifyingViewport != null) {
myMagnifyingViewport.magnificationFinished(magnification);
myMagnifyingViewport = null;
magnification = 0;
}
}
@Override
public void swipedLeft(SwipeEvent event) {
myManager.activateTrackpad();
myManager.processLeftSwipe(myFrame);
ActionManager actionManager = ActionManager.getInstance();
AnAction forward = actionManager.getAction("Forward");
if (forward == null) return;
actionManager.tryToExecute(forward, createMouseEventWrapper(myFrame), null, null, false);
}
@Override
public void swipedRight(SwipeEvent event) {
myManager.activateTrackpad();
myManager.processRightSwipe(myFrame);
ActionManager actionManager = ActionManager.getInstance();
AnAction back = actionManager.getAction("Back");
if (back == null) return;
actionManager.tryToExecute(back, createMouseEventWrapper(myFrame), null, null, false);
}
private static MouseEvent createMouseEventWrapper(IdeFrame frame) {
return new MouseEvent(frame.getComponent(), ActionEvent.ACTION_PERFORMED, System.currentTimeMillis(), 0, 0, 0, 0, false, 0);
}
@Override
public void magnify(MagnificationEvent event) {
myManager.activateTrackpad();
magnification += event.getMagnification();
if (myMagnifyingViewport != null) {
myMagnifyingViewport.magnify(magnification);
}
}
public void remove(JComponent cmp) {
@@ -39,14 +39,11 @@ import java.util.Map;
public class MouseGestureManager implements ApplicationComponent {
private static final Logger LOG = Logger.getInstance("MouseGestureManager");
private ActionManagerImpl myActionManager;
private Map<IdeFrame, Object> myListeners = new HashMap<IdeFrame, Object>();
private boolean HAS_TRACKPAD = false;
public MouseGestureManager(ActionManagerImpl actionManager) {
myActionManager = actionManager;
public MouseGestureManager() {
}
public void add(final IdeFrame frame) {
@@ -76,38 +73,6 @@ public class MouseGestureManager implements ApplicationComponent {
return HAS_TRACKPAD;
}
protected static void processMagnification(IdeFrame frame, double magnification) {
Point mouse = MouseInfo.getPointerInfo().getLocation();
SwingUtilities.convertPointFromScreen(mouse, frame.getComponent());
Component componentAt = SwingUtilities.getDeepestComponentAt(frame.getComponent(), mouse.x, mouse.y);
if (componentAt != null) {
Editor editor = PlatformDataKeys.EDITOR.getData(DataManager.getInstance().getDataContext(componentAt));
if (editor != null) {
double currentSize = editor.getColorsScheme().getEditorFontSize();
int defaultFontSize = EditorColorsManager.getInstance().getGlobalScheme().getEditorFontSize();
((EditorEx)editor).setFontSize((int)(Math.max(currentSize + magnification * 3, defaultFontSize)));
}
}
}
protected void processLeftSwipe(IdeFrame frame) {
AnAction forward = myActionManager.getAction("Forward");
if (forward == null) return;
myActionManager.tryToExecute(forward, createMouseEventWrapper(frame), null, null, false);
}
protected void processRightSwipe(IdeFrame frame) {
AnAction back = myActionManager.getAction("Back");
if (back == null) return;
myActionManager.tryToExecute(back, createMouseEventWrapper(frame), null, null, false);
}
private static MouseEvent createMouseEventWrapper(IdeFrame frame) {
return new MouseEvent(frame.getComponent(), ActionEvent.ACTION_PERFORMED, System.currentTimeMillis(), 0, 0, 0, 0, false, 0);
}
public void remove(IdeFrame frame) {
if (!Registry.is("actionSystem.mouseGesturesEnabled")) return;
@@ -19,21 +19,21 @@ import com.intellij.ide.IdeEventQueue;
import com.intellij.ide.ui.UISettings;
import com.intellij.openapi.actionSystem.DataProvider;
import com.intellij.openapi.actionSystem.PlatformDataKeys;
import com.intellij.openapi.editor.VisualPosition;
import com.intellij.ui.components.Magnificator;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.impl.ApplicationImpl;
import com.intellij.openapi.editor.colors.EditorColorsManager;
import com.intellij.openapi.editor.ex.util.EditorUtil;
import com.intellij.openapi.ui.Queryable;
import com.intellij.openapi.ui.TypingTarget;
import com.intellij.openapi.util.ActionCallback;
import com.intellij.util.ReflectionUtil;
import com.intellij.util.ui.UIUtil;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ComponentEvent;
import java.awt.event.FocusEvent;
import java.awt.event.InputMethodEvent;
import java.awt.im.InputContext;
import java.awt.im.InputMethodRequests;
import java.util.Map;
@@ -46,6 +46,19 @@ public class EditorComponentImpl extends JComponent implements Scrollable, DataP
enableInputMethods(true);
setFocusCycleRoot(true);
setOpaque(true);
putClientProperty(Magnificator.CLIENT_PROPERTY_KEY, new Magnificator() {
@Override
public Point magnify(double scale, Point at) {
VisualPosition magnificationPosition = myEditor.xyToVisualPosition(at);
double currentSize = myEditor.getColorsScheme().getEditorFontSize();
int defaultFontSize = EditorColorsManager.getInstance().getGlobalScheme().getEditorFontSize();
myEditor.setFontSize((int)(double)(int)(Math.max(currentSize * scale, defaultFontSize)));
return myEditor.visualPositionToXY(magnificationPosition);
}
});
}
public EditorImpl getEditor() {