/*******************************************************************************
* Copyright (c) 2016 itemis AG and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Matthias Wienand (itemis AG) - initial API and implementation
*
*******************************************************************************/
package org.eclipse.gef.mvc.tests.fx;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.fail;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.gef.common.adapt.AdapterKey;
import org.eclipse.gef.mvc.fx.MvcFxModule;
import org.eclipse.gef.mvc.fx.domain.IDomain;
import org.eclipse.gef.mvc.fx.models.FocusModel;
import org.eclipse.gef.mvc.fx.parts.IContentPartFactory;
import org.eclipse.gef.mvc.fx.parts.IRootPart;
import org.eclipse.gef.mvc.fx.policies.FocusTraversalPolicy;
import org.eclipse.gef.mvc.fx.viewer.IViewer;
import org.eclipse.gef.mvc.tests.fx.rules.FXNonApplicationThreadRule;
import org.eclipse.gef.mvc.tests.fx.stubs.Cell;
import org.eclipse.gef.mvc.tests.fx.stubs.CellContentPartFactory;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.multibindings.MapBinder;
import javafx.scene.Node;
public class FocusTraversalPolicyTests {
public static String NEXT = "NEXT";
public static String PREV = "PREV";
@Rule
public FXNonApplicationThreadRule ctx = new FXNonApplicationThreadRule();
private IDomain domain;
private IViewer viewer;
private FocusModel focusModel;
private FocusTraversalPolicy traversePolicy;
@Before
public void activate() throws Throwable {
if (domain == null) {
Injector injector = Guice.createInjector(new MvcFxModule() {
@Override
protected void bindIDomainAdapters(MapBinder<AdapterKey<?>, Object> adapterMapBinder) {
bindContentIViewerAsIDomainAdapter(adapterMapBinder);
}
@Override
protected void configure() {
super.configure();
binder().bind(IContentPartFactory.class).to(CellContentPartFactory.class);
// bind FocusModel
// AdapterMaps.getAdapterMapBinder(binder(),
// InfiniteCanvasViewer.class,
// HistoricizingDomain.CONTENT_VIEWER_ROLE)
// .addBinding(AdapterKey.defaultRole()).to(FocusModel.class);
// // bind FocusTraversalPolicy
// AdapterMaps.getAdapterMapBinder(binder(),
// AbstractFXRootPart.class)
// .addBinding(AdapterKey.defaultRole()).to(FocusTraversalPolicy.class);
}
});
domain = injector.getInstance(IDomain.class);
viewer = domain.getAdapter(AdapterKey.get(IViewer.class, IDomain.CONTENT_VIEWER_ROLE));
ctx.createScene(viewer.getCanvas(), 100, 100);
ctx.runAndWait(() -> {
focusModel = viewer.getAdapter(FocusModel.class);
IRootPart<? extends Node> rootPart = viewer.getRootPart();
traversePolicy = rootPart.getAdapter(FocusTraversalPolicy.class);
});
}
assertNotNull(focusModel);
assertNotNull(traversePolicy);
ctx.runAndWait(() -> {
domain.activate();
});
}
/**
* Performs the given focus traversal actions and checks that the specified
* cells are focused afterwards. A cell is only focusable if its name starts
* with "C".
*
* @param cells
* Mapping from cell names to {@link Cell} objects.
* @param objects
* Alternating: Focus traversal actions ({@link #NEXT} or
* {@link #PREV}) and cell names ({@link Cell#name}).
*/
protected void checkFocusTraversal(Map<String, Cell> cells, String... objects) throws Throwable {
if (objects == null) {
throw new IllegalArgumentException("objects may not be null");
}
if (objects.length % 2 != 0) {
throw new IllegalArgumentException("even number of objects expected");
}
for (int i = 0; i < objects.length; i++) {
if (i % 2 == 0) {
String fta = objects[i];
ctx.runAndWait(() -> {
traversePolicy.init();
if (NEXT.equals(fta)) {
traversePolicy.focusNext();
} else if (PREV.equals(fta)) {
traversePolicy.focusPrevious();
} else {
throw new IllegalArgumentException("focus traversal action (NEXT or PREV) expected");
}
try {
domain.execute(traversePolicy.commit(), new NullProgressMonitor());
} catch (ExecutionException e) {
e.printStackTrace();
fail(e.getMessage());
}
});
} else {
if (objects[i] == null) {
assertNull("performed " + Arrays.asList(objects).subList(0, i).toString()
+ ", expected null, but got " + focusModel.getFocus(), focusModel.getFocus());
} else {
assertNotNull("performed " + Arrays.asList(objects).subList(0, i).toString() + ", expected cell "
+ objects[i] + ", but got null", focusModel.getFocus());
Cell cell = cells.get(objects[i]);
assertSame("performed " + Arrays.asList(objects).subList(0, i).toString(), cell,
focusModel.getFocus().getContent());
}
}
}
}
@After
public void deactivate() throws Throwable {
ctx.runAndWait(() -> {
viewer.getContents().setAll(Collections.emptyList());
domain.deactivate();
});
}
/**
* All parts are focusable, cell tree:
*
* <pre>
* R-C0
* R-C1
* </pre>
*
* Check initial state:
*
* <ol>
* <li>No focus part.
* </ol>
*
* Check focus next/previous:
*
* <ol>
* <li>Focus Next => C0 is focused.
* <li>Focus Previous => No focus part.
* <li>Focus Previous => C1 is focused.
* <li>Focus Next => No focus part.
* </ol>
*
* Check focus cycle (next/previous):
*
* <ol>
* <li>Focus Next => C0 is focused.
* <li>Focus Next => C1 is focused.
* <li>Focus Next => No focus part.
* <li>Focus Previous => C1 is focused.
* <li>Focus Previous => C0 is focused.
* <li>Focus Previous => No focus part.
* </ol>
*/
@Test
public void test_RC0_RC1() throws Throwable {
Map<String, Cell> cells = new HashMap<>();
Cell root = Cell.createCellTree(String.join("\n", "R-C0", "R-C1"), cells);
ctx.runAndWait(() -> {
viewer.getContents().setAll(root.children);
});
// check initial state
assertNull(focusModel.getFocus());
// check first next/prev
checkFocusTraversal(cells, NEXT, "C0", PREV, null, PREV, "C1", NEXT, null);
// check full cycle (forwards)
checkFocusTraversal(cells, NEXT, "C0", NEXT, "C1", NEXT, null);
// check full cycle (backwards)
checkFocusTraversal(cells, PREV, "C1", PREV, "C0", PREV, null);
}
/**
* All parts are focusable, cell tree:
*
* <pre>
* R-C-C0-C00
* R-C-C1-C10
* R-C-C2-C20
* </pre>
*
* Check initial state:
*
* <ol>
* <li>No focus part.
* </ol>
*
* Check focus next/previous:
*
* <ol>
* <li>Focus Next => C is focused.
* <li>Focus Previous => No focus part.
* <li>Focus Previous => C20 is focused.
* <li>Focus Next => No focus part.
* </ol>
*
* Check focus cycle (next/previous):
*
* <ol>
* <li>Focus Next => C is focused.
* <li>Focus Next => C0 is focused.
* <li>Focus Next => C00 is focused.
* <li>Focus Next => C1 is focused.
* <li>Focus Next => C10 is focused.
* <li>Focus Next => C2 is focused.
* <li>Focus Next => C20 is focused.
* <li>Focus Next => No focus part.
* <li>Focus Previous => C20 is focused.
* <li>Focus Previous => C2 is focused.
* <li>Focus Previous => C10 is focused.
* <li>Focus Previous => C1 is focused.
* <li>Focus Previous => C00 is focused.
* <li>Focus Previous => C0 is focused.
* <li>Focus Previous => C is focused.
* <li>Focus Previous => No focus part.
* </ol>
*/
@Test
public void test_RCC0C00_RCC1C10_RCC2C20() throws Throwable {
Map<String, Cell> cells = new HashMap<>();
Cell root = Cell.createCellTree(String.join("\n", "R-C-C0-C00", "R-C-C1-C10", "R-C-C2-C20"), cells);
ctx.runAndWait(() -> {
viewer.getContents().setAll(root.children);
});
// check initial state
assertNull(focusModel.getFocus());
// check first next/prev
checkFocusTraversal(cells, NEXT, "C", PREV, null, PREV, "C20", NEXT, null);
// check full cycle (forwards)
checkFocusTraversal(cells, NEXT, "C", NEXT, "C0", NEXT, "C00", NEXT, "C1", NEXT, "C10", NEXT, "C2", NEXT, "C20",
NEXT, null);
// check full cycle (backwards)u
checkFocusTraversal(cells, PREV, "C20", PREV, "C2", PREV, "C10", PREV, "C1", PREV, "C00", PREV, "C0", PREV, "C",
PREV, null);
}
/**
* First and last parts are not focusable, only the part in the middle is,
* cell tree:
*
* <pre>
* R-V0
* R-C1
* R-V2
* </pre>
*
* Check initial state:
*
* <ol>
* <li>No focus part.
* </ol>
*
* Check focus next/previous:
*
* <ol>
* <li>Focus Next => C1 is focused.
* <li>Focus Previous => No focus part.
* <li>Focus Previous => C1 is focused.
* <li>Focus Next => No focus part.
* </ol>
*
* Check focus cycle (next/previous):
*
* <ol>
* <li>Focus Next => C1 is focused.
* <li>Focus Next => No focus part.
* <li>Focus Previous => C1 is focused.
* <li>Focus Previous => No focus part.
* </ol>
*/
@Test
public void test_RV0_RC1_RV2() throws Throwable {
Map<String, Cell> cells = new HashMap<>();
Cell root = Cell.createCellTree(String.join("\n", "R-V0", "R-C1", "R-V2"), cells);
ctx.runAndWait(() -> {
viewer.getContents().setAll(root.children);
});
// check initial state
assertNull(focusModel.getFocus());
// check first next/prev
checkFocusTraversal(cells, NEXT, "C1", PREV, null, PREV, "C1", NEXT, null);
// check full cycle (forwards)
checkFocusTraversal(cells, NEXT, "C1", NEXT, null);
// check full cycle (backwards)
checkFocusTraversal(cells, PREV, "C1", PREV, null);
}
/**
* Intermediate parts are not focusable, cell tree:
*
* <pre>
* R-V0-C0
* R-V1-C1
* </pre>
*
* Check initial state:
*
* <ol>
* <li>No focus part.
* </ol>
*
* Check focus next/previous:
*
* <ol>
* <li>Focus Next => C0 is focused.
* <li>Focus Previous => No focus part.
* <li>Focus Previous => C1 is focused.
* <li>Focus Next => No focus part.
* </ol>
*
* Check focus cycle (next/previous):
*
* <ol>
* <li>Focus Next => C0 is focused.
* <li>Focus Next => C1 is focused.
* <li>Focus Next => No focus part.
* <li>Focus Previous => C1 is focused.
* <li>Focus Previous => C0 is focused.
* <li>Focus Previous => No focus part.
* </ol>
*/
@Test
public void test_RV0C0_RV1C1() throws Throwable {
Map<String, Cell> cells = new HashMap<>();
Cell root = Cell.createCellTree(String.join("\n", "R-V0-C0", "R-V1-C1"), cells);
ctx.runAndWait(() -> {
viewer.getContents().setAll(root.children);
});
// check initial state
assertNull(focusModel.getFocus());
// check first next/prev
checkFocusTraversal(cells, NEXT, "C0", PREV, null, PREV, "C1", NEXT, null);
// check full cycle (forwards)
checkFocusTraversal(cells, NEXT, "C0", NEXT, "C1", NEXT, null);
// check full cycle (backwards)
checkFocusTraversal(cells, PREV, "C1", PREV, "C0", PREV, null);
}
}