A layered image editor for Linux and Omarchy, ported from Compositor (github.com/robbietilton/Compositor), whose Swift source is its specification. It reads and writes Compositor's .comp projects. Packages for Ubuntu, Fedora and Arch come from the release workflow; scripts/install/ holds one install script per distribution family, and flake.nix serves NixOS. Co-Authored-By: Claude <noreply@anthropic.com>
462 lines
19 KiB
C++
462 lines
19 KiB
C++
#include "InlineTextFixtures.h"
|
|
|
|
// Typing on the canvas: keys, moves, undo, deletions, spacing.
|
|
class InlineTextEditorTests : public QObject {
|
|
Q_OBJECT
|
|
private slots:
|
|
void aDraftOpensTheEditorAndItsTextGrows();
|
|
void typingIsOneUndoStep();
|
|
void anotherDraftStartsAfresh();
|
|
void arrowsHomeAndEndMoveAndShiftSelects();
|
|
void upAndDownKeepTheirColumn();
|
|
void deletionsReachCharactersWordsAndLines();
|
|
void aWrappedParagraphMovesByItsLines();
|
|
void aCaretPastTheBoxHasNoLine();
|
|
void aFinalNewlineHasALineForTheCaret();
|
|
void arrowsFollowARightToLeftParagraph();
|
|
void returnTabEscapeAndFinishing();
|
|
void altWithTheArrowsSetsSpacing();
|
|
};
|
|
|
|
void InlineTextEditorTests::aDraftOpensTheEditorAndItsTextGrows()
|
|
{
|
|
TextCanvas shown;
|
|
InlineTextEditor &editor = shown.editor();
|
|
QCOMPARE(editor.draftID(), shown.session.textDraft().value().id);
|
|
QCOMPARE(editor.logicalSize(), EditorSession::textBoxSize(shown.session.textDraft().value().style));
|
|
QCOMPARE(editor.shownTransform(), (LayerTransform{.origin = {20, 30}, .size = editor.logicalSize()}));
|
|
const double empty = editor.logicalSize().width();
|
|
shown.type(QStringLiteral("Grow"));
|
|
QCOMPARE(shown.content(), QString("Grow"));
|
|
QCOMPARE(editor.logicalSize(), EditorSession::textBoxSize(shown.session.textDraft().value().style));
|
|
QVERIFY(editor.logicalSize().width() > empty);
|
|
// Six view points a handle at any zoom.
|
|
QCOMPARE(editor.handleSize(), 6.0);
|
|
shown.session.zoom(2);
|
|
QCOMPARE(editor.handleSize(), 3.0);
|
|
shown.session.zoom(4);
|
|
QCOMPARE(editor.handleSize(), 2.0);
|
|
shown.session.zoom(0.005);
|
|
QCOMPARE(editor.handleSize(), 600.0);
|
|
shown.session.zoom(1);
|
|
// Content set elsewhere pulls the caret back inside.
|
|
TextDraft shorter = shown.session.textDraft().value();
|
|
shorter.style.content = QStringLiteral("Gr");
|
|
shown.session.setTextDraft(shorter);
|
|
QCOMPARE(editor.caretPosition(), 2);
|
|
QCOMPARE(editor.anchor(), 2);
|
|
// Point text on a scaled layer grows at that scale.
|
|
QVERIFY(shown.session.finishText());
|
|
const QUuid id = shown.session.activeLayerID().value();
|
|
const QSize pixels = layerWith(shown.session, id).asset.value().size();
|
|
rewrite(shown.session, [&](ProjectSnapshot &snapshot) {
|
|
record(snapshot, id).transform = LayerTransform{.origin = {50, 40}, .size = QSizeF(pixels) * 2, .rotation = 30};
|
|
});
|
|
shown.session.editActiveText();
|
|
InlineTextEditor &scaled = shown.editor();
|
|
const QPointF corner = scaled.shownTransform().point(QPointF(0, 0));
|
|
shown.key(Qt::Key_End);
|
|
shown.type(QStringLiteral(" more"));
|
|
QCOMPARE(scaled.shownTransform().size, scaled.logicalSize() * 2);
|
|
QVERIFY((scaled.shownTransform().point(QPointF(0, 0)) - corner).manhattanLength() < 1e-9);
|
|
QCOMPARE(scaled.shownTransform().rotation, 30.0);
|
|
// Handles stay six points across a layer twice the size.
|
|
shown.session.zoom(1);
|
|
QCOMPARE(scaled.handleSize(), 3.0);
|
|
// A box keeps its size however much is typed.
|
|
shown.session.cancelText();
|
|
shown.session.beginText(QRectF(10, 10, 200, 100));
|
|
shown.type(QStringLiteral("Boxed words that go on"));
|
|
QCOMPARE(shown.editor().logicalSize(), QSizeF(200, 100));
|
|
// A box shows the transform its draft holds, never grown.
|
|
QVERIFY(shown.session.finishText());
|
|
shown.session.editActiveText();
|
|
TextDraft boxed = shown.session.textDraft().value();
|
|
boxed.style.boxSize = QSizeF(300, 100);
|
|
boxed.transform = LayerTransform{.origin = {10, 10}, .size = {300, 100}};
|
|
shown.session.setTextDraft(boxed);
|
|
QCOMPARE(shown.editor().shownTransform(), boxed.transform.value());
|
|
}
|
|
|
|
void InlineTextEditorTests::typingIsOneUndoStep()
|
|
{
|
|
TextCanvas shown;
|
|
shown.type(QStringLiteral("Hi you"));
|
|
QCOMPARE(shown.editor().caretPosition(), 6);
|
|
shown.key(Qt::Key_Z, Qt::ControlModifier);
|
|
QCOMPARE(shown.content(), QString());
|
|
shown.key(Qt::Key_Z, Qt::ControlModifier | Qt::ShiftModifier);
|
|
QCOMPARE(shown.content(), QString("Hi you"));
|
|
QCOMPARE(shown.editor().caretPosition(), 6);
|
|
// A move ends the step; deleting starts another.
|
|
shown.key(Qt::Key_Left);
|
|
shown.type(QStringLiteral("!"));
|
|
shown.key(Qt::Key_Backspace);
|
|
shown.key(Qt::Key_Backspace);
|
|
QCOMPARE(shown.content(), QString("Hi yu"));
|
|
shown.key(Qt::Key_Z, Qt::ControlModifier);
|
|
QCOMPARE(shown.content(), QString("Hi yo!u"));
|
|
shown.key(Qt::Key_Z, Qt::ControlModifier);
|
|
QCOMPARE(shown.content(), QString("Hi you"));
|
|
// Typing drops the redo; a move ends the step.
|
|
shown.key(Qt::Key_End);
|
|
shown.type(QStringLiteral("r"));
|
|
shown.key(Qt::Key_Z, Qt::ControlModifier | Qt::ShiftModifier);
|
|
QCOMPARE(shown.content(), QString("Hi your"));
|
|
shown.key(Qt::Key_Left);
|
|
shown.type(QStringLiteral("s"));
|
|
shown.key(Qt::Key_Z, Qt::ControlModifier);
|
|
QCOMPARE(shown.content(), QString("Hi your"));
|
|
// A fresh draft starts with nothing to undo or redo.
|
|
shown.key(Qt::Key_Z, Qt::ControlModifier);
|
|
shown.session.cancelText();
|
|
shown.session.beginText(QPointF(100, 100), true);
|
|
shown.key(Qt::Key_Z, Qt::ControlModifier);
|
|
shown.key(Qt::Key_Z, Qt::ControlModifier | Qt::ShiftModifier);
|
|
QCOMPARE(shown.content(), QString());
|
|
QVERIFY(shown.session.textDraft());
|
|
shown.type(QStringLiteral("x"));
|
|
shown.key(Qt::Key_Z, Qt::ControlModifier);
|
|
QCOMPARE(shown.content(), QString());
|
|
}
|
|
|
|
void InlineTextEditorTests::anotherDraftStartsAfresh()
|
|
{
|
|
TextCanvas shown;
|
|
// A draft set over this drops its steps, as Swift's.
|
|
shown.type(QStringLiteral("ab"));
|
|
TextDraft other = shown.session.textDraft().value();
|
|
other.id = QUuid::createUuid();
|
|
other.style.content = QStringLiteral("new");
|
|
shown.session.setTextDraft(other);
|
|
QCOMPARE(shown.editor().draftID(), other.id);
|
|
shown.type(QStringLiteral("s"));
|
|
QCOMPARE(shown.content(), QString("nesw"));
|
|
shown.key(Qt::Key_Z, Qt::ControlModifier);
|
|
shown.key(Qt::Key_Z, Qt::ControlModifier);
|
|
QCOMPARE(shown.content(), QString("new"));
|
|
other.id = QUuid::createUuid();
|
|
shown.session.setTextDraft(other);
|
|
shown.key(Qt::Key_Z, Qt::ControlModifier | Qt::ShiftModifier);
|
|
QCOMPARE(shown.content(), QString("new"));
|
|
// A composition in progress ends with its draft.
|
|
shown.compose(QStringLiteral("ni"));
|
|
other.id = QUuid::createUuid();
|
|
other.style.content = QStringLiteral("NEW");
|
|
shown.session.setTextDraft(other);
|
|
QVERIFY(!shown.editor().marked());
|
|
shown.commit(QStringLiteral("X"));
|
|
QCOMPARE(shown.content(), QString("NEWX"));
|
|
shown.key(Qt::Key_Z, Qt::ControlModifier);
|
|
QCOMPARE(shown.content(), QString("NEW"));
|
|
}
|
|
|
|
void InlineTextEditorTests::arrowsHomeAndEndMoveAndShiftSelects()
|
|
{
|
|
TextCanvas shown;
|
|
shown.type(QStringLiteral("one two"));
|
|
shown.key(Qt::Key_Return);
|
|
shown.type(QStringLiteral("three"));
|
|
shown.key(Qt::Key_Home);
|
|
QCOMPARE(shown.editor().caretPosition(), 8);
|
|
shown.key(Qt::Key_Up);
|
|
QCOMPARE(shown.editor().caretPosition(), 0);
|
|
shown.key(Qt::Key_Right, Qt::ControlModifier);
|
|
QCOMPARE(shown.editor().caretPosition(), 3);
|
|
shown.key(Qt::Key_Right, Qt::ControlModifier | Qt::ShiftModifier);
|
|
QCOMPARE(shown.selection(), (TextRange{3, 7}));
|
|
// A plain arrow collapses a selection to its side.
|
|
shown.key(Qt::Key_Left);
|
|
QCOMPARE(shown.selection(), (TextRange{3, 3}));
|
|
shown.key(Qt::Key_End, Qt::ShiftModifier);
|
|
QCOMPARE(shown.selection(), (TextRange{3, 7}));
|
|
shown.key(Qt::Key_Right);
|
|
QCOMPARE(shown.editor().caretPosition(), 7);
|
|
shown.key(Qt::Key_Left, Qt::ControlModifier);
|
|
QCOMPARE(shown.editor().caretPosition(), 4);
|
|
// Down keeps its first x; below the last, the end.
|
|
shown.key(Qt::Key_Down);
|
|
QVERIFY(shown.editor().caretPosition() > 8 && shown.editor().caretPosition() < 13);
|
|
shown.key(Qt::Key_Down);
|
|
QCOMPARE(shown.editor().caretPosition(), 13);
|
|
shown.key(Qt::Key_Home, Qt::ControlModifier);
|
|
QCOMPARE(shown.editor().caretPosition(), 0);
|
|
shown.key(Qt::Key_End, Qt::ControlModifier | Qt::ShiftModifier);
|
|
QCOMPARE(shown.selection(), (TextRange{0, 13}));
|
|
// A character a step; above the first line, its start.
|
|
shown.key(Qt::Key_Home, Qt::ControlModifier);
|
|
shown.key(Qt::Key_Right);
|
|
shown.key(Qt::Key_Right);
|
|
shown.key(Qt::Key_Left);
|
|
QCOMPARE(shown.caret(), 1);
|
|
shown.key(Qt::Key_Up);
|
|
QCOMPARE(shown.caret(), 0);
|
|
// Nothing lies past either end.
|
|
shown.key(Qt::Key_Left);
|
|
QCOMPARE(shown.caret(), 0);
|
|
shown.key(Qt::Key_End, Qt::ControlModifier);
|
|
shown.key(Qt::Key_Right);
|
|
QCOMPARE(shown.caret(), 13);
|
|
}
|
|
|
|
void InlineTextEditorTests::upAndDownKeepTheirColumn()
|
|
{
|
|
TextCanvas shown;
|
|
shown.type(QStringLiteral("abcdef"));
|
|
shown.key(Qt::Key_Return);
|
|
shown.type(QStringLiteral("ab"));
|
|
shown.key(Qt::Key_Return);
|
|
shown.type(QStringLiteral("abcdef"));
|
|
shown.key(Qt::Key_Home, Qt::ControlModifier);
|
|
shown.key(Qt::Key_End);
|
|
// Through a short line and back, as NSTextView.
|
|
shown.key(Qt::Key_Down);
|
|
QCOMPARE(shown.caret(), 9);
|
|
shown.key(Qt::Key_Down);
|
|
QCOMPARE(shown.caret(), 16);
|
|
shown.key(Qt::Key_Up);
|
|
shown.key(Qt::Key_Up);
|
|
QCOMPARE(shown.caret(), 6);
|
|
// An edit starts the column afresh.
|
|
shown.key(Qt::Key_Down);
|
|
shown.key(Qt::Key_Backspace);
|
|
shown.key(Qt::Key_Down);
|
|
QCOMPARE(shown.caret(), 10);
|
|
}
|
|
|
|
void InlineTextEditorTests::aWrappedParagraphMovesByItsLines()
|
|
{
|
|
TextCanvas shown;
|
|
shown.session.cancelText();
|
|
shown.session.beginText(QRectF(10, 10, 300, 400));
|
|
shown.type(QStringLiteral("alpha beta gamma"));
|
|
// One word a line: Home keeps to its line.
|
|
shown.key(Qt::Key_Home);
|
|
QCOMPARE(shown.editor().caretPosition(), 11);
|
|
shown.key(Qt::Key_Up);
|
|
QCOMPARE(shown.editor().caretPosition(), 6);
|
|
// A wrapped line's end stays before its breaking space.
|
|
shown.key(Qt::Key_End);
|
|
QCOMPARE(shown.editor().caretPosition(), 10);
|
|
shown.key(Qt::Key_Up);
|
|
QCOMPARE(shown.editor().caretPosition(), 4);
|
|
shown.key(Qt::Key_Down);
|
|
shown.key(Qt::Key_Down);
|
|
QVERIFY(shown.editor().caretPosition() > 11 && shown.editor().caretPosition() <= 16);
|
|
// The last line keeps its space: End goes past it.
|
|
shown.key(Qt::Key_End, Qt::ControlModifier);
|
|
shown.type(QStringLiteral(" "));
|
|
shown.key(Qt::Key_Home);
|
|
shown.key(Qt::Key_End);
|
|
QCOMPARE(shown.editor().caretPosition(), 17);
|
|
}
|
|
|
|
void InlineTextEditorTests::aCaretPastTheBoxHasNoLine()
|
|
{
|
|
TextCanvas shown;
|
|
shown.session.cancelText();
|
|
// Room for one line: "beta" falls past the bottom.
|
|
shown.session.beginText(QRectF(10, 10, 300, 120));
|
|
shown.type(QStringLiteral("alpha beta"));
|
|
shown.key(Qt::Key_Up);
|
|
QCOMPARE(shown.caret(), 10);
|
|
shown.key(Qt::Key_Home);
|
|
QCOMPARE(shown.caret(), 0);
|
|
// The laid line still wraps before its space.
|
|
shown.key(Qt::Key_End);
|
|
QCOMPARE(shown.caret(), 5);
|
|
shown.key(Qt::Key_Up);
|
|
QCOMPARE(shown.caret(), 0);
|
|
shown.key(Qt::Key_End);
|
|
shown.key(Qt::Key_Right);
|
|
shown.key(Qt::Key_End);
|
|
QCOMPARE(shown.caret(), 10);
|
|
}
|
|
|
|
void InlineTextEditorTests::deletionsReachCharactersWordsAndLines()
|
|
{
|
|
TextCanvas shown;
|
|
shown.type(QStringLiteral("alpha beta gamma"));
|
|
shown.key(Qt::Key_Backspace, Qt::AltModifier);
|
|
QCOMPARE(shown.content(), QString("alpha beta "));
|
|
shown.key(Qt::Key_Backspace, Qt::ControlModifier);
|
|
QCOMPARE(shown.content(), QString());
|
|
shown.type(QStringLiteral("alpha beta"));
|
|
shown.key(Qt::Key_Home);
|
|
shown.key(Qt::Key_Delete, Qt::AltModifier);
|
|
QCOMPARE(shown.content(), QString(" beta"));
|
|
shown.key(Qt::Key_Delete);
|
|
QCOMPARE(shown.content(), QString("beta"));
|
|
shown.key(Qt::Key_Delete, Qt::ControlModifier);
|
|
QCOMPARE(shown.content(), QString());
|
|
// A character as a reader counts it: the emoji whole.
|
|
shown.type(QStringLiteral("a"));
|
|
shown.typeText(QStringLiteral("😀"));
|
|
QCOMPARE(shown.content(), QString("a😀"));
|
|
shown.key(Qt::Key_Backspace);
|
|
QCOMPARE(shown.content(), QString("a"));
|
|
// A letter and its accent move and go as one.
|
|
shown.typeText(QStringLiteral("e\u0301"));
|
|
shown.key(Qt::Key_Left);
|
|
QCOMPARE(shown.caret(), 1);
|
|
shown.key(Qt::Key_Right);
|
|
QCOMPARE(shown.caret(), 3);
|
|
shown.key(Qt::Key_Backspace);
|
|
QCOMPARE(shown.content(), QString("a"));
|
|
// A selection goes first, whatever the key.
|
|
shown.type(QStringLiteral("bc"));
|
|
shown.key(Qt::Key_Left, Qt::ShiftModifier);
|
|
shown.key(Qt::Key_Left, Qt::ShiftModifier);
|
|
shown.key(Qt::Key_Delete, Qt::ControlModifier);
|
|
QCOMPARE(shown.content(), QString("a"));
|
|
// Shift adds nothing; the keypad's Delete deletes.
|
|
shown.type(QStringLiteral("b cd"));
|
|
shown.key(Qt::Key_Backspace, Qt::ShiftModifier);
|
|
QCOMPARE(shown.content(), QString("ab c"));
|
|
shown.key(Qt::Key_Backspace, Qt::ShiftModifier | Qt::AltModifier);
|
|
QCOMPARE(shown.content(), QString("ab "));
|
|
shown.key(Qt::Key_Home);
|
|
shown.key(Qt::Key_Delete, Qt::KeypadModifier);
|
|
QCOMPARE(shown.content(), QString("b "));
|
|
// At either end nothing goes, and the redo lasts.
|
|
shown.key(Qt::Key_Z, Qt::ControlModifier);
|
|
QCOMPARE(shown.content(), QString("ab "));
|
|
QCOMPARE(shown.caret(), 0);
|
|
shown.key(Qt::Key_Backspace);
|
|
shown.key(Qt::Key_End);
|
|
shown.key(Qt::Key_Delete);
|
|
shown.key(Qt::Key_Z, Qt::ControlModifier | Qt::ShiftModifier);
|
|
QCOMPARE(shown.content(), QString("b "));
|
|
}
|
|
|
|
void InlineTextEditorTests::returnTabEscapeAndFinishing()
|
|
{
|
|
TextCanvas shown;
|
|
shown.type(QStringLiteral("A"));
|
|
shown.key(Qt::Key_Return);
|
|
shown.key(Qt::Key_Tab);
|
|
shown.key(Qt::Key_Space);
|
|
shown.type(QStringLiteral("BM"));
|
|
QCOMPARE(shown.content(), QString("A\n\t BM"));
|
|
// Tool letters and Space type here: the canvas keeps none.
|
|
QCOMPARE(shown.session.tool(), NavigationTool::type);
|
|
QCOMPARE(shown.editor().logicalSize().height(), EditorSession::textBoxSize(shown.session.textDraft().value().style).height());
|
|
const size_t count = shown.session.document().value().layers.size();
|
|
shown.key(Qt::Key_Escape);
|
|
QVERIFY(!shown.session.textDraft() && !shown.canvas->inlineTextEditor());
|
|
QCOMPARE(shown.session.document().value().layers.size(), count);
|
|
shown.session.beginText(QPointF(40, 40), true);
|
|
shown.type(QStringLiteral("Kept"));
|
|
shown.key(Qt::Key_Return, Qt::ControlModifier);
|
|
QVERIFY(!shown.session.textDraft());
|
|
QCOMPARE(shown.session.activeLayer().value().liveText().value().style.content, QString("Kept"));
|
|
shown.session.editActiveText();
|
|
shown.key(Qt::Key_End);
|
|
shown.type(QStringLiteral("!"));
|
|
shown.key(Qt::Key_Enter, Qt::ControlModifier);
|
|
QCOMPARE(shown.session.activeLayer().value().liveText().value().style.content, QString("Kept!"));
|
|
}
|
|
|
|
void InlineTextEditorTests::altWithTheArrowsSetsSpacing()
|
|
{
|
|
TextCanvas shown;
|
|
shown.type(QStringLiteral("Spaced"));
|
|
const auto style = [&] { return shown.session.textDraft().value().style; };
|
|
shown.key(Qt::Key_Right, Qt::AltModifier);
|
|
QCOMPARE(style().tracking, 1.0);
|
|
shown.key(Qt::Key_Left, Qt::AltModifier | Qt::ShiftModifier);
|
|
QCOMPARE(style().tracking, -9.0);
|
|
// Up closes the lines from what Auto works out to.
|
|
shown.key(Qt::Key_Up, Qt::AltModifier);
|
|
QCOMPARE(style().leading, 72 * 1.2 - 1);
|
|
shown.key(Qt::Key_Down, Qt::AltModifier | Qt::ShiftModifier);
|
|
QCOMPARE(style().leading, 72 * 1.2 + 9);
|
|
shown.session.changeTextStyle([](LayerTextStyle &changed) { changed.leading = 5; });
|
|
shown.key(Qt::Key_Up, Qt::AltModifier | Qt::ShiftModifier);
|
|
QCOMPARE(style().leading, 1.0);
|
|
// Past the style's bounds nothing changes.
|
|
shown.session.changeTextStyle([](LayerTextStyle &changed) { changed.tracking = 995; });
|
|
shown.key(Qt::Key_Right, Qt::AltModifier | Qt::ShiftModifier);
|
|
QCOMPARE(style().tracking, 995.0);
|
|
QCOMPARE(shown.content(), QString("Spaced"));
|
|
QCOMPARE(shown.editor().caretPosition(), 6);
|
|
}
|
|
|
|
void InlineTextEditorTests::aFinalNewlineHasALineForTheCaret()
|
|
{
|
|
TextCanvas shown;
|
|
shown.session.cancelText();
|
|
// In a box, Return makes TextKit's extra line, the caret's.
|
|
shown.session.beginText(QRectF(10, 10, 300, 300));
|
|
shown.type(QStringLiteral("A"));
|
|
shown.key(Qt::Key_Return);
|
|
shown.key(Qt::Key_Home);
|
|
shown.type(QStringLiteral("B"));
|
|
QCOMPARE(shown.content(), QString("A\nB"));
|
|
// Point text keeps its measure; that line lies past it.
|
|
shown.session.cancelText();
|
|
shown.session.beginText(QPointF(20, 30), true);
|
|
shown.type(QStringLiteral("A"));
|
|
const QSizeF one = shown.editor().logicalSize();
|
|
shown.key(Qt::Key_Return);
|
|
QCOMPARE(shown.editor().logicalSize(), one);
|
|
shown.key(Qt::Key_Up);
|
|
QCOMPARE(shown.caret(), 0);
|
|
shown.key(Qt::Key_Down);
|
|
QCOMPARE(shown.caret(), 2);
|
|
shown.key(Qt::Key_Home);
|
|
shown.type(QStringLiteral("B"));
|
|
QCOMPARE(shown.content(), QString("A\nB"));
|
|
// A final U+2028 breaks the line the same way.
|
|
const QString separator(QChar::LineSeparator);
|
|
shown.session.cancelText();
|
|
shown.session.beginText(QRectF(10, 10, 300, 300));
|
|
shown.typeText(QStringLiteral("A") + separator);
|
|
shown.key(Qt::Key_Home);
|
|
shown.type(QStringLiteral("B"));
|
|
QCOMPARE(shown.content(), QStringLiteral("A") + separator + QStringLiteral("B"));
|
|
// A line ends before the separator that breaks it.
|
|
shown.key(Qt::Key_Home, Qt::ControlModifier);
|
|
shown.key(Qt::Key_End);
|
|
QCOMPARE(shown.caret(), 1);
|
|
}
|
|
|
|
void InlineTextEditorTests::arrowsFollowARightToLeftParagraph()
|
|
{
|
|
TextCanvas shown;
|
|
shown.typeText(QStringLiteral("אבג דה"));
|
|
// Right goes back through Hebrew, as Qt's fields move.
|
|
shown.key(Qt::Key_Home);
|
|
shown.key(Qt::Key_Left);
|
|
QCOMPARE(shown.caret(), 1);
|
|
shown.key(Qt::Key_Right);
|
|
QCOMPARE(shown.caret(), 0);
|
|
// A plain arrow collapses to its own side.
|
|
shown.key(Qt::Key_Left, Qt::ShiftModifier);
|
|
shown.key(Qt::Key_Left, Qt::ShiftModifier);
|
|
QCOMPARE(shown.selection(), (TextRange{0, 2}));
|
|
shown.key(Qt::Key_Right);
|
|
QCOMPARE(shown.selection(), (TextRange{0, 0}));
|
|
shown.key(Qt::Key_Left, Qt::ShiftModifier);
|
|
shown.key(Qt::Key_Left);
|
|
QCOMPARE(shown.selection(), (TextRange{1, 1}));
|
|
// Words go the same way.
|
|
shown.key(Qt::Key_Left, Qt::ControlModifier);
|
|
QCOMPARE(shown.caret(), 3);
|
|
shown.key(Qt::Key_Right, Qt::ControlModifier);
|
|
QCOMPARE(shown.caret(), 0);
|
|
// Each paragraph reads its own way, whatever parts them.
|
|
for (const QString &separator : {QStringLiteral("\n"), QStringLiteral("\r"), QString(QChar(0x2029))}) {
|
|
TextDraft draft = shown.session.textDraft().value();
|
|
draft.style.content = QStringLiteral("אב") + separator + QStringLiteral("ab");
|
|
shown.session.setTextDraft(draft);
|
|
shown.key(Qt::Key_End, Qt::ControlModifier);
|
|
shown.key(Qt::Key_Home);
|
|
shown.key(Qt::Key_Right);
|
|
QCOMPARE(shown.caret(), 4);
|
|
}
|
|
}
|
|
|
|
QTEST_MAIN(InlineTextEditorTests)
|
|
#include "InlineTextEditorTests.moc"
|