aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTor Norbye <tnorbye@google.com>2011-12-21 16:10:47 -0800
committerTor Norbye <tnorbye@google.com>2011-12-22 11:23:58 -0800
commitd7f5ef53fe74b55f0c8b385b32e94fae6671910a (patch)
treead4b2d3d1dd9ee61772b9a9d8764611a816047dd
parentab36f4e7488358dea4ab6b54ee2b7bef3da0232b (diff)
downloadsdk-d7f5ef53fe74b55f0c8b385b32e94fae6671910a.zip
sdk-d7f5ef53fe74b55f0c8b385b32e94fae6671910a.tar.gz
sdk-d7f5ef53fe74b55f0c8b385b32e94fae6671910a.tar.bz2
Add textfield to resource chooser for @string resources
This changeset adds a new textfield to the bottom of the resource chooser in some scenarios, such as when editing @string resources via Edit Text in the layout editor. This text field updates to show the @string/resourceName value whenever you update the selection in the filter, but it also allows you to edit the text to anything else, such as a literla String (not a resource). This makes it easy to edit simple labels. See the following adt-dev thread for some background on this: http://groups.google.com/group/adt-dev/browse_thread/ thread/d53e71c2970ce369?pli=1 There is also a new label below the text field which shows the "resolved value". For example, if you've chosen "hello" in the filter list, the new value text will show "@string/hello", and the new label will show "Hello World". This will hopefully make it more obvious what it is you're editing. And, similar to the widget render preview for drawable resources, this makes it easier to browse and pick labels, especially when browsing framework resources. This is currently only enabled for @string and @dimen resources, but we could conceivably add it for other resource types too. Longer term, we need inline editing of widgets (where values entered automatically get translated into resources), but this is a stopgap measure which makes it easier to build simple apps quickly, and which still makes it simple to create new strings or select existing ones from the filter list. (And we have both Lint and the Extract String refactoring to help identify and fix hardcoded strings later on.) While working on this I also discovered the root cause for this bug: 20589: The reference chooser is not usable for framework styles Turns out org.eclipse.ui.dialogs.AbstractElementListSelectionDialog has a handleEmptyList() method which calls setEnabled(false) on the filter list when the list is empty. Crucially, there is no corresponding setEnabled(true) call when the list is made non-empty! When the user switches between framework and project resources, the list is temporarily empty, so the list gets disabled, and that's why it's not responding to menu clicks at all. This changeset also fixes that bug. Change-Id: Id441104d617fa4440daba3da2dc63e2c999f48c1
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/BaseViewRule.java3
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/ClientRulesEngine.java18
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/MarginChooser.java35
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ResourceChooser.java210
4 files changed, 250 insertions, 16 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/BaseViewRule.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/BaseViewRule.java
index 320455d..6e5b287 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/BaseViewRule.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/BaseViewRule.java
@@ -227,7 +227,8 @@ public class BaseViewRule extends AbstractViewRule {
? ResourceType.STYLE.getName()
: ResourceType.STRING.getName();
String oldValue = selectedNodes.size() == 1
- ? firstNode.getStringAttr(null, ATTR_STYLE)
+ ? (isStyle ? firstNode.getStringAttr(ATTR_STYLE, actionId)
+ : firstNode.getStringAttr(ANDROID_URI, actionId))
: ""; //$NON-NLS-1$
oldValue = ensureValidString(oldValue);
v = mRulesEngine.displayResourceInput(resourceTypeName, oldValue);
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/ClientRulesEngine.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/ClientRulesEngine.java
index f636e34..71fcb26 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/ClientRulesEngine.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/ClientRulesEngine.java
@@ -263,6 +263,18 @@ class ClientRulesEngine implements IClientRulesEngine {
systemRepository, shell);
dlg.setPreviewHelper(new ResourcePreviewHelper(dlg, graphicalEditor));
+ // When editing Strings, allow editing the value text directly. When we
+ // get inline editing support (where values entered directly into the
+ // textual widget are translated automatically into a resource) this can
+ // go away.
+ if (resourceTypeName.equals(ResourceType.STRING.getName())) {
+ dlg.setResourceResolver(graphicalEditor.getResourceResolver());
+ dlg.setShowValueText(true);
+ } else if (resourceTypeName.equals(ResourceType.DIMEN.getName())
+ || resourceTypeName.equals(ResourceType.INTEGER.getName())) {
+ dlg.setResourceResolver(graphicalEditor.getResourceResolver());
+ }
+
if (validator != null) {
// Ensure wide enough to accommodate validator error message
dlg.setSize(85, 10);
@@ -285,15 +297,15 @@ class ClientRulesEngine implements IClientRulesEngine {
@Override
public String[] displayMarginInput(String all, String left, String right, String top,
String bottom) {
- AndroidXmlEditor editor = mRulesEngine.getEditor().getLayoutEditor();
+ GraphicalEditorPart editor = mRulesEngine.getEditor();
IProject project = editor.getProject();
if (project != null) {
Shell shell = AdtPlugin.getDisplay().getActiveShell();
if (shell == null) {
return null;
}
- AndroidTargetData data = editor.getTargetData();
- MarginChooser dialog = new MarginChooser(shell, project, data, all, left, right,
+ AndroidTargetData data = editor.getLayoutEditor().getTargetData();
+ MarginChooser dialog = new MarginChooser(shell, editor, data, all, left, right,
top, bottom);
if (dialog.open() == Window.OK) {
return dialog.getMargins();
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/MarginChooser.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/MarginChooser.java
index 509fec2..066f4a1 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/MarginChooser.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/MarginChooser.java
@@ -16,6 +16,7 @@
package com.android.ide.eclipse.adt.internal.ui;
import com.android.ide.common.resources.ResourceRepository;
+import com.android.ide.eclipse.adt.internal.editors.layout.gle2.GraphicalEditorPart;
import com.android.ide.eclipse.adt.internal.resources.manager.ProjectResources;
import com.android.ide.eclipse.adt.internal.resources.manager.ResourceManager;
import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData;
@@ -36,8 +37,11 @@ import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.dialogs.SelectionStatusDialog;
+/**
+ * Dialog for choosing margins
+ */
public class MarginChooser extends SelectionStatusDialog implements Listener {
- private IProject mProject;
+ private GraphicalEditorPart mEditor;
private AndroidTargetData mTargetData;
private Text mLeftField;
private Text mRightField;
@@ -55,11 +59,23 @@ public class MarginChooser extends SelectionStatusDialog implements Listener {
// Client data key for resource buttons pointing to the associated text field
private final static String PROP_TEXTFIELD = "textField"; //$NON-NLS-1$
- public MarginChooser(Shell parent, IProject project, AndroidTargetData targetData, String all,
+ /**
+ * Constructs a new margin chooser dialog.
+ *
+ * @param parent parent widget
+ * @param editor associated layout editor
+ * @param targetData current SDK target
+ * @param all current value for the all margins attribute
+ * @param left current value for the left margin
+ * @param right current value for the right margin
+ * @param top current value for the top margin
+ * @param bottom current value for the bottom margin
+ */
+ public MarginChooser(Shell parent, GraphicalEditorPart editor, AndroidTargetData targetData, String all,
String left, String right, String top, String bottom) {
super(parent);
setTitle("Edit Margins");
- mProject = project;
+ mEditor = editor;
mTargetData = targetData;
mInitialAll = all;
mInitialLeft = left;
@@ -68,6 +84,7 @@ public class MarginChooser extends SelectionStatusDialog implements Listener {
mInitialBottom = bottom;
}
+ @SuppressWarnings("unused") // SWT constructors have side effects, "new Label" is not unused.
@Override
protected Control createDialogArea(Composite parent) {
Composite container = new Composite(parent, SWT.NONE);
@@ -166,6 +183,12 @@ public class MarginChooser extends SelectionStatusDialog implements Listener {
};
}
+ /**
+ * Returns the margins in the order all, left, right, top, bottom
+ *
+ * @return the margins in the order all, left, right, top, bottom, never
+ * null
+ */
public String[] getMargins() {
return mMargins;
}
@@ -198,11 +221,13 @@ public class MarginChooser extends SelectionStatusDialog implements Listener {
Button button = (Button) event.widget;
// Open a resource chooser dialog for specified resource type.
+ IProject project = mEditor.getProject();
ProjectResources projectRepository = ResourceManager.getInstance()
- .getProjectResources(mProject);
+ .getProjectResources(project);
ResourceRepository frameworkRepository = mTargetData.getFrameworkResources();
- ResourceChooser dlg = new ResourceChooser(mProject, ResourceType.DIMEN,
+ ResourceChooser dlg = new ResourceChooser(project, ResourceType.DIMEN,
projectRepository, frameworkRepository, getShell());
+ dlg.setResourceResolver(mEditor.getResourceResolver());
Text text = (Text) button.getData(PROP_TEXTFIELD);
dlg.setCurrentResource(text.getText().trim());
if (dlg.open() == Window.OK) {
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ResourceChooser.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ResourceChooser.java
index d6635f7..b424435 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ResourceChooser.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ResourceChooser.java
@@ -17,8 +17,13 @@
package com.android.ide.eclipse.adt.internal.ui;
+import static com.android.ide.common.resources.ResourceResolver.PREFIX_ANDROID_RESOURCE_REF;
+import static com.android.ide.common.resources.ResourceResolver.PREFIX_RESOURCE_REF;
+
+import com.android.ide.common.rendering.api.ResourceValue;
import com.android.ide.common.resources.ResourceItem;
import com.android.ide.common.resources.ResourceRepository;
+import com.android.ide.common.resources.ResourceResolver;
import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.AdtUtils;
import com.android.ide.eclipse.adt.internal.assetstudio.OpenCreateAssetSetWizardAction;
@@ -42,6 +47,8 @@ import org.eclipse.jface.window.Window;
import org.eclipse.ltk.ui.refactoring.RefactoringWizard;
import org.eclipse.ltk.ui.refactoring.RefactoringWizardOpenOperation;
import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.GridData;
@@ -53,6 +60,7 @@ import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.dialogs.AbstractElementListSelectionDialog;
@@ -68,7 +76,7 @@ import java.util.regex.Pattern;
/**
* A dialog to let the user select a resource based on a resource type.
*/
-public class ResourceChooser extends AbstractElementListSelectionDialog {
+public class ResourceChooser extends AbstractElementListSelectionDialog implements ModifyListener {
/** The return code from the dialog for the user choosing "Clear" */
public static final int CLEAR_RETURN_CODE = -5;
/** The dialog button ID for the user choosing "Clear" */
@@ -85,9 +93,40 @@ public class ResourceChooser extends AbstractElementListSelectionDialog {
private String mCurrentResource;
private final IProject mProject;
private IInputValidator mInputValidator;
+
+ /** Helper object used to draw previews for drawables and colors. */
private ResourcePreviewHelper mPreviewHelper;
/**
+ * Textfield for editing the actual returned value, updated when selection
+ * changes. Only shown if {@link #mShowValueText} is true.
+ */
+ private Text mEditValueText;
+
+ /**
+ * Whether the {@link #mEditValueText} textfield should be shown when the dialog is created.
+ */
+ private boolean mShowValueText;
+
+ /**
+ * Flag indicating whether it's the first time {@link #handleSelectionChanged()} is called.
+ * This is used to filter out the first selection event, always called by the superclass
+ * when the widget is created, to distinguish between "the dialog was created" and
+ * "the user clicked on a selection result", since only the latter should wipe out the
+ * manual user edit shown in the value text.
+ */
+ private boolean mFirstSelect = true;
+
+ /**
+ * Label used to show the resolved value in the resource chooser. Only shown
+ * if the {@link #mResourceResolver} field is set.
+ */
+ private Label mResolvedLabel;
+
+ /** Resource resolver used to show actual values for resources selected. (Optional). */
+ private ResourceResolver mResourceResolver;
+
+ /**
* Creates a Resource Chooser dialog.
* @param project Project being worked on
* @param type The type of the resource to choose
@@ -107,21 +146,58 @@ public class ResourceChooser extends AbstractElementListSelectionDialog {
mFrameworkResources = frameworkResources;
mProjectResourcePattern = Pattern.compile(
- "@" + mResourceType.getName() + "/(.+)"); //$NON-NLS-1$ //$NON-NLS-2$
+ PREFIX_RESOURCE_REF + mResourceType.getName() + "/(.+)"); //$NON-NLS-1$
mSystemResourcePattern = Pattern.compile(
- "@android:" + mResourceType.getName() + "/(.+)"); //$NON-NLS-1$ //$NON-NLS-2$
+ PREFIX_ANDROID_RESOURCE_REF + mResourceType.getName() + "/(.+)"); //$NON-NLS-1$
setTitle("Resource Chooser");
setMessage(String.format("Choose a %1$s resource",
mResourceType.getDisplayName().toLowerCase()));
}
+ /**
+ * Sets whether this dialog should show the value field as a separate text
+ * value (and take the resulting value of the dialog from this text field
+ * rather than from the selection)
+ *
+ * @param showValueText if true, show the value text field
+ */
+ public void setShowValueText(boolean showValueText) {
+ mShowValueText = showValueText;
+ }
+
+ /**
+ * Sets the resource resolver to use to show resolved values for the current
+ * selection
+ *
+ * @param resourceResolver the resource resolver to use
+ */
+ public void setResourceResolver(ResourceResolver resourceResolver) {
+ mResourceResolver = resourceResolver;
+ }
+
+ /**
+ * Sets the {@link ResourcePreviewHelper} to use to preview drawable
+ * resources, if any
+ *
+ * @param previewHelper the helper to use
+ */
public void setPreviewHelper(ResourcePreviewHelper previewHelper) {
mPreviewHelper = previewHelper;
}
@Override
+ public void create() {
+ super.create();
+
+ if (mShowValueText) {
+ mEditValueText.selectAll();
+ mEditValueText.setFocus();
+ }
+ }
+
+ @Override
protected void createButtonsForButtonBar(Composite parent) {
createButton(parent, CLEAR_BUTTON_ID, "Clear", false /*defaultButton*/);
super.createButtonsForButtonBar(parent);
@@ -140,6 +216,10 @@ public class ResourceChooser extends AbstractElementListSelectionDialog {
public void setCurrentResource(String resource) {
mCurrentResource = resource;
+
+ if (mShowValueText && mEditValueText != null) {
+ mEditValueText.setText(resource);
+ }
}
public String getCurrentResource() {
@@ -152,6 +232,18 @@ public class ResourceChooser extends AbstractElementListSelectionDialog {
@Override
protected void computeResult() {
+ if (mShowValueText) {
+ mCurrentResource = mEditValueText.getText();
+ if (mCurrentResource.length() == 0) {
+ mCurrentResource = null;
+ }
+ return;
+ }
+
+ computeResultFromSelection();
+ }
+
+ private void computeResultFromSelection() {
if (getSelectionIndex() == -1) {
mCurrentResource = null;
return;
@@ -182,6 +274,9 @@ public class ResourceChooser extends AbstractElementListSelectionDialog {
// create the "New Resource" button
createNewResButtons(top);
+ // Optionally create the value text field, if {@link #mShowValueText} is true
+ createValueField(top);
+
setupResourceList();
selectResourceString(mCurrentResource);
@@ -200,9 +295,16 @@ public class ResourceChooser extends AbstractElementListSelectionDialog {
public void widgetSelected(SelectionEvent e) {
super.widgetSelected(e);
if (mProjectButton.getSelection()) {
+ // Clear selection before changing the list contents. This works around
+ // a bug in the superclass where switching to the framework resources,
+ // choosing one of the last resources, then switching to the project
+ // resources would cause an exception when calling getSelection() because
+ // selection state doesn't get cleared when we set new contents on
+ // the filtered list.
+ fFilteredList.setSelection(new int[0]);
setupResourceList();
updateNewButton(false /*isSystem*/);
- updatePreview();
+ updateValue();
}
}
});
@@ -213,9 +315,10 @@ public class ResourceChooser extends AbstractElementListSelectionDialog {
public void widgetSelected(SelectionEvent e) {
super.widgetSelected(e);
if (mSystemButton.getSelection()) {
+ fFilteredList.setSelection(new int[0]);
setupResourceList();
updateNewButton(true /*isSystem*/);
- updatePreview();
+ updateValue();
}
}
});
@@ -287,6 +390,7 @@ public class ResourceChooser extends AbstractElementListSelectionDialog {
items = newItems;
Arrays.sort(items);
setListElements(items);
+ fFilteredList.setEnabled(newItems.length > 0);
}
selectItemName(newName, items);
@@ -294,6 +398,62 @@ public class ResourceChooser extends AbstractElementListSelectionDialog {
});
}
+ /**
+ * Creates the value text field.
+ *
+ * @param top the parent composite
+ */
+ private void createValueField(Composite top) {
+ if (mShowValueText) {
+ mEditValueText = new Text(top, SWT.BORDER);
+ if (mCurrentResource != null) {
+ mEditValueText.setText(mCurrentResource);
+ }
+ mEditValueText.addModifyListener(this);
+
+ GridData data = new GridData();
+ data.grabExcessVerticalSpace = false;
+ data.grabExcessHorizontalSpace = true;
+ data.horizontalAlignment = GridData.FILL;
+ data.verticalAlignment = GridData.BEGINNING;
+ mEditValueText.setLayoutData(data);
+ mEditValueText.setFont(top.getFont());
+ }
+
+ if (mResourceResolver != null) {
+ mResolvedLabel = new Label(top, SWT.NONE);
+ GridData data = new GridData();
+ data.grabExcessVerticalSpace = false;
+ data.grabExcessHorizontalSpace = true;
+ data.horizontalAlignment = GridData.FILL;
+ data.verticalAlignment = GridData.BEGINNING;
+ mResolvedLabel.setLayoutData(data);
+ }
+ }
+
+ private void updateResolvedLabel() {
+ if (mResourceResolver == null) {
+ return;
+ }
+
+ String v = null;
+ if (mCurrentResource != null) {
+ v = mCurrentResource;
+ if (mCurrentResource.startsWith(PREFIX_RESOURCE_REF)) {
+ ResourceValue value = mResourceResolver.findResValue(mCurrentResource, false);
+ if (value != null) {
+ v = value.getValue();
+ }
+ }
+ }
+
+ if (v == null) {
+ v = "";
+ }
+
+ mResolvedLabel.setText(String.format("Resolved Value: %1$s", v));
+ }
+
@Override
protected void handleSelectionChanged() {
super.handleSelectionChanged();
@@ -313,14 +473,31 @@ public class ResourceChooser extends AbstractElementListSelectionDialog {
}
}
- updatePreview();
+ updateValue();
}
- private void updatePreview() {
+ private void updateValue() {
if (mPreviewHelper != null) {
computeResult();
mPreviewHelper.updatePreview(mResourceType, mCurrentResource);
}
+
+ if (mShowValueText) {
+ if (mFirstSelect) {
+ mFirstSelect = false;
+ mEditValueText.selectAll();
+ } else {
+ computeResultFromSelection();
+ mEditValueText.setText(mCurrentResource != null ? mCurrentResource : "");
+ }
+ }
+
+ if (mResourceResolver != null) {
+ if (!mShowValueText) {
+ computeResultFromSelection();
+ }
+ updateResolvedLabel();
+ }
}
private String createNewFile(ResourceType type) {
@@ -419,6 +596,7 @@ public class ResourceChooser extends AbstractElementListSelectionDialog {
Arrays.sort(arrayItems);
setListElements(arrayItems);
+ fFilteredList.setEnabled(arrayItems.length > 0);
return arrayItems;
}
@@ -483,6 +661,24 @@ public class ResourceChooser extends AbstractElementListSelectionDialog {
mNewButton.setEnabled(!isSystem && ResourceHelper.canCreateResourceType(mResourceType));
}
+ // ---- Implements ModifyListener ----
+
+ @Override
+ public void modifyText(ModifyEvent e) {
+ if (e.getSource() == mEditValueText && mResourceResolver != null) {
+ mCurrentResource = mEditValueText.getText();
+
+ if (mCurrentResource.startsWith(PREFIX_RESOURCE_REF)) {
+ if (mProjectResourcePattern.matcher(mCurrentResource).matches() ||
+ mSystemResourcePattern.matcher(mCurrentResource).matches()) {
+ updateResolvedLabel();
+ }
+ } else {
+ updateResolvedLabel();
+ }
+ }
+ }
+
/** Dialog asking for a Name/Value pair */
private class NameValueDialog extends SelectionStatusDialog implements Listener {
private org.eclipse.swt.widgets.Text mNameText;