GEF Programmers' Guide 中文版(GEF 交互操作)

GEF 中的交互类型

这一节我们将讲述 GEF 框架中包含的各种交互操作和每个交互操作相关联的组件。交互可以是任何影响模型或者 UI 界面状态的行为。许多交互是可视化的,也有一些不能图形化地显示。交互可能包含一下内容:

1.     菜单操作 ( 通常在工具栏、菜单栏和弹出菜单中 )

2.     点击

3.     点击并拖拽 .

4.     将鼠标停留在某处

5.     鼠标拖放

6.     按键 .

同时我们还将讲述每个交互的参与者和它们的处理,包括:

1.     处理输入的工具

2.     被选择的菜单操作

3.     ID 以及 EditPart 的工具或菜单操作处理的请求的实例。 ID RequestConstants 类定义;

4.     处理请求的角色。它们是 EditPolicy 接口定义的常量;

5.     用来处理交互的编辑策略;

选择 (Selection )

Tools

Request

Edit Policies and Roles

Actions

SelectionTool
MarqueeTool
SelectEditPartTracker

*GraphicalViewerKeyHandler

SelectionRequest
DirectEditRequest
REQ_SELECTION_HOVER
REQ_OPEN
REQ_DIRECT_EDIT

SelectionEditPolicy
DirectEditPolicy
SELECTION_FEEDBACK_ROLE

SelectAllAction

Viewer 中选择是最基本和普遍的交互操作,这里讨论的大部分的交互操作都基于选中的对象。而选择本身也是一个复杂的过程。上面的表单总结了选择工具以及它涉及到的请求、编辑策略和菜单操作。

首先定义一下选中对象 (Selection) ,选中的是 EditPartViewer 里的一系列 EditPart 。选中对象的修改是由 Viewer 提供的方法控制的,而不是直接操作对象本身。选中对象不会为空,即使所有的选中的对象都被清空了, Viewer 的根模型对应的 EditPart 将默认被选中。最新一次选中的对象将作为首选对象。

与选中密切相关的是聚焦 (Focus) 。聚焦发生在一个单独的 EditPart 上,用于通过键盘操作选中的对象。通过改变聚焦点,用户可以在不修改选中对象的前提下由一个 EditPart 切换到另一个 EditPart 。如果没有显示的聚焦点,那么首选对象默认为聚焦对象。

Viewer 通知 EditPart 何时被选中,而 EditPart 负责显示选中和聚焦的状态。典型的, EditPolicy 会给图形添加一些 Handle 表明它被选中。在 Logic 示例中,使用 ResizableEditPolicy LED 和电路部分加上了 Handle 。黑色的部分标识首选对象。

选中对象的 Handle 关系到它的拖拽和大小,进一步关系到图形的布局管理器,所以通常是父 EditPart 安装相应的 EditPolicy ,使得子图形显示恰当的 Handle 。比如,安装了 XYLayoutPolicy EditPart 将自动给它的子 EditPart 安装 ResizableEditPolicy 编辑策略。

连接可能通过修改连接图形的线条宽度来提示选中,比如 Logic 示例中的 WireEditPart 。连接的 Handle 是由 EndpointEditPart BendpointEditPolicy 共同负责的。

selection handles
Selection Handles

选中对象的目标定位 (Targeting) 和反馈

选择对象的时候, Selection Tool 首先使用 SelectionRequest 来定位目标 EditPart 。在极少数情况下,目标 EditPart 不可选中,这时候目标定位失败。 在不断的鼠标定位的过程中, Selection Tool 将对当前的目标 EditPart 调用 showFeedback() 方法,向它传递一个类型为 REQ_SELECTION SelectionRequest 。值得推荐的是,许多应用程序忽略了这个请求,因为当鼠标在图形上移动时,不断的刷新反馈会使用户崩溃。 因此,当用户的鼠标停留在某个图形上时,会额外发送一个类型为 REQ_SELECTION_HOVER 的反馈请求。很多时候, EditPart 会这么处理这个请求 —— 在图形上弹出一个对话框用来显示信息,就像工具提示条 (Tooltip) 一样。 SELECTION_FEEDBACK_ROLE 角色就用来处理这种反馈请求,如果你需要类似的反馈处理,记得在安装编辑策略的时候使用它。

