diff options
author | Raphael Moll <ralf@android.com> | 2010-09-10 22:19:11 -0700 |
---|---|---|
committer | Raphael Moll <ralf@android.com> | 2010-09-10 22:26:24 -0700 |
commit | 64f63f92627da1c469250d8b4894ca7f282ffab2 (patch) | |
tree | 0d276abf93e786a97277185841b138a35bb329d1 /eclipse | |
parent | 0a3b9b3c4e73abf826866cec9fca394d690caed2 (diff) | |
download | sdk-64f63f92627da1c469250d8b4894ca7f282ffab2.zip sdk-64f63f92627da1c469250d8b4894ca7f282ffab2.tar.gz sdk-64f63f92627da1c469250d8b4894ca7f282ffab2.tar.bz2 |
GLE2: Properly represent mixed choices in dynamic context menu.
Example: when display the context menu for a multiple selection,
some items might be on wrap_parent whilst others might be in
match_parent. This new change has the menu display a different
icon for these choices and indicate that N out of M items are
in a given state. Basically this converts boolean support to
a tristate support.
Change-Id: Ia409a87fa17e5d36912ace6dde5f4eb094fa865f
Diffstat (limited to 'eclipse')
3 files changed, 64 insertions, 23 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/icons/match_multiple.png b/eclipse/plugins/com.android.ide.eclipse.adt/icons/match_multiple.png Binary files differnew file mode 100755 index 0000000..79ffc2d --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/icons/match_multiple.png diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/editors/layout/gscripts/MenuAction.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/editors/layout/gscripts/MenuAction.java index d3f34e5..942ad85 100755 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/editors/layout/gscripts/MenuAction.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/editors/layout/gscripts/MenuAction.java @@ -236,6 +236,9 @@ public abstract class MenuAction { /** * A toggle is a simple on/off action, displayed as an item in a context menu * with a check mark if the item is checked. + * <p/> + * Two toggles are equal if they have the same id, title and group-id. + * It is expected for the checked state and action closure to be different. */ public static class Toggle extends Action { /** @@ -303,6 +306,9 @@ public abstract class MenuAction { * Implementation detail: empty choices will not be displayed in the context menu. * <p/> * Choice items are sorted by their id, using String's natural sorting order. + * <p/> + * Two multiple choices are equal if they have the same id, title, group-id and choices. + * It is expected for the current state and action closure to be different. */ public static class Choices extends Action { diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/DynamicContextMenu.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/DynamicContextMenu.java index f9657af..97f2b95 100755 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/DynamicContextMenu.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/DynamicContextMenu.java @@ -19,6 +19,7 @@ package com.android.ide.eclipse.adt.internal.editors.layout.gle2; import com.android.ide.eclipse.adt.AdtPlugin; import com.android.ide.eclipse.adt.editors.layout.gscripts.IViewRule; import com.android.ide.eclipse.adt.editors.layout.gscripts.MenuAction; +import com.android.ide.eclipse.adt.internal.editors.IconFactory; import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditor; import com.android.ide.eclipse.adt.internal.editors.layout.gre.NodeProxy; import com.android.ide.eclipse.adt.internal.editors.layout.gre.RulesEngine; @@ -35,14 +36,11 @@ import org.eclipse.jface.action.Separator; import groovy.lang.Closure; import java.util.ArrayList; -import java.util.Arrays; import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; import java.util.TreeMap; +import java.util.Map.Entry; import java.util.regex.Pattern; @@ -373,6 +371,7 @@ import java.util.regex.Pattern; final TreeMap<String, ArrayList<MenuAction>> actionsMap) { final RulesEngine gre = mCanvas.getRulesEngine(); + IconFactory factory = IconFactory.getInstance(); MenuManager submenu = new MenuManager(firstAction.getTitle(), firstAction.getId()); // Convert to a tree map as needed so that keys be naturally ordered. @@ -380,14 +379,7 @@ import java.util.regex.Pattern; choiceMap = new TreeMap<String, String>(choiceMap); } - String current = firstAction.getCurrent(); - Set<String> currents = null; - if (current.indexOf(MenuAction.Choices.CHOICE_SEP) >= 0) { - currents = new HashSet<String>( - Arrays.asList(current.split( - Pattern.quote(MenuAction.Choices.CHOICE_SEP)))); - current = null; - } + String sepPattern = Pattern.quote(MenuAction.Choices.CHOICE_SEP); for (Entry<String, String> entry : choiceMap.entrySet() ) { final String key = entry.getKey(); @@ -402,26 +394,65 @@ import java.util.regex.Pattern; continue; } - final boolean isChecked = - (currents != null && currents.contains(key)) || - key.equals(current); + final List<MenuAction> actions = actionsMap.get(firstAction.getId()); + + if (actions == null || actions.isEmpty()) { + continue; + } + + // Are all actions for this id checked, unchecked, or in a mixed state? + int numOff = 0; + int numOn = 0; + for (MenuAction a2 : actions) { + MenuAction.Choices choice = (MenuAction.Choices) a2; + String current = choice.getCurrent(); + boolean found = false; + + if (current.indexOf(MenuAction.Choices.CHOICE_SEP) >= 0) { + // current choice has a separator, so it's a flag with multiple values + // selected. Compare keys with the split values. + if (current.indexOf(key) >= 0) { + for(String value : current.split(sepPattern)) { + if (key.equals(value)) { + found = true; + break; + } + } + } + } else { + // current choice has no separator, simply compare to the key + found = key.equals(current); + } + + if (found) { + numOn++; + } else { + numOff++; + } + } + + // We consider the item to be checked if all actions are all checked. + // This means a mixed item will be first toggled from off to on by all the closures. + final boolean isChecked = numOff == 0 && numOn > 0; + boolean isMixed = numOff > 0 && numOn > 0; + + if (isMixed) { + title += String.format(" (%1$d/%2$d)", numOn, numOff + numOn); + } Action a = new Action(title, IAction.AS_CHECK_BOX) { @Override public void run() { - final List<MenuAction> actions = actionsMap.get(firstAction.getId()); - if (actions == null || actions.isEmpty()) { - return; - } - String label = String.format("Change attribute %s", actions.get(0).getTitle()); + String label = + String.format("Change attribute %1$s", actions.get(0).getTitle()); if (actions.size() > 1) { - label += String.format(" (%d elements)", actions.size()); + label += String.format(" (%1$d elements)", actions.size()); } if (mEditor.isEditXmlModelPending()) { // This should not be happening. - logError("Action '%s' failed: XML changes pending, document might be corrupt.", //$NON-NLS-1$ + logError("Action '%1$s' failed: XML changes pending, document might be corrupt.", //$NON-NLS-1$ label); return; } @@ -443,8 +474,12 @@ import java.util.regex.Pattern; }); } }; - a.setId(String.format("%s_%s", firstAction.getId(), key)); //$NON-NLS-1$ + a.setId(String.format("%1$s_%2$s", firstAction.getId(), key)); //$NON-NLS-1$ a.setChecked(isChecked); + if (isMixed) { + a.setImageDescriptor(factory.getImageDescriptor("match_multiple")); //$NON-NLS-1$ + } + submenu.add(a); } |