Skip to content

Commit

Permalink
Add redo/undoStackSize properties to track stack sizes (#53)
Browse files Browse the repository at this point in the history
* Add redo/undoStackSize properties to track stack sizes

* Use boolean and move stackEmpty properties to viewModel only

* Fix tests

Co-authored-by: jose.pereda <jose.pereda@gluonhq.com>
  • Loading branch information
jperedadnr and jperedadnr authored Feb 24, 2022
1 parent 77fd317 commit 0989e1a
Show file tree
Hide file tree
Showing 8 changed files with 209 additions and 45 deletions.
9 changes: 7 additions & 2 deletions src/main/java/com/gluonhq/Main.java
Original file line number Diff line number Diff line change
Expand Up @@ -91,14 +91,19 @@ public Double fromString(String s) {
CheckBox editableProp = new CheckBox("Editable");
editableProp.selectedProperty().bindBidirectional(editor.editableProperty());

Button undoButton = actionButton(LineAwesomeSolid.UNDO, editor.getActionFactory().undo());
undoButton.disableProperty().bind(editor.undoStackEmptyProperty());
Button redoButton = actionButton(LineAwesomeSolid.REDO, editor.getActionFactory().redo());
redoButton.disableProperty().bind(editor.redoStackEmptyProperty());

ToolBar toolbar = new ToolBar();
toolbar.getItems().setAll(
actionButton(LineAwesomeSolid.CUT, editor.getActionFactory().cut()),
actionButton(LineAwesomeSolid.COPY, editor.getActionFactory().copy()),
actionButton(LineAwesomeSolid.PASTE, editor.getActionFactory().paste()),
new Separator(Orientation.VERTICAL),
actionButton(LineAwesomeSolid.UNDO, editor.getActionFactory().undo()),
actionButton(LineAwesomeSolid.REDO, editor.getActionFactory().redo()),
undoButton,
redoButton,
new Separator(Orientation.VERTICAL),
fontFamilies,
fontSize,
Expand Down
45 changes: 25 additions & 20 deletions src/main/java/com/gluonhq/richtext/RichTextArea.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,16 @@


import com.gluonhq.richtext.viewmodel.ActionFactory;
import javafx.beans.property.*;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ReadOnlyBooleanProperty;
import javafx.beans.property.ReadOnlyBooleanWrapper;
import javafx.beans.property.ReadOnlyIntegerProperty;
import javafx.beans.property.ReadOnlyIntegerWrapper;
import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.scene.control.Control;
import javafx.scene.control.SkinBase;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;

import java.util.Objects;

Expand Down Expand Up @@ -61,22 +66,23 @@ public final int getTextLength() {
return textLengthProperty.get();
}

// // codecProperty
// private final ObjectProperty<Codec> codecProperty = new SimpleObjectProperty<>(this, "codec");
// public final ObjectProperty<Codec> codecProperty() {
// return codecProperty;
// }
// public final Codec getCodec() {
// return codecProperty.get();
// }
// public final void setCodec(Codec value) {
// codecProperty.set(value);
// }
//
// public interface Codec {
// OutputStream decode(List<Node> nodes);
// List<Node> encode(InputStream stream);
// }
// undoStackSizeProperty
final ReadOnlyBooleanWrapper undoStackEmptyProperty = new ReadOnlyBooleanWrapper(this, "undoStackEmpty");
public ReadOnlyBooleanProperty undoStackEmptyProperty() {
return undoStackEmptyProperty.getReadOnlyProperty();
}
public boolean isUndoStackEmpty() {
return undoStackEmptyProperty.get();
}

// redoStackSizeProperty
final ReadOnlyBooleanWrapper redoStackEmptyProperty = new ReadOnlyBooleanWrapper(this, "redoStackEmpty");
public ReadOnlyBooleanProperty redoStackEmptyProperty() {
return redoStackEmptyProperty.getReadOnlyProperty();
}
public boolean isRedoStackEmpty() {
return redoStackEmptyProperty.get();
}

public void execute( Action action ) {
if ( getSkin() instanceof RichTextAreaSkin ) {
Expand All @@ -90,6 +96,5 @@ public ActionFactory getActionFactory() {
return RichTextAreaSkin.getActionFactory();
}


}

4 changes: 3 additions & 1 deletion src/main/java/com/gluonhq/richtext/RichTextAreaSkin.java
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ interface ActionBuilder extends Function<KeyEvent, Action>{}
entry( new KeyCodeCombination(X, SHORTCUT_DOWN), e -> ACTION_FACTORY.cut()),
entry( new KeyCodeCombination(V, SHORTCUT_DOWN), e -> ACTION_FACTORY.paste()),
entry( new KeyCodeCombination(Z, SHORTCUT_DOWN), e -> ACTION_FACTORY.undo()),
entry( new KeyCodeCombination(Z, SHORTCUT_DOWN, SHIFT_DOWN), e -> ACTION_FACTORY.paste()),
entry( new KeyCodeCombination(Z, SHORTCUT_DOWN, SHIFT_DOWN), e -> ACTION_FACTORY.redo()),
entry( new KeyCodeCombination(ENTER, SHIFT_ANY), e -> ACTION_FACTORY.insertText("\n")),
entry( new KeyCodeCombination(BACK_SPACE, SHIFT_ANY), e -> ACTION_FACTORY.removeText(-1)),
entry( new KeyCodeCombination(DELETE), e -> ACTION_FACTORY.removeText(0)),
Expand Down Expand Up @@ -114,6 +114,8 @@ protected RichTextAreaSkin(final RichTextArea control) {
control.editableProperty().addListener(this::editableChangeListener);
editableChangeListener(null); // sets up all related listeners
control.textLengthProperty.bind(viewModel.textLengthProperty());
control.undoStackEmptyProperty.bind(viewModel.undoStackEmptyProperty());
control.redoStackEmptyProperty.bind(viewModel.redoStackEmptyProperty());

//TODO remove listener on viewModel change
viewModel.caretPositionProperty().addListener( (o,ocp, p) -> {
Expand Down
12 changes: 6 additions & 6 deletions src/main/java/com/gluonhq/richtext/model/PieceTable.java
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,8 @@ Piece appendTextInternal(String text, TextDecoration decoration) {
* @param text new text
*/
@Override
public void append( String text ) {
commander.execute( new AppendCmd(text));
public void append(String text) {
commander.execute(new AppendCmd(text));
}

@Override
Expand All @@ -135,8 +135,8 @@ public void walkFragments(BiConsumer<String, TextDecoration> onFragment) {
* @throws IllegalArgumentException if insertPosition is not valid
*/
@Override
public void insert( final String text, final int insertPosition ) {
commander.execute( new InsertCmd(text, insertPosition));
public void insert(final String text, final int insertPosition) {
commander.execute(new InsertCmd(text, insertPosition));
}

/**
Expand All @@ -146,8 +146,8 @@ public void insert( final String text, final int insertPosition ) {
* @throws IllegalArgumentException if deletePosition is not valid
*/
@Override
public void delete( final int deletePosition, int length ) {
commander.execute( new DeleteCmd(deletePosition, length));
public void delete(final int deletePosition, int length) {
commander.execute(new DeleteCmd(deletePosition, length));
}

/**
Expand Down
1 change: 1 addition & 0 deletions src/main/java/com/gluonhq/richtext/model/TextBuffer.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.gluonhq.richtext.model;

import javafx.beans.property.ReadOnlyBooleanProperty;
import javafx.beans.property.ReadOnlyIntegerProperty;

import java.text.CharacterIterator;
Expand Down
26 changes: 23 additions & 3 deletions src/main/java/com/gluonhq/richtext/undo/CommandManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,30 @@ public class CommandManager<T> {
final Deque<AbstractCommand<T>> undoStack = new ArrayDeque<>();
final Deque<AbstractCommand<T>> redoStack = new ArrayDeque<>();
final T context;
private final Runnable runnable;

public CommandManager(T context ) {
public CommandManager(T context) {
this(context, null);
}

public CommandManager(T context, Runnable runnable) {
this.context = context;
this.runnable = runnable;
}

public void execute( AbstractCommand<T> cmd ) {
public void execute(AbstractCommand<T> cmd) {
Objects.requireNonNull(cmd).redo(context);
undoStack.push(cmd);
redoStack.clear();
end();
}

public void undo() {
if (!undoStack.isEmpty()) {
var cmd = undoStack.pop();
cmd.undo(context);
redoStack.push(cmd);

end();
}
}

Expand All @@ -34,8 +41,21 @@ public void redo() {
var cmd = redoStack.pop();
cmd.redo(context);
undoStack.push(cmd);
end();
}
}

public boolean isUndoStackEmpty() {
return undoStack.isEmpty();
}

public boolean isRedoStackEmpty() {
return redoStack.isEmpty();
}

private void end() {
if (runnable != null) {
runnable.run();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
import com.gluonhq.richtext.undo.CommandManager;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ReadOnlyBooleanProperty;
import javafx.beans.property.ReadOnlyBooleanWrapper;
import javafx.beans.property.ReadOnlyIntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleObjectProperty;
Expand All @@ -29,7 +31,7 @@ public class RichTextAreaViewModel {
public enum Direction { FORWARD, BACK, UP, DOWN }

private final TextBuffer textBuffer;
private final CommandManager<RichTextAreaViewModel> commandManager = new CommandManager<>(this);
private final CommandManager<RichTextAreaViewModel> commandManager = new CommandManager<>(this, this::updateProperties);
private BreakIterator wordIterator;


Expand Down Expand Up @@ -80,6 +82,24 @@ public final int getTextLength() {
return textBuffer.getTextLength();
}

// undoStackSizeProperty
final ReadOnlyBooleanWrapper undoStackEmptyProperty = new ReadOnlyBooleanWrapper(this, "undoStackEmpty", true);
public ReadOnlyBooleanProperty undoStackEmptyProperty() {
return undoStackEmptyProperty.getReadOnlyProperty();
}
public boolean isUndoStackEmpty() {
return undoStackEmptyProperty.get();
}

// redoStackSizeProperty
final ReadOnlyBooleanWrapper redoStackEmptyProperty = new ReadOnlyBooleanWrapper(this, "redoStackEmpty", true);
public ReadOnlyBooleanProperty redoStackEmptyProperty() {
return redoStackEmptyProperty.getReadOnlyProperty();
}
public boolean isRedoStackEmpty() {
return redoStackEmptyProperty.get();
}

public RichTextAreaViewModel(TextBuffer textBuffer, BiFunction<Double, Boolean, Integer> getNextRowPosition) {
this.textBuffer = Objects.requireNonNull(textBuffer); // TODO convert to property
this.getNextRowPosition = Objects.requireNonNull(getNextRowPosition);
Expand Down Expand Up @@ -313,4 +333,9 @@ private void lineEnd() {
int pos = getNextRowPosition.apply(Double.MAX_VALUE, false);
setCaretPosition(pos);
}

private void updateProperties() {
undoStackEmptyProperty.set(commandManager.isUndoStackEmpty());
redoStackEmptyProperty.set(commandManager.isRedoStackEmpty());
}
}
Loading

0 comments on commit 0989e1a

Please sign in to comment.