在反应香蕉中执行单个开关 [英] Performing a single switch in reactive-banana

查看:117
本文介绍了在反应香蕉中执行单个开关的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用 reactive-banana 构建一个多模式编辑器,而且大多数情况下它都很完美。为了扩展我的场景,编辑器是一些映射软件,或者您可以将其视为非常简单的矢量图形编辑器。它目前有两种状态 - 选择模式多边形创建模式。在选择模式下,用户可以使用鼠标右键来选择先前创建的多边形(理论上,您可以选择新的选定模式),或者可以使用鼠标左键创建新的多边形按钮。

我们的目的是,当按下鼠标左键时,我们从选择模式切换到多边形创建模式。在这种模式下,鼠标左键意味着添加一个新的顶点,直到用户返回到原始顶点。在这一点上,他们已经关闭了多边形,所以我们返回到选择模式。



我已经实现了这几种不同的方式,最近注意到事件转换几乎使这非常优雅。我可以有:

  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 =<<<<<<<<<<><>> 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 一次,永远不会有回来的可能性。基本上我想用结果 defaultMode 产生的 Behavior t Diagram createCodeMode



我明白 reactive-banana ,但我现在愿意接受这一点。上面的公式比我迄今为止所写的任何其他内容都精确得多 - 比如拥有一个 CurrentState 变量并根据其内容过滤各种事件。我遇到的问题是它太大了,给我留下太多空间让我搞砸了。通过切换,我只能在范围内处理我可以处理的事件。

解决方案

这个问题有些开放性,无法给出明确的答案。但我当然可以提出我的意见。 ; - )



然而,我可能会做的是将之间的切换中的行为一种模式。如果我们暂时忘记了FRP,你的程序看起来有点像递归调用自己的一对函数:

  defaultMode =。 ..`andthen` sectorMode 
sectorMode = ...`andthen` defaultMode

它被写入有点像顺序程序,先做这个模式,然后做那个模式。我认为没有什么问题,尽管默认的API反应式香蕉,特别是 switchB ,并不能很好地支持这种风格。你提到(私下)你可以写一个

  once :: Event t a  - >事件t a 

组合器,可以通过事件的第一次出现,但放弃其余部分。这确实是你需要的连续风格。



虽然你总是回到默认模式,但我可能会尝试一种不同的方法,每种模式都有一个事件表明它想要被切换。交换本身由外部实体负责。这个想法是通过一些更高阶的组合器来避免上述程序中的显式递归。在伪代码中,这看起来像这样:

  modeManager = switchB initialMode changeMode 
changeMode = defaultModeSwitch`union `sectorModeSwitch

尽管我对于细节有些不确定。实际上,我并不完全确定它是否可以工作,但您可能仍然需要一次 combinator。



无论如何,这只是关于如何进行切换的想法。我完全同意,切换是处理不同模式的正确方法。


I'm building a multi-modal editor using reactive-banana - and for the most part it's going perfect. To expand on my scenario, the editor is some mapping software, or you could think of it as a very simple vector graphics editor. It currently has two states - selection mode and polygon creation mode. In selection mode, the user is able to select previously created polygons with the right mouse button (which would in theory take you to a new selected mode) or they can begin creating a new polygon with the left mouse button.

The intention is, when the left mouse button is pressed, we switch from selection mode into polygon creation mode. In this mode, a left mouse button means "add a new vertex", until the user returns to the original vertex. At this point, they have closed the polygon, so we return to selection mode.

I've implemented this a few different ways, and recently noticed that event switch almost makes this very elegant. I can have:

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)

Along with

createSectorMode :: Frameworks t
                 => HadoomGUI
                 -> SectorBuilder
                 -> Point V2 Double
                 -> Moment t (Behavior t Diagram)
createSectorMode HadoomGUI{..} initialSectorBuilder firstVertex =
  do mouseClicked <- registerMouseClicked guiMap
     ...

This certainly works - for a single mouse click. If I click on the map once, I switch into sector creation mode from the state I was just in. However, if I click again, defaultMode receives the click event and switches into a new polygon creation mode, throwing away my previous state.

What I'd like to do is switch in defaultMode once and never have the possibility of coming back. Essentially I want to "swap" the Behavior t Diagram produced by defaultMode with the result of createSectorMode.

I understand reactive-banana has problems with garbage collection of dynamic events, but I'm willing to live with that for now. The above formulation is significantly more precise than anything else I've written so far - such as having a single CurrentState variable and filtering various events based on the contents of that. The problem I have with this is that it's too big, and leaves way too much scope for me to mess things up. With switching, I only have in scope the events I can about.

解决方案

The problem is somewhat open ended, so I can't give a definite answer. But I can certainly give my opinion. ;-)

However, what I would probably do is to separate the switching between modes from the behavior within a mode. If we forget about FRP for a moment, your program looks a bit like pair of functions that recursively call themselves:

defaultMode = ... `andthen` sectorMode
sectorMode  = ... `andthen` defaultMode

It's written a bit like a "sequential" program, "first do this mode, then do that mode". I think there is nothing wrong with that, though the default API reactive-banana, in particular switchB, does not support that style very well. You mentioned (privately) that you can write a

once :: Event t a -> Event t a

combinator that lets through the first occurrence of an event, but discards the rest. This is indeed what you would need for the sequential style.

Since you always return to the default mode, though, I would probably try a different approach, where each mode has an event that indicates that it wants to be switched away. The switching itself is taken care of by an "outside" entity. The idea is to avoid the explicit recursion in the program above by some higher-order combinator. In pseudo-code, this would look something like this:

modeManager = switchB initialMode changeMode
changeMode  = defaultModeSwitch `union` sectorModeSwitch

though I am a bit unsure about the details. In fact, I'm not entirely sure if it works at all, you probably still need the once combinator.

Anyway, that's just an idea on how to go about the switching. I fully agree that switching is the right way to deal with different modes.

这篇关于在反应香蕉中执行单个开关的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