TornadoFX如何使用子窗口模型列表创建MDI? [英] TornadoFX How to create MDI with list of child windows models?

查看:145
本文介绍了TornadoFX如何使用子窗口模型列表创建MDI?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下组件:

class ChildModel:ViewModel() { //or it may be an POJO, it does not matter
  val value ....      
} 

class ParentView: View() {
  ...
  //Maybe this should be implemented into ParentViewModel   
  val childrenList:List<ChildModel>

  fun addFragmentAsChild() {
    //should:
    // 1. display fragment within ParentView
    // 2. add fragment into modelList (or fragmentList - it does not matter -  important to have access to the model of every child )  
  }

  fun deleteFragmentAsChild() {
    //should destroy child and remove item from childrenList   
    //should work also on manual closing 
  }         
}

class ChildFragment: Fragment() {
  val model = ChildModel()      
...
}

摘要:我想创建MDI并可以访问每个孩子的模型.

Summary: I want to create MDI and have access to model for every child.

我尝试在"openInternalWindow"帮助下执行此操作,但是我无法创建多个子实例,并且必须手动管理列表-这很糟糕.

I try do this with help "openInternalWindow" , but i can't create multiple child instances and i must manually manage the list - it's bad.

class InstrumentsView: View() {
  override val root = BorderPane()
  val instrumentList = ArrayList<InstrumentFragment>()

  init {
    with(root){
      top = menubar {
        menu("Tools") {
          menuitem("Add instrument", "Shortcut+A") {
            val newFragment = InstrumentFragment()
            instrumentList.add(newFragment)
            println(instrumentList.size)
            openInternalWindow(newFragment, modal = false)
          }

        }
      }
    }
  }
}

如何正确处理tornadofx?

How to do it right tornadofx way?

推荐答案

在此示例中,我将使用视图模型和作用域来跟踪每个乐器编辑器的项目.我们需要确保乐器是唯一的,以便在关闭编辑器时将其从列表中删除.我创建了一个具有ID和名称的Instrument域对象:

In this example I'll use a view model and scoping to keep track of the item for each instrument editor. We need to make sure the instruments are unique so we can remove them from the list when the editor is closed. I created an Instrument domain object with an id and a name:

class Instrument {
    val idProperty = SimpleObjectProperty<UUID>(UUID.randomUUID())
    var id by idProperty

    val nameProperty = SimpleStringProperty()
    var name by nameProperty

    override fun equals(other: Any?): Boolean {
        if (this === other) return true
        if (other?.javaClass != javaClass) return false

        other as Instrument

        if (id != other.id) return false

        return true
    }

    override fun hashCode(): Int {
        return id.hashCode()
    }
}

我们希望可以在仪器编辑器中插入一个视图模型.默认情况下,我们将确保视图模型包含新的工具.它包含name属性的外观,因此我们可以将其绑定到编辑器输入字段.

We want a view model we can inject in the instrument editor. We'll make sure the view model contains a new instrument by default. It contains a facade for the name property so we can bind it to an editor input field.

class InstrumentModel: ItemViewModel<Instrument>() {
    init {
        item = Instrument()
        item.name = "New instrument"
    }
    val name = bind { item?.nameProperty }
}

A Fragment具有onDockonUndock的回调,可用于跟踪该片段的模型.我们可以使用事件来表明这一点.声明以下事件:

A Fragment has callbacks for onDock and onUndock which can be used to keep track of the model for that fragment. We can use events to signal this. Declare the following events:

class InstrumentAdded(val instrument: Instrument) : FXEvent()
class InstrumentRemoved(val instrument: Instrument) : FXEvent()

覆盖InstrumentFragment中的停靠回调以触发这些事件:

Override the docking callbacks in InstrumentFragment to fire these events:

override fun onDock() {
    fire(InstrumentAdded(model.item))
}

override fun onUndock() {
    fire(InstrumentRemoved(model.item))
}

现在,我们将在主视图InstrumentsView中保留乐器列表.也可能在Controller中.

For now we'll keep the list of instruments in the main view, InstrumentsView. This could just as well be in a Controller.

