com.android.tools.idea.rendering.IncludeOverlay.java Source code

Java tutorial

Introduction

Here is the source code for com.android.tools.idea.rendering.IncludeOverlay.java

Source

/*
 * 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.rendering;

import com.android.annotations.VisibleForTesting;
import com.android.tools.idea.configurations.OverlayContainer;
import com.google.common.collect.Lists;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.awt.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/**
 * The {@link IncludeOverlay} class renders masks to <b>partially</b> hide everything outside
 * an included file's own content. This overlay is in use when you are editing an included
 * file shown within a different file's context (e.g. "Show In > other").
 * <p>
 * TODO: Instead of masking out the entire viewport, consider only masking out the root
 * layout, and leaving the system bars alone? One problem with that approach is that
 * we may be rendering the action bar from the outer layout, which is really system UI
 * that should be grayed out. Perhaps we can consider combining the root bounds with the
 * bottom bounds of the root layout? We also need to handle the case where the surrounding
 * layout does not fill up the full height of the available screen (e.g. its layout_height
 * is wrap_content).
 */
public class IncludeOverlay extends Overlay {
    /** Mask transparency - 0 is transparent, 255 is opaque */
    private static final int MASK_TRANSPARENCY = 160;

    private final OverlayContainer myContainer;

    /**
     * Constructs an {@link IncludeOverlay} tied to the given result.
     *
     * @param container The {@link OverlayContainer} containing this overlay
     */
    public IncludeOverlay(@NotNull OverlayContainer container) {
        myContainer = container;
    }

    @Override
    public void paint(@Nullable Component component, @NotNull Graphics2D gc, int deltaX, int deltaY) {
        RenderedViewHierarchy viewHierarchy = myContainer.getViewHierarchy();
        if (viewHierarchy == null) {
            return;
        }
        List<RenderedView> includedRoots = viewHierarchy.getIncludedRoots();
        if (includedRoots == null || includedRoots.size() == 0 || component == null) {
            // We don't support multiple included children yet. When that works,
            // this code should use a BSP tree to figure out which regions to paint
            // to leave holes in the mask.
            return;
        }

        //noinspection UseJBColor
        gc.setColor(Color.GRAY);
        Composite prevComposite = gc.getComposite();
        gc.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, MASK_TRANSPARENCY / 255.0f));

        Shape prevClip = gc.getClip();
        Shape clip = setScreenClip(myContainer, component, gc, deltaX, deltaY);
        Collection<Rectangle> masks = computeMasks(component, includedRoots);
        for (Rectangle r : masks) {
            gc.fillRect(r.x + deltaX, r.y + deltaY, r.width, r.height);
        }
        gc.setComposite(prevComposite);
        if (clip != null) {
            gc.setClip(prevClip);
        }
    }

    /** Computes the set of rectangles we should paint to cover everything up <b>except</b> for
     * the included root bounds. The coordinates will be in the coordinate system of the given
     * component.
     */
    protected Collection<Rectangle> computeMasks(Component component, List<RenderedView> includedRoots) {
        Dimension fullImageSize = myContainer.getFullImageSize();
        Rectangle whole = new Rectangle(0, 0, fullImageSize.width, fullImageSize.height);
        whole = myContainer.fromModel(component, whole);
        List<Rectangle> includedBounds = Lists.newArrayListWithExpectedSize(includedRoots.size());
        for (RenderedView view : includedRoots) {
            includedBounds.add(myContainer.fromModel(component, view.getBounds()));
        }
        return subtractRectangles(whole, includedBounds);
    }

    /**
     * Given a Rectangle, remove holes from it (specified as a collection of Rectangles) such
     * that the result is a list of rectangles that cover everything that is not a hole.
     *
     * @param rectangle the rectangle to subtract from
     * @param holes the holes to subtract from the rectangle
     * @return a list of sub rectangles that remain after subtracting out the given list of holes
     */
    @VisibleForTesting
    static Collection<Rectangle> subtractRectangles(Rectangle rectangle, Collection<Rectangle> holes) {
        List<Rectangle> result = new ArrayList<Rectangle>();
        result.add(rectangle);

        for (Rectangle hole : holes) {
            List<Rectangle> tempResult = new ArrayList<Rectangle>();
            for (Rectangle r : result) {
                if (hole.intersects(r)) {
                    // Clip the hole to fit the rectangle bounds
                    Rectangle h = hole.intersection(r);

                    // Split the rectangle

                    // Above (includes the NW and NE corners)
                    if (h.y > r.y) {
                        tempResult.add(new Rectangle(r.x, r.y, r.width, h.y - r.y));
                    }

                    // Left (not including corners)
                    if (h.x > r.x) {
                        tempResult.add(new Rectangle(r.x, h.y, h.x - r.x, h.height));
                    }

                    int hx2 = h.x + h.width;
                    int hy2 = h.y + h.height;
                    int rx2 = r.x + r.width;
                    int ry2 = r.y + r.height;

                    // Below (includes the SW and SE corners)
                    if (hy2 < ry2) {
                        tempResult.add(new Rectangle(r.x, hy2, r.width, ry2 - hy2));
                    }

                    // Right (not including corners)
                    if (hx2 < rx2) {
                        tempResult.add(new Rectangle(hx2, h.y, rx2 - hx2, h.height));
                    }
                } else {
                    tempResult.add(r);
                }
            }

            result = tempResult;
        }

        return result;
    }
}