DesignSurface & Events

This commit is contained in:
Alexander Lobas
2012-02-16 00:06:40 +04:00
parent f1ff94b0fd
commit 938b2ed628
21 changed files with 960 additions and 77 deletions
@@ -17,6 +17,7 @@ package com.intellij.android.designer.designSurface;
import com.android.ide.common.rendering.api.RenderSession;
import com.android.ide.common.rendering.api.Result;
import com.android.ide.common.rendering.api.ViewInfo;
import com.android.ide.common.resources.configuration.*;
import com.android.resources.NightMode;
import com.android.resources.UiMode;
@@ -25,6 +26,7 @@ import com.intellij.android.designer.componentTree.AndroidTreeDecorator;
import com.intellij.android.designer.model.RadViewComponent;
import com.intellij.designer.componentTree.TreeComponentDecorator;
import com.intellij.designer.designSurface.DesignerEditorPanel;
import com.intellij.designer.model.RadComponent;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.vfs.VirtualFile;
import org.jetbrains.android.facet.AndroidFacet;
@@ -41,6 +43,7 @@ import javax.xml.parsers.SAXParserFactory;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.InputStream;
import java.util.List;
/**
* @author Alexander Lobas
@@ -52,6 +55,8 @@ public final class AndroidDesignerEditorPanel extends DesignerEditorPanel {
public AndroidDesignerEditorPanel(@NotNull Module module, @NotNull VirtualFile file) {
super(module, file);
// TODO: (profile|device|target|...|theme) panel
// TODO: use platform DOM
// (temp code)
@@ -116,10 +121,15 @@ public final class AndroidDesignerEditorPanel extends DesignerEditorPanel {
}
}
RootView rootView = new RootView(mySession.getImage(), 30, 20);
updateRootComponent(mySession.getRootViews(), rootView);
JPanel rootPanel = new JPanel(null);
rootPanel.add(new RootView(mySession.getImage(), 30, 20));
rootPanel.setBackground(Color.WHITE);
rootPanel.add(rootView);
myLayeredPane.add(rootPanel, LAYER_COMPONENT);
showDesignerCard();
}
catch (Throwable e) {
@@ -127,6 +137,46 @@ public final class AndroidDesignerEditorPanel extends DesignerEditorPanel {
}
}
private void updateRootComponent(List<ViewInfo> views, JComponent nativeComponent) {
RadViewComponent rootComponent = (RadViewComponent)myRootComponent;
int size = views.size();
if (size == 1) {
RadViewComponent newRootComponent = new RadViewComponent(null, "Device Screen");
newRootComponent.getChildren().add(rootComponent);
rootComponent.setParent(newRootComponent);
updateComponent(rootComponent, views.get(0), nativeComponent, 0, 0);
myRootComponent = rootComponent = newRootComponent;
}
else {
List<RadComponent> children = rootComponent.getChildren();
for (int i = 0; i < size; i++) {
updateComponent((RadViewComponent)children.get(i), views.get(i), nativeComponent, 0, 0);
}
}
rootComponent.setNativeComponent(nativeComponent);
rootComponent.setBounds(0, 0, nativeComponent.getWidth(), nativeComponent.getHeight());
}
private static void updateComponent(RadViewComponent component, ViewInfo view, JComponent nativeComponent, int parentX, int parentY) {
component.setNativeComponent(nativeComponent);
int left = parentX + view.getLeft();
int top = parentY + view.getTop();
component.setBounds(left, top, view.getRight() - view.getLeft(), view.getBottom() - view.getTop());
List<ViewInfo> views = view.getChildren();
List<RadComponent> children = component.getChildren();
int size = views.size();
for (int i = 0; i < size; i++) {
updateComponent((RadViewComponent)children.get(i), views.get(i), nativeComponent, left, top);
}
}
@Override
public TreeComponentDecorator getTreeDecorator() {
return myTreeDecorator;
@@ -17,6 +17,7 @@ package com.intellij.android.designer.model;
import com.intellij.designer.model.RadComponent;
import javax.swing.*;
import java.awt.*;
import java.util.ArrayList;
import java.util.List;
@@ -29,6 +30,8 @@ import java.util.List;
public class RadViewComponent extends RadComponent {
private final String myTitle;
private final List<RadComponent> myChildren = new ArrayList<RadComponent>();
private Component myNativeComponent;
private final Rectangle myBounds = new Rectangle();
public RadViewComponent(RadViewComponent parent, String title) {
myTitle = title;
@@ -49,6 +52,24 @@ public class RadViewComponent extends RadComponent {
@Override
public Rectangle getBounds() {
return new Rectangle();
return myBounds;
}
public void setBounds(int x, int y, int width, int height) {
myBounds.setBounds(x, y, width, height);
}
public void setNativeComponent(Component nativeComponent) {
myNativeComponent = nativeComponent;
}
@Override
public Point convertPoint(Component component, int x, int y) {
return SwingUtilities.convertPoint(component, x, y, myNativeComponent);
}
@Override
public Point convertPoint(int x, int y, Component component) {
return SwingUtilities.convertPoint(myNativeComponent, x, y, component);
}
}
@@ -15,22 +15,82 @@
*/
package com.intellij.designer.componentTree;
import com.intellij.designer.designSurface.ComponentSelectionListener;
import com.intellij.designer.designSurface.DesignerEditorPanel;
import com.intellij.designer.designSurface.EditableArea;
import com.intellij.designer.model.RadComponent;
import com.intellij.ide.util.treeView.AbstractTreeBuilder;
import com.intellij.ide.util.treeView.AbstractTreeStructure;
import com.intellij.ide.util.treeView.NodeDescriptor;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultTreeModel;
import java.util.Comparator;
import javax.swing.tree.TreeSelectionModel;
/**
* @author Alexander Lobas
*/
public final class ComponentTreeBuilder extends AbstractTreeBuilder {
public final class ComponentTreeBuilder extends AbstractTreeBuilder implements ComponentSelectionListener, TreeSelectionListener {
private final EditableArea mySurfaceArea;
private final TreeSelectionModel myTreeSelectionModel;
public ComponentTreeBuilder(ComponentTree tree, DesignerEditorPanel designer) {
super(tree, (DefaultTreeModel)tree.getModel(), new TreeContentProvider(designer), null); // TODO: comparator?
initRootNode();
mySurfaceArea = designer.getSurfaceArea();
myTreeSelectionModel = getTree().getSelectionModel();
// TODO: restore expanded state
setTreeSelection();
addListeners();
}
@Override
public void dispose() {
removeListeners();
super.dispose();
}
private void addListeners() {
mySurfaceArea.addSelectionListener(this);
myTreeSelectionModel.addTreeSelectionListener(this);
}
private void removeListeners() {
mySurfaceArea.removeSelectionListener(this);
myTreeSelectionModel.removeTreeSelectionListener(this);
}
private void handleSelection(Runnable runnable) {
try {
removeListeners();
runnable.run();
}
finally {
addListeners();
}
}
@Override
public void selectionChanged(EditableArea area) {
handleSelection(new Runnable() {
@Override
public void run() {
queueUpdate();
setTreeSelection();
}
});
}
private void setTreeSelection() {
select(mySurfaceArea.getSelection().toArray(), null);
}
@Override
public void valueChanged(TreeSelectionEvent e) {
handleSelection(new Runnable() {
@Override
public void run() {
mySurfaceArea.setSelection(getSelectedElements(RadComponent.class));
}
});
}
}
@@ -26,7 +26,7 @@ import java.awt.*;
/**
* @author Alexander Lobas
*/
public class CaptionPanel extends JComponent implements EditableArea, DataProvider {
public class CaptionPanel extends JComponent implements DataProvider {
public CaptionPanel(DesignerEditorPanel designer, boolean horizontal) {
setBorder(IdeBorderFactory.createBorder(horizontal ? SideBorder.BOTTOM : SideBorder.RIGHT));
@@ -0,0 +1,25 @@
/*
* Copyright 2000-2012 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 java.util.EventListener;
/**
* @author Alexander Lobas
*/
public interface ComponentSelectionListener extends EventListener {
void selectionChanged(EditableArea area);
}
@@ -0,0 +1,46 @@
/*
* Copyright 2000-2012 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 com.intellij.designer.model.RadComponent;
import javax.swing.*;
import java.awt.*;
/**
* @author Alexander Lobas
*/
public class DecorationLayer extends JComponent {
private final EditableArea myArea;
public DecorationLayer(EditableArea area) {
myArea = area;
}
@Override
public void paint(Graphics g) {
g.setColor(Color.RED);
((Graphics2D)g).setStroke(new BasicStroke(2));
for (RadComponent component : myArea.getSelection()) {
Rectangle bounds = component.getBounds();
Point location = component.convertPoint(bounds.x, bounds.y, this);
g.drawRect(location.x, location.y, bounds.width, bounds.height);
}
paintChildren(g);
}
}
@@ -18,7 +18,9 @@ package com.intellij.designer.designSurface;
import com.intellij.designer.componentTree.TreeComponentDecorator;
import com.intellij.designer.designSurface.tools.InputTool;
import com.intellij.designer.designSurface.tools.SelectionTool;
import com.intellij.designer.designSurface.tools.ToolProvider;
import com.intellij.designer.model.RadComponent;
import com.intellij.designer.model.RadComponentVisitor;
import com.intellij.openapi.actionSystem.DataProvider;
import com.intellij.openapi.application.ex.ApplicationManagerEx;
import com.intellij.openapi.module.Module;
@@ -35,7 +37,7 @@ import java.awt.*;
/**
* @author Alexander Lobas
*/
public abstract class DesignerEditorPanel extends JPanel implements DataProvider {
public abstract class DesignerEditorPanel extends JPanel implements ToolProvider, DataProvider {
private final CardLayout myLayout = new CardLayout();
protected static final Integer LAYER_COMPONENT = JLayeredPane.DEFAULT_LAYER;
@@ -53,7 +55,8 @@ public abstract class DesignerEditorPanel extends JPanel implements DataProvider
private JScrollPane myScrollPane;
protected JLayeredPane myLayeredPane;
private GlassLayer myGlassLayer;
private InputTool myTool = new SelectionTool();
private InputTool myTool;
protected EditableArea mySurfaceArea;
@NonNls private final static String ERROR_CARD = "error";
private JLabel myErrorLabel;
@@ -64,6 +67,7 @@ public abstract class DesignerEditorPanel extends JPanel implements DataProvider
setLayout(myLayout);
createDesignerCard();
createErrorCard();
loadDefaultTool();
}
private void createDesignerCard() {
@@ -73,8 +77,6 @@ public abstract class DesignerEditorPanel extends JPanel implements DataProvider
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 1;
gbc.weightx = 0.0;
gbc.weighty = 0.0;
gbc.fill = GridBagConstraints.BOTH;
myVerticalCaption = new CaptionPanel(this, false);
@@ -87,13 +89,42 @@ public abstract class DesignerEditorPanel extends JPanel implements DataProvider
myDesignerCard.add(myHorizontalCaption, gbc);
myLayeredPane = new MyLayeredPane();
myGlassLayer = new GlassLayer(this);
mySurfaceArea = new EditableArea() {
@Override
public void setCursor(Cursor cursor) {
myLayeredPane.setCursor(cursor);
}
@Override
public JComponent getNativeComponent() {
return myLayeredPane;
}
@Override
protected void fireSelectionChanged() {
super.fireSelectionChanged();
myLayeredPane.revalidate();
myLayeredPane.repaint();
}
@Override
public RadComponent findTarget(int x, int y) {
if (myRootComponent != null) {
FindComponentVisitor visitor = new FindComponentVisitor(x, y);
myRootComponent.accept(visitor, false);
return visitor.getResult();
}
return null;
}
};
myGlassLayer = new GlassLayer(this, mySurfaceArea);
myLayeredPane.add(myGlassLayer, LAYER_GLASS);
myLayeredPane.add(new DecorationLayer(mySurfaceArea), LAYER_DECORATION);
gbc.gridx = 1;
gbc.gridy = 1;
gbc.weightx = 1.0;
gbc.weighty = 1.0;
gbc.weightx = 1;
gbc.weighty = 1;
myScrollPane = ScrollPaneFactory.createScrollPane(myLayeredPane);
myScrollPane.setBackground(Color.WHITE);
@@ -109,6 +140,33 @@ public abstract class DesignerEditorPanel extends JPanel implements DataProvider
myLayout.show(this, DESIGNER_CARD);
}
public EditableArea getSurfaceArea() {
return mySurfaceArea;
}
public InputTool getActiveTool() {
return myTool;
}
@Override
public void setActiveTool(InputTool tool) {
if (myTool != null) {
myTool.deactivate();
}
myTool = tool;
if (myTool != null) {
myTool.setToolProvider(this);
myTool.activate();
}
}
@Override
public void loadDefaultTool() {
setActiveTool(new SelectionTool());
}
public final void showError(@NonNls String message, Throwable e) {
myRootComponent = null;
myErrorLabel.setText(message + e.toString());
@@ -119,10 +177,6 @@ public abstract class DesignerEditorPanel extends JPanel implements DataProvider
}
}
public InputTool getActiveTool() {
return myTool;
}
@Override
public Object getData(@NonNls String dataId) {
return null; //To change body of implemented methods use File | Settings | File Templates.
@@ -133,7 +187,7 @@ public abstract class DesignerEditorPanel extends JPanel implements DataProvider
}
public JComponent getPreferredFocusedComponent() {
return null; // TODO: Auto-generated method stub
return myDesignerCard.isVisible() ? myGlassLayer : myErrorLabel;
}
public Object[] getTreeRoots() {
@@ -196,4 +250,34 @@ public abstract class DesignerEditorPanel extends JPanel implements DataProvider
return false;
}
}
private class FindComponentVisitor implements RadComponentVisitor {
private RadComponent myResult;
private final int myX;
private final int myY;
public FindComponentVisitor(int x, int y) {
myX = x;
myY = y;
}
public RadComponent getResult() {
return myResult;
}
@Override
public boolean visit(RadComponent component) {
return myResult == null;
}
@Override
public void endVisit(RadComponent component) {
if (myResult == null) {
Point location = component.convertPoint(myLayeredPane, myX, myY);
if (component.getBounds().contains(location)) {
myResult = component;
}
}
}
}
}
@@ -15,8 +15,74 @@
*/
package com.intellij.designer.designSurface;
import com.intellij.designer.model.RadComponent;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.event.EventListenerList;
import java.awt.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
* @author Alexander Lobas
*/
public interface EditableArea {
public abstract class EditableArea {
private final EventListenerList myListenerList = new EventListenerList();
private List<RadComponent> mySelection = new ArrayList<RadComponent>();
public void addSelectionListener(ComponentSelectionListener listener) {
myListenerList.add(ComponentSelectionListener.class, listener);
}
public void removeSelectionListener(ComponentSelectionListener listener) {
myListenerList.remove(ComponentSelectionListener.class, listener);
}
protected void fireSelectionChanged() {
for (ComponentSelectionListener listener : myListenerList.getListeners(ComponentSelectionListener.class)) {
listener.selectionChanged(this);
}
}
@NotNull
public List<RadComponent> getSelection() {
return mySelection;
}
public boolean isSelected(@NotNull RadComponent component) {
return mySelection.contains(component);
}
public void select(@NotNull RadComponent component) {
mySelection = new ArrayList<RadComponent>();
mySelection.add(component);
fireSelectionChanged();
}
public void deselect(@NotNull RadComponent component) {
mySelection.remove(component);
fireSelectionChanged();
}
public void appendSelection(@NotNull RadComponent component) {
mySelection.remove(component);
mySelection.add(component);
fireSelectionChanged();
}
public void setSelection(@NotNull Collection<RadComponent> components) {
mySelection = new ArrayList<RadComponent>(components);
fireSelectionChanged();
}
public abstract void setCursor(@Nullable Cursor cursor);
@NotNull
public abstract JComponent getNativeComponent();
@Nullable
public abstract RadComponent findTarget(int x, int y);
}
@@ -16,6 +16,7 @@
package com.intellij.designer.designSurface;
import com.intellij.designer.designSurface.tools.InputTool;
import com.intellij.designer.designSurface.tools.ToolProvider;
import com.intellij.openapi.actionSystem.DataProvider;
import com.intellij.ui.popup.PopupOwner;
import org.jetbrains.annotations.NonNls;
@@ -28,28 +29,30 @@ import java.awt.event.MouseEvent;
/**
* @author Alexander Lobas
*/
public final class GlassLayer extends JComponent implements PopupOwner, EditableArea, DataProvider {
private final DesignerEditorPanel myDesigner;
public final class GlassLayer extends JComponent implements PopupOwner, DataProvider {
private final ToolProvider myToolProvider;
private final EditableArea myArea;
public GlassLayer(DesignerEditorPanel designer) {
myDesigner = designer;
public GlassLayer(ToolProvider provider, EditableArea area) {
myToolProvider = provider;
myArea = area;
enableEvents(AWTEvent.KEY_EVENT_MASK | AWTEvent.MOUSE_EVENT_MASK | AWTEvent.MOUSE_MOTION_EVENT_MASK);
}
@Override
protected void processKeyEvent(KeyEvent event) {
try {
InputTool tool = myDesigner.getActiveTool();
InputTool tool = myToolProvider.getActiveTool();
switch (event.getID()) {
case KeyEvent.KEY_PRESSED:
tool.keyPressed(event, this);
tool.keyPressed(event, myArea);
break;
case KeyEvent.KEY_TYPED:
tool.keyTyped(event, this);
tool.keyTyped(event, myArea);
break;
case KeyEvent.KEY_RELEASED:
tool.keyReleased(event, this);
tool.keyReleased(event, myArea);
break;
}
}
@@ -67,24 +70,24 @@ public final class GlassLayer extends JComponent implements PopupOwner, Editable
requestFocusInWindow();
}
try {
InputTool tool = myDesigner.getActiveTool();
InputTool tool = myToolProvider.getActiveTool();
switch (event.getID()) {
case MouseEvent.MOUSE_PRESSED:
tool.mouseDown(event, this);
tool.mouseDown(event, myArea);
break;
case MouseEvent.MOUSE_RELEASED:
tool.mouseUp(event, this);
tool.mouseUp(event, myArea);
break;
case MouseEvent.MOUSE_ENTERED:
tool.mouseEntered(event, this);
tool.mouseEntered(event, myArea);
break;
case MouseEvent.MOUSE_EXITED:
tool.mouseExited(event, this);
tool.mouseExited(event, myArea);
break;
case MouseEvent.MOUSE_CLICKED:
if (event.getClickCount() == 2) {
tool.mouseDoubleClick(event, this);
tool.mouseDoubleClick(event, myArea);
}
break;
}
@@ -97,14 +100,14 @@ public final class GlassLayer extends JComponent implements PopupOwner, Editable
@Override
protected void processMouseMotionEvent(MouseEvent event) {
try {
InputTool tool = myDesigner.getActiveTool();
InputTool tool = myToolProvider.getActiveTool();
switch (event.getID()) {
case MouseEvent.MOUSE_MOVED:
tool.mouseMove(event, this);
tool.mouseMove(event, myArea);
break;
case MouseEvent.MOUSE_DRAGGED:
tool.mouseDrag(event, this);
tool.mouseDrag(event, myArea);
break;
}
}
@@ -114,7 +117,7 @@ public final class GlassLayer extends JComponent implements PopupOwner, Editable
}
private void handleException(Throwable e) {
myDesigner.showError("Edit error: ", e);
myToolProvider.showError("Edit error: ", e);
}
@Override
@@ -0,0 +1,22 @@
/*
* Copyright 2000-2012 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.tools;
/**
* @author Alexander Lobas
*/
public abstract class AbstractCreationTool extends TargetingTool {
}
@@ -0,0 +1,22 @@
/*
* Copyright 2000-2012 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.tools;
/**
* @author Alexander Lobas
*/
public class CreationTool extends AbstractCreationTool {
}
@@ -0,0 +1,22 @@
/*
* Copyright 2000-2012 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.tools;
/**
* @author Alexander Lobas
*/
public class DragTracker extends TargetingTool {
}
@@ -1,7 +1,7 @@
/*
* Copyright 2000-2012 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* Licensed under the Apache Licensevent, 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
*
@@ -16,7 +16,11 @@
package com.intellij.designer.designSurface.tools;
import com.intellij.designer.designSurface.EditableArea;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
@@ -24,60 +28,272 @@ import java.awt.event.MouseEvent;
* @author Alexander Lobas
*/
public abstract class InputTool {
/**
* The final state for a tool to be in. Once a tool reaches this state, it will not change states
* until it is activated() again.
*/
protected static final int STATE_NONE = 0;
/**
* The first state that a tool is in. The tool will generally be in this state immediately
* following {@link #activate()}.
*/
protected static final int STATE_INIT = 1;
/**
* The state indicating that one or more buttons is pressed, but the user has not moved past the
* drag threshold. Many tools will do nothing during this state but wait until
* {@link #STATE_DRAG_IN_PROGRESS} is entered.
*/
protected static final int STATE_DRAG = 2;
/**
* The state indicating that the drag detection theshold has been passed, and a drag is in
* progress.
*/
protected static final int STATE_DRAG_IN_PROGRESS = 3;
/**
* The state indicating that an input event has invalidated the interaction. For example, during a
* mouse drag, pressing additional mouse button might invalidate the drag.
*/
protected static final int STATE_INVALID = 4;
public void keyTyped(KeyEvent e, EditableArea area) throws Exception {
private ToolProvider myToolProvider;
protected EditableArea myArea;
private Object myCommand;
private boolean myActive;
private boolean myCanUnload = true;
protected int myState;
protected int myCurrentScreenX;
protected int myCurrentScreenY;
protected InputEvent myInputEvent;
protected int myButton;
protected int myStartScreenX;
protected int myStartScreenY;
private boolean myCanPastThreshold;
//////////////////////////////////////////////////////////////////////////////////////////
//
// State
//
//////////////////////////////////////////////////////////////////////////////////////////
public void activate() {
resetState();
myState = STATE_INIT;
myActive = true;
}
public void keyPressed(KeyEvent e, EditableArea area) throws Exception {
public void deactivate() {
myActive = false;
setCommand(null);
}
public void keyReleased(KeyEvent e, EditableArea area) throws Exception {
protected void resetState() {
myCurrentScreenX = 0;
myCurrentScreenY = 0;
myInputEvent = null;
myButton = 0;
myStartScreenX = 0;
myStartScreenY = 0;
myCanPastThreshold = false;
}
public void mouseDown(MouseEvent e, EditableArea area) throws Exception {
public final void setToolProvider(ToolProvider provider) {
myToolProvider = provider;
}
public void mouseUp(MouseEvent e, EditableArea area) throws Exception {
public final void setArea(@Nullable EditableArea area) {
if (myArea != area) {
setCursor(null);
myArea = area;
if (myArea != null) {
Point mouseLocation = MouseInfo.getPointerInfo().getLocation();
SwingUtilities.convertPointFromScreen(mouseLocation, myArea.getNativeComponent());
myCurrentScreenX = mouseLocation.x;
myCurrentScreenY = mouseLocation.y;
}
refreshCursor();
}
}
public void mouseMove(MouseEvent e, EditableArea area) throws Exception {
protected final boolean unloadWhenFinished() {
return myCanUnload;
}
public void mouseDrag(MouseEvent e, EditableArea area) throws Exception {
public final void setUnloadWhenFinished(boolean value) {
myCanUnload = value;
}
public void mouseDoubleClick(MouseEvent e, EditableArea area) throws Exception {
protected final void setCommand(@Nullable Object command) {
myCommand = command;
refreshCursor();
}
public void mouseEntered(MouseEvent e, EditableArea area) throws Exception {
//////////////////////////////////////////////////////////////////////////////////////////
//
// Cursor
//
//////////////////////////////////////////////////////////////////////////////////////////
private Cursor myDefaultCursor;
private Cursor myDisabledCursor;
protected final void setCursor(@Nullable Cursor cursor) {
if (myArea != null) {
myArea.setCursor(cursor);
}
}
public void mouseExited(MouseEvent e, EditableArea area) throws Exception {
public void refreshCursor() {
if (myActive) {
setCursor(calculateCursor());
}
}
@Nullable
protected Cursor calculateCursor() {
if (myState == STATE_NONE) {
return null;
}
if (myCommand == null) {
return getDisabledCursor();
}
return getDefaultCursor();
}
protected final Cursor getDefaultCursor() {
return myDefaultCursor;
}
public final void setDefaultCursor(@Nullable Cursor cursor) {
if (myDefaultCursor != cursor) {
myDefaultCursor = cursor;
refreshCursor();
}
}
protected final Cursor getDisabledCursor() {
return myDisabledCursor == null ? getDefaultCursor() : myDisabledCursor;
}
public final void setDisabledCursor(@Nullable Cursor cursor) {
if (myDisabledCursor != cursor) {
myDisabledCursor = cursor;
refreshCursor();
}
}
//////////////////////////////////////////////////////////////////////////////////////////
//
// Keyboard
//
//////////////////////////////////////////////////////////////////////////////////////////
public void keyTyped(KeyEvent event, EditableArea area) throws Exception {
}
public void keyPressed(KeyEvent event, EditableArea area) throws Exception {
}
public void keyReleased(KeyEvent event, EditableArea area) throws Exception {
}
//////////////////////////////////////////////////////////////////////////////////////////
//
// Mouse
//
//////////////////////////////////////////////////////////////////////////////////////////
private static final int DRAG_THRESHOLD = 5;
private void setEvent(MouseEvent event) {
myCurrentScreenX = event.getX();
myCurrentScreenY = event.getY();
myButton = event.getButton();
myInputEvent = event;
}
private boolean movedPastThreshold() {
if (!myCanPastThreshold) {
myCanPastThreshold =
Math.abs(myStartScreenX - myCurrentScreenX) > DRAG_THRESHOLD
|| Math.abs(myStartScreenY - myCurrentScreenY) > DRAG_THRESHOLD;
}
return myCanPastThreshold;
}
protected void handleButtonDown(int button) {
}
protected void handleButtonUp(int button) {
}
protected void handleMove() {
}
protected void handleDrag() {
}
protected void handleDragStarted() {
}
protected void handleDragInProgress() {
}
protected void handleDoubleClick(int button) {
}
protected void handleAreaExited() {
}
protected void handleFinished() {
if (myCanUnload) {
myToolProvider.loadDefaultTool();
}
else {
deactivate();
activate();
}
}
public void mouseDown(MouseEvent event, EditableArea area) throws Exception {
setArea(area);
setEvent(event);
myStartScreenX = event.getX();
myStartScreenY = event.getY();
handleButtonDown(event.getButton());
}
public void mouseUp(MouseEvent event, EditableArea area) throws Exception {
setArea(area);
setEvent(event);
handleButtonUp(event.getButton());
}
public void mouseMove(MouseEvent event, EditableArea area) throws Exception {
setArea(area);
setEvent(event);
if (myState == STATE_DRAG_IN_PROGRESS) {
handleDragInProgress();
}
else {
handleMove();
}
}
public void mouseDrag(MouseEvent event, EditableArea area) throws Exception {
setArea(area);
boolean wasDragging = movedPastThreshold();
setEvent(event);
handleDrag();
if (movedPastThreshold()) {
if (!wasDragging) {
handleDragStarted();
}
handleDragInProgress();
}
}
public void mouseDoubleClick(MouseEvent event, EditableArea area) throws Exception {
setArea(area);
setEvent(event);
handleDoubleClick(event.getButton());
}
public void mouseEntered(MouseEvent event, EditableArea area) throws Exception {
setEvent(event);
if (myArea != null) {
handleAreaExited();
}
setArea(area);
}
public void mouseExited(MouseEvent event, EditableArea area) throws Exception {
if (myArea == area) {
setEvent(event);
handleAreaExited();
setArea(null);
}
}
}
@@ -0,0 +1,22 @@
/*
* Copyright 2000-2012 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.tools;
/**
* @author Alexander Lobas
*/
public class MarqueeTracker extends InputTool {
}
@@ -0,0 +1,22 @@
/*
* Copyright 2000-2012 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.tools;
/**
* @author Alexander Lobas
*/
public class PasteTool extends AbstractCreationTool {
}
@@ -0,0 +1,22 @@
/*
* Copyright 2000-2012 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.tools;
/**
* @author Alexander Lobas
*/
public class ResizeTracker extends InputTool {
}
@@ -15,8 +15,86 @@
*/
package com.intellij.designer.designSurface.tools;
import com.intellij.designer.designSurface.EditableArea;
import com.intellij.designer.model.RadComponent;
import com.intellij.designer.utils.Cursors;
import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.util.List;
/**
* @author Alexander Lobas
*/
public class SelectionTool extends TargetingTool {
public class SelectionTool extends InputTool {
public SelectionTool() {
setDisabledCursor(Cursors.getNoCursor());
}
private void performSelection(RadComponent component) {
if (myInputEvent.isControlDown()) {
if (myArea.isSelected(component)) {
myArea.deselect(component);
}
else {
myArea.appendSelection(component);
}
}
else if (myInputEvent.isShiftDown()) {
myArea.appendSelection(component);
}
else {
myArea.select(component);
}
}
@Override
protected void handleButtonDown(int button) {
if (myState == STATE_INIT && (button == 1 || button == 3)) {
RadComponent component = myArea.findTarget(myCurrentScreenX, myCurrentScreenY);
if (component != null) {
performSelection(component);
}
}
if (button == 1) {
if (myState == STATE_INIT) {
myState = STATE_DRAG;
}
}
else {
if (button == 3) {
myState = STATE_NONE;
}
else {
myState = STATE_INVALID;
}
setCommand(null);
}
}
@Override
protected void handleButtonUp(int button) {
myState = STATE_INIT;
refreshCursor();
}
protected Cursor calculateCursor() {
return myState == STATE_INIT || myState == STATE_DRAG
? getDefaultCursor()
: super.calculateCursor();
}
@Override
public void keyPressed(KeyEvent event, EditableArea area) throws Exception {
if (event.getKeyCode() == KeyEvent.VK_ESCAPE) {
List<RadComponent> selection = area.getSelection();
if (!selection.isEmpty()) {
RadComponent component = selection.get(0).getParent();
if (component != null) {
area.select(component);
}
}
}
}
}
@@ -0,0 +1,31 @@
/*
* Copyright 2000-2012 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.tools;
import org.jetbrains.annotations.NonNls;
/**
* @author Alexander Lobas
*/
public interface ToolProvider {
InputTool getActiveTool();
void setActiveTool(InputTool tool);
void loadDefaultTool();
void showError(@NonNls String message, Throwable e);
}
@@ -56,10 +56,6 @@ public abstract class RadComponent {
return getChildren();
}
public JComponent getNativeRootComponent() {
return null;
}
public Rectangle getBounds() {
return null;
}
@@ -91,4 +87,22 @@ public abstract class RadComponent {
public final void putClientProperty(@NotNull Object key, Object value) {
myClientProperties.put(key, value);
}
public void accept(RadComponentVisitor visitor, boolean forward) {
if (visitor.visit(this)) {
List<RadComponent> children = getChildren();
if (forward) {
for (RadComponent child : children) {
child.accept(visitor, forward);
}
}
else {
int size = children.size();
for (int i = size - 1; i >= 0; i--) {
children.get(i).accept(visitor, forward);
}
}
visitor.endVisit(this);
}
}
}
@@ -0,0 +1,25 @@
/*
* Copyright 2000-2012 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.model;
/**
* @author Alexander Lobas
*/
public interface RadComponentVisitor {
boolean visit(RadComponent component);
void endVisit(RadComponent component);
}
@@ -0,0 +1,32 @@
/*
* Copyright 2000-2012 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.utils;
import java.awt.*;
/**
* @author Alexander Lobas
*/
public final class Cursors {
public static Cursor getNoCursor() {
try {
return Cursor.getSystemCustomCursor("MoveNoDrop.32x32");
}
catch (Exception ex) {
return Cursor.getDefaultCursor();
}
}
}