ADT: GraphicalEditorPart is the new GLE2.

This CL represents the base for the new "Graphical Editor Part".

First, GLE2 has been renamed in GraphicalEditorPart. That's the
final name, I swear I won't change it again (until next month that is.)

The editor part has 3 composites: the top ConfigConfiguration
(same as usual, reused as-is), a new PaletteComposite and a new
LayoutCanavas. This last one displays the rendering image and
will deal with interactivity.

The LayoutCanvas is actually stacked with a label which displays
the rendering error. After a rendering, either the error or the
canvas is visible, depending on the success of the operation.
That would make it easier to have a different mechanism, for example
the error could be next to the last known rendering, they don't
have to be mutually exclusive.

It is worth noting that GraphicalEditorPart is 95% similar to
the GLE1, reusing all the glue code that we had to handle
layout requestes, refresh requests, sdk/framework load listeners,
configuration and file input changes, etc.

Both PaletteComposite and LayoutCanvas are currently embryonic
at best, just to make sure the editor part is structured correctly.

Change-Id: I36c2ae4d85a68e68a349adc63a718f06375e12c5
This commit is contained in:
Raphael
2009-08-26 23:28:05 -07:00
parent 3befd76efa
commit 3a5851d125
9 changed files with 1330 additions and 202 deletions

View File