val instruments = FXCollections.observableArrayList<Instrument>()

在主视图的init类中,我们将订阅我们创建的事件并修改列表:

In the init class of the main view, we'll subscribe to the events we created and modify our list:

subscribe<InstrumentAdded> {
    instruments.add(it.instrument)
}
subscribe<InstrumentRemoved> {
    instruments.remove(it.instrument)
}

"New Instrument"操作将在新的Scope中打开一个新的InstrumentEditor,因此我们可以将视图模型注入其中,并获得该编辑器唯一的实例.

The "New Instrument" action will open a new InstrumentEditor in a new Scope so we can inject the view model into it and get an instance unique to that editor.

menuitem("Add instrument", "Shortcut+A") {
    find<InstrumentFragment>(Scope()).openWindow()
}

很遗憾,我们不能使用openInternalWindow,因为它目前一次仅支持一个内部窗口.因此,我改用openWindow.

Unfortunately, we can't use openInternalWindow as it currently only supports one internal window at a time. Therefore I used openWindow instead.

如果要从某个操作中关闭编辑器,则可以从片段内的任何位置调用closeModal().

If you want to close the editor from an action, you can call closeModal() from anywhere inside the fragment.

我提供了一个带有TableView的完整示例应用程序,该应用程序显示了当前打开的工具.它看起来像下面的图像.请注意,在从模型中清除更改并在表中将其显示之前,您需要先单击保存.

I've included a complete example app with a TableView that shows the currently open instruments. It will look like the image below. Notice that you need to hit save before the changes are flushed from the model and visible in the table.

我希望这是您想要的,或者至少可以根据此示例对其进行修改以适合您的用例.

I hope this is what you're looking for, or that you at least can modify it to fit your use case based on this sample.

import javafx.beans.property.SimpleObjectProperty
import javafx.beans.property.SimpleStringProperty
import javafx.collections.FXCollections
import tornadofx.*
import java.util.*

class Instrument {
    val idProperty = SimpleObjectProperty<UUID>(UUID.randomUUID())
    var id by idProperty

    val nameProperty = SimpleStringProperty()
    var name by nameProperty

    override fun equals(other: Any?): Boolean {
        if (this === other) return true
        if (other?.javaClass != javaClass) return false

        other as Instrument

        if (id != other.id) return false

        return true
    }

    override fun hashCode(): Int {
        return id.hashCode()
    }
}

class InstrumentModel : ItemViewModel<Instrument>() {
    init {
        item = Instrument()
        item.name = "New instrument"
    }

    val name = bind { item?.nameProperty }
}

class InstrumentAdded(val instrument: Instrument) : FXEvent()
class InstrumentRemoved(val instrument: Instrument) : FXEvent()

class InstrumentFragment : Fragment("Instrument Editor") {
    val model: InstrumentModel by inject()

    override val root = form {
        prefWidth = 300.0
        fieldset("Edit instrument") {
            field("Name") {
                textfield(model.name)
            }
        }
        button("Save") {
            setOnAction {
                model.commit()
            }
        }
    }

    override fun onDock() {
        fire(InstrumentAdded(model.item))
    }

    override fun onUndock() {
        fire(InstrumentRemoved(model.item))
    }
}

class InstrumentsView : View() {
    val instruments = FXCollections.observableArrayList<Instrument>()

    override val root = borderpane {
        setPrefSize(400.0, 300.0)
        top {
            menubar {
                menu("Tools") {
                    menuitem("Add instrument", "Shortcut+A") {
                        find<InstrumentFragment>(Scope()).openWindow()
                    }
                }
            }
        }
        center {
            tableview(instruments) {
                column("Name", Instrument::nameProperty)
                columnResizePolicy = SmartResize.POLICY
            }
        }
    }

    init {
        subscribe<InstrumentAdded> {
            instruments.add(it.instrument)
        }
        subscribe<InstrumentRemoved> {
            instruments.remove(it.instrument)
        }
    }

}

这篇关于TornadoFX如何使用子窗口模型列表创建MDI?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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