/* * Copyright (C) 2014 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.wizard; import com.google.common.base.Function; import com.google.common.base.Functions; import com.google.common.base.Objects; import com.google.common.collect.ImmutableList; import com.google.common.collect.Sets; import com.intellij.openapi.Disposable; import com.intellij.openapi.util.text.StringUtil; import com.intellij.ui.HideableDecorator; import com.intellij.ui.IdeBorderFactory; import com.intellij.ui.JBColor; import com.intellij.ui.components.JBList; import com.intellij.util.ArrayUtil; import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; import javax.swing.border.Border; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import javax.swing.plaf.basic.BasicHTML; import javax.swing.text.View; import java.awt.*; import java.awt.image.BufferedImage; import java.util.List; import java.util.Set; import static com.android.tools.idea.wizard.WizardConstants.SELECTED_MODULE_TYPE_KEY; /** * This step allows the user to select which type of module they want to create. */ public class ChooseModuleTypeStep extends DynamicWizardStepWithHeaderAndDescription { private final Iterable<ModuleTemplateProvider> myModuleTypesProviders; private JPanel myPanel; private JBList myModuleTypeList; private ASGallery<ModuleTemplate> myFormFactorGallery; private JPanel myTypeListPlaceholder; private JPanel myModulesPanel; private boolean myIsSynchronizingSelection = false; public ChooseModuleTypeStep(Iterable<ModuleTemplateProvider> moduleTypesProviders, @Nullable Disposable parentDisposable) { super("Choose Module Type", "Select an option below to create your new module", null, parentDisposable); HideableDecorator decorator = new HideableDecorator(myTypeListPlaceholder, "More Modules", false); decorator.setContentComponent(myModulesPanel); decorator.setOn(false); myModuleTypeList.setCellRenderer(new TemplateListCellRenderer()); myModuleTypesProviders = moduleTypesProviders; myModuleTypeList.setBorder(BorderFactory.createLineBorder(UIUtil.getBorderColor())); myFormFactorGallery.setBorder(BorderFactory.createLineBorder(UIUtil.getBorderColor())); setBodyComponent(myPanel); } @Nullable @Contract("null->null") public static Image iconToImage(@Nullable Icon icon) { if (icon == null) { return null; } else { BufferedImage image = UIUtil.createImage(icon.getIconWidth(), icon.getIconHeight(), BufferedImage.TYPE_4BYTE_ABGR); Graphics2D graphics = image.createGraphics(); graphics.setBackground(JBColor.background()); graphics.setColor(JBColor.background()); icon.paintIcon(null, graphics, 0, 0); return image; } } @Override public void init() { super.init(); ImmutableList.Builder<ModuleTemplate> galleryTemplates = ImmutableList.builder(); ImmutableList.Builder<ModuleTemplate> extrasTemplates = ImmutableList.builder(); Set<FormFactorUtils.FormFactor> formFactorSet = Sets.newHashSet(); for (ModuleTemplateProvider provider : myModuleTypesProviders) { for (ModuleTemplate moduleTemplate : provider.getModuleTemplates()) { if (moduleTemplate.isGalleryModuleType()) { galleryTemplates.add(moduleTemplate); } else { extrasTemplates.add(moduleTemplate); } FormFactorUtils.FormFactor formFactor = moduleTemplate.getFormFactor(); if (formFactor != null) { formFactorSet.add(formFactor); } } } for (final FormFactorUtils.FormFactor formFactor : formFactorSet) { registerValueDeriver(FormFactorUtils.getInclusionKey(formFactor), new ValueDeriver<Boolean>() { @Nullable @Override public Boolean deriveValue(@NotNull ScopedStateStore state, @Nullable ScopedStateStore.Key changedKey, @Nullable Boolean currentValue) { ModuleTemplate moduleTemplate = myState.get(SELECTED_MODULE_TYPE_KEY); return moduleTemplate != null && Objects.equal(formFactor, moduleTemplate.getFormFactor()); } }); } List<ModuleTemplate> galleryTemplatesList = galleryTemplates.build(); List<ModuleTemplate> extrasTemplatesList = extrasTemplates.build(); myModuleTypeList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); myModuleTypeList.setModel(JBList.createDefaultListModel(ArrayUtil.toObjectArray(extrasTemplatesList))); myFormFactorGallery.setModel(JBList.createDefaultListModel(ArrayUtil.toObjectArray(galleryTemplatesList))); ModuleTypeBinding binding = new ModuleTypeBinding(); register(SELECTED_MODULE_TYPE_KEY, myPanel, binding); myModuleTypeList.addListSelectionListener(new ModuleTypeSelectionListener(true)); myFormFactorGallery.addListSelectionListener(new ModuleTypeSelectionListener(false)); if (!galleryTemplatesList.isEmpty()) { myState.put(SELECTED_MODULE_TYPE_KEY, galleryTemplatesList.get(0)); } } @Override public boolean commitStep() { ModuleTemplate selected = myState.get(SELECTED_MODULE_TYPE_KEY); if (selected != null) { selected.updateWizardStateOnSelection(myState); } return true; } @NotNull @Override public String getStepName() { return "Choose Module Type Step"; } @Override public JComponent getPreferredFocusedComponent() { ModuleTemplate moduleTemplate = myState.get(SELECTED_MODULE_TYPE_KEY); return moduleTemplate == null || moduleTemplate.isGalleryModuleType() ? myFormFactorGallery : myModuleTypeList; } private void createUIComponents() { myFormFactorGallery = new ASGallery<ModuleTemplate>(JBList.createDefaultListModel(), new Function<ModuleTemplate, Image>() { @Override public Image apply(ModuleTemplate input) { return iconToImage(input.getIcon()); } }, Functions.toStringFunction(), new Dimension(150, 150)); } private static class TemplateListCellRenderer implements ListCellRenderer { private final JPanel myPanel = new JPanel(new BorderLayout(32, 0)); private final JLabel myDescriptionLabel = new JLabel(); private JLabel myLabel = new JLabel(); public TemplateListCellRenderer() { myLabel.setFont(UIUtil.getListFont()); myDescriptionLabel.setFont(UIUtil.getToolTipFont()); myDescriptionLabel.setVerticalAlignment(SwingConstants.TOP); myPanel.add(myLabel, BorderLayout.NORTH); myPanel.add(myDescriptionLabel, BorderLayout.CENTER); } private static int getInsetsWidth(Insets insets) { return insets.left + insets.right; } @Override public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { Color bg = UIUtil.getListBackground(); Color fg = UIUtil.getListForeground(); Color descriptionFg = UIUtil.getInactiveTextColor(); Border border = BorderFactory.createCompoundBorder(UIUtil.getTableFocusCellHighlightBorder(), IdeBorderFactory.createEmptyBorder(5)); if (isSelected) { bg = UIUtil.getListSelectionBackground(); fg = UIUtil.getListSelectionForeground(); descriptionFg = fg; } if (!cellHasFocus) { border = IdeBorderFactory.createEmptyBorder(border.getBorderInsets(myLabel)); } myPanel.setBorder(border); if (value instanceof ModuleTemplate) { String name = ((ModuleTemplate)value).getName(); myLabel.setText(name); String description = ((ModuleTemplate)value).getDescription(); if (!StringUtil.isEmpty(description) && !BasicHTML.isHTMLString(description)) { description = "<html>" + description + "</html>"; } if (!StringUtil.isEmpty(description)) { View htmlView = BasicHTML.createHTMLView(myDescriptionLabel, description); int width = 630 - (getInsetsWidth(border.getBorderInsets(myPanel)) + getInsetsWidth(list.getInsets())); htmlView.setSize(width, 500); int preferredSpan = (int)htmlView.getPreferredSpan(View.Y_AXIS); myDescriptionLabel.setPreferredSize(new Dimension(width, preferredSpan)); } myDescriptionLabel.setText(description); } myLabel.setForeground(fg); myDescriptionLabel.setForeground(descriptionFg); myPanel.setBackground(bg); return myPanel; } } private class ModuleTypeBinding extends ComponentBinding<ModuleTemplate, JPanel> { @Override public void setValue(@Nullable ModuleTemplate newValue, @NotNull JPanel component) { if (newValue == null) { myModuleTypeList.clearSelection(); myFormFactorGallery.setSelectedElement(null); } else if (!newValue.isGalleryModuleType()) { myModuleTypeList.setSelectedValue(newValue, true); myFormFactorGallery.setSelectedElement(null); } else { myModuleTypeList.clearSelection(); myFormFactorGallery.setSelectedElement(newValue); } } @Nullable @Override public ModuleTemplate getValue(@NotNull JPanel component) { ModuleTemplate moduleTemplate = myFormFactorGallery.getSelectedElement(); if (moduleTemplate == null) { moduleTemplate = (ModuleTemplate)myModuleTypeList.getSelectedValue(); } return moduleTemplate; } } private class ModuleTypeSelectionListener implements ListSelectionListener { private final boolean myGallery; public ModuleTypeSelectionListener(boolean gallery) { myGallery = gallery; } @Override public void valueChanged(ListSelectionEvent e) { if (myIsSynchronizingSelection) { return; } myIsSynchronizingSelection = true; if (myGallery) { myFormFactorGallery.setSelectedElement(null); } else { myModuleTypeList.clearSelection(); } myIsSynchronizingSelection = false; saveState(myPanel); } } }