mirror of
https://github.com/hneemann/Digital.git
synced 2025-09-13 06:49:36 -04:00
first working tutorial
This commit is contained in:
parent
8b5fe67016
commit
376a7259be
@ -452,7 +452,7 @@ public final class Keys {
|
||||
* enables the grid
|
||||
*/
|
||||
public static final Key<Boolean> SETTINGS_GRID
|
||||
= new Key<>("grid", false);
|
||||
= new Key<>("grid", true);
|
||||
|
||||
/**
|
||||
* enables the wire bits view
|
||||
@ -786,6 +786,14 @@ public final class Keys {
|
||||
* Circuit is generic
|
||||
*/
|
||||
public static final Key<Boolean> IS_GENERIC =
|
||||
new Key<Boolean>("isGeneric", false).setSecondary();
|
||||
new Key<>("isGeneric", false).setSecondary();
|
||||
|
||||
|
||||
/**
|
||||
* Enables the tutorial
|
||||
*/
|
||||
public static final Key<Boolean> SETTINGS_SHOW_TUTORIAL =
|
||||
new Key<>("showTutorial", true).setSecondary();
|
||||
|
||||
|
||||
}
|
||||
|
@ -43,6 +43,7 @@ import de.neemann.digital.gui.components.testing.TestAllDialog;
|
||||
import de.neemann.digital.gui.components.testing.ValueTableDialog;
|
||||
import de.neemann.digital.gui.components.tree.LibraryTreeModel;
|
||||
import de.neemann.digital.gui.components.tree.SelectTree;
|
||||
import de.neemann.digital.gui.tutorial.InitialTutorial;
|
||||
import de.neemann.digital.gui.release.CheckForNewRelease;
|
||||
import de.neemann.digital.gui.remote.DigitalHandler;
|
||||
import de.neemann.digital.gui.remote.RemoteException;
|
||||
@ -1837,6 +1838,9 @@ public final class Main extends JFrame implements ClosingWindowListener.ConfirmS
|
||||
}
|
||||
main.setVisible(true);
|
||||
|
||||
if (Settings.getInstance().getAttributes().get(Keys.SETTINGS_SHOW_TUTORIAL))
|
||||
new InitialTutorial(main).setVisible(true);
|
||||
|
||||
CheckForNewRelease.showReleaseDialog(main);
|
||||
});
|
||||
}
|
||||
|
@ -61,6 +61,7 @@ public final class Settings implements AttributeListener {
|
||||
intList.add(Keys.SETTINGS_TOOLCHAIN_CONFIG);
|
||||
intList.add(Keys.SETTINGS_FONT_SCALING);
|
||||
intList.add(Keys.SETTINGS_MAC_MOUSE);
|
||||
intList.add(Keys.SETTINGS_SHOW_TUTORIAL);
|
||||
|
||||
settingsKeys = Collections.unmodifiableList(intList);
|
||||
|
||||
|
@ -133,6 +133,7 @@ public class CircuitComponent extends JComponent implements ChangedListener, Lib
|
||||
private Mouse mouse = Mouse.getMouse();
|
||||
private Circuit shallowCopy;
|
||||
private CircuitScrollPanel circuitScrollPanel;
|
||||
private ModificationListener modificationListener;
|
||||
|
||||
|
||||
/**
|
||||
@ -429,6 +430,8 @@ public class CircuitComponent extends JComponent implements ChangedListener, Lib
|
||||
try {
|
||||
if (modification != null) {
|
||||
undoManager.apply(modification);
|
||||
if (modificationListener != null)
|
||||
modificationListener.modified(modification);
|
||||
if (circuitScrollPanel != null)
|
||||
circuitScrollPanel.sizeChanged();
|
||||
}
|
||||
@ -620,6 +623,16 @@ public class CircuitComponent extends JComponent implements ChangedListener, Lib
|
||||
getCircuit().clearState();
|
||||
}
|
||||
requestFocusInWindow();
|
||||
|
||||
if (modificationListener != null)
|
||||
modificationListener.modified(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if circuit is running
|
||||
*/
|
||||
public boolean isRunning() {
|
||||
return activeMouseController == mouseRun;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2314,6 +2327,15 @@ public class CircuitComponent extends JComponent implements ChangedListener, Lib
|
||||
new MouseControllerWizard(wizardNotification).activate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the modification listener.
|
||||
*
|
||||
* @param modificationListener is called every time the circuit is modified
|
||||
*/
|
||||
public void setModificationListener(ModificationListener modificationListener) {
|
||||
this.modificationListener = modificationListener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deactivate a wizard
|
||||
*/
|
||||
@ -2366,4 +2388,15 @@ public class CircuitComponent extends JComponent implements ChangedListener, Lib
|
||||
void closed();
|
||||
}
|
||||
|
||||
/**
|
||||
* Listener to get notified if the circuit has changed
|
||||
*/
|
||||
public interface ModificationListener {
|
||||
/**
|
||||
* Called if the circuit was modified
|
||||
*
|
||||
* @param modification the modification
|
||||
*/
|
||||
void modified(Modification<Circuit> modification);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,196 @@
|
||||
/*
|
||||
* Copyright (c) 2019 Helmut Neemann.
|
||||
* Use of this source code is governed by the GPL v3 license
|
||||
* that can be found in the LICENSE file.
|
||||
*/
|
||||
package de.neemann.digital.gui.tutorial;
|
||||
|
||||
import de.neemann.digital.core.NodeException;
|
||||
import de.neemann.digital.core.basic.XOr;
|
||||
import de.neemann.digital.core.element.ElementTypeDescription;
|
||||
import de.neemann.digital.core.element.Keys;
|
||||
import de.neemann.digital.core.io.In;
|
||||
import de.neemann.digital.core.io.Out;
|
||||
import de.neemann.digital.draw.elements.Circuit;
|
||||
import de.neemann.digital.draw.elements.PinException;
|
||||
import de.neemann.digital.draw.elements.VisualElement;
|
||||
import de.neemann.digital.draw.library.ElementNotFoundException;
|
||||
import de.neemann.digital.draw.model.ModelCreator;
|
||||
import de.neemann.digital.gui.Main;
|
||||
import de.neemann.digital.gui.Settings;
|
||||
import de.neemann.digital.gui.components.CircuitComponent;
|
||||
import de.neemann.digital.gui.components.modification.ModifyInsertWire;
|
||||
import de.neemann.digital.lang.Lang;
|
||||
import de.neemann.digital.undo.Modification;
|
||||
import de.neemann.digital.undo.Modifications;
|
||||
import de.neemann.gui.LineBreaker;
|
||||
import de.neemann.gui.Screen;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.WindowAdapter;
|
||||
import java.awt.event.WindowEvent;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* The tutorial dialog.
|
||||
*/
|
||||
public class InitialTutorial extends JDialog implements CircuitComponent.ModificationListener {
|
||||
private static final ArrayList<Step> STEPS = new ArrayList<>();
|
||||
|
||||
static {
|
||||
STEPS.add(new Step("tutorial1", (cc, mod, t) -> contains(cc, In.DESCRIPTION)));
|
||||
STEPS.add(new Step("tutorial2", (cc, mod, t) -> contains(cc, In.DESCRIPTION, In.DESCRIPTION)));
|
||||
STEPS.add(new Step("tutorial3", (cc, mod, t) -> contains(cc, In.DESCRIPTION, In.DESCRIPTION, XOr.DESCRIPTION)));
|
||||
STEPS.add(new Step("tutorial4", (cc, mod, t) -> contains(cc, In.DESCRIPTION, In.DESCRIPTION, XOr.DESCRIPTION, Out.DESCRIPTION)));
|
||||
STEPS.add(new Step("tutorial5", (cc, mod, t) -> contains(mod, ModifyInsertWire.class)));
|
||||
STEPS.add(new Step("tutorial6", (cc, mod, t) -> isWorking(cc)));
|
||||
STEPS.add(new Step("tutorial7", (cc, mod, t) -> cc.isRunning()));
|
||||
STEPS.add(new Step("tutorial8", (cc, mod, t) -> !cc.isRunning()));
|
||||
STEPS.add(new Step("tutorial9", (cc, mod, t) -> isIONamed(cc, 1, t)));
|
||||
STEPS.add(new Step("tutorial10", (cc, mod, t) -> isIONamed(cc, 3, t)));
|
||||
}
|
||||
|
||||
private static boolean isIONamed(CircuitComponent cc, int expected, InitialTutorial t) {
|
||||
HashSet<String> set = new HashSet<>();
|
||||
int num = 0;
|
||||
for (VisualElement ve : cc.getCircuit().getElements()) {
|
||||
if (ve.equalsDescription(In.DESCRIPTION) || ve.equalsDescription(Out.DESCRIPTION)) {
|
||||
String l = ve.getElementAttributes().getLabel();
|
||||
if (!l.isEmpty()) {
|
||||
if (set.contains(l)) {
|
||||
t.setTextByID("tutorialUniqueIdents");
|
||||
return false;
|
||||
}
|
||||
set.add(l);
|
||||
num++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return num == expected;
|
||||
}
|
||||
|
||||
private static boolean isWorking(CircuitComponent cc) {
|
||||
try {
|
||||
new ModelCreator(cc.getCircuit(), cc.getLibrary()).createModel(false);
|
||||
return true;
|
||||
} catch (PinException | NodeException | ElementNotFoundException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean contains(Modification<Circuit> mod, Class<? extends Modification> modifyClass) {
|
||||
if (mod == null)
|
||||
return false;
|
||||
if (mod.getClass() == modifyClass)
|
||||
return true;
|
||||
if (mod instanceof Modifications) {
|
||||
Modifications m = (Modifications) mod;
|
||||
for (Object i : m.getModifications())
|
||||
if (i.getClass() == modifyClass)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean contains(CircuitComponent cc, ElementTypeDescription... descriptions) {
|
||||
ArrayList<VisualElement> el = new ArrayList<>(cc.getCircuit().getElements());
|
||||
if (el.size() != descriptions.length)
|
||||
return false;
|
||||
for (ElementTypeDescription d : descriptions) {
|
||||
Iterator<VisualElement> it = el.iterator();
|
||||
while (it.hasNext()) {
|
||||
if (it.next().equalsDescription(d)) {
|
||||
it.remove();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return el.isEmpty();
|
||||
}
|
||||
|
||||
|
||||
private final JTextPane text;
|
||||
private final CircuitComponent circuitComponent;
|
||||
private int stepIndex;
|
||||
|
||||
/**
|
||||
* Creates the tutorial dialog.
|
||||
* @param main the main class
|
||||
*/
|
||||
public InitialTutorial(Main main) {
|
||||
super(main, Lang.get("tutorial"), false);
|
||||
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
|
||||
setAlwaysOnTop(true);
|
||||
circuitComponent = main.getCircuitComponent();
|
||||
circuitComponent.setModificationListener(this);
|
||||
|
||||
addWindowListener(new WindowAdapter() {
|
||||
@Override
|
||||
public void windowClosed(WindowEvent windowEvent) {
|
||||
circuitComponent.setModificationListener(null);
|
||||
}
|
||||
});
|
||||
|
||||
text = new JTextPane();
|
||||
text.setEditable(false);
|
||||
text.setFont(Screen.getInstance().getFont(1.2f));
|
||||
text.setPreferredSize(new Dimension(300, 400));
|
||||
|
||||
getContentPane().add(new JScrollPane(text));
|
||||
|
||||
pack();
|
||||
|
||||
final Point ml = main.getLocation();
|
||||
setLocation(ml.x - getWidth(), ml.y);
|
||||
|
||||
stepIndex = -1;
|
||||
incIndex();
|
||||
|
||||
}
|
||||
|
||||
private void incIndex() {
|
||||
stepIndex++;
|
||||
if (stepIndex == STEPS.size()) {
|
||||
Settings.getInstance().getAttributes().set(Keys.SETTINGS_SHOW_TUTORIAL, false);
|
||||
dispose();
|
||||
} else {
|
||||
setTextByID(STEPS.get(stepIndex).getId());
|
||||
}
|
||||
}
|
||||
|
||||
private void setTextByID(String id) {
|
||||
final String s = Lang.get(id);
|
||||
text.setText(new LineBreaker(1000).breakLines(s));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void modified(Modification<Circuit> modification) {
|
||||
if (STEPS.get(stepIndex).getChecker().accomplished(circuitComponent, modification, this))
|
||||
incIndex();
|
||||
}
|
||||
|
||||
private static final class Step {
|
||||
private final String id;
|
||||
private final Checker checker;
|
||||
|
||||
private Step(String id, Checker checker) {
|
||||
this.id = id;
|
||||
this.checker = checker;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public Checker getChecker() {
|
||||
return checker;
|
||||
}
|
||||
}
|
||||
|
||||
private interface Checker {
|
||||
boolean accomplished(CircuitComponent circuitComponent, Modification<Circuit> modification, InitialTutorial t);
|
||||
}
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
/*
|
||||
* Copyright (c) 2019 Helmut Neemann.
|
||||
* Use of this source code is governed by the GPL v3 license
|
||||
* that can be found in the LICENSE file.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Classes used to show the initial tutorial
|
||||
*/
|
||||
package de.neemann.digital.gui.tutorial;
|
@ -27,6 +27,13 @@ public final class Modifications<A extends Copyable<A>> implements Modification<
|
||||
m.modify(a);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The contained modifications
|
||||
*/
|
||||
public ArrayList<Modification<A>> getModifications() {
|
||||
return modifications;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name;
|
||||
|
@ -82,21 +82,27 @@ public class LineBreaker {
|
||||
|
||||
StringBuilder word = new StringBuilder();
|
||||
pos = indent;
|
||||
boolean lastLineBreak=false;
|
||||
for (int i = 0; i < text.length(); i++) {
|
||||
char c = text.charAt(i);
|
||||
switch (c) {
|
||||
case '\n':
|
||||
if (preserveLineBreaks) {
|
||||
if (preserveLineBreaks || lastLineBreak) {
|
||||
addWord(word);
|
||||
lineBreak();
|
||||
break;
|
||||
} else {
|
||||
addWord(word);
|
||||
lastLineBreak = true;
|
||||
}
|
||||
break;
|
||||
case '\r':
|
||||
case ' ':
|
||||
addWord(word);
|
||||
lastLineBreak = false;
|
||||
break;
|
||||
default:
|
||||
word.append(c);
|
||||
lastLineBreak = false;
|
||||
}
|
||||
}
|
||||
addWord(word);
|
||||
|
@ -1348,6 +1348,9 @@ Sind evtl. die Namen der Variablen nicht eindeutig?</string>
|
||||
<string name="key_isGeneric">Schaltung ist generisch</string>
|
||||
<string name="key_isGeneric_tt">Erlaubt die Erzeugung von generischen Schaltungen.</string>
|
||||
|
||||
<string name="key_showTutorial">Tutorial beim Start anzeigen</string>
|
||||
<string name="key_showTutorial_tt">Aktiviert das Tutorial.</string>
|
||||
|
||||
<string name="mod_insertWire">Leitung eingefügt.</string>
|
||||
<string name="mod_insertCopied">Aus Zwischenablage eingefügt.</string>
|
||||
<string name="mod_setKey_N0_in_element_N1">Wert ''{0}'' in Element ''{1}'' verändert.</string>
|
||||
@ -1949,4 +1952,57 @@ Daher steht auch das Signal 'D_out' zur Verfügung, um in diesem Fall den Wert z
|
||||
Gibt es aus einem Zustand keinen unbedingten Übergang, bleibt der Automat in diesem Zustand, wenn keine
|
||||
andere Übergangsbedingung erfüllt ist.
|
||||
</body></html>]]></string>
|
||||
|
||||
<string name="tutorial">Tutorial</string>
|
||||
<string name="tutorial1">Im Folgenden werden Sie mit einem kurzen Tutorial zur ersten
|
||||
Schaltung geführt:
|
||||
|
||||
Fügen Sie einen Eingang in die Schaltung ein.
|
||||
Sie finden den Eingang im Menu Bauteile▸IO.</string>
|
||||
<string name="tutorial2">Fügen Sie nun einen zweiten Eingang in die Schaltung ein.
|
||||
Sie können auch auf den Eingang in der Toolbar klicken.
|
||||
|
||||
Setzen Sie den zweiten Eingang am besten mit zwei Gitterabständen unter den ersten Eingang.
|
||||
Sie können die Schaltung verschieben, wenn Sie die rechte Maustaste gedrückt halten.
|
||||
Durch klicken auf die Eingänge können Sie diese verschieben.
|
||||
</string>
|
||||
<string name="tutorial3">Als nächstes soll ein "Exklusiv Oder" Gatter eingefügt werden.
|
||||
Sie finden dieses Gatter im Menu Bauteile▸Logisch.
|
||||
Setzen Sie dieses Bauteil mit zwei Gitterabständen rechts neben die Eingänge.
|
||||
</string>
|
||||
<string name="tutorial4">Als letztes Bauteil soll noch ein Ausgang eingefügt werden.
|
||||
Setzen Sie diesen mit ebenfalls zwei Gitterabständen rechts neben das "Exklusiv Oder" Gatter.
|
||||
</string>
|
||||
<string name="tutorial5">Um die Schaltung zu vervollständigen, sind Verbindungsleitungen zu ziehen.
|
||||
|
||||
Klicken Sie auf den roten Punkt am ersten Eingang und verbinden Sie diesen mit einem Eingang
|
||||
des "Exklusiv Oder" Gatters, indem Sie danach auf einen blauen Punkt des "Exklusiv Oder"
|
||||
Gatters klicken.
|
||||
</string>
|
||||
<string name="tutorial6">Verbinden Sie die roten Punkte der Eingänge mit den blauen Punkten des
|
||||
"Exklusiv Oder" Gatters und den roten Punkt des "Exklusiv Oder" Gatters mit dem blauen Punkte
|
||||
des Ausgangs.
|
||||
|
||||
Durch Klicken können Sie die Leitung anheften. Rechts-Klick bricht das Zeichnen der Leitung ab.
|
||||
</string>
|
||||
<string name="tutorial7">Damit ist Ihre erste Schaltung funktionsfähig.
|
||||
Um die Simulation zu starten, können Sie auf den Play-Knopf in der Toolbar klicken.
|
||||
</string>
|
||||
<string name="tutorial8">
|
||||
Die Simulation ist jetzt aktiv.
|
||||
Jetzt können Sie die Eingänge umschalten indem Sie darauf klicken.
|
||||
Um die Simulation zu beenden, klicken Sie auf den Stop-Knopf in der Toolbar.
|
||||
</string>
|
||||
<string name="tutorial9">
|
||||
Der Vollständigkeit halber sollen die Ein- und Ausgänge benannt werden.
|
||||
|
||||
Durch Rechts-Klick auf einen Ausgang öffnet sich ein Dialog.
|
||||
Hier kann der Ausgang mit einer Bezeichnung versehen werden.
|
||||
</string>
|
||||
<string name="tutorial10">
|
||||
Benennen Sie alle Ein- und Ausgänge.
|
||||
</string>
|
||||
<string name="tutorialUniqueIdents">
|
||||
Die Ein- und Ausgänge sollten eindeutig benannt sein.
|
||||
</string>
|
||||
</resources>
|
||||
|
@ -1334,6 +1334,9 @@
|
||||
<string name="key_isGeneric">Circuit is generic</string>
|
||||
<string name="key_isGeneric_tt">Allows to create a generic circuit.</string>
|
||||
|
||||
<string name="key_showTutorial">Show Tutorial at Startup</string>
|
||||
<string name="key_showTutorial_tt">Enables the tutorial.</string>
|
||||
|
||||
<string name="mod_insertWire">Inserted wire.</string>
|
||||
<string name="mod_insertCopied">Insert from clipboard.</string>
|
||||
<string name="mod_setKey_N0_in_element_N1">Value ''{0}'' in component ''{1}'' modified.</string>
|
||||
@ -1918,4 +1921,48 @@ Therefore, the signal 'D_out' is also available to check the value in this case.
|
||||
transition condition is met.
|
||||
</body></html>]]></string>
|
||||
|
||||
<string name="tutorial">Tutorial</string>
|
||||
<string name="tutorial1">In the following you will be guided to the first circuit with a short tutorial:
|
||||
|
||||
Add an input into the circuit. You will find the input in the menu Components▸IO.</string>
|
||||
<string name="tutorial2">Now add a second input to the circuit. You can also click on the input
|
||||
in the toolbar.
|
||||
|
||||
It is best to place the second input with two grid spacings under the first input.
|
||||
You can move the circuit by holding down the right mouse button.
|
||||
By clicking on the inputs you can move them.</string>
|
||||
<string name="tutorial3">Next, an "Exclusive Or" gate is to be inserted.
|
||||
You can find this gate in the menu Components▸Logic.
|
||||
Place this component with two grid spacings to the right of the inputs.</string>
|
||||
<string name="tutorial4">The last component to be inserted is an output.
|
||||
Set it with two grid spacings to the right of the "Exclusive Or" gate.
|
||||
</string>
|
||||
<string name="tutorial5">In order to complete the circuit, connecting wires must be drawn.
|
||||
|
||||
Click on the red dot at the first input and connect it to an input of the "Exclusive Or" gate,
|
||||
by clicking on a blue dot of the "Exclusive Or" gate.
|
||||
</string>
|
||||
<string name="tutorial6">Connect the red dots of the inputs to the blue dots of the
|
||||
"Exclusive Or" gate and the red dot of the "Exclusive Or" gate to the blue dot of the output.
|
||||
|
||||
You can pin the wire by clicking. Right-click cancels the drawing of the wire.
|
||||
</string>
|
||||
<string name="tutorial7">Your first circuit is now functional.
|
||||
To start the simulation, you can click on the Play button in the toolbar.
|
||||
</string>
|
||||
<string name="tutorial8">
|
||||
The simulation is now active. Now you can switch the inputs by clicking on them.
|
||||
To stop the simulation, click on the Stop button in the toolbar.
|
||||
</string>
|
||||
<string name="tutorial9">
|
||||
For completeness, the inputs and outputs should be labeled.
|
||||
|
||||
Right-click on an output to open a dialog. Here the output can be given a name.</string>
|
||||
<string name="tutorial10">
|
||||
Label all inputs and outputs.
|
||||
</string>
|
||||
<string name="tutorialUniqueIdents">
|
||||
The inputs and outputs should be uniquely named.
|
||||
</string>
|
||||
|
||||
</resources>
|
@ -68,7 +68,7 @@ public class TestLang extends TestCase {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (String key : map.keySet()) {
|
||||
if (!keys.contains(key)) {
|
||||
if (!(key.startsWith("key_") || key.startsWith("elem_"))) {
|
||||
if (!(key.startsWith("key_") || key.startsWith("elem_") || key.startsWith("tutorial"))) {
|
||||
if (sb.length() > 0)
|
||||
sb.append(", ");
|
||||
sb.append('"').append(key).append('"');
|
||||
|
@ -12,8 +12,8 @@ import junit.framework.TestCase;
|
||||
public class LineBreakerTest extends TestCase {
|
||||
|
||||
public void testBreakLines() throws Exception {
|
||||
assertEquals("this is a test string", new LineBreaker(60).breakLines("this \n\n is \n a test \n\r string"));
|
||||
assertEquals("this is a test\nstring", new LineBreaker(14).breakLines("this \n\n is \n a test \n\r string"));
|
||||
assertEquals("this\nis a test string", new LineBreaker(60).breakLines("this \n\n is \n a test \n\r string"));
|
||||
assertEquals("this\nis a test\nstring", new LineBreaker(14).breakLines("this \n\n is \n a test \n\r string"));
|
||||
assertEquals("This is a test string. This\n" +
|
||||
"is a test string. This is a\n" +
|
||||
"test string.", new LineBreaker(27).breakLines("This is a test string. This is a test string. This is a test string."));
|
||||
|
Loading…
x
Reference in New Issue
Block a user