diff options
author | Siva Velusamy <vsiva@google.com> | 2015-06-04 12:23:03 -0700 |
---|---|---|
committer | Deepanshu Gupta <deepanshu@google.com> | 2015-07-13 17:39:09 -0700 |
commit | 4727e0140e84369d0eb166268883be3eed5896e3 (patch) | |
tree | 86eecb6497d4a4b679a4a489e54ada5fa49b0608 /designer | |
parent | d0cd897dbd0ab280da18870c3c1f79dcbb01f2c2 (diff) | |
download | idea-4727e0140e84369d0eb166268883be3eed5896e3.tar.gz |
property sheet: editing support and other renderer improvements
This CL includes a number of changes to the property sheet:
Renderer improvements:
- The renderer and editor for booleans now use a ThreeStateCheckbox
instead of a checkbox to allow for 3 states (yes, no, default).
The actual widget used will be tweaked after UX review.
- The default renderer for resource references will now attempt
to show an icon for color and drawable references.
- Hovering over a cell will automatically show a browse button
that can be clicked to bring up the resource chooser dialog.
Editors:
- Includes editing support for:
- booleans (using a ThreeStateCheckbox)
- enums (using a JComboBox)
- and other reference properties (just an EditorTextField)
- Flag properties are missing, and auto complete within the text
fields is also missing
Misc:
- A "Show expert properties" toolbar button has been added. This
doesn't do anything right now, but will be wired up to show
just a filtered list of properties. This CL includes it just so
that the toolbar has some action setup in it.
Change-Id: I089ce7da5158b6565b695c536e24d6411a73b0c9
(cherry picked from commit 846e9f1f055ea958a489874704950ae186689762)
Diffstat (limited to 'designer')
19 files changed, 985 insertions, 76 deletions
diff --git a/designer/src/com/android/tools/idea/uibuilder/model/NlComponent.java b/designer/src/com/android/tools/idea/uibuilder/model/NlComponent.java index da8ce5e8763..7c563ae90a0 100644 --- a/designer/src/com/android/tools/idea/uibuilder/model/NlComponent.java +++ b/designer/src/com/android/tools/idea/uibuilder/model/NlComponent.java @@ -445,6 +445,9 @@ public class NlComponent { public void setAttribute(@Nullable String namespace, @NonNull String attribute, @Nullable String value) { // Handle validity myTag.setAttribute(attribute, namespace, value); + if (snapshot != null) { + snapshot.setAttribute(attribute, namespace, null, value); + } } @Nullable diff --git a/designer/src/com/android/tools/idea/uibuilder/property/NlPropertiesModel.java b/designer/src/com/android/tools/idea/uibuilder/property/NlPropertiesModel.java index 304145ddf4d..65b0dfd0655 100644 --- a/designer/src/com/android/tools/idea/uibuilder/property/NlPropertiesModel.java +++ b/designer/src/com/android/tools/idea/uibuilder/property/NlPropertiesModel.java @@ -30,6 +30,7 @@ import java.util.List; public class NlPropertiesModel extends PTableModel { @Nullable private NlComponent myComponent; + private boolean myShowingExpertProperties; public void update(@NotNull Iterable<NlComponent> selection, @Nullable final Runnable postUpdateRunnable) { // TODO: handle multiple selections: show properties common to all selections @@ -43,8 +44,6 @@ public class NlPropertiesModel extends PTableModel { return; } - final String tagName = first.getTagName(); - // Obtaining the properties, especially the first time around on a big project // can take close to a second, so we do it on a separate thread.. ApplicationManager.getApplication().executeOnPooledThread(new Runnable() { @@ -66,4 +65,12 @@ public class NlPropertiesModel extends PTableModel { } }); } + + public boolean isShowingExpertProperties() { + return myShowingExpertProperties; + } + + public void setShowExpertProperties(boolean en) { + myShowingExpertProperties = en; + } }
\ No newline at end of file diff --git a/designer/src/com/android/tools/idea/uibuilder/property/NlPropertiesPanel.java b/designer/src/com/android/tools/idea/uibuilder/property/NlPropertiesPanel.java index f0227e47ed5..67ddcaa0eb5 100644 --- a/designer/src/com/android/tools/idea/uibuilder/property/NlPropertiesPanel.java +++ b/designer/src/com/android/tools/idea/uibuilder/property/NlPropertiesPanel.java @@ -15,11 +15,18 @@ */ package com.android.tools.idea.uibuilder.property; +import com.android.tools.idea.uibuilder.model.NlComponent; import com.android.tools.idea.uibuilder.property.ptable.PTable; import com.android.tools.idea.uibuilder.surface.ScreenView; +import com.google.common.collect.Iterables; +import com.intellij.openapi.actionSystem.ActionPlaces; +import com.intellij.openapi.actionSystem.ActionToolbar; +import com.intellij.openapi.actionSystem.DefaultActionGroup; +import com.intellij.openapi.actionSystem.impl.ActionButton; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.ui.components.JBLabel; +import com.intellij.ui.components.JBPanel; import com.intellij.ui.components.JBScrollPane; -import com.intellij.ui.table.JBTable; import com.intellij.util.Alarm; import com.intellij.util.ui.UIUtil; import com.intellij.util.ui.update.MergingUpdateQueue; @@ -29,16 +36,14 @@ import org.jetbrains.annotations.NotNull; import javax.swing.*; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; -import javax.swing.table.TableCellRenderer; -import javax.swing.table.TableColumn; import java.awt.*; -import java.util.Enumeration; public class NlPropertiesPanel extends JPanel implements ChangeListener { private final PTable myTable; private final ScreenView myScreenView; private final NlPropertiesModel myModel; private MergingUpdateQueue myUpdateQueue; + private JBLabel mySelectedComponentLabel; public NlPropertiesPanel(@NotNull ScreenView screenView) { super(new BorderLayout()); @@ -54,11 +59,28 @@ public class NlPropertiesPanel extends JPanel implements ChangeListener { myTable.getEmptyText().setText("No selected component"); + JPanel headerPanel = createHeaderPanel(); + add(headerPanel, BorderLayout.NORTH); add(new JBScrollPane(myTable), BorderLayout.CENTER); myScreenView.getSelectionModel().addListener(this); } + @NotNull + private JPanel createHeaderPanel() { + JBPanel panel = new JBPanel(new BorderLayout()); + + mySelectedComponentLabel = new JBLabel(""); + panel.add(mySelectedComponentLabel, BorderLayout.CENTER); + + ShowExpertProperties showExpertAction = new ShowExpertProperties(myModel); + ActionButton showExpertButton = new ActionButton(showExpertAction, showExpertAction.getTemplatePresentation(), ActionPlaces.UNKNOWN, + ActionToolbar.DEFAULT_MINIMUM_BUTTON_SIZE); + panel.add(showExpertButton, BorderLayout.LINE_END); + + return panel; + } + @Override public void stateChanged(ChangeEvent e) { myTable.setPaintBusy(true); @@ -68,6 +90,11 @@ public class NlPropertiesPanel extends JPanel implements ChangeListener { myModel.update(myScreenView.getSelectionModel().getSelection(), new Runnable() { @Override public void run() { + // TODO: handle multiple selections + final NlComponent first = Iterables.getFirst(myScreenView.getSelectionModel().getSelection(), null); + if (first != null) { + mySelectedComponentLabel.setText(first.getTagName()); + } myTable.setPaintBusy(false); } }); diff --git a/designer/src/com/android/tools/idea/uibuilder/property/NlProperty.java b/designer/src/com/android/tools/idea/uibuilder/property/NlProperty.java index 7a55529e65f..5bfe7d6974f 100644 --- a/designer/src/com/android/tools/idea/uibuilder/property/NlProperty.java +++ b/designer/src/com/android/tools/idea/uibuilder/property/NlProperty.java @@ -18,16 +18,19 @@ package com.android.tools.idea.uibuilder.property; import com.android.SdkConstants; import com.android.tools.idea.uibuilder.model.NlComponent; import com.android.tools.idea.uibuilder.property.ptable.PTableItem; +import com.android.tools.idea.uibuilder.property.editors.NlPropertyEditors; import com.android.tools.idea.uibuilder.property.renderer.NlPropertyRenderers; import com.google.common.base.Objects; import com.google.common.collect.ImmutableSet; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.command.WriteCommandAction; import com.intellij.xml.NamespaceAwareXmlAttributeDescriptor; import com.intellij.xml.XmlAttributeDescriptor; import org.jetbrains.android.dom.attrs.AttributeDefinition; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import javax.swing.table.TableCellEditor; import javax.swing.table.TableCellRenderer; import java.util.Collections; import java.util.List; @@ -63,6 +66,11 @@ public class NlProperty extends PTableItem { myDefinition = attributeDefinition; } + @NotNull + public NlComponent getComponent() { + return myComponent; + } + @Override @NotNull public String getName() { @@ -75,6 +83,19 @@ public class NlProperty extends PTableItem { return myComponent.getAttribute(myNamespace, myName); } + @Override + public void setValue(Object value) { + assert ApplicationManager.getApplication().isDispatchThread(); + final String attrValue = value == null ? null : value.toString(); + String msg = String.format("Set %1$s.%2$s to %3$s", myComponent.getTagName(), myName, attrValue); + new WriteCommandAction.Simple(myComponent.getModel().getProject(), msg, myComponent.getTag().getContainingFile()) { + @Override + protected void run() throws Throwable { + myComponent.setAttribute(myNamespace, myName, attrValue); + } + }.execute(); + } + @NotNull public List<String> getParentStylables() { return myDefinition == null ? Collections.<String>emptyList() : myDefinition.getParentStyleables(); @@ -92,6 +113,16 @@ public class NlProperty extends PTableItem { } @Override + public boolean isEditable(int col) { + return NlPropertyEditors.get(this) != null; + } + + @Override + public TableCellEditor getCellEditor() { + return NlPropertyEditors.get(this); + } + + @Override public String toString() { return Objects.toStringHelper(this) .add("name", myName) diff --git a/designer/src/com/android/tools/idea/uibuilder/property/ShowExpertProperties.java b/designer/src/com/android/tools/idea/uibuilder/property/ShowExpertProperties.java new file mode 100644 index 00000000000..fdc33567406 --- /dev/null +++ b/designer/src/com/android/tools/idea/uibuilder/property/ShowExpertProperties.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * 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.android.tools.idea.uibuilder.property; + +import com.intellij.icons.AllIcons; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.Presentation; +import com.intellij.openapi.actionSystem.ToggleAction; +import org.jetbrains.annotations.NotNull; + +public class ShowExpertProperties extends ToggleAction { + private final NlPropertiesModel myModel; + + public ShowExpertProperties(@NotNull NlPropertiesModel model) { + Presentation presentation = getTemplatePresentation(); + String text = "Show expert properties"; + presentation.setText(text); + presentation.setDescription(text); + presentation.setIcon(AllIcons.General.Filter); + + myModel = model; + } + + @Override + public boolean isSelected(AnActionEvent e) { + return myModel.isShowingExpertProperties(); + } + + @Override + public void setSelected(AnActionEvent e, boolean state) { + myModel.setShowExpertProperties(state); + } + + @Override + public boolean isDumbAware() { + return true; + } +} diff --git a/designer/src/com/android/tools/idea/uibuilder/property/editors/NlBooleanEditor.java b/designer/src/com/android/tools/idea/uibuilder/property/editors/NlBooleanEditor.java new file mode 100644 index 00000000000..9303970fe13 --- /dev/null +++ b/designer/src/com/android/tools/idea/uibuilder/property/editors/NlBooleanEditor.java @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * 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.android.tools.idea.uibuilder.property.editors; + +import com.android.tools.idea.uibuilder.property.NlProperty; +import com.android.tools.idea.uibuilder.property.renderer.NlBooleanRenderer; +import com.intellij.openapi.ui.FixedSizeButton; +import com.intellij.openapi.util.SystemInfo; +import com.intellij.ui.UIBundle; +import com.intellij.util.ui.AbstractTableCellEditor; +import com.intellij.util.ui.ThreeStateCheckBox; +import com.intellij.util.ui.UIUtil; +import org.jetbrains.android.uipreview.ChooseResourceDialog; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +public class NlBooleanEditor extends AbstractTableCellEditor implements ActionListener { + private final JPanel myPanel; + private final FixedSizeButton myBrowseButton; + private final ThreeStateCheckBox myCheckbox; + + private NlProperty myProperty; + private Object myValue; + + public NlBooleanEditor() { + myPanel = new JPanel(new BorderLayout(SystemInfo.isMac ? 0 : 2, 0)); + + myCheckbox = new ThreeStateCheckBox(); + myPanel.add(myCheckbox, BorderLayout.LINE_START); + + myBrowseButton = new FixedSizeButton(myCheckbox); + myBrowseButton.setToolTipText(UIBundle.message("component.with.browse.button.browse.button.tooltip.text")); + myPanel.add(myBrowseButton, BorderLayout.LINE_END); + + myCheckbox.addActionListener(this); + myBrowseButton.addActionListener(this); + } + + @Override + public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) { + assert value instanceof NlProperty; + + myProperty = (NlProperty)value; + + Color fg = UIUtil.getTableSelectionForeground(); + Color bg = UIUtil.getTableSelectionBackground(); + + myPanel.setForeground(fg); + myPanel.setBackground(bg); + + for (int i = 0; i < myPanel.getComponentCount(); i++) { + Component comp = myPanel.getComponent(i); + comp.setForeground(fg); + comp.setBackground(bg); + } + + String propValue = myProperty.getValue(); + myValue = propValue; + ThreeStateCheckBox.State state = NlBooleanRenderer.getState(propValue); + myCheckbox.setState(state == null ? ThreeStateCheckBox.State.NOT_SELECTED : state); + + return myPanel; + } + + @Override + public Object getCellEditorValue() { + return myValue; + } + + @Override + public void actionPerformed(ActionEvent e) { + if (e.getSource() == myCheckbox) { + myValue = NlBooleanRenderer.getBoolean(myCheckbox.getState()); + stopCellEditing(); + } else if (e.getSource() == myBrowseButton) { + ChooseResourceDialog dialog = NlReferenceEditor.showResourceChooser(myProperty); + if (dialog.showAndGet()) { + myValue = dialog.getResourceName(); + stopCellEditing(); + } else { + cancelCellEditing(); + } + } + } +} diff --git a/designer/src/com/android/tools/idea/uibuilder/property/editors/NlEnumEditor.java b/designer/src/com/android/tools/idea/uibuilder/property/editors/NlEnumEditor.java new file mode 100644 index 00000000000..1eae24c874e --- /dev/null +++ b/designer/src/com/android/tools/idea/uibuilder/property/editors/NlEnumEditor.java @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * 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.android.tools.idea.uibuilder.property.editors; + +import com.android.tools.idea.uibuilder.property.NlProperty; +import com.intellij.openapi.ui.FixedSizeButton; +import com.intellij.openapi.util.SystemInfo; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.ui.UIBundle; +import com.intellij.ui.components.JBCheckBox; +import com.intellij.util.ArrayUtil; +import com.intellij.util.ui.AbstractTableCellEditor; +import org.jetbrains.android.dom.attrs.AttributeDefinition; +import org.jetbrains.android.uipreview.ChooseResourceDialog; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +public class NlEnumEditor extends AbstractTableCellEditor implements ActionListener { + private static final String UNSET = "<unset>"; + + private final JPanel myPanel; + private final JComboBox myCombo; + private final FixedSizeButton myBrowseButton; + + private NlProperty myProperty; + private Object myValue; + + public NlEnumEditor() { + myPanel = new JPanel(new BorderLayout(SystemInfo.isMac ? 0 : 2, 0)); + + myCombo = new JComboBox(); + myCombo.setEditable(true); + myPanel.add(myCombo, BorderLayout.CENTER); + + myBrowseButton = new FixedSizeButton(new JBCheckBox()); + myBrowseButton.setToolTipText(UIBundle.message("component.with.browse.button.browse.button.tooltip.text")); + myPanel.add(myBrowseButton, BorderLayout.LINE_END); + + myCombo.addActionListener(this); + myBrowseButton.addActionListener(this); + } + + @Override + public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) { + assert value instanceof NlProperty; + myProperty = (NlProperty)value; + + String propValue = StringUtil.notNullize(myProperty.getValue()); + myValue = propValue; + + AttributeDefinition definition = myProperty.getDefinition(); + String[] values = definition == null ? ArrayUtil.EMPTY_STRING_ARRAY : definition.getValues(); + + DefaultComboBoxModel model = new DefaultComboBoxModel(values); + model.insertElementAt(UNSET, 0); + if (model.getIndexOf(propValue) == -1) { + model.insertElementAt(propValue, 1); + } + model.setSelectedItem(propValue); + myCombo.setModel(model); + + return myPanel; + } + + @Override + public Object getCellEditorValue() { + return UNSET.equals(myValue) ? null : myValue; + } + + @Override + public void actionPerformed(ActionEvent e) { + if (e.getSource() == myBrowseButton) { + ChooseResourceDialog dialog = NlReferenceEditor.showResourceChooser(myProperty); + if (dialog.showAndGet()) { + myValue = dialog.getResourceName(); + stopCellEditing(); + } else { + cancelCellEditing(); + } + } + else if (e.getSource() == myCombo) { + myValue = myCombo.getModel().getSelectedItem(); + stopCellEditing(); + } + } +} diff --git a/designer/src/com/android/tools/idea/uibuilder/property/editors/NlPropertyEditors.java b/designer/src/com/android/tools/idea/uibuilder/property/editors/NlPropertyEditors.java new file mode 100644 index 00000000000..ff2c7918fdd --- /dev/null +++ b/designer/src/com/android/tools/idea/uibuilder/property/editors/NlPropertyEditors.java @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * 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.android.tools.idea.uibuilder.property.editors; + +import com.android.tools.idea.uibuilder.property.NlProperty; +import com.android.tools.idea.uibuilder.property.renderer.NlBooleanRenderer; +import org.jetbrains.android.dom.attrs.AttributeDefinition; +import org.jetbrains.android.dom.attrs.AttributeFormat; +import org.jetbrains.annotations.NotNull; + +import javax.swing.table.TableCellEditor; +import java.util.Set; + +public class NlPropertyEditors { + private static NlBooleanEditor ourBooleanEditor; + private static NlEnumEditor ourComboEditor; + private static NlReferenceEditor ourDefaultEditor; + + public static TableCellEditor get(@NotNull NlProperty property) { + AttributeDefinition definition = property.getDefinition(); + if (definition == null) { + // TODO: default to text editor + return null; + } + + Set<AttributeFormat> formats = definition.getFormats(); + if (formats.isEmpty()) { + // either we don't know the format or we support multiple formats + // TODO: default to text editor + return null; + } + + // TODO: there can be more than one format, we need to make this more customizable + if (formats.contains(AttributeFormat.Boolean)) { + return getBooleanEditor(); + } else if (formats.contains(AttributeFormat.Enum)) { + return getComboEditor(); + } + + return getDefaultEditor(); + } + + private static TableCellEditor getBooleanEditor() { + if (ourBooleanEditor == null) { + ourBooleanEditor = new NlBooleanEditor(); + } + + return ourBooleanEditor; + } + + private static TableCellEditor getComboEditor() { + if (ourComboEditor == null) { + ourComboEditor = new NlEnumEditor(); + } + + return ourComboEditor; + } + + private static TableCellEditor getDefaultEditor() { + if (ourDefaultEditor == null) { + ourDefaultEditor = new NlReferenceEditor(); + } + + return ourDefaultEditor; + } +} diff --git a/designer/src/com/android/tools/idea/uibuilder/property/editors/NlReferenceEditor.java b/designer/src/com/android/tools/idea/uibuilder/property/editors/NlReferenceEditor.java new file mode 100644 index 00000000000..a7e5405cf67 --- /dev/null +++ b/designer/src/com/android/tools/idea/uibuilder/property/editors/NlReferenceEditor.java @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * 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.android.tools.idea.uibuilder.property.editors; + +import com.android.resources.ResourceType; +import com.android.tools.idea.uibuilder.property.NlProperty; +import com.android.tools.idea.uibuilder.property.renderer.NlDefaultRenderer; +import com.intellij.android.designer.propertyTable.editors.ResourceEditor; +import com.intellij.openapi.editor.impl.EditorComponentImpl; +import com.intellij.openapi.editor.impl.EditorImpl; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.ui.FixedSizeButton; +import com.intellij.openapi.util.SystemInfo; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.ui.EditorTextField; +import com.intellij.ui.UIBundle; +import com.intellij.ui.components.JBCheckBox; +import com.intellij.ui.components.JBLabel; +import com.intellij.util.ui.AbstractTableCellEditor; +import org.jetbrains.android.dom.AndroidDomUtil; +import org.jetbrains.android.dom.attrs.AttributeDefinition; +import org.jetbrains.android.dom.attrs.AttributeFormat; +import org.jetbrains.android.uipreview.ChooseResourceDialog; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.*; +import java.util.EnumSet; +import java.util.Set; + +public class NlReferenceEditor extends AbstractTableCellEditor implements ActionListener { + private final JPanel myPanel; + private final JBLabel myLabel; + private final EditorTextField myEditorTextField; + private final FixedSizeButton myBrowseButton; + + private NlProperty myProperty; + private String myValue; + + public NlReferenceEditor() { + myPanel = new JPanel(new BorderLayout(SystemInfo.isMac ? 0 : 2, 0)); + + myLabel = new JBLabel(); + myPanel.add(myLabel, BorderLayout.LINE_START); + + myEditorTextField = new EditorTextField(); + myPanel.add(myEditorTextField, BorderLayout.CENTER); + + myBrowseButton = new FixedSizeButton(new JBCheckBox()); + myBrowseButton.setToolTipText(UIBundle.message("component.with.browse.button.browse.button.tooltip.text")); + myPanel.add(myBrowseButton, BorderLayout.LINE_END); + + myEditorTextField + .registerKeyboardAction(this, KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); + selectTextOnFocusGain(myEditorTextField); + + myBrowseButton.addActionListener(this); + } + + public static void selectTextOnFocusGain(EditorTextField textField) { + textField.addFocusListener(new FocusAdapter() { + @Override + public void focusGained(FocusEvent focusEvent) { + Object source = focusEvent.getSource(); + if (source instanceof EditorComponentImpl) { + EditorImpl editor = ((EditorComponentImpl)source).getEditor(); + editor.getSelectionModel().setSelection(0, editor.getDocument().getTextLength()); + } + } + }); + } + + @Override + public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) { + assert value instanceof NlProperty; + myProperty = (NlProperty)value; + + Icon icon = NlDefaultRenderer.getIcon(myProperty); + myLabel.setIcon(icon); + myLabel.setVisible(icon != null); + + String propValue = StringUtil.notNullize(myProperty.getValue()); + myValue = propValue; + myEditorTextField.setText(propValue); + + return myPanel; + } + + @Override + public Object getCellEditorValue() { + return myValue; + } + + @Override + public void actionPerformed(ActionEvent e) { + if (e.getSource() == myBrowseButton) { + ChooseResourceDialog dialog = showResourceChooser(myProperty); + if (dialog.showAndGet()) { + myValue = dialog.getResourceName(); + stopCellEditing(); + } else { + cancelCellEditing(); + } + } + else if (e.getSource() == myEditorTextField) { + myValue = myEditorTextField.getDocument().getText(); + stopCellEditing(); + } + } + + public static ChooseResourceDialog showResourceChooser(@NotNull NlProperty p) { + Module m = p.getComponent().getModel().getModule(); + AttributeDefinition definition = p.getDefinition(); + Set<AttributeFormat> formats = definition != null ? definition.getFormats() : EnumSet.allOf(AttributeFormat.class); + + // for some special known properties, we can narrow down the possible types (rather than the all encompassing reference type) + String type = definition != null ? AndroidDomUtil.SPECIAL_RESOURCE_TYPES.get(definition.getName()) : null; + ResourceType[] types = type == null ? ResourceEditor.convertTypes(formats) : new ResourceType[]{ResourceType.getEnum(type)}; + + return new ChooseResourceDialog(m, types, p.getValue(), p.getComponent().getTag()); + } +} diff --git a/designer/src/com/android/tools/idea/uibuilder/property/ptable/PTable.java b/designer/src/com/android/tools/idea/uibuilder/property/ptable/PTable.java index d983251632e..41e27556fd3 100644 --- a/designer/src/com/android/tools/idea/uibuilder/property/ptable/PTable.java +++ b/designer/src/com/android/tools/idea/uibuilder/property/ptable/PTable.java @@ -21,6 +21,7 @@ import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NotNull; import javax.swing.*; +import javax.swing.table.TableCellEditor; import javax.swing.table.TableCellRenderer; import javax.swing.table.TableModel; import java.awt.*; @@ -31,11 +32,20 @@ public class PTable extends JBTable { private final PNameRenderer myNameRenderer = new PNameRenderer(); private PTableModel myModel; + private int myMouseHoverRow; + private int myMouseHoverCol; + public PTable(@NotNull PTableModel model) { super(model); myModel = model; - setMaxItemsForSizeCalculation(20); + // since the row heights are uniform, there is no need to look at more than a few items + setMaxItemsForSizeCalculation(5); + + // When a label cannot be fully displayed, hovering over it results in a popup that extends beyond the + // cell bounds to show the full value. We don't need this feature as it'll end up covering parts of the + // cell we don't want covered. + setExpandableItemsEnabled(false); setShowColumns(false); setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN); @@ -50,6 +60,10 @@ public class PTable extends JBTable { setRowSelectionAllowed(true); addMouseListener(new MouseTableListener()); + + HoverListener hoverListener = new HoverListener(); + addMouseMotionListener(hoverListener); + addMouseListener(hoverListener); } @Override @@ -68,6 +82,17 @@ public class PTable extends JBTable { return value.getCellRenderer(); } + @Override + public TableCellEditor getCellEditor(int row, int column) { + PTableItem value = (PTableItem)getValueAt(row, column); + return value.getCellEditor(); + } + + public boolean isHover(int row, int col) { + return row == myMouseHoverRow && col == myMouseHoverCol; + } + + // Expand/Collapse group items if necessary private class MouseTableListener extends MouseAdapter { @Override public void mousePressed(MouseEvent e) { @@ -97,4 +122,43 @@ public class PTable extends JBTable { } } } + + // Repaint cells on mouse hover + private class HoverListener extends MouseAdapter { + private int myPreviousHoverRow = -1; + private int myPreviousHoverCol = -1; + + @Override + public void mouseMoved(MouseEvent e) { + myMouseHoverRow = rowAtPoint(e.getPoint()); + if (myMouseHoverRow >= 0) { + myMouseHoverCol = columnAtPoint(e.getPoint()); + } + + // remove hover from the previous cell + if (myPreviousHoverRow != -1 && (myPreviousHoverRow != myMouseHoverRow || myPreviousHoverCol != myMouseHoverCol)) { + repaint(getCellRect(myPreviousHoverRow, myPreviousHoverCol, true)); + myPreviousHoverRow = -1; + } + + if (myMouseHoverCol < 0) { + return; + } + + // repaint cell that has the hover + repaint(getCellRect(myMouseHoverRow, myMouseHoverCol, true)); + + myPreviousHoverRow = myMouseHoverRow; + myPreviousHoverCol = myMouseHoverCol; + } + + @Override + public void mouseExited(MouseEvent e) { + if (myMouseHoverRow != -1 && myMouseHoverCol != -1) { + Rectangle cellRect = getCellRect(myMouseHoverRow, 1, true); + myMouseHoverRow = myMouseHoverCol = -1; + repaint(cellRect); + } + } + } } diff --git a/designer/src/com/android/tools/idea/uibuilder/property/ptable/PTableItem.java b/designer/src/com/android/tools/idea/uibuilder/property/ptable/PTableItem.java index e339a3e8d30..627a3639bde 100644 --- a/designer/src/com/android/tools/idea/uibuilder/property/ptable/PTableItem.java +++ b/designer/src/com/android/tools/idea/uibuilder/property/ptable/PTableItem.java @@ -19,6 +19,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.table.DefaultTableCellRenderer; +import javax.swing.table.TableCellEditor; import javax.swing.table.TableCellRenderer; import java.util.Collections; import java.util.List; @@ -61,7 +62,18 @@ public abstract class PTableItem { public abstract String getName(); + public void setValue(Object value) { + } + public String getTooltipText() { return null; } + + public TableCellEditor getCellEditor() { + return null; + } + + public boolean isEditable(int col) { + return false; + } } diff --git a/designer/src/com/android/tools/idea/uibuilder/property/ptable/PTableModel.java b/designer/src/com/android/tools/idea/uibuilder/property/ptable/PTableModel.java index 1b231309216..5cd9f280dcf 100644 --- a/designer/src/com/android/tools/idea/uibuilder/property/ptable/PTableModel.java +++ b/designer/src/com/android/tools/idea/uibuilder/property/ptable/PTableModel.java @@ -48,6 +48,17 @@ public class PTableModel extends AbstractTableModel { return myItems.get(row); } + @Override + public void setValueAt(Object value, int row, int col) { + myItems.get(row).setValue(value); + fireTableCellUpdated(row, col); + } + + @Override + public boolean isCellEditable(int row, int col) { + return col == 1 && myItems.get(row).isEditable(col); + } + // TODO: if we want to support multiple levels of hierarchy, then this should // be updated to collapse children recursively public void collapse(int row) { diff --git a/designer/src/com/android/tools/idea/uibuilder/property/ptable/renderers/PNameRenderer.java b/designer/src/com/android/tools/idea/uibuilder/property/ptable/renderers/PNameRenderer.java index c4d0b713259..e546ecae99c 100644 --- a/designer/src/com/android/tools/idea/uibuilder/property/ptable/renderers/PNameRenderer.java +++ b/designer/src/com/android/tools/idea/uibuilder/property/ptable/renderers/PNameRenderer.java @@ -15,9 +15,7 @@ */ package com.android.tools.idea.uibuilder.property.ptable.renderers; -import com.android.annotations.VisibleForTesting; import com.android.tools.idea.uibuilder.property.ptable.PTableItem; -import com.intellij.openapi.util.Couple; import com.intellij.ui.ColoredTableCellRenderer; import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NotNull; @@ -34,14 +32,10 @@ public class PNameRenderer implements TableCellRenderer { myRenderer.clear(); PTableItem item = (PTableItem)value; - Component focusOwner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner(); - boolean tableHasFocus = focusOwner != null && SwingUtilities.isDescendingFrom(focusOwner, table); - - Color background = table.getBackground(); Icon icon = UIUtil.getTreeNodeIcon(item.isExpanded(), isSelected, cellHasFocus); myRenderer.getTableCellRendererComponent(table, value, isSelected, cellHasFocus, row, column); - myRenderer.setBackground(isSelected ? UIUtil.getTreeSelectionBackground(tableHasFocus) : background); + myRenderer.setBackground(isSelected ? UIUtil.getTableSelectionBackground() : table.getBackground()); int beforeIcon = getBeforeIconSpacing(getDepth(item), icon.getIconWidth()); int afterIcon = getAfterIconSpacing(icon.getIconWidth()); diff --git a/designer/src/com/android/tools/idea/uibuilder/property/renderer/NlAttributeRenderer.java b/designer/src/com/android/tools/idea/uibuilder/property/renderer/NlAttributeRenderer.java new file mode 100644 index 00000000000..f0547295801 --- /dev/null +++ b/designer/src/com/android/tools/idea/uibuilder/property/renderer/NlAttributeRenderer.java @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * 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.android.tools.idea.uibuilder.property.renderer; + +import com.android.tools.idea.uibuilder.property.NlProperty; +import com.android.tools.idea.uibuilder.property.ptable.PTable; +import com.intellij.openapi.ui.FixedSizeButton; +import com.intellij.openapi.util.SystemInfo; +import com.intellij.ui.UIBundle; +import com.intellij.ui.components.JBCheckBox; +import com.intellij.util.ui.UIUtil; +import org.jetbrains.android.dom.attrs.AttributeFormat; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; +import javax.swing.table.TableCellRenderer; +import java.awt.*; +import java.util.Set; + +public abstract class NlAttributeRenderer implements NlPropertyRenderer, TableCellRenderer { + private final JPanel myPanel; + private final FixedSizeButton myBrowseButton; + + public NlAttributeRenderer() { + myPanel = new JPanel(new BorderLayout(SystemInfo.isMac ? 0 : 2, 0)); + + myBrowseButton = new FixedSizeButton(new JBCheckBox()); + myBrowseButton.setToolTipText(UIBundle.message("component.with.browse.button.browse.button.tooltip.text")); + myPanel.add(myBrowseButton, BorderLayout.LINE_END); + } + + public JPanel getContentPanel() { + return myPanel; + } + + @Override + public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int col) { + assert value instanceof NlProperty; + assert table instanceof PTable; + + Color fg, bg; + if (isSelected) { + fg = UIUtil.getTableSelectionForeground(); + bg = UIUtil.getTableSelectionBackground(); + } + else { + fg = UIUtil.getTableForeground(); + bg = UIUtil.getTableBackground(); + } + + myPanel.setForeground(fg); + myPanel.setBackground(bg); + + for (int i = 0; i < myPanel.getComponentCount(); i++) { + Component comp = myPanel.getComponent(i); + comp.setForeground(fg); + comp.setBackground(bg); + } + + boolean hover = ((PTable)table).isHover(row, col); + myBrowseButton.setVisible(hover); + + customizeRenderContent(table, (NlProperty)value, isSelected, hasFocus, row, col); + + return myPanel; + } + + public abstract void customizeRenderContent(@NotNull JTable table, + @NotNull NlProperty p, + boolean selected, + boolean hasFocus, + int row, + int col); + + @Override + public abstract boolean canRender(@NotNull NlProperty p, @NotNull Set<AttributeFormat> formats); +} diff --git a/designer/src/com/android/tools/idea/uibuilder/property/renderer/NlBooleanRenderer.java b/designer/src/com/android/tools/idea/uibuilder/property/renderer/NlBooleanRenderer.java index 321dd4b38e3..d0c841c56d8 100644 --- a/designer/src/com/android/tools/idea/uibuilder/property/renderer/NlBooleanRenderer.java +++ b/designer/src/com/android/tools/idea/uibuilder/property/renderer/NlBooleanRenderer.java @@ -15,29 +15,97 @@ */ package com.android.tools.idea.uibuilder.property.renderer; +import com.android.SdkConstants; +import com.android.ide.common.resources.ResourceResolver; import com.android.tools.idea.uibuilder.property.NlProperty; +import com.intellij.ui.SimpleColoredComponent; +import com.intellij.ui.SimpleTextAttributes; +import com.intellij.util.ui.ThreeStateCheckBox; import com.intellij.util.ui.UIUtil; +import org.jetbrains.android.dom.attrs.AttributeFormat; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import javax.swing.*; -import javax.swing.table.TableCellRenderer; import java.awt.*; +import java.util.Set; + +public class NlBooleanRenderer extends NlAttributeRenderer { + private final ThreeStateCheckBox myCheckbox; + private final SimpleColoredComponent myLabel; + + public NlBooleanRenderer() { + JPanel panel = getContentPanel(); + + myCheckbox = new ThreeStateCheckBox(); + panel.add(myCheckbox, BorderLayout.LINE_START); + + myLabel = new SimpleColoredComponent(); + panel.add(myLabel, BorderLayout.CENTER); + } -public class NlBooleanRenderer extends JCheckBox implements TableCellRenderer { @Override - public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { - assert value instanceof NlProperty; - NlProperty p = (NlProperty)value; + public void customizeRenderContent(@NotNull JTable table, @NotNull NlProperty p, boolean selected, boolean hasFocus, int row, int col) { + myCheckbox.setEnabled(true); + myLabel.clear(); + + String propValue = p.getValue(); + ThreeStateCheckBox.State state = getState(propValue); + if (state == null && propValue != null) { + myLabel.append(propValue, modifyAttributes(selected, SimpleTextAttributes.GRAY_ITALIC_ATTRIBUTES)); + + // TODO: what happens if this is configuration dependent? (in theory, those should be edited in the theme editor) + ResourceResolver resourceResolver = p.getComponent().getModel().getConfiguration().getResourceResolver(); + if (resourceResolver != null) { + String resolvedValue = resourceResolver.findResValue(propValue, false).getValue(); + state = getState(resolvedValue); + } + } + + if (state != null) { + myCheckbox.setEnabled(true); + myCheckbox.setState(state); + } else { + myCheckbox.setEnabled(false); + } + } + + @NotNull + private static SimpleTextAttributes modifyAttributes(boolean selected, SimpleTextAttributes attributes) { + return selected ? new SimpleTextAttributes(attributes.getStyle(), UIUtil.getTableSelectionForeground()) : attributes; + } - if (isSelected) { - setForeground(UIUtil.getTableSelectionForeground()); - setBackground(UIUtil.getTableSelectionBackground()); + @Nullable + public static ThreeStateCheckBox.State getState(@Nullable String s) { + if (s == null) { + return ThreeStateCheckBox.State.DONT_CARE; + } + else if (SdkConstants.VALUE_TRUE.equalsIgnoreCase(s)) { + return ThreeStateCheckBox.State.SELECTED; + } + else if (SdkConstants.VALUE_FALSE.equalsIgnoreCase(s)) { + return ThreeStateCheckBox.State.NOT_SELECTED; } else { - setForeground(UIUtil.getTableForeground()); - setBackground(UIUtil.getTableBackground()); + return null; } + } - setSelected(Boolean.valueOf(p.getValue())); - return this; + @Nullable + public static Boolean getBoolean(ThreeStateCheckBox.State state) { + switch (state) { + case DONT_CARE: + return null; + case SELECTED: + return Boolean.TRUE; + case NOT_SELECTED: + default: + return Boolean.FALSE; + } + } + + @Override + public boolean canRender(@NotNull NlProperty p, @NotNull Set<AttributeFormat> formats) { + return formats.contains(AttributeFormat.Boolean); } } diff --git a/designer/src/com/android/tools/idea/uibuilder/property/renderer/NlDefaultRenderer.java b/designer/src/com/android/tools/idea/uibuilder/property/renderer/NlDefaultRenderer.java new file mode 100644 index 00000000000..93b56940743 --- /dev/null +++ b/designer/src/com/android/tools/idea/uibuilder/property/renderer/NlDefaultRenderer.java @@ -0,0 +1,164 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * 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.android.tools.idea.uibuilder.property.renderer; + +import com.android.SdkConstants; +import com.android.annotations.VisibleForTesting; +import com.android.ide.common.rendering.api.ResourceValue; +import com.android.ide.common.resources.ResourceResolver; +import com.android.resources.ResourceType; +import com.android.tools.idea.rendering.GutterIconCache; +import com.android.tools.idea.rendering.ResourceHelper; +import com.android.tools.idea.uibuilder.property.NlProperty; +import com.intellij.ui.SimpleColoredComponent; +import com.intellij.ui.SimpleTextAttributes; +import com.intellij.util.ui.ColorIcon; +import org.jetbrains.android.AndroidColorAnnotator; +import org.jetbrains.android.dom.attrs.AttributeFormat; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.awt.*; +import java.io.File; +import java.util.Set; + +public class NlDefaultRenderer extends NlAttributeRenderer implements NlPropertyRenderer { + public static final int ICON_SIZE = 14; + private final SimpleColoredComponent myLabel; + + public NlDefaultRenderer() { + JPanel panel = getContentPanel(); + + myLabel = new SimpleColoredComponent(); + panel.add(myLabel, BorderLayout.CENTER); + } + + @Override + public void customizeRenderContent(@NotNull JTable table, @NotNull NlProperty p, boolean selected, boolean hasFocus, int row, int col) { + myLabel.clear(); + customize(p, col); + } + + + @VisibleForTesting + void customize(NlProperty property, int column) { + if (column == 0) { + appendName(property); + } else { + appendValue(property); + } + } + + private void appendValue(@NotNull NlProperty property) { + String value = property.getValue(); + + Icon icon = getIcon(property); + if (icon != null) { + myLabel.setIcon(icon); + } + + if (value == null) { + value = ""; + } + myLabel.append(value, SimpleTextAttributes.REGULAR_ATTRIBUTES); + myLabel.setToolTipText(value); + } + + @Nullable + public static Icon getIcon(@NotNull NlProperty property) { + String text = property.getValue(); + if (text == null) { + return null; + } + + if (isColorValue(text)) { + return getColorIcon(text); + } + + ResourceResolver resolver = property.getComponent().getModel().getConfiguration().getResourceResolver(); + if (resolver == null) { + return null; + } + + if (text.startsWith(SdkConstants.COLOR_RESOURCE_PREFIX) + || text.startsWith(SdkConstants.ANDROID_COLOR_RESOURCE_PREFIX)) { + return getColorIcon(resolver, property, text); + } + + if (text.startsWith(SdkConstants.DRAWABLE_PREFIX) || + text.startsWith(SdkConstants.ANDROID_DRAWABLE_PREFIX) || + text.startsWith(SdkConstants.MIPMAP_PREFIX)) { + return getDrawableIcon(resolver, property, text); + } + + return null; + } + + @Nullable + private static Icon getDrawableIcon(@NotNull ResourceResolver resolver, @NotNull NlProperty property, @NotNull String value) { + boolean isFrameworkRes = value.startsWith(SdkConstants.ANDROID_PREFIX); + ResourceType type = value.startsWith(SdkConstants.MIPMAP_PREFIX) ? ResourceType.MIPMAP : ResourceType.DRAWABLE; + ResourceValue drawable = resolver.resolveValue(type, property.getName(), value, isFrameworkRes); + if (drawable == null) { + return null; + } + + File file = AndroidColorAnnotator.pickBestBitmap(ResourceHelper.resolveDrawable(resolver, drawable)); + return file == null ? null : GutterIconCache.getInstance().getIcon(file.getPath()); + } + + @Nullable + private static Icon getColorIcon(@NotNull ResourceResolver resolver, @NotNull NlProperty property, @NotNull String value) { + boolean isFrameworkRes = value.startsWith(SdkConstants.ANDROID_COLOR_RESOURCE_PREFIX); + ResourceValue resourceValue = resolver.resolveValue(ResourceType.COLOR, property.getName(), value, isFrameworkRes); + if (resourceValue == null) { + return null; + } + + String resolvedValue = resourceValue.getValue(); + if (isColorValue(resolvedValue)) { + return getColorIcon(resolvedValue); + } + + return null; + } + + private static boolean isColorValue(@Nullable String value) { + return value != null && value.startsWith("#") && value.matches("#\\p{XDigit}+"); + } + + @Nullable + private static Icon getColorIcon(@NotNull String hexColor) { + Color color = ResourceHelper.parseColor(hexColor); + return color == null ? null : new ColorIcon(ICON_SIZE, color, true); + } + + private void appendName(@NotNull NlProperty property) { + myLabel.append(property.getName()); + myLabel.setToolTipText(property.getTooltipText()); + } + + @Override + public boolean canRender(@NotNull NlProperty p, @NotNull Set<AttributeFormat> formats) { + return true; + } + + @VisibleForTesting + SimpleColoredComponent getLabel() { + return myLabel; + } +} diff --git a/designer/src/com/android/tools/idea/uibuilder/property/renderer/NlPropertyRenderer.java b/designer/src/com/android/tools/idea/uibuilder/property/renderer/NlPropertyRenderer.java index 32aa55e17dc..56f3f4e505a 100644 --- a/designer/src/com/android/tools/idea/uibuilder/property/renderer/NlPropertyRenderer.java +++ b/designer/src/com/android/tools/idea/uibuilder/property/renderer/NlPropertyRenderer.java @@ -15,46 +15,12 @@ */ package com.android.tools.idea.uibuilder.property.renderer; -import com.android.annotations.VisibleForTesting; import com.android.tools.idea.uibuilder.property.NlProperty; -import com.intellij.ui.ColoredTableCellRenderer; -import com.intellij.ui.SimpleTextAttributes; +import org.jetbrains.android.dom.attrs.AttributeFormat; import org.jetbrains.annotations.NotNull; -import javax.swing.*; +import java.util.Set; -public class NlPropertyRenderer extends ColoredTableCellRenderer { - @Override - protected void customizeCellRenderer(JTable table, Object value, boolean selected, boolean hasFocus, int row, int column) { - if (!(value instanceof NlProperty)) { - return; - } - - setPaintFocusBorder(false); - setFocusBorderAroundIcon(true); - - customize((NlProperty)value, column); - } - - @VisibleForTesting - void customize(NlProperty property, int column) { - if (column == 0) { - appendName(property); - } else { - appendValue(property); - } - } - - private void appendValue(@NotNull NlProperty property) { - String value = property.getValue(); - if (value == null) { - value = ""; - } - append(value, SimpleTextAttributes.REGULAR_BOLD_ATTRIBUTES); - } - - private void appendName(@NotNull NlProperty property) { - append(property.getName()); - setToolTipText(property.getTooltipText()); - } +public interface NlPropertyRenderer { + boolean canRender(@NotNull NlProperty p, @NotNull Set<AttributeFormat> formats); } diff --git a/designer/src/com/android/tools/idea/uibuilder/property/renderer/NlPropertyRenderers.java b/designer/src/com/android/tools/idea/uibuilder/property/renderer/NlPropertyRenderers.java index 9e98312b762..f722ed88b68 100644 --- a/designer/src/com/android/tools/idea/uibuilder/property/renderer/NlPropertyRenderers.java +++ b/designer/src/com/android/tools/idea/uibuilder/property/renderer/NlPropertyRenderers.java @@ -24,7 +24,7 @@ import javax.swing.table.TableCellRenderer; import java.util.Set; public class NlPropertyRenderers { - private static NlPropertyRenderer ourDefaultRenderer; + private static NlDefaultRenderer ourDefaultRenderer; private static NlBooleanRenderer ourBooleanRenderer; @NotNull @@ -36,14 +36,17 @@ public class NlPropertyRenderers { Set<AttributeFormat> formats = definition.getFormats(); if (formats.size() == 1 && formats.contains(AttributeFormat.Boolean)) { - return getBooleanRenderer(); + NlBooleanRenderer renderer = getBooleanRenderer(); + if (renderer.canRender(p, formats)) { + return renderer; + } } return getDefaultRenderer(); } @NotNull - private static TableCellRenderer getBooleanRenderer() { + private static NlBooleanRenderer getBooleanRenderer() { if (ourBooleanRenderer == null) { ourBooleanRenderer = new NlBooleanRenderer(); } @@ -52,9 +55,9 @@ public class NlPropertyRenderers { } @NotNull - private static TableCellRenderer getDefaultRenderer() { + private static NlDefaultRenderer getDefaultRenderer() { if (ourDefaultRenderer == null) { - ourDefaultRenderer = new NlPropertyRenderer(); + ourDefaultRenderer = new NlDefaultRenderer(); } return ourDefaultRenderer; diff --git a/designer/testSrc/com/android/tools/idea/uibuilder/property/renderer/NlPropertyRendererTest.java b/designer/testSrc/com/android/tools/idea/uibuilder/property/renderer/NlDefaultRendererTest.java index c01db776254..3ed1b93fd4b 100644 --- a/designer/testSrc/com/android/tools/idea/uibuilder/property/renderer/NlPropertyRendererTest.java +++ b/designer/testSrc/com/android/tools/idea/uibuilder/property/renderer/NlDefaultRendererTest.java @@ -27,7 +27,7 @@ import org.jetbrains.annotations.NotNull; import java.util.List; -public class NlPropertyRendererTest extends LayoutTestCase { +public class NlDefaultRendererTest extends LayoutTestCase { public void testSimple() { @Language("XML") String source = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" + @@ -41,7 +41,7 @@ public class NlPropertyRendererTest extends LayoutTestCase { List<NlProperty> properties = NlProperties.getInstance().getProperties(MockNlComponent.create(subTags[0])); - NlPropertyRenderer renderer = new NlPropertyRenderer(); + NlDefaultRenderer renderer = new NlDefaultRenderer(); NlProperty property = NlPropertiesTest.getPropertyByName(properties, "id"); validateRendering(renderer, property, "id", "@+id/textView"); @@ -53,16 +53,16 @@ public class NlPropertyRendererTest extends LayoutTestCase { validateRendering(renderer, property, "focusable", ""); } - private static void validateRendering(@NotNull NlPropertyRenderer renderer, + private static void validateRendering(@NotNull NlDefaultRenderer renderer, @NotNull NlProperty property, @NotNull String name, @NotNull String value) { - renderer.clear(); + renderer.getLabel().clear(); renderer.customize(property, 0); - assertEquals(name, renderer.getCharSequence(true)); + assertEquals(name, renderer.getLabel().getCharSequence(true)); - renderer.clear(); + renderer.getLabel().clear(); renderer.customize(property, 1); - assertEquals(value, renderer.getCharSequence(true)); + assertEquals(value, renderer.getLabel().getCharSequence(true)); } } |