extract calculation of cursor position in own class
Signed-off-by: Florian Thienel <florian@thienel.org>
diff --git a/org.eclipse.vex.core.tests/src/org/eclipse/vex/core/internal/boxes/TestCursorPosition.java b/org.eclipse.vex.core.tests/src/org/eclipse/vex/core/internal/boxes/TestCursorPosition.java
new file mode 100644
index 0000000..4cea87f
--- /dev/null
+++ b/org.eclipse.vex.core.tests/src/org/eclipse/vex/core/internal/boxes/TestCursorPosition.java
@@ -0,0 +1,110 @@
+/*******************************************************************************
+ * Copyright (c) 2015 Florian Thienel 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:
+ * Florian Thienel - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.vex.core.internal.boxes;
+
+import static org.junit.Assert.assertEquals;
+
+import org.eclipse.core.runtime.QualifiedName;
+import org.eclipse.vex.core.internal.dom.Document;
+import org.eclipse.vex.core.internal.visualization.DocumentRootVisualization;
+import org.eclipse.vex.core.internal.visualization.ParagraphVisualization;
+import org.eclipse.vex.core.internal.visualization.StructureElementVisualization;
+import org.eclipse.vex.core.internal.visualization.TextVisualization;
+import org.eclipse.vex.core.internal.visualization.VisualizationChain;
+import org.eclipse.vex.core.provisional.dom.IDocument;
+import org.eclipse.vex.core.provisional.dom.IElement;
+import org.eclipse.vex.core.provisional.dom.IParent;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * @author Florian Thienel
+ */
+public class TestCursorPosition {
+
+ private RootBox rootBox;
+ private ContentMap contentMap;
+ private CursorPosition cursorPosition;
+
+ @Before
+ public void setUp() throws Exception {
+ rootBox = createTestModel();
+ contentMap = new ContentMap();
+ contentMap.setRootBox(rootBox);
+ cursorPosition = new CursorPosition(contentMap);
+ }
+
+ @Test
+ public void canMoveCursorOneCharacterLeft() throws Exception {
+ cursorPosition.setOffset(5);
+ cursorPosition.left();
+ assertEquals(4, cursorPosition.getOffset());
+ }
+
+ @Test
+ public void whenAtFirstPosition_cannotMoveCursorOneCharacterLeft() throws Exception {
+ cursorPosition.setOffset(0);
+ cursorPosition.left();
+ assertEquals(0, cursorPosition.getOffset());
+ }
+
+ @Test
+ public void canMoveCursorOneCharacterRight() throws Exception {
+ cursorPosition.setOffset(5);
+ cursorPosition.right();
+ assertEquals(6, cursorPosition.getOffset());
+ }
+
+ @Test
+ public void whenAtLastPosition_cannotMoveCursorOneCharacterRight() throws Exception {
+ cursorPosition.setOffset(37);
+ cursorPosition.right();
+ assertEquals(37, cursorPosition.getOffset());
+ }
+
+ private static RootBox createTestModel() {
+ final IDocument document = createTestDocument();
+ final VisualizationChain visualizationChain = buildVisualizationChain();
+ return visualizationChain.visualizeRoot(document);
+ }
+
+ private static VisualizationChain buildVisualizationChain() {
+ final VisualizationChain visualizationChain = new VisualizationChain();
+ visualizationChain.addForRoot(new DocumentRootVisualization());
+ visualizationChain.addForStructure(new ParagraphVisualization());
+ visualizationChain.addForStructure(new StructureElementVisualization());
+ visualizationChain.addForInline(new TextVisualization());
+ return visualizationChain;
+ }
+
+ private static IDocument createTestDocument() {
+ final Document document = new Document(new QualifiedName(null, "doc"));
+ insertSection(document.getRootElement());
+ insertSection(document.getRootElement());
+ return document;
+ }
+
+ private static void insertSection(final IParent parent) {
+ final IElement section = insertElement(parent, "section");
+ insertText(insertElement(section, "para"), "LOREM IPSUM");
+ insertElement(section, "para");
+ }
+
+ private static IElement insertElement(final IParent parent, final String localName) {
+ final IDocument document = parent.getDocument();
+ return document.insertElement(parent.getEndOffset(), new QualifiedName(null, localName));
+ }
+
+ private static void insertText(final IParent parent, final String text) {
+ final IDocument document = parent.getDocument();
+ document.insertText(parent.getEndOffset(), text);
+ }
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/boxes/ContentMap.java b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/boxes/ContentMap.java
index f3be2c9..38c1b0c 100644
--- a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/boxes/ContentMap.java
+++ b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/boxes/ContentMap.java
@@ -16,9 +16,32 @@
public class ContentMap {
private RootBox rootBox;
+ private IContentBox outmostContentBox;
public void setRootBox(final RootBox rootBox) {
this.rootBox = rootBox;
+ outmostContentBox = findOutmostContentBox();
+ }
+
+ private IContentBox findOutmostContentBox() {
+ return rootBox.accept(new DepthFirstTraversal<IContentBox>(null) {
+ @Override
+ public IContentBox visit(final NodeReference box) {
+ return box;
+ }
+
+ @Override
+ public IContentBox visit(final TextContent box) {
+ return box;
+ }
+ });
+ }
+
+ public int getLastPosition() {
+ if (outmostContentBox == null) {
+ return 0;
+ }
+ return outmostContentBox.getEndOffset();
}
public IContentBox findBoxForPosition(final int offset) {
diff --git a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/boxes/CursorPosition.java b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/boxes/CursorPosition.java
new file mode 100644
index 0000000..03839fb
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/boxes/CursorPosition.java
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ * Copyright (c) 2015 Florian Thienel 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:
+ * Florian Thienel - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.vex.core.internal.boxes;
+
+/**
+ * @author Florian Thienel
+ */
+public class CursorPosition {
+
+ private final ContentMap contentMap;
+ private int offset;
+
+ public CursorPosition(final ContentMap contentMap) {
+ this.contentMap = contentMap;
+ }
+
+ public void setOffset(final int offset) {
+ this.offset = offset;
+ }
+
+ public int getOffset() {
+ return offset;
+ }
+
+ public void left() {
+ offset = Math.max(0, offset - 1);
+ }
+
+ public void right() {
+ offset = Math.min(offset + 1, contentMap.getLastPosition());
+ }
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/widget/swt/BoxWidget.java b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/widget/swt/BoxWidget.java
index 934e023..548b577 100644
--- a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/widget/swt/BoxWidget.java
+++ b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/widget/swt/BoxWidget.java
@@ -30,6 +30,7 @@
import org.eclipse.swt.widgets.Display;
import org.eclipse.vex.core.internal.boxes.ContentMap;
import org.eclipse.vex.core.internal.boxes.Cursor;
+import org.eclipse.vex.core.internal.boxes.CursorPosition;
import org.eclipse.vex.core.internal.boxes.IContentBox;
import org.eclipse.vex.core.internal.boxes.RootBox;
import org.eclipse.vex.core.internal.core.Graphics;
@@ -45,6 +46,7 @@
private final ContentMap contentMap;
private final Cursor cursor;
+ private final CursorPosition cursorPosition;
/*
* Use double buffering with a dedicated render thread to render the box model: This prevents flickering and keeps
@@ -77,6 +79,7 @@
contentMap = new ContentMap();
contentMap.setRootBox(rootBox);
cursor = new Cursor(contentMap);
+ cursorPosition = new CursorPosition(contentMap);
}
public void setContent(final RootBox rootBox) {
@@ -172,8 +175,7 @@
cursorRight();
break;
case SWT.HOME:
- setCursorPosition(0);
- invalidate();
+ cursorHome();
break;
default:
break;
@@ -189,32 +191,41 @@
invalidate();
}
- private void cursorLeft() {
- setCursorPosition(Math.max(0, cursor.getPosition() - 1));
- invalidate();
- }
-
- private void cursorRight() {
- setCursorPosition(cursor.getPosition() + 1);
- invalidate();
- }
-
private void setCursorPositionInBoxByAbsoluteCoordinates(final IContentBox box, final int x, final int y) {
runWithGraphics(new IRunnableWithGraphics<Object>() {
@Override
public Integer run(final Graphics graphics) {
final int offset = box.getOffsetForCoordinates(graphics, x - box.getAbsoluteLeft(), y - box.getAbsoluteTop());
+ cursorPosition.setOffset(offset);
cursor.setPosition(graphics, offset);
return null;
}
});
}
- private void setCursorPosition(final int offset) {
+ private void cursorLeft() {
+ cursorPosition.left();
+ transferCursorPositionToCursor();
+ invalidate();
+ }
+
+ private void cursorRight() {
+ cursorPosition.right();
+ transferCursorPositionToCursor();
+ invalidate();
+ }
+
+ private void cursorHome() {
+ cursorPosition.setOffset(0);
+ transferCursorPositionToCursor();
+ invalidate();
+ }
+
+ private void transferCursorPositionToCursor() {
runWithGraphics(new IRunnableWithGraphics<Object>() {
@Override
public Integer run(final Graphics graphics) {
- cursor.setPosition(graphics, offset);
+ cursor.setPosition(graphics, cursorPosition.getOffset());
return null;
}
});