/*
* 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.tests.gui.framework.fixture.layout;
import com.android.tools.idea.configurations.ConfigurationToolBar;
import com.google.common.collect.Lists;
import com.intellij.android.designer.AndroidDesignerEditor;
import com.intellij.android.designer.designSurface.AndroidDesignerEditorPanel;
import com.intellij.android.designer.model.RadViewComponent;
import com.intellij.designer.model.RadComponent;
import com.intellij.openapi.application.ApplicationManager;
import org.fest.swing.core.Robot;
import org.fest.swing.fixture.ComponentFixture;
import org.fest.swing.timing.Condition;
import org.fest.swing.timing.Pause;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.awt.*;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import static com.android.tools.idea.tests.gui.framework.GuiTests.SHORT_TIMEOUT;
import static org.junit.Assert.*;
/**
* Fixture wrapping the the layout editor for a particular file
*/
public class LayoutEditorFixture extends ComponentFixture<AndroidDesignerEditorPanel> implements LayoutFixture {
private final AndroidDesignerEditorPanel myPanel;
public LayoutEditorFixture(@NotNull Robot robot, @NotNull AndroidDesignerEditor editor) {
super(robot, (AndroidDesignerEditorPanel)editor.getDesignerPanel());
myPanel = (AndroidDesignerEditorPanel)editor.getDesignerPanel();
}
@Override
@NotNull
public RenderErrorPanelFixture getRenderErrors() {
return new RenderErrorPanelFixture(robot, this, myPanel);
}
@Override
@NotNull
public ConfigurationToolbarFixture getToolbar() {
AndroidDesignerEditorPanel panel = myPanel;
ConfigurationToolBar toolbar = robot.finder().findByType(panel, ConfigurationToolBar.class, true);
assertNotNull(toolbar);
return new ConfigurationToolbarFixture(robot, this, panel, toolbar);
}
/** Returns the palette associated with this layout editor */
@NotNull
public LayoutPaletteFixture getPaletteFixture() {
return new LayoutPaletteFixture(robot, this, myPanel);
}
/** Returns the property sheet associated with this layout editor */
@NotNull
public PropertySheetFixture getPropertySheetFixture() {
return new PropertySheetFixture(robot, this, myPanel);
}
@NotNull
@Override
public Object waitForRenderToFinish() {
return waitForNextRenderToFinish(null);
}
/** Rendering token used by {@link #waitForRenderToFinish()} */
private Object myPreviousRender;
@Override
public void waitForNextRenderToFinish() {
myPreviousRender = waitForNextRenderToFinish(myPreviousRender);
}
@NotNull
@Override
public Object waitForNextRenderToFinish(@Nullable final Object previous) {
robot.waitForIdle();
Pause.pause(new Condition("Render is pending") {
@Override
public boolean test() {
return !myPanel.isRenderPending() && myPanel.getLastResult() != null && myPanel.getLastResult() != previous;
}
}, SHORT_TIMEOUT);
robot.waitForIdle();
Object token = myPanel.getLastResult();
assert token != null;
return token;
}
@Override
public void requireRenderSuccessful() {
waitForRenderToFinish();
requireRenderSuccessful(false, false);
}
@Override
public void requireRenderSuccessful(boolean allowErrors, boolean allowWarnings) {
getRenderErrors().requireRenderSuccessful(allowErrors, allowWarnings);
}
/**
* Searches for the nth occurrence of a given view in the layout. The ordering of widgets of the same
* type is by visual order, first vertically, then horizontally (and finally by XML source offset, if they exactly overlap
* as for example would happen in a {@code <merge>}
*/
@NotNull
public LayoutEditorComponentFixture findView(final String tag, int occurrence) {
waitForRenderToFinish();
AndroidDesignerEditorPanel panel = myPanel;
final List<RadViewComponent> components = Lists.newArrayList();
final RadComponent rootComponent = panel.getRootComponent();
assertNotNull(rootComponent);
assertTrue(rootComponent.getClass().getName(), rootComponent instanceof RadViewComponent);
ApplicationManager.getApplication().runReadAction(new Runnable() {
@Override
public void run() {
addComponents(tag, (RadViewComponent)rootComponent, components);
// Sort by visual order
Collections.sort(components, new Comparator<RadViewComponent>() {
@Override
public int compare(RadViewComponent component1, RadViewComponent component2) {
Rectangle bounds1 = component1.getBounds();
Rectangle bounds2 = component2.getBounds();
int delta = bounds1.y - bounds2.y;
if (delta != -1) {
return delta;
}
delta = bounds1.x - bounds2.x;
if (delta != -1) {
return delta;
}
// Unlikely
return component1.getTag().getTextOffset() - component2.getTag().getTextOffset();
}
});
}
});
assertTrue("Only " + components.size() + " found, not enough for occurrence #" + occurrence, components.size() > occurrence);
RadViewComponent component = components.get(occurrence);
return createComponentFixture(component);
}
private LayoutEditorComponentFixture createComponentFixture(RadViewComponent component) {
return new LayoutEditorComponentFixture(robot, component, this, myPanel);
}
/** Requires the selection to have the given number of selected widgets */
public LayoutEditorFixture requireSelectionCount(int count) {
assertEquals(count, getSelection().size());
return this;
}
/** Requires the selection to have the given number of selected widgets */
public LayoutEditorFixture requireSelection(List<LayoutEditorComponentFixture> components) {
assertEquals(components, getSelection());
return this;
}
/** Returns a list of the selected views */
@NotNull
public List<LayoutEditorComponentFixture> getSelection() {
List<LayoutEditorComponentFixture> selection = Lists.newArrayList();
for (RadComponent component : myPanel.getSurfaceArea().getSelection()) {
if (component instanceof RadViewComponent) {
selection.add(createComponentFixture((RadViewComponent)component));
}
}
return selection;
}
private static void addComponents(@NotNull String tag, @NotNull RadViewComponent component, @NotNull List<RadViewComponent> components) {
if (tag.equals(component.getTag().getName())) {
components.add(component);
}
for (RadComponent child : component.getChildren()) {
if (child instanceof RadViewComponent) {
addComponents(tag, (RadViewComponent)child, components);
}
}
}
}