- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
如何在 ReactFX 中正确组合多个属性更改流以用于 UndoFX(或任何用例)?
这是我想要完成的任务的简短说明(完整的示例代码是 posted at GitHub ):
有一个具有两个属性的示例模型。为了简单起见,它们是双重属性
public class DataModel {
private DoubleProperty a, b;
//...
//with appropriate getters, setters, equals, hashcode
//...
}
在示例代码中,有一些按钮可以更改一个或两个属性。如果这就是更改,我想撤消对两者的更改。
根据 UndoFX 示例,还有从基类继承的每个更改类(此处也缩写):
public abstract class ChangeBase<T> implements UndoChange {
protected final T oldValue, newValue;
protected final DataModel model;
protected ChangeBase(DataModel model, T oldValue, T newValue) {
this.model = model;
this.oldValue = oldValue;
this.newValue = newValue;
}
public abstract ChangeBase<T> invert();
public abstract void redo();
public Optional<ChangeBase<?>> mergeWith(ChangeBase<?> other) {
return Optional.empty();
}
@Override
public int hashCode() {
return Objects.hash(this.oldValue, this.newValue);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final ChangeBase<?> other = (ChangeBase<?>) obj;
if (!Objects.equals(this.oldValue, other.oldValue)) {
return false;
}
if (!Objects.equals(this.newValue, other.newValue)) {
return false;
}
if (!Objects.equals(this.model, other.model)) {
return false;
}
return true;
}
}
public class ChangeA extends ChangeBase<Double> {
//...
//constructors and other method implementations
//..
@Override
public void redo() {
System.out.println("ChangeA redo "+this);
this.model.setA(this.newValue);
}
}
public class ChangeB extends ChangeBase<Double> {
//...
//constructors and other method implementations
//...
@Override
public void redo() {
System.out.println("ChangeA redo "+this);
this.model.setB(this.newValue);
}
}
所有更改都实现一个接口(interface)
public interface UndoChange {
public void redo();
public UndoChange invert();
public Optional<UndoChange> mergeWith(UndoChange other);
}
阅读完文档后,我首先创建一个事件流来捕获每个属性的更改:
EventStream<UndoChange> changeAStream =
EventStreams.changesOf(model.aProperty())
.map(c -> new ChangeA(model, (Change<Number>)c));
EventStream<UndoChange> changeBStream =
EventStreams.changesOf(model.bProperty())
.map(c -> new ChangeB(model, (Change<Number>)c));
我的第一次尝试是像这样合并流
EventStream<UndoChange> bothStream = EventStreams.merge(changeAStream, changeBStream);
在这种情况下发生的情况是,如果 A 和 B 属性同时更改,则流中将有两个更改,并且每个更改将单独撤消而不是一起撤消。每次调用 setter 都会在适当的流中放入一个更改,然后将其发送到 bothStream
,其中包含两个单独的事件,而不是一个。
经过更多阅读后,我尝试将流和映射合并到一个单独的更改对象中:
EventStream<UndoChange> bothStream = EventStreams.combine(changeAStream, changeBStream).map(ChangeBoth::new);
其中 ChangeBoth
定义为:
public class ChangeBoth implements UndoChange {
private final ChangeA aChange;
private final ChangeB bChange;
public ChangeBoth(ChangeA ac, ChangeB bc) {
this.aChange = ac;
this.bChange = bc;
}
public ChangeBoth(Tuple2<UndoChange, UndoChange> tuple) {
this.aChange = ((ChangeBoth)tuple.get1()).aChange;
this.bChange = ((ChangeBoth)tuple.get2()).bChange;
}
@Override
public UndoChange invert() {
System.out.println("ChangeBoth invert "+this);
return new ChangeBoth(new ChangeA(this.aChange.model, this.aChange.newValue, this.aChange.oldValue),
new ChangeB(this.bChange.model, this.bChange.newValue, this.bChange.oldValue));
}
@Override
public void redo() {
System.out.println("ChangeBoth redo "+this);
DataModel model = this.aChange.model;
model.setA(this.aChange.newValue);
model.setB(this.bChange.newValue);
}
//...
// plus appropriate mergeWith, hashcode, equals
//...
}
这会导致抛出IllegalStateException:收到意外更改
。经过一番挖掘,我确定了为什么会发生这种情况:当 ChangeBoth
被撤消时(通过 invert()
和 redo()
调用),它将每个属性设置回旧值。但是,当它设置每个属性时,更改会通过流发回,从而在将两个属性设置回旧值之间将新的 ChangeBoth
放入流中。
回到我的问题:正确的方法是什么?有没有一种方法可以将两个属性的更改流合并到一个更改对象中,而不会导致此问题?
根据 Tomas 的回答,我添加/更改了以下代码(注意:存储库中的代码已更新):
changeAStream
和 changeBstream
保持不变。
我没有合并流,而是按照 Tomas 的建议创建了一个二元运算符来将两个更改减少为一个:
BinaryOperator<UndoChange> abOp = (c1, c2) -> {
ChangeA ca = null;
if(c1 instanceof ChangeA) {
ca = (ChangeA)c1;
}
ChangeB cb = null;
if(c2 instanceof ChangeB) {
cb = (ChangeB)c2;
}
return new ChangeBoth(ca, cb);
};
并将事件流更改为
SuspendableEventStream<UndoChange> bothStream = EventStreams.merge(changeAStream, changeBStream).reducible(abOp);
现在按钮操作未在 setonAction
中实现,而是通过事件流处理
EventStreams.eventsOf(bothButton, ActionEvent.ACTION)
.suspenderOf(bothStream)
.subscribe((ActionEvent event) ->{
System.out.println("stream action");
model.setA(Math.random()*10.0);
model.setB(Math.random()*10.0);
});
这对于适当组合事件非常有效,但撤消 A+B 更改仍然会中断。它适用于单独的 A 和 B 更改。这是两个 A+B 更改然后撤消的示例
A+B Button Action in event stream
Change in A stream
Change in B stream
A+B Button Action in event stream
Change in A stream
Change in B stream
ChangeBoth attempting merge with combinedeventstreamtest.ChangeBoth@775ec8e8... merged
undo 6.897901340713284 2.853416510829745
ChangeBoth invert combinedeventstreamtest.ChangeBoth@aae83334
ChangeA invert combinedeventstreamtest.ChangeA@32ee049a
ChangeB invert combinedeventstreamtest.ChangeB@4919dd13
ChangeBoth redo combinedeventstreamtest.ChangeBoth@b2155b1e
Change in A stream
Exception in thread "JavaFX Application Thread" java.lang.IllegalArgumentException: Unexpected change received.
Expected:
combinedeventstreamtest.ChangeBoth@b2155b1e
Received:
combinedeventstreamtest.ChangeA@2ad21e08
Change in B stream
托马斯很友善地指出了解决方案(我应该已经意识到了)。只需在执行 redo()
时暂停即可:
UndoManager<UndoChange> um = UndoManagerFactory.unlimitedHistoryUndoManager(
bothStream,
c -> c.invert(),
c -> bothStream.suspendWhile(c::redo),
(c1, c2) -> c1.mergeWith(c2)
);
最佳答案
因此,任务是将 bothStream
发出的更改合并为一个。在处理按钮点击期间。
您将需要一个函数来减少两个 UndoChange
合二为一:
BinaryOperator<UndoChange> reduction = ???; // implement as appropriate
制造bothStream
在“暂停”时将更改减少为一项:
SuspendableEventStream<UndoChange> bothStream =
EventStreams.merge(changeAStream, changeBStream).reducible(reduction);
现在您只需暂停 bothStream
在处理按钮点击时。这可以这样完成:
EventStreams.eventsOf(bothButton, ActionEvent.ACTION) // Observe actions of bothButton ...
.suspenderOf(bothStream) // but suspend bothStream ...
.subscribe((ActionEvent event) -> { // before handling the action.
model.setA(Math.random()*10.0);
model.setB(Math.random()*10.0);
})
同时暂停bothStream
当从撤消管理器中撤消/重做更改时,从 bothStream
发出(组合)更改的精确逆撤消组合更改时(这是使 UndoManager
满意所必需的)。这可以通过包装 apply
来完成UndoManager
的参数bothStream.suspendWhile()
中的构造函数,例如:
UndoManagerFactory.unlimitedHistoryUndoManager(
bothStream,
c -> c.invert(),
c -> bothStream.suspendWhile(c::redo) // suspend while applying the change
)
关于java - 在 ReactFX 中组合多个变更流,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47803831/
我有一个相当简单的 iPhone 应用程序,它将一组 UITableView 结果下载到其模型类中,以及一个设置为观察这些更改的 View Controller using KVO . 这个系统运行良
我们的一名团队成员(位于不同地区)已搁置 P4 中的更改,更改列表为 1234。 现在,如果我想查看 snf 修改了哪些文件,有哪些更改,我该怎么做? 我应该使用什么 P4 命令来查看我们的团队成员所
有没有办法获取特定分支的特定变更列表之后的变更列表列表? p4 changes (some flag ?) (CL#) //depot/project 最佳答案 这可以使用以下语法来完成(假设您想查看
我是 drupal 的新手,但学得很快。我有 drupal 7,我正在努力创建一个基于 Zen 的 starterkit 子主题的主题。我正在尝试找到可以自定义“提交者”行的位置。 默认情况如下: 由
我正在尝试将附件的 ShareKit API 代码更改为以下代码: dialog.attachment = [NSString stringWithFormat:@"{\"name\":\"%@\",
所以我的理解是,在 symfony 开发环境中,如果 use_controller 设置为 true,则 Assets 是通过 Controller 在页面加载时生成的。 然而,这非常耗时,并且将其设
我正在编写一个程序,它需要了解一台机器的所有 IP 地址,并且数据包通过它们的连接进行传输。我可以在运行主程序之前使用“gethostbyname”获取 IP 列表,但是如果之后有任何 IP 可用怎么
我很好奇是否有人可以概述服务器端哪些类型的 WCF 合约(接口(interface))更改会破坏尝试发送消息的客户端,以及原因。我相信 WCF 可以处理某些差异,但我不确定您可以安全地更改哪些内容以及
在 AngularJS 中,创建和使用自定义服务进行 DOM 操作并在 UI 的不同部分共享相同的功能是一种常见的做法,我的问题也与此案例相关,如下所示: 我的应用程序中有一个侧边栏组件和一个用于最小
我尝试使用像 WPF-MVVM 这样的 Knockout。 在 WPF 中,有一个选项可以在属性更改时通知虚拟机。 例如:如果我有一个文本框,我可以使用 UpdateSourceTrigger=Pro
我需要一些使用 Apollo 2.1 中新的查询和突变组件的帮助,尤其是多个查询和突变。 我有以下问题: 我有一个 graphql 请求,该请求依赖于之前的 graphql 结果,我该如何处理这个问题
Paypal 似乎已经改变了他们的 IPN 测试界面。 (将 IPN 欺骗到您的 IPN 页面以进行测试。 以下是我现在从IPN收到的数据。 KEY: receipt_ID - VALUE: KEY:
从雅虎财经请求数据似乎已经改变或现在被阻止。以下商品数据请求自 2017 年 5 月起不再有效。有谁知道是否有新的方式来提出此请求? http://chartapi.finance.yahoo.c
In this post他们在 LinkedIn 上提到开放 API 仍将支持与公司 API 共享内容,但如果您进一步查看他们的 Developer Program Transition Guide他
我对 Xcode 很陌生,我想在添加可能会破坏我的项目的更改之前弄清楚版本控制。我正在单独处理这个项目,并且在我的 mac 上保存了一个 Git 存储库,所以我认为我不需要将它与 GitHub 帐户链
我是一名优秀的程序员,十分优秀!