com.android.tools.idea.uibuilder.property.NlPropertiesManager.java Source code

Java tutorial

Introduction

Here is the source code for com.android.tools.idea.uibuilder.property.NlPropertiesManager.java

Source

/*
 * Copyright (C) 2016 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.android.tools.adtui.workbench.ToolContent;
import com.android.tools.idea.uibuilder.api.ViewHandler;
import com.android.tools.idea.uibuilder.model.ModelListener;
import com.android.tools.idea.uibuilder.model.NlComponent;
import com.android.tools.idea.uibuilder.model.NlModel;
import com.android.tools.idea.uibuilder.property.editors.NlPropertyEditors;
import com.android.tools.idea.uibuilder.surface.DesignSurface;
import com.android.tools.idea.uibuilder.surface.DesignSurfaceListener;
import com.android.tools.idea.uibuilder.surface.ScreenView;
import com.android.util.PropertiesMap;
import com.google.common.collect.ImmutableTable;
import com.google.common.collect.Table;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.project.Project;
import com.intellij.ui.components.JBLoadingPanel;
import com.intellij.util.Alarm;
import com.intellij.util.ui.UIUtil;
import com.intellij.util.ui.update.MergingUpdateQueue;
import com.intellij.util.ui.update.Update;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import javax.swing.*;
import java.awt.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

public class NlPropertiesManager implements ToolContent<DesignSurface>, DesignSurfaceListener, ModelListener {
    public final static int UPDATE_DELAY_MSECS = 250;

    private final Project myProject;
    private final JBLoadingPanel myLoadingPanel;
    private final NlPropertiesPanel myPropertiesPanel;
    private final NlPropertyEditors myEditors;

    @Nullable
    private DesignSurface mySurface;
    @Nullable
    private ScreenView myScreenView;

    private MergingUpdateQueue myUpdateQueue;
    private boolean myFirstLoad = true;
    private boolean myLoading;

    public NlPropertiesManager(@NotNull Project project, @Nullable DesignSurface designSurface) {
        myProject = project;
        myLoadingPanel = new JBLoadingPanel(new BorderLayout(), project, 20);
        myEditors = NlPropertyEditors.getInstance(project);
        myPropertiesPanel = new NlPropertiesPanel(this, this);
        myLoadingPanel.add(myPropertiesPanel);
        setToolContext(designSurface);
    }

    // TODO:
    public void activatePropertySheet() {
        myPropertiesPanel.activatePropertySheet();
    }

    // TODO:
    public void activateInspector() {
        myPropertiesPanel.activateInspector();
    }

    @Override
    public void setToolContext(@Nullable DesignSurface designSurface) {
        if (designSurface == mySurface) {
            return;
        }

        if (mySurface != null) {
            mySurface.removeListener(this);
        }

        mySurface = designSurface;
        if (mySurface == null) {
            setScreenView(null);
        } else {
            mySurface.addListener(this);
            ScreenView screenView = mySurface.getCurrentScreenView();
            setScreenView(screenView);
            List<NlComponent> selection = screenView != null ? screenView.getSelectionModel().getSelection()
                    : Collections.emptyList();
            componentSelectionChanged(mySurface, selection);
        }
    }

    @NotNull
    @Override
    public JComponent getComponent() {
        return myLoadingPanel;
    }

    @NotNull
    @Override
    public JComponent getFocusedComponent() {
        return myPropertiesPanel;
    }

    @NotNull
    @Override
    public List<AnAction> getGearActions() {
        return Collections.emptyList();
    }

    @NotNull
    @Override
    public List<AnAction> getAdditionalActions() {
        return Collections.singletonList(new ViewAllPropertiesAction(myPropertiesPanel));
    }

    @Override
    public void registerCloseAutoHideWindow(@NotNull Runnable runnable) {
    }

    @Override
    public boolean supportsFiltering() {
        return true;
    }

    @Override
    public void setFilter(@NotNull String filter) {
        myPropertiesPanel.setFilter(filter);
    }

    @Nullable
    public DesignSurface getDesignSurface() {
        return mySurface;
    }

    private void setScreenView(@Nullable ScreenView screenView) {
        if (screenView == myScreenView) {
            return;
        }

        if (myScreenView != null) {
            myScreenView.getModel().removeListener(this);
        }

        myScreenView = screenView;
        if (myScreenView != null) {
            myScreenView.getModel().addListener(this);
        }
    }

    @NotNull
    public Project getProject() {
        return myProject;
    }

    @NotNull
    public NlPropertyEditors getPropertyEditors() {
        return myEditors;
    }

    @Nullable
    private MergingUpdateQueue getUpdateQueue() {
        ApplicationManager.getApplication().assertIsDispatchThread();

        if (myUpdateQueue == null) {
            myUpdateQueue = new MergingUpdateQueue("android.layout.propertysheet", UPDATE_DELAY_MSECS, true, null,
                    mySurface, null, Alarm.ThreadToUse.SWING_THREAD);
        }
        return myUpdateQueue;
    }

    private void setSelectedComponents(@NotNull List<NlComponent> components,
            @Nullable Runnable postUpdateRunnable) {
        // 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(() -> {
            Table<String, String, NlPropertyItem> properties = !components.isEmpty()
                    ? NlProperties.getInstance().getProperties(components)
                    : ImmutableTable.of();

            UIUtil.invokeLaterIfNeeded(() -> {
                if (myProject.isDisposed()) {
                    return;
                }
                myPropertiesPanel.setItems(components, properties, this);
                if (postUpdateRunnable != null) {
                    myLoading = false;
                    postUpdateRunnable.run();
                }
            });
        });
    }

    @NotNull
    public PropertiesMap getDefaultProperties(@NotNull List<NlComponent> components) {
        if (components.isEmpty()) {
            return PropertiesMap.EMPTY_MAP;
        }
        if (mySurface == null) {
            return PropertiesMap.EMPTY_MAP;
        }
        ScreenView view = mySurface.getCurrentScreenView();
        if (view == null) {
            return PropertiesMap.EMPTY_MAP;
        }
        Map<Object, PropertiesMap> map = view.getModel().getDefaultProperties();
        List<PropertiesMap> propertiesMaps = new ArrayList<>(components.size());
        for (NlComponent component : components) {
            PropertiesMap propertiesMap = map.get(component.getSnapshot());
            if (propertiesMap == null) {
                return PropertiesMap.EMPTY_MAP;
            }
            propertiesMaps.add(propertiesMap);
        }
        PropertiesMap first = propertiesMaps.get(0);
        if (propertiesMaps.size() == 1) {
            return first;
        }
        PropertiesMap commonProperties = new PropertiesMap();
        for (Map.Entry<String, PropertiesMap.Property> property : first.entrySet()) {
            boolean include = true;
            for (int index = 1; index < propertiesMaps.size(); index++) {
                PropertiesMap other = propertiesMaps.get(index);
                if (!property.getValue().equals(other.get(property.getKey()))) {
                    include = false;
                    break;
                }
            }
            if (include) {
                commonProperties.put(property.getKey(), property.getValue());
            }
        }
        return commonProperties;
    }

    public void setValue(@NotNull NlProperty property, @Nullable String value) {
        property.setValue(value);

        // TODO: refresh all custom inspectors
    }

    public void updateSelection() {
        if (mySurface == null || myScreenView == null) {
            return;
        }
        List<NlComponent> selection = myScreenView.getModel().getSelectionModel().getSelection();
        componentSelectionChanged(mySurface, selection);
    }

    // ---- Implements DesignSurfaceListener ----

    @Override
    public void componentSelectionChanged(@NotNull DesignSurface surface,
            @NotNull final List<NlComponent> newSelection) {
        if (surface != mySurface) {
            return;
        }

        MergingUpdateQueue queue = getUpdateQueue();
        if (queue == null) {
            return;
        }

        if (!newSelection.isEmpty() && myFirstLoad) {
            myFirstLoad = false;
            myLoadingPanel.startLoading();
        }
        myLoading = true;
        queue.queue(new Update("updateProperties") {
            @Override
            public void run() {
                setSelectedComponents(newSelection, myLoadingPanel::stopLoading);
            }

            @Override
            public boolean canEat(Update update) {
                return true;
            }
        });
    }

    @Override
    public void screenChanged(@NotNull DesignSurface surface, @Nullable ScreenView screenView) {
    }

    @Override
    public void modelChanged(@NotNull DesignSurface surface, @Nullable NlModel model) {
    }

    @Override
    public void modelChangedOnLayout(@NotNull NlModel model, boolean animate) {
        // Do nothing
    }

    @Override
    public boolean activatePreferredEditor(@NotNull DesignSurface surface, @NotNull NlComponent component) {
        ViewHandler handler = component.getViewHandler();
        String propertyName = handler != null ? handler.getPreferredProperty() : null;
        if (propertyName == null) {
            return false;
        }
        return myPropertiesPanel.activatePreferredEditor(propertyName, myLoading);
    }

    @Override
    public void modelChanged(@NotNull NlModel model) {
    }

    @Override
    public void modelRendered(@NotNull NlModel model) {
        myPropertiesPanel.modelRendered(this);
    }

    @Override
    public void dispose() {
        setToolContext(null);
    }
}