Javafx UndoFX:撤消/重做记录拖动的每个像素
我正在使用UndoFX和ReactFX为我的2D形状应用程序实现撤消/重做功能 问题是,当我移动形状时,EventStream会记录每个X/Y像素的移动。我只想记录最后一个位置(当用户释放拖动时) 到目前为止,我所尝试的: 而不是使用Javafx UndoFX:撤消/重做记录拖动的每个像素,javafx,reactfx,Javafx,Reactfx,我正在使用UndoFX和ReactFX为我的2D形状应用程序实现撤消/重做功能 问题是,当我移动形状时,EventStream会记录每个X/Y像素的移动。我只想记录最后一个位置(当用户释放拖动时) 到目前为止,我所尝试的: 而不是使用changesOf(rect.xProperty()).map(c->newxchange(c))和 changesOf(rect.yProperty()).map(c->newychange(c)) 我创建了一个DoubleProperty x,y,并在释放用户鼠
changesOf(rect.xProperty()).map(c->newxchange(c))代码>和
changesOf(rect.yProperty()).map(c->newychange(c))代码>
我创建了一个DoubleProperty x,y
,并在释放用户鼠标时将shape x,y属性保存到这些变量中。
最后,我将changesOf更改为:changesOf(this.x).map(c->newxchange(c))代码>和更改(this.y).map(c->newychange(c))代码>
但这并没有起作用,它的表现和以前一样
....
private class xChange extends RectangleChange<Double> {
public xChange(Double oldValue, Double newValue) {
super(oldValue, newValue);
}
public xChange(Change<Number> c) {
super(c.getOldValue().doubleValue(), c.getNewValue().doubleValue());
}
@Override void redo() { rect.setX(newValue); }
@Override xChange invert() { return new xChange(newValue, oldValue); }
@Override Optional<RectangleChange<?>> mergeWith(RectangleChange<?> other) {
if(other instanceof xChange) {
return Optional.of(new xChange(oldValue, ((xChange) other).newValue));
} else {
return Optional.empty();
}
}
@Override
public boolean equals(Object other) {
if(other instanceof xChange) {
xChange that = (xChange) other;
return Objects.equals(this.oldValue, that.oldValue)
&& Objects.equals(this.newValue, that.newValue);
} else {
return false;
}
}
}
...
EventStream<xChange> xChanges = changesOf(rect.xProperty()).map(c -> new xChange(c));
EventStream<yChange> yChanges = changesOf(rect.yProperty()).map(c -> new yChange(c));
changes = merge(widthChanges, heightChanges, xChanges, yChanges);
undoManager = UndoManagerFactory.unlimitedHistoryUndoManager(
changes, // stream of changes to observe
c -> c.invert(), // function to invert a change
c -> c.redo(), // function to undo a change
(c1, c2) -> c1.mergeWith(c2)); // function to merge two changes
。。。。
私有类xChange扩展了矩形更改{
公共交换(双倍旧值,双倍新值){
超级(旧值、新值);
}
公共交易所(更改c){
超级(c.getOldValue().doubleValue(),c.getNewValue().doubleValue());
}
@重写void redo(){rect.setX(newValue);}
@重写xChange invert(){返回新的xChange(newValue,oldValue);}
@覆盖可选您需要将x中的更改与y中的更改合并。目前,无法合并x中的更改和y中的更改,因此如果移动形状使其交替进行x和y更改(例如,对角移动),则每个单独的更改将不会与前一个更改合并
一种方法是生成更改,其新旧值均为位置,例如由Point2D
对象表示。下面是一个快速示例:
import java.util.Objects;
import java.util.Optional;
import org.fxmisc.undo.UndoManager;
import org.fxmisc.undo.UndoManagerFactory;
import org.reactfx.EventStream;
import org.reactfx.EventStreams;
import org.reactfx.SuspendableEventStream;
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.geometry.Insets;
import javafx.geometry.Point2D;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class UndoRectangle extends Application {
@Override
public void start(Stage primaryStage) {
Rectangle rect = new Rectangle(50, 50, 150, 100);
rect.setFill(Color.CORNFLOWERBLUE);
EventStream<PositionChange> xChanges = EventStreams.changesOf(rect.xProperty()).map(c -> {
double oldX = c.getOldValue().doubleValue();
double newX = c.getNewValue().doubleValue();
double y = rect.getY();
return new PositionChange(new Point2D(oldX, y), new Point2D(newX, y));
});
EventStream<PositionChange> yChanges = EventStreams.changesOf(rect.yProperty()).map(c -> {
double oldY = c.getOldValue().doubleValue();
double newY = c.getNewValue().doubleValue();
double x = rect.getX();
return new PositionChange(new Point2D(x, oldY), new Point2D(x, newY));
});
SuspendableEventStream<PositionChange> posChanges = EventStreams.merge(xChanges, yChanges)
.reducible(PositionChange::merge);
UndoManager undoManager = UndoManagerFactory.unlimitedHistoryUndoManager(posChanges,
PositionChange::invert,
c -> posChanges.suspendWhile(() -> {
rect.setX(c.getNewPosition().getX());
rect.setY(c.getNewPosition().getY());
}),
(c1, c2) -> Optional.of(c1.merge(c2))
);
class MouseLoc { double x, y ; }
MouseLoc mouseLoc = new MouseLoc();
rect.setOnMousePressed(e -> {
mouseLoc.x = e.getSceneX();
mouseLoc.y = e.getSceneY();
});
rect.setOnMouseDragged(e -> {
rect.setX(rect.getX() + e.getSceneX() - mouseLoc.x);
rect.setY(rect.getY() + e.getSceneY() - mouseLoc.y);
mouseLoc.x = e.getSceneX();
mouseLoc.y = e.getSceneY();
});
rect.setOnMouseReleased(e -> undoManager.preventMerge());
Pane pane = new Pane(rect);
Button undo = new Button("Undo");
undo.disableProperty().bind(Bindings.not(undoManager.undoAvailableProperty()));
undo.setOnAction(e -> undoManager.undo());
Button redo = new Button("Redo");
redo.disableProperty().bind(Bindings.not(undoManager.redoAvailableProperty()));
redo.setOnAction(e -> undoManager.redo());
HBox buttons = new HBox(5, undo, redo);
buttons.setAlignment(Pos.CENTER);
BorderPane.setMargin(buttons, new Insets(5));
BorderPane root = new BorderPane(pane, null, null, buttons, null);
Scene scene = new Scene(root, 600, 600);
primaryStage.setScene(scene);
primaryStage.show();
}
public static class PositionChange {
private final Point2D oldPosition ;
private final Point2D newPosition ;
public PositionChange(Point2D oldPos, Point2D newPos) {
this.oldPosition = oldPos ;
this.newPosition = newPos ;
}
public Point2D getOldPosition() {
return oldPosition;
}
public Point2D getNewPosition() {
return newPosition;
}
public PositionChange merge(PositionChange other) {
return new PositionChange(oldPosition, other.newPosition);
}
public PositionChange invert() {
return new PositionChange(newPosition, oldPosition);
}
@Override
public boolean equals(Object o) {
if (o instanceof PositionChange) {
PositionChange other = (PositionChange) o ;
return Objects.equals(oldPosition, other.oldPosition)
&& Objects.equals(newPosition, other.newPosition);
} else return false ;
}
@Override
public int hashCode() {
return Objects.hash(oldPosition, newPosition);
}
}
public static void main(String[] args) {
launch(args);
}
}
导入java.util.Objects;
导入java.util.Optional;
导入org.fxmisc.undo.UndoManager;
导入org.fxmisc.undo.UndoManagerFactory;
导入org.reactfx.EventStream;
导入org.reactfx.EventStreams;
导入org.reactfx.SuspendableEventStream;
导入javafx.application.application;
导入javafx.beans.binding.Bindings;
导入javafx.geometry.Insets;
导入javafx.geometry.Point2D;
导入javafx.geometry.Pos;
导入javafx.scene.scene;
导入javafx.scene.control.Button;
导入javafx.scene.layout.BorderPane;
导入javafx.scene.layout.HBox;
导入javafx.scene.layout.Pane;
导入javafx.scene.paint.Color;
导入javafx.scene.shape.Rectangle;
导入javafx.stage.stage;
公共类扩展应用程序{
@凌驾
公共无效开始(阶段primaryStage){
矩形rect=新矩形(50,50,150,100);
直毛(颜色:矢车菊蓝);
EventStream xChanges=EventStreams.changesOf(rect.xProperty()).map(c->{
double oldX=c.getOldValue().doubleValue();
double newX=c.getNewValue().doubleValue();
双y=rect.getY();
返回新位置更改(新点2D(旧X,y)、新点2D(新X,y));
});
EventStream yChanges=EventStreams.changesOf(rect.yProperty()).map(c->{
double oldY=c.getOldValue().doubleValue();
double newY=c.getNewValue().doubleValue();
double x=rect.getX();
返回新位置更改(新点2D(x,旧)、新点2D(x,新));
});
SuspendableEventStream posChanges=EventStreams.merge(xChanges,yChanges)
.可还原(位置更改::合并);
UndoManager UndoManager=UndoManagerFactory.unlimitedHistoryUndoManager(posChanges,
位置更改::反转,
c->posChanges.suspendWhile(()->{
rect.setX(c.getNewPosition().getX());
rect.setY(c.getNewPosition().getY());
}),
(c1,c2)->可选的(c1.合并(c2))
);
类MouseLoc{double x,y;}
MouseLoc MouseLoc=新的MouseLoc();
矩形设置鼠标按下(e->{
mouseLoc.x=e.getSceneX();
mouseLoc.y=e.getSceneY();
});
rect.setonMouseDrawed(e->{
rect.setX(rect.getX()+e.getSceneX()-mouseLoc.x);
rect.setY(rect.getY()+e.getSceneY()-mouseLoc.y);
mouseLoc.x=e.getSceneX();
mouseLoc.y=e.getSceneY();
});
rect.setOnMouseReleased(e->undoManager.preventMerge());
窗格=新窗格(矩形);
按钮撤消=新按钮(“撤消”);
undo.disableProperty().bind(Bindings.not(undoManager.undoAvailableProperty());
undo.setOnAction(e->undoManager.undo());
按钮重做=新按钮(“重做”);
redo.disableProperty().bind(Bindings.not(undoManager.redoavaailableProperty());
redo.setOnAction(e->undoManager.redo());
HBox按钮=新HBox(5,撤消,重做);
按钮。设置对齐(位置中心);
边框窗格。设置边距(按钮、新插图(5));
BorderPane根=新的BorderPane(窗格,null,null,按钮,null);
场景=新场景(root,600600);
初级阶段。场景(场景);
primaryStage.show();
}
公共静态类位置更改{
私人终点位置;
私人终点2D新位置;
公共位置更改(点2D旧位置、点2D新位置){
this.oldPosition=oldPos;
this.newPosition=newPos;
}
公共点2d getOldPosition(){
返回旧位置;
}
公共点2D getNewPosition(){
返回新位置;
}
公共职位变更合并(职位变更其他){
返回新位置更改(旧位置、其他.新位置);
}
公共位置更改反转(){
返回新位置更改(新位置、旧位置);
}
@凌驾
公共布尔等于(对象o){
如果(位置更改的o实例){
位置变更其他=(位置变更)o;
返回Objects.equals(oldPosition,other.oldPositi