使用这些反馈请求的好处是 Selection Tool 不用频繁的提示这些反馈信息。比如用户开始拖拽一个图形了,他可能不希望在拖拽的时候一直有一个弹出的消息框,提示你一些的反馈信息。还有一点需要提示,在其他工具激活的时候,不会再有选择反馈。

使用 DragTracker 进行选择

一旦用户点击鼠标,一个 DragTracker 的实例将被创建,用于进行选择目标定位。它将返回一个 SelectEditPartTracker 对象或者是 DragEditPartTracker 对象,来进行选择,具体返回哪一种类型的对象取决于是否允许拖拽。这些跟踪者 (Tracker) 将在恰当的时候修改选中对象,同时它还会根据 SHIFT 或者 CTRL 按键是否按下来来处理选中。这意味着一次可以选择多个对象。

填充模型的 EditPart 不会被 Tracker 选中,因为它不会出现在一个多重选中的情况下。这时候会产生 DeselectAddTracker 或者 MarqueeDragTracker 。记住在没有选中任何对象时,填充模型将被选中以保证选中对象不为空。

其它的选择请求

EditPart 可能会处理两个与选中相关的请求,这些请求与交互过程中鼠标点击相关。第一个是双击,在 GEF 称为展开 (RES_OPEN) ,这个请求用于处理打开、展开或者显示对话框等操作。也就是说,如果你想在图形上捕获双击事件,处理这个请求即可,不需要在图形上建立鼠标事件监听。另一个请求是直接编辑 ((REQ_DIRECT_EDIT) 。举一个直接编辑的示例,对于一个文本框或者标签,用户可能想直接在界面上修改它的文字描述。首先选中图形,隔一段时间后再次单击这个图形,就可以产生这个请求了。如果注册了快捷键 ( 一般为 F2) ,选中后点击 F2 可以获得同样的效果。注意需要隔一段时间再次点击,这是为了与双击请求进行区别。

菜单操作 Selection Actions

GEF 提供了用于全选的菜单操作—— SelectAllAction ,它会将当前 Viewer 下所有的 EditPart 都选中。

使用键盘选择 Selection using the Keyboard

如果在图形界面上安装了 GraphicalViewKeyHandler ,那么键盘选择就可以启用了。它负责接收当前工具传递来的按键事件,如果事件涉及到选择操作, Selection Tool 将负责分发这些事件。

注意 DragTracker 不必用于 GEF TreeViewer ,后者使用其他的方法处理选择、拖拽等操作。

基本模型操作

删除 (Delete)

Tools

Requests

Edit Policies and Roles

Actions

 

REQ_DELETE

COMPONENT_ROLE
CONNECTION_ROLE
RootComponentEditPolicy

DeleteAction

delete

删除是所有 GEF 程序都应该支持的通用操作。工作台在编辑菜单上内置了一个全局的删除菜单操作,应用程序只用注册一下 DeleteAction ,就可以使用删除操作了。 DeleteAction 向当前选中的对象发送一个 REQ_DELETE 类型的请求,所有的 EditPart 都应该安装一个提示用户是否支持删除操作的编辑策略。

EditPart 分为基本组件 (Component) 和连接两种,组件是构成 EdiPart 结构的基本元素,它们都是 RootEditPart 的子 EdiPart 。而连接稍有不同,它们属于源端点对象和目的端点对象拥有的。

COMPONENT_ROLE 关键字用于安装组件编辑策略。用户可以用过扩展 ComponentEdiPartPolicy 并重写与删除命令相关的方法。 RootComponentEditPolicy 用于填充模型的 EditPart ,它的作用在于避免 Root Figure 被删除。 注意这里有几个概念需要区别一下:填充模型对应的 EditPart content EditPart ,与 GraphicalViewer RootEditPart 是不一样的。

COMPONENT_ROLE 关键字用于安装连接编辑策略,用户可以通过扩展 ConnectionEditPolicy 并重写与删除命令相关的方法来实现连接的删除操作。

note 实现删除操作的命令往往比较困难,尤其在有连接情况下。该命令必须判断被删除的对象是否拥有连接,或者它的子节点是否拥有连接,如果存在,这些连接都必须删除。注意在删除连接的过程中可能出现重复删除,比如同时选中了连接的源端点和目的端点。

创建 (Creation )

Tools

Requests

Edit Policies and Roles

Actions

CreationTool
 

REQ_CREATE
Create

CONTAINER_ROLE
LAYOUT_ROLE
TREE_CONTAINER_ROLE
ContainerEditPolicy
LayoutEditPolicy

CopyTemplateAction
PasteTemplateAction

TemplateTransferDropTargetListener
TemplateTransferDragSourceListener

create

CreateRequest 用来给 EditPart 创建子节点,它以 REQ_CREATE 为标识。创建过程可能发生在以下三种情况下:点击、拖拽或粘贴。该请求提供位置、对象和对象类型信息,对象和对象类型信息都由 CreateFactory 提供。创建请求隐藏了 fcatory ,直接与创建的对象打交道。在一些时候,它还会包括尺寸信息。

产生 CreateRequest Producing CreateRequests

创建工具提供了加载光标模式来记录新创建的对象的位置。 当点击并拖动鼠标时,创建工具将跟踪用户定义的矩形的大小。 创建工具位于绘图板里,由 CreationToolEntry 实现。 松开鼠标时,创建工具可以保留设置并用于下次创建,也可以恢复为默认。

拖放也可以实现创建,拖拽的源可以是任何事物,一般来说是 PaletteViewer 。拖放需要使用类型为 Template 的绘图元素,而 TemplateTransfer 负责根据 Template 拖拽源转换为拖拽目的。同时 PaletteViewer 要注册 TemplateTransferDragSourceListener TemplateTransferDropTargetListener 监听器。根据创建的模型的不同,这两个监听器都要重写,并获得与模型对应的请求。

CombinedTemplateCreationEntry 支持以上两种创建方式。

处理创建请求

目标 EditPart 负责显示反馈和返回用于创建模型的命令。 GEF 提供了两种处理创建操作的编辑策略。一种策略是为视图定制的,包括图形化的视图和树形结构的视图,对应的编辑策略分别为: LAYOUT_ROLE TREE_CONTAINER_ROLE

另一种策略只针对模型,应用程序可以借此来区分通过图形界面创建和非图形界面创建公用的部分。在大多数场景下,逻辑是在命令中实现的,因此这种编辑策略其实没有多大的用处。

LayoutEditPart 根据容器的布局管理器来完成创建过程,比如,如果容器使用了 XYLayout 布局,那么相应的创建命令中就必须使用 (x y w h) 四个元素来对创建的子节点进行约束。不使用约束 (Constraint) 的布局可能会要求得到根据拖放位置计算出的位置索引 (Index) ,并以此来确定创建的子节点的位置。针对基本的布局类型, GEF 内置了几个抽象的 EditPolicy ,用户可以扩展它们来实现自己的编辑策略。

TreeContainerEditPolicy 用于支持树形结构的 Viewer 的子节点创建,这时候它需要确定用于节点创建和反馈的索引位置。

PasteTemplateAction 可以用于在不使用鼠标的情况下创建对象,这使得创建更方便了。同时 CopyTemplateAction 会被添加到绘图板中。复制时,内部机制完成数据的转换和复制,紧接着在粘贴时, PasteTemplateAction 会恢复转换的数据,产生 CreateRequest 并发送给 EditPart 这种交互操作中没有用到鼠标,因此新建节点的位置不确定,这也限制了在复制的时候只允许选中一个对象。

note 当创建命令被重做时,它必须保证恢复到第一次创建子节点时的状态。如果某个命令创建了子节点,接下来的命令又对这个节点进行了修改,那么此时的重做操作很可能失败。

移动和调整大小

Tools

Requests

Edit Policies and Roles

Actions

DragEditPartsTracker
ResizeTracker

ChangeBoundsRequest
AlignmentRequest

REQ_MOVE
REQ_ADD
REQ_ORPHAN

 

REQ_CLONE
REQ_ALIGN
REQ_RESIZE

LayoutEditPolicy
ResizableEditPolicy
ContainerEditPolicy

AlignmentAction
MatchSizeAction

 

move
移动操作

resize
调整大小操作

DragEditPartsTracker 对基本的选择行为进行了扩展,它允许在图形编辑器中拖动选中的对象。这会带来三个潜在的交互操作:移动,重置父节点和复制。这三种操作都使用 ChangeBoundRequest ChangeBoundRequest 继承自 GroupRequest ,它包含大小的增量、位置的偏移和鼠标的最终位置。

拖动选中对象时,如果拖动完毕后的对象仍处在它的父节点范围中,那么将产生 RES_MOVE 请求。如果越出范围,那么将产生重置父节点的请求—— REQ_ORPHAN ,这个请求将被发送至原来的父节点,同时新的父节点会产生一个新的请求—— RES_ADD 。当拖动鼠标的同时按下了 CTRL(MAC 系统中为 ALT) ,操作往往被视作复制,将产生 RES_CLONE 的请求,并发送给目标 EditPart

以上的请求都要求目标对象处理矩形信息和鼠标位置信息,然后 LayoutEditPolicy 接手负责处理请求,处理的过程依请求类型而定。对于使用四元约束式的布局管理器,原始的位置和大小信息都将废弃,更新为请求记录下来的新的位置和大小信息。对于用索引来布局的管理器,鼠标的位置仍然作为生成新的布局索引的依据。

可以选择用 ContainerEditPolicy 来处理在 ADD ORPHAN CLONE 请求中与布局无关的命令。

调整大小 Resizing

在同一个范围内修改图形的约束将带来大小调整。注意约束 (Constraint) 的左侧或者上侧改变时,节点的位置一般也随之更改了。大小调整只对支持约束的布局管理器有效,比如 XYLayout ResizableEditPolicy 使用了 8 个用于调整大小的 Handle ,当对象被选中时, ResizeTracker 负责响应大小调整。 SHIFT CTRL 按键可以用来辅助完成大小调整操作。

显示在图形上 Handle 的数量和位置由图形所在的布局管理器决定,比如在表格中,会出现插入调整、依附、列间距和其他属性的 Handle 。有一些布局不要 Handle ,但是在图形的四个边角依然会出现 Handle ,以提示用户选中。拖拽这些 Handle 的效果与拖动图形一样。

Handle 和布局的关系如此紧密,因此强烈建议使用父节点的 LayoutEditPolicy 来安装 PRIMARY_DRAG_ROLE 编辑策略角色。如果在编辑中,容器的布局管理器发生变化了,这时候布局相关的编辑策略也会与新的布局绑定。同时所有子节点的编辑策略也将更新。

MatchSizeAction 可以根据首选对象的大小来匹配所有选中的对象。它的实现与手动调整单个对象的大小一样,也使用了同样的请求。

AlignmenAction 用于对齐多个对象的位置,它使用了 AlignmentRequest 请求。 AlignmentRequest 继承自 ChangeBoundsRequest 。在大多数情况下, AlignmentAction 的行为与移动操作一样,不同的是,它可以根据首选对象和对齐的方向来对齐所有选中的对象。

连接创建 Connection Creation

Tools

Requests

Edit Policies and Roles

Actions

ConnectionCreationTool
ConnectionDragCreationTool

CreateConnectionRequest
REQ_CONNECTION_START
REQ_CONNECTION_END

GraphicalNodeEditPolicy
NODE_ROLE

 

connection create ConnectionCreationTool 用于在节点之间创建连接。创建操作需要用户选中相应的工具,然后选择连接的两个端点。点击 ESC 将撤销连接的创建。 ConnectionDragCreationTool 与前者类似,区别是它是一次鼠标的拖拽。 ConnectionDragCreationTool is similar, but the interaction is a single mouse drag. This tool can be returned as the drag tracker from a handle or even an editpart in some cases.

创建过程分为两部分,第一步是定义连接的源节点 (Source) 。源节点不仅仅包含节点信息,还可能还包括节点上某个特定的端口 (Port ,作为连接的锚点 ) REQ_CONNECTION_START 类型的 CreateConnectionRequest 请求这时候会确定源端点对应的 EditPart ,并且产生创建命令。这时候命令只保存了连接的源节点信息,还没有完全实现, GEF 是不会尝试执行它,甚至都不会理会它是否可以执行。

第二步是定义连接的目的节点 (Target) GEF 同样使用 CreateConnectionRequest 请求,但这次它的是 REQ_CONNECTION_END 类型的。在第一步中我们得到了一个创建的命令,保存在请求中。命令还没有完全实现,至少它要知道连接的另一个端点——目的节点的信息,这时候我们取出这个命令,将目的节点的信息保存到命令中。至此,才形成一个完整的连接创建命令。当然 GEF 也需要进行命令可执行性的判断,如果允许执行,那么将创建出一条新的连接。

上面的过程中, source target 对应的 EditPart 需要在连接创建的过程中显示一些反馈信息,比如图形可能会被一些高亮显示的依附点包围,提示它将是一个连接的端点。同时连接过程中会出现一条连接线 (ConnectionFigure) ,然后确定连接线的锚点。 GraphicalNodeEditPolicy 负责显示连接线,而 NodeEditPart 则负责为连接提供锚点。

连接的创建需要多个组件协作完成,同时也需要它们做出图形化的反馈显示,这些反馈包括被选中的节点周围出现的依附点 ( 锚点 ) 和新出现的连接线。

编辑连接

Tools

Requests

Edit Policies and Roles

Actions

ConnectionEndpointTracker

ReconnectRequest
REQ_RECONNECT_SOURCE
REQ_RECONNECT_TARGET

ConnectionEndpointEditPolicy
ENDPOINT_ROLE
GraphicalNodeEditPolicy
NODE_ROLE

 

拖动连接的端点的操作称为重置连接 (Reconnect) ,在端点上移动连接点的位置也属于重置连接。

connectin edit

 

 

GEF 使用 ConnectionEndpointEditPolicy 在连接的两个端点加上一些 Handle ,这个策略的角色为 ENDPOINT_ROLE 。每个 Handle 都会返回一个重置连接的 Tacker ,它们记录连接的端点位置。重置连接的命令由 GraphicalNodeEditPolicy 提供,命令的执行与连接的新端点相关。

在拖动连接端点的过程中, Tracker 发送 ReconnectRequest 请求给新的端点对象,这个对象上安装的 GraphicalNodeEditPolicy 生成重置连接命令。重置连接命令需要知道连接的新旧端点,然后判断连接的重置是否有效,若有效将执行重置处理。

 

折线连接 (Bending Connections)

Tools

Requests

Edit Policies and Roles

Actions

ConnectionBendpointTracker

BendpointRequest
REQ_MOVE_BENDPOINT
REQ_CREATE_BENDPOINT

BendpointEditPolicy
CONNECTION_BENDPOINTS_ROLE

 

 

连接的路径有多种,有些连接使用拐点 (Bendpoint) 来定义连接的路径。我们可以通过安装 BendpointEditPolicy(CONNECTION_BENDPOINTS) 来编辑连接的拐点,而折点的修改将改变连接线的路径。这个编辑策略会在连接线上显示一系列的拐点,在用户选择修改连线路径的位置会出现一个代表拐点的 Handle

每个 Handle 都有一个 ConnectionBendpointTracker ,在用户尝试弯曲连接的时候,它会向连接对应的 EditPart 发送 BendpointRequest 请求。对于已有的拐点,请求有两种类型: REQ_MOVE_BENDPOINT REQ_CREATE_BENDPOINT 。当然将拐点拉回原来的位置,可以处理为移动拐点,也可以处理为删除拐点,视编辑策略的实现而定。

 

bendpoint

 

 

 

示例图显示了 Logic 中的连线。 ShortestPathConnectionRouter 在连接遇到阻碍时会计算出最短的路径并绕过阻碍,这就形成了一些拐点。拐点保存在连接路由的拐点列表汇总。

 

conection example

 

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值