@@ -244,6 +244,7 @@ public class AdtPlugin extends AbstractUIPlugin {
* (non-Javadoc)
* @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext)
*/
@SuppressWarnings("deprecation")
@Override
public void start(BundleContext context) throws Exception {
super.start(context);

View File

@@ -1,174 +0,0 @@
/*
* Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php
*
* 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.android.ide.eclipse.adt.internal.editors.layout;
import com.android.ide.eclipse.adt.internal.editors.uimodel.UiDocumentNode;
import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode;
import com.android.ide.eclipse.adt.internal.resources.configurations.FolderConfiguration;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.gef.ui.parts.SelectionSynchronizer;
import org.eclipse.swt.dnd.Clipboard;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.part.EditorPart;
/**
* Graphical layout editor part, version 2.
*
* @since GLE2
*/
public class GLE2 extends EditorPart implements IGraphicalLayoutEditor {
/*
* Useful notes:
* To understand Drag'n'drop:
* http://www.eclipse.org/articles/Article-Workbench-DND/drag_drop.html
*/
/** Reference to the layout editor */
private final LayoutEditor mLayoutEditor;
public GLE2(LayoutEditor layoutEditor) {
mLayoutEditor = layoutEditor;
setPartName("Graphical Layout");
}
// ------------------------------------
// Methods overridden from base classes
//------------------------------------
/**
* Initializes the editor part with a site and input.
* {@inheritDoc}
*/
@Override
public void init(IEditorSite site, IEditorInput input) throws PartInitException {
setSite(site);
setInput(input);
}
@Override
public void dispose() {
super.dispose();
}
@Override
public void doSave(IProgressMonitor monitor) {
// TODO Auto-generated method stub
}
@Override
public void doSaveAs() {
// TODO Auto-generated method stub
}
@Override
public boolean isDirty() {
// TODO Auto-generated method stub
return false;
}
@Override
public boolean isSaveAsAllowed() {
// TODO Auto-generated method stub
return false;
}
@Override
public void createPartControl(Composite parent) {
// TODO Auto-generated method stub
}
@Override
public void setFocus() {
// TODO Auto-generated method stub
}
public void activated() {
// TODO Auto-generated method stub
}
public void deactivated() {
// TODO Auto-generated method stub
}
public void editNewFile(FolderConfiguration configuration) {
// TODO Auto-generated method stub
}
public Clipboard getClipboard() {
// TODO Auto-generated method stub
return null;
}
public LayoutEditor getLayoutEditor() {
// TODO Auto-generated method stub
return null;
}
public UiDocumentNode getModel() {
// TODO Auto-generated method stub
return null;
}
public SelectionSynchronizer getSelectionSynchronizer() {
// TODO Auto-generated method stub
return null;
}
public void onXmlModelChanged() {
// TODO Auto-generated method stub
}
public void recomputeLayout() {
// TODO Auto-generated method stub
}
public void reloadEditor() {
// TODO Auto-generated method stub
}
public void reloadPalette() {
// TODO Auto-generated method stub
}
public void selectModel(UiElementNode uiNodeModel) {
// TODO Auto-generated method stub
}
public void reloadLayout(boolean codeChange, boolean rChange,
boolean resChange) {
// TODO Auto-generated method stub
}
}

View File

@@ -19,6 +19,7 @@ package com.android.ide.eclipse.adt.internal.editors.layout;
import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.internal.editors.IconFactory;
import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditor.UiEditorActions;
import com.android.ide.eclipse.adt.internal.editors.layout.LayoutReloadMonitor.ILayoutReloadListener;
import com.android.ide.eclipse.adt.internal.editors.layout.configuration.ConfigurationComposite;
import com.android.ide.eclipse.adt.internal.editors.layout.configuration.ConfigurationComposite.IConfigListener;
import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.ViewElementDescriptor;
@@ -115,7 +116,7 @@ import java.util.Map;
* @since GLE1
*/
public class GraphicalLayoutEditor extends GraphicalEditorWithPalette
implements IGraphicalLayoutEditor, IConfigListener {
implements IGraphicalLayoutEditor, IConfigListener, ILayoutReloadListener {
/** Reference to the layout editor */
@@ -388,7 +389,7 @@ public class GraphicalLayoutEditor extends GraphicalEditorWithPalette
/**
* Used by LayoutEditor.UiEditorActions.selectUiNode to select a new UI Node
* created by {@link ElementCreateCommand#execute()}.
* created by {@link ElementCreateCommand#execute()}.
*
* @param uiNodeModel The {@link UiElementNode} to select.
*/
@@ -814,7 +815,6 @@ public class GraphicalLayoutEditor extends GraphicalEditorWithPalette
/**
* Recomputes the layout with the help of layoutlib.
*/
@SuppressWarnings("deprecation")
public void recomputeLayout() {
doXmlReload(false /* force */);
try {
@@ -1339,4 +1339,5 @@ public class GraphicalLayoutEditor extends GraphicalEditorWithPalette
}
}
}

View File

@@ -16,7 +16,6 @@
package com.android.ide.eclipse.adt.internal.editors.layout;
import com.android.ide.eclipse.adt.internal.editors.layout.LayoutReloadMonitor.ILayoutReloadListener;
import com.android.ide.eclipse.adt.internal.editors.layout.parts.ElementCreateCommand;
import com.android.ide.eclipse.adt.internal.editors.uimodel.UiDocumentNode;
import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode;
@@ -31,7 +30,7 @@ import org.eclipse.ui.IEditorPart;
*
* @since GLE2
*/
/*package*/ interface IGraphicalLayoutEditor extends IEditorPart, ILayoutReloadListener {
/*package*/ interface IGraphicalLayoutEditor extends IEditorPart {
/**
* Sets the UI for the edition of a new file.

View File

@@ -0,0 +1,88 @@
/*
* Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php
*
* 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.android.ide.eclipse.adt.internal.editors.layout;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferInt;
import java.awt.image.Raster;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.ImageData;
import org.eclipse.swt.graphics.PaletteData;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Composite;
/**
* Displays the image rendered by the {@link GraphicalEditorPart} and handles
* the interaction with the widgets.
* <p/>
*
* @since GLE2
*
* TODO list:
* - make sure it is scrollable (Canvas derives from Scrollable, so prolly just setting bounds.)
* - handle selection (will need the model, aka the root node)/
* - handle drop target (from palette)/
* - handle drag'n'drop (internal, for moving/duplicating)/
* - handle context menu (depending on selection)/
* - selection synchronization with the outline (both ways)/
* - preserve selection during editor input change if applicable (e.g. when changing configuration.)
*/
public class LayoutCanvas extends Canvas {
private Image mImage;
public LayoutCanvas(Composite parent) {
super(parent, SWT.BORDER);
addPaintListener(new PaintListener() {
public void paintControl(PaintEvent e) {
paint(e);
}
});
}
public void setImage(BufferedImage awtImage) {
// Convert the AWT image into an SWT image.
int width = awtImage.getWidth();
int height = awtImage.getHeight();
Raster raster = awtImage.getData(new java.awt.Rectangle(width, height));
int[] imageDataBuffer = ((DataBufferInt)raster.getDataBuffer()).getData();
ImageData imageData = new ImageData(width, height, 32,
new PaletteData(0x00FF0000, 0x0000FF00, 0x000000FF));
imageData.setPixels(0, 0, imageDataBuffer.length, imageDataBuffer, 0);
mImage = new Image(getDisplay(), imageData);
redraw();
}
private void paint(PaintEvent e) {
if (mImage != null) {
GC gc = e.gc;
gc.drawImage(mImage, 0, 0);
}
}
}

View File

@@ -132,7 +132,7 @@ public class LayoutEditor extends AndroidEditor implements IShowEditorInput, IPa
if (mGraphicalEditor == null) {
if (System.getenv("USE_GLE2") != null) { //$NON-NLS-1$ //$NON-NLS-2$
mGraphicalEditor = new GLE2(this);
mGraphicalEditor = new GraphicalEditorPart(this);
} else {
mGraphicalEditor = new GraphicalLayoutEditor(this);
}

View File

@@ -31,6 +31,7 @@ import org.eclipse.core.resources.IResourceDelta;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
@@ -39,16 +40,16 @@ import java.util.Map.Entry;
* Monitor for file changes triggering a layout redraw.
*/
public final class LayoutReloadMonitor implements IFileListener, IResourceEventListener {
// singleton, enforced by private constructor.
private final static LayoutReloadMonitor sThis = new LayoutReloadMonitor();
/**
* Map of listeners by IProject.
*/
private final Map<IProject, List<ILayoutReloadListener>> mListenerMap =
new HashMap<IProject, List<ILayoutReloadListener>>();
private final static int CHANGE_CODE = 0;
private final static int CHANGE_RESOURCES = 1;
private final static int CHANGE_R = 2;
@@ -60,7 +61,7 @@ public final class LayoutReloadMonitor implements IFileListener, IResourceEventL
* <li>CHANGE_R: R clas change flag</li></ul>
*/
private final Map<IProject, boolean[]> mChangedProjects = new HashMap<IProject, boolean[]>();
/**
* Classes which implement this interface provide a method to respond to resource changes
* triggering a layout redraw
@@ -72,22 +73,22 @@ public final class LayoutReloadMonitor implements IFileListener, IResourceEventL
* @param rChange The trigger happened due to a change in the R class.
* @param resChange The trigger happened due to a resource change.
*/
void reloadLayout(boolean codeChange, boolean rChange, boolean resChange);
void reloadLayout(boolean codeChange, boolean rChange, boolean resChange);
}
/**
* Returns the single instance of {@link LayoutReloadMonitor}.
*/
public static LayoutReloadMonitor getMonitor() {
return sThis;
}
private LayoutReloadMonitor() {
ResourceMonitor monitor = ResourceMonitor.getMonitor();
monitor.addFileListener(this, IResourceDelta.ADDED | IResourceDelta.CHANGED);
monitor.addResourceEventListener(this);
}
/**
* Adds a listener for a given {@link IProject}.
* @param project
@@ -100,15 +101,13 @@ public final class LayoutReloadMonitor implements IFileListener, IResourceEventL
list = new ArrayList<ILayoutReloadListener>();
mListenerMap.put(project, list);
}
list.add(listener);
}
}
/**
* Removes a listener for a given {@link IProject}.
* @param project
* @param listener
*/
public void removeListener(IProject project, ILayoutReloadListener listener) {
synchronized (mListenerMap) {
@@ -119,10 +118,28 @@ public final class LayoutReloadMonitor implements IFileListener, IResourceEventL
}
}
/**
* Removes a listener, no matter which {@link IProject} it was associated with.
*/
public void removeListener(ILayoutReloadListener listener) {
synchronized (mListenerMap) {
for (List<ILayoutReloadListener> list : mListenerMap.values()) {
Iterator<ILayoutReloadListener> it = list.iterator();
while (it.hasNext()) {
ILayoutReloadListener i = it.next();
if (i == listener) {
it.remove();
}
}
}
}
}
/*
* (non-Javadoc)
* @see com.android.ide.eclipse.editors.resources.manager.ResourceMonitor.IFileListener#fileChanged(org.eclipse.core.resources.IFile, org.eclipse.core.resources.IMarkerDelta[], int)
*
*
* Callback for ResourceMonitor.IFileListener. Called when a file changed.
* This records the changes for each project, but does not notify listeners.
* @see #resourceChangeEventEnd
@@ -137,7 +154,7 @@ public final class LayoutReloadMonitor implements IFileListener, IResourceEventL
changeFlags[CHANGE_R]) {
return;
}
// now check that the file is *NOT* a layout file (those automatically trigger a layout
// reload and we don't want to do it twice.
ResourceFolder resFolder = ResourceManager.getInstance().getResourceFolder(file);
@@ -148,7 +165,7 @@ public final class LayoutReloadMonitor implements IFileListener, IResourceEventL
changeFlags = new boolean[CHANGE_COUNT];
mChangedProjects.put(project, changeFlags);
}
changeFlags[CHANGE_RESOURCES] = true;
}
} else if (AndroidConstants.EXT_CLASS.equals(file.getFileExtension())) {
@@ -171,14 +188,14 @@ public final class LayoutReloadMonitor implements IFileListener, IResourceEventL
}
}
}
/*
* (non-Javadoc)
* @see com.android.ide.eclipse.editors.resources.manager.ResourceMonitor.IResourceEventListener#resourceChangeEventStart()
*
*
* Callback for ResourceMonitor.IResourceEventListener. Called at the beginning of a resource
* change event. This is called once, while fileChanged can be called several times.
*
*
*/
public void resourceChangeEventStart() {
// nothing to be done here, it all happens in the resourceChangeEventEnd
@@ -187,7 +204,7 @@ public final class LayoutReloadMonitor implements IFileListener, IResourceEventL
/*
* (non-Javadoc)
* @see com.android.ide.eclipse.editors.resources.manager.ResourceMonitor.IResourceEventListener#resourceChangeEventEnd()
*
*
* Callback for ResourceMonitor.IResourceEventListener. Called at the end of a resource
* change event. This is where we notify the listeners.
*/
@@ -196,9 +213,9 @@ public final class LayoutReloadMonitor implements IFileListener, IResourceEventL
synchronized (mListenerMap) {
for (Entry<IProject, boolean[]> project : mChangedProjects.entrySet()) {
List<ILayoutReloadListener> listeners = mListenerMap.get(project.getKey());
boolean[] flags = project.getValue();
if (listeners != null) {
for (ILayoutReloadListener listener : listeners) {
listener.reloadLayout(flags[CHANGE_CODE], flags[CHANGE_R],
@@ -207,7 +224,7 @@ public final class LayoutReloadMonitor implements IFileListener, IResourceEventL
}
}
}
// empty the list.
mChangedProjects.clear();
}

View File

@@ -0,0 +1,118 @@
/*
* Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php
*
* 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.android.ide.eclipse.adt.internal.editors.layout;
import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor;
import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import java.util.List;
/**
* A palette composite for the {@link GraphicalEditorPart}.
* <p/>
* The palette contains several groups, each with a UI name (e.g. layouts and views) and each
* with a list of element descriptors.
* <p/>
*
* @since GLE2
*
* TODO list:
* - *Mandatory* for a first release:
* - Currently this displays elements as buttons. Eventually this needs to either be replaced
* by custom drawing right in here or we need to use a custom control.
* - Needs to be able to originate drag'n'drop from these controls onto the GEP.
* - Scroll the list.
* - For later releases:
* - Ability to collapse palettes or dockable palettes.
* - Different view strategies: big icon, small icons, text vs no text, compact grid.
* - This would only be useful with meaningful icons. Out current 1-letter icons are not enough
* to get rid of text labels.
* - Would be nice to have context-sensitive tools items, e.g. selection arrow tool,
* group selection tool, alignment, etc.
*/
public class PaletteComposite extends Composite {
/**
* Create the composite.
* @param parent The parent composite.
*/
public PaletteComposite(Composite parent) {
super(parent, SWT.BORDER | SWT.V_SCROLL);
}
@Override
protected void checkSubclass() {
// Disable the check that prevents subclassing of SWT components
}
/**
* Load or reload the palette elements by using the layour and view descriptors from the
* given target data.
*
* @param targetData The target data that contains the descriptors. If null or empty,
* no groups will be created.
*/
public void reloadPalette(AndroidTargetData targetData) {
for (Control c : getChildren()) {
c.dispose();
}
if (targetData != null) {
GridLayout gl = new GridLayout(1, false);
gl.horizontalSpacing = 0;
gl.verticalSpacing = 0;
gl.marginHeight = 2;
gl.marginBottom = 2;
gl.marginLeft = 2;
gl.marginRight = 2;
gl.marginTop = 2;
gl.marginBottom = 2;
setLayout(gl);
/* STOPSHIP */
Label l = new Label(this, SWT.NONE);
l.setText("*** PLACEHOLDER ***"); //$NON-NLS-1$
l.setToolTipText("Temporary mock for the palette. Needs to scroll, needs no buttons, needs to drag'n'drop."); //$NON-NLS-1$
addGroup("Layouts", targetData.getLayoutDescriptors().getLayoutDescriptors());
addGroup("Views", targetData.getLayoutDescriptors().getViewDescriptors());
}
layout(true);
}
private void addGroup(String uiName, List<ElementDescriptor> descriptors) {
Label label = new Label(this, SWT.NONE);
label.setText(uiName);
for (ElementDescriptor desc : descriptors) {
Button b = new Button(this, SWT.PUSH);
b.setText(desc.getUiName());
b.setImage(desc.getIcon());
b.setToolTipText(desc.getTooltip());
b.setData(desc);
}
}
}