如何在 Eclipse3/Eclipse4 混合编辑器 (DIEditorPart) 中填充 undo/redo?
How to populate undo/redo in an Eclipse3/Eclipse4 hybrid editor (DIEditorPart)?
我的应用程序是混合的 E3/E4 因为我想使用 Eclipse 的一部分 Workbench,但也让我的东西(在某种程度上)为以后纯基于 E4 的应用程序做好准备。
在这个应用程序中,我有一个使用自定义编辑器输入的编辑器(不是基于文件,而是基于从数据库中提取的数据;当用户保存更改时,它们将被写回数据库)。我的本地内存中数据表示由 EMF / Xcore 管理,编辑器使用手动设计的 GUI,该 GUI 通过 EMF 编辑数据绑定工作,即我使用的是 EditingDomain
(即基本的 AdapterFactoryEditingDomain
与 BasicCommandStack
) 跟踪所有更改。
为了将我的 E4 编辑器插入 E3 编辑器,我使用了兼容层,这里特别是 DIEditorPart
。
虽然到目前为止一切正常,但我还没有能够 undo/redo 工作。
我的代码看起来像这样(我正在使用 Scala 和 Scala-IDE):
E3编辑桥:
final class CustomEditorPartE3Bridge extends DIEditorPart(classOf[CustomEditorPart])
和 "real" 部分:
final class CustomEditorPart {
@Inject private var _ctx: IEclipseContext = _
private var _view: Option[MyCustomEditorView] = None
@Inject private var _dirty: MDirtyable = _
@Inject @Optional private var _dirtyE3: IDirtyProviderService = _
private var doPersist: () => Unit = () => {}
@PostConstruct
def init(input: IEditorInput): Unit = input match {
case i: MyCustomEditorInput => initPart(i)
case _ =>
throw new IllegalStateException("Required a %s but got a %s".
format(classOf[MyCustomEditorInput].getName, input.getClass.getName))
}
private def initPart(input: MyCustomEditorInput): Unit = {
val cc = _ctx.createChild()
// Now we need an adapter factory and a respective editing domain
// to enable Undo and Redo
val adapterFactory = new ModelAdapterFactory // generated with Xcore
val cs = new BasicCommandStack
val domain = new AdapterFactoryEditingDomain(adapterFactory, cs)
// We need the editing domain in the control for Databinding
cc.set(classOf[EditingDomain], domain)
// Now we setup the view
_view = Some(ContextInjectionFactory.make(classOf[MyCustomEditorView], cc))
// And we handle dirtying of our part
object csl extends CommandStackListener {
def commandStackChanged(eo: EventObject): Unit = {
val dirty = cs.isSaveNeeded()
if (_dirtyE3 == null) {
_dirty.setDirty(dirty)
} else {
Display.getDefault.asyncExec(() => _dirtyE3.setDirtyState(dirty))
}
}
}
cs.addCommandStackListener(csl)
// Finally, we setup our saving routine.
doPersist = () => { /* not relevant here */ }
}
@Focus
def setFocus(): Unit = _view.foreach(_.setFocus)
@PersistState
def persistState(): Unit = {}
@Persist
def commit(): Unit = doPersist()
}
那么,我如何贡献 undo/redo 以便 E3 的 undo/redo 机制启动?我是否必须以某种方式将我的 EditingDomain
传播回我的 E3 桥并设置一些操作栏贡献者,或者我可以注入一些东西来为我设置 undo/redo 吗?
我将此作为答案发布,以便将其标记为解决方案。
在摆弄和搜索 EHandlerService
用法的各种示例后,我想出了这个:
当我使用 DIEditorPart
时,我可以注入一个 IWorkbenchPart
。如果稍后在基于纯 E4 的应用程序中使用该部件,这可能不存在,我将其作为 @Optional
注入并稍后测试 null
。
所以除了E4方式,我还有以下方式:
@Inject @Optional private var _workbenchPart: IWorkbenchPart = _
// In my method
val cc: IEclipseContext = ...
val domain: EditingDomain = ...
cc.set(classOf[EditingDomain], domain)
if (_workbenchPart != null) {
val undoAction = ContextInjectionFactory.make(classOf[UndoAction], cc)
val redoAction = ContextInjectionFactory.make(classOf[RedoAction], cc)
val site = _workbenchPart.getSite.asInstanceOf[{
def getActionBars(): org.eclipse.ui.IActionBars
}]
val targetActionBars = site.getActionBars
if (targetActionBars != null) {
targetActionBars.setGlobalActionHandler(ActionFactory.UNDO.getId, undoAction)
targetActionBars.setGlobalActionHandler(ActionFactory.REDO.getId, redoAction)
}
}
我的 UndoAction
和 RedoAction
看起来像这样:
abstract class UndoRedoAction(canExecute: CommandStack => Boolean,
execute: CommandStack => Unit) extends Action {
@Inject private var _domain: EditingDomain = _
@PostConstruct
private def init(): Unit = {
setEnabled(canExecute(_domain.getCommandStack))
object csl extends CommandStackListener {
def commandStackChanged(eo: EventObject): Unit = setEnabled(canExecute(_domain.getCommandStack))
}
_domain.getCommandStack.addCommandStackListener(csl)
}
override final def run(): Unit = execute(_domain.getCommandStack)
}
final class UndoAction extends UndoRedoAction(_.canUndo, _.undo)
final class RedoAction extends UndoRedoAction(_.canRedo, _.redo)
这有点乱七八糟,但它确实有效。
我的应用程序是混合的 E3/E4 因为我想使用 Eclipse 的一部分 Workbench,但也让我的东西(在某种程度上)为以后纯基于 E4 的应用程序做好准备。
在这个应用程序中,我有一个使用自定义编辑器输入的编辑器(不是基于文件,而是基于从数据库中提取的数据;当用户保存更改时,它们将被写回数据库)。我的本地内存中数据表示由 EMF / Xcore 管理,编辑器使用手动设计的 GUI,该 GUI 通过 EMF 编辑数据绑定工作,即我使用的是 EditingDomain
(即基本的 AdapterFactoryEditingDomain
与 BasicCommandStack
) 跟踪所有更改。
为了将我的 E4 编辑器插入 E3 编辑器,我使用了兼容层,这里特别是 DIEditorPart
。
虽然到目前为止一切正常,但我还没有能够 undo/redo 工作。
我的代码看起来像这样(我正在使用 Scala 和 Scala-IDE):
E3编辑桥:
final class CustomEditorPartE3Bridge extends DIEditorPart(classOf[CustomEditorPart])
和 "real" 部分:
final class CustomEditorPart {
@Inject private var _ctx: IEclipseContext = _
private var _view: Option[MyCustomEditorView] = None
@Inject private var _dirty: MDirtyable = _
@Inject @Optional private var _dirtyE3: IDirtyProviderService = _
private var doPersist: () => Unit = () => {}
@PostConstruct
def init(input: IEditorInput): Unit = input match {
case i: MyCustomEditorInput => initPart(i)
case _ =>
throw new IllegalStateException("Required a %s but got a %s".
format(classOf[MyCustomEditorInput].getName, input.getClass.getName))
}
private def initPart(input: MyCustomEditorInput): Unit = {
val cc = _ctx.createChild()
// Now we need an adapter factory and a respective editing domain
// to enable Undo and Redo
val adapterFactory = new ModelAdapterFactory // generated with Xcore
val cs = new BasicCommandStack
val domain = new AdapterFactoryEditingDomain(adapterFactory, cs)
// We need the editing domain in the control for Databinding
cc.set(classOf[EditingDomain], domain)
// Now we setup the view
_view = Some(ContextInjectionFactory.make(classOf[MyCustomEditorView], cc))
// And we handle dirtying of our part
object csl extends CommandStackListener {
def commandStackChanged(eo: EventObject): Unit = {
val dirty = cs.isSaveNeeded()
if (_dirtyE3 == null) {
_dirty.setDirty(dirty)
} else {
Display.getDefault.asyncExec(() => _dirtyE3.setDirtyState(dirty))
}
}
}
cs.addCommandStackListener(csl)
// Finally, we setup our saving routine.
doPersist = () => { /* not relevant here */ }
}
@Focus
def setFocus(): Unit = _view.foreach(_.setFocus)
@PersistState
def persistState(): Unit = {}
@Persist
def commit(): Unit = doPersist()
}
那么,我如何贡献 undo/redo 以便 E3 的 undo/redo 机制启动?我是否必须以某种方式将我的 EditingDomain
传播回我的 E3 桥并设置一些操作栏贡献者,或者我可以注入一些东西来为我设置 undo/redo 吗?
我将此作为答案发布,以便将其标记为解决方案。
在摆弄和搜索 EHandlerService
用法的各种示例后,我想出了这个:
当我使用 DIEditorPart
时,我可以注入一个 IWorkbenchPart
。如果稍后在基于纯 E4 的应用程序中使用该部件,这可能不存在,我将其作为 @Optional
注入并稍后测试 null
。
所以除了E4方式,我还有以下方式:
@Inject @Optional private var _workbenchPart: IWorkbenchPart = _
// In my method
val cc: IEclipseContext = ...
val domain: EditingDomain = ...
cc.set(classOf[EditingDomain], domain)
if (_workbenchPart != null) {
val undoAction = ContextInjectionFactory.make(classOf[UndoAction], cc)
val redoAction = ContextInjectionFactory.make(classOf[RedoAction], cc)
val site = _workbenchPart.getSite.asInstanceOf[{
def getActionBars(): org.eclipse.ui.IActionBars
}]
val targetActionBars = site.getActionBars
if (targetActionBars != null) {
targetActionBars.setGlobalActionHandler(ActionFactory.UNDO.getId, undoAction)
targetActionBars.setGlobalActionHandler(ActionFactory.REDO.getId, redoAction)
}
}
我的 UndoAction
和 RedoAction
看起来像这样:
abstract class UndoRedoAction(canExecute: CommandStack => Boolean,
execute: CommandStack => Unit) extends Action {
@Inject private var _domain: EditingDomain = _
@PostConstruct
private def init(): Unit = {
setEnabled(canExecute(_domain.getCommandStack))
object csl extends CommandStackListener {
def commandStackChanged(eo: EventObject): Unit = setEnabled(canExecute(_domain.getCommandStack))
}
_domain.getCommandStack.addCommandStackListener(csl)
}
override final def run(): Unit = execute(_domain.getCommandStack)
}
final class UndoAction extends UndoRedoAction(_.canUndo, _.undo)
final class RedoAction extends UndoRedoAction(_.canRedo, _.redo)
这有点乱七八糟,但它确实有效。