在 reactive-banana 中执行单个开关
Performing a single switch in reactive-banana
我正在使用 reactive-banana
构建一个多模态编辑器 - 在大多数情况下它会很完美。为了扩展我的场景,编辑器是一些绘图软件,或者您可以将其视为一个非常简单的矢量图形编辑器。它目前有两种状态——select离子模式和多边形创建模式。在 selection 模式下,用户可以使用鼠标右键 select 先前创建的多边形(理论上这会带您进入新的 selected 模式) 或者他们可以用鼠标左键开始创建一个新的多边形。
目的是,当按下鼠标左键时,我们从 selection 模式切换到多边形创建模式。在此模式下,鼠标左键意味着 "add a new vertex",直到用户 return 到达原始顶点。此时,他们已经关闭了多边形,所以我们return到select离子模式。
我已经用几种不同的方式实现了这一点,最近发现事件切换几乎使它变得非常优雅。我可以拥有:
defaultMode :: Frameworks t => HadoomGUI -> Moment t (Behavior t Diagram)
defaultMode gui@HadoomGUI{..} =
do mouseMoved <- registerMotionNotify guiMap
mouseClicked <- registerMouseClicked guiMap
let lmbClicked = ...
gridCoords = ...
diagram = ...
switchToCreateSector <- execute ((\m ->
FrameworksMoment
(=<< trimB =<< createSectorMode gui emptySectorBuilder m)) <$>
(gridCoords <@ lmbClicked))
return (switchB diagram switchToCreateSector)
与
一起
createSectorMode :: Frameworks t
=> HadoomGUI
-> SectorBuilder
-> Point V2 Double
-> Moment t (Behavior t Diagram)
createSectorMode HadoomGUI{..} initialSectorBuilder firstVertex =
do mouseClicked <- registerMouseClicked guiMap
...
这确实有效 - 只需单击一下鼠标。如果我点击一次地图,我会从刚才的状态切换到扇区创建模式。但是,如果我再次点击,defaultMode
会收到点击事件并切换进入 新 多边形创建模式,抛弃我以前的状态。
我想做的是 defaultMode
切换一次,再也没有回来的可能。本质上我想 "swap" defaultMode
产生的 Behavior t Diagram
和 createSectorMode
.
的结果
我知道 reactive-banana
在动态事件的垃圾回收方面存在问题,但我暂时愿意接受它。上面的公式比我迄今为止所写的任何其他公式都要精确得多——例如有一个 CurrentState
变量并根据它的内容过滤各种事件。我遇到的问题是它太大了,给我留下了太多的余地来把事情搞砸。通过切换,我只能在范围内处理我能处理的事件。
这个问题有点开放性,所以我不能给出明确的答案。但我当然可以发表我的意见。 ;-)
但是,我可能会做的是将在 模式之间的切换与在 模式内的行为分开。如果我们暂时忘记 FRP,您的程序看起来有点像递归调用自身的一对函数:
defaultMode = ... `andthen` sectorMode
sectorMode = ... `andthen` defaultMode
写的有点像"sequential"程序,"first do this mode, then do that mode"。我认为这没什么问题,尽管默认的 API reactive-banana,特别是 switchB
并不能很好地支持这种风格。你提到(私下)你可以写一个
once :: Event t a -> Event t a
允许第一次出现的事件通过但丢弃其余事件的组合器。这确实是顺序样式所需要的。
不过,由于您总是 return 默认模式,我可能会尝试一种不同的方法,其中每种模式都有一个事件表明它想要切换。切换本身由 "outside" 实体负责。这个想法是为了避免上面程序中某些高阶组合器的显式递归。在伪代码中,这看起来像这样:
modeManager = switchB initialMode changeMode
changeMode = defaultModeSwitch `union` sectorModeSwitch
虽然我对细节有点不确定。事实上,我不完全确定它是否有效,你可能仍然需要 once
组合器。
无论如何,这只是关于如何进行切换的想法。我完全同意切换是处理不同模式的正确方法。
我正在使用 reactive-banana
构建一个多模态编辑器 - 在大多数情况下它会很完美。为了扩展我的场景,编辑器是一些绘图软件,或者您可以将其视为一个非常简单的矢量图形编辑器。它目前有两种状态——select离子模式和多边形创建模式。在 selection 模式下,用户可以使用鼠标右键 select 先前创建的多边形(理论上这会带您进入新的 selected 模式) 或者他们可以用鼠标左键开始创建一个新的多边形。
目的是,当按下鼠标左键时,我们从 selection 模式切换到多边形创建模式。在此模式下,鼠标左键意味着 "add a new vertex",直到用户 return 到达原始顶点。此时,他们已经关闭了多边形,所以我们return到select离子模式。
我已经用几种不同的方式实现了这一点,最近发现事件切换几乎使它变得非常优雅。我可以拥有:
defaultMode :: Frameworks t => HadoomGUI -> Moment t (Behavior t Diagram)
defaultMode gui@HadoomGUI{..} =
do mouseMoved <- registerMotionNotify guiMap
mouseClicked <- registerMouseClicked guiMap
let lmbClicked = ...
gridCoords = ...
diagram = ...
switchToCreateSector <- execute ((\m ->
FrameworksMoment
(=<< trimB =<< createSectorMode gui emptySectorBuilder m)) <$>
(gridCoords <@ lmbClicked))
return (switchB diagram switchToCreateSector)
与
一起createSectorMode :: Frameworks t
=> HadoomGUI
-> SectorBuilder
-> Point V2 Double
-> Moment t (Behavior t Diagram)
createSectorMode HadoomGUI{..} initialSectorBuilder firstVertex =
do mouseClicked <- registerMouseClicked guiMap
...
这确实有效 - 只需单击一下鼠标。如果我点击一次地图,我会从刚才的状态切换到扇区创建模式。但是,如果我再次点击,defaultMode
会收到点击事件并切换进入 新 多边形创建模式,抛弃我以前的状态。
我想做的是 defaultMode
切换一次,再也没有回来的可能。本质上我想 "swap" defaultMode
产生的 Behavior t Diagram
和 createSectorMode
.
我知道 reactive-banana
在动态事件的垃圾回收方面存在问题,但我暂时愿意接受它。上面的公式比我迄今为止所写的任何其他公式都要精确得多——例如有一个 CurrentState
变量并根据它的内容过滤各种事件。我遇到的问题是它太大了,给我留下了太多的余地来把事情搞砸。通过切换,我只能在范围内处理我能处理的事件。
这个问题有点开放性,所以我不能给出明确的答案。但我当然可以发表我的意见。 ;-)
但是,我可能会做的是将在 模式之间的切换与在 模式内的行为分开。如果我们暂时忘记 FRP,您的程序看起来有点像递归调用自身的一对函数:
defaultMode = ... `andthen` sectorMode
sectorMode = ... `andthen` defaultMode
写的有点像"sequential"程序,"first do this mode, then do that mode"。我认为这没什么问题,尽管默认的 API reactive-banana,特别是 switchB
并不能很好地支持这种风格。你提到(私下)你可以写一个
once :: Event t a -> Event t a
允许第一次出现的事件通过但丢弃其余事件的组合器。这确实是顺序样式所需要的。
不过,由于您总是 return 默认模式,我可能会尝试一种不同的方法,其中每种模式都有一个事件表明它想要切换。切换本身由 "outside" 实体负责。这个想法是为了避免上面程序中某些高阶组合器的显式递归。在伪代码中,这看起来像这样:
modeManager = switchB initialMode changeMode
changeMode = defaultModeSwitch `union` sectorModeSwitch
虽然我对细节有点不确定。事实上,我不完全确定它是否有效,你可能仍然需要 once
组合器。
无论如何,这只是关于如何进行切换的想法。我完全同意切换是处理不同模式的正确方法。