using gfx
using fwt
using flux
using afIoc
** Autobuild me!
class EntityTree {
@Inject private QuickFluxIcons icons
@Inject private EventHub eventHub
@Inject private Registry registry
private EntityTreeModel? entityTreeModel
private Text findWidget := Text()
Tree tree := Tree()
Pane content
|EntityMeta|[] filterFuncs := |EntityMeta|[,]
|->Entity[]| rootsFunc := |->Entity[]| { Entity#.emptyList }
Menu? emptyPopup
Str findText {
get { findWidget.text }
private set { }
}
Entity[] roots {
get { entityTreeModel.roots }
private set { }
}
new make(Bool showFilter, |This| inject) {
inject(this)
content = EdgePane {
if (showFilter)
top = EdgePane {
center = findWidget
right = EdgePane {
left = Button() {
text = "Go"
onAction.add { findWidget.onAction.fire(Event()) }
}
right = Button() {
text = "Clear"
onAction.add { findWidget.text = ""; findWidget.onAction.fire(Event()) }
}
}
}
center = tree
}
findWidget.onAction.add |->| {
entityTreeModel.collapseAll(tree)
tree.selected = [,]
if (findText.isEmpty)
return
entityTreeModel.walk |EntityMeta node| {
if (entityContains(node.entity, findText.lower)) {
node.parentNode?.show(tree)
tree.selected = [node.entity]
}
}
}
tree.onAction.add |event| {
entityTreeModel.toMeta(event.data).action
}
tree.onPopup.add |event| {
if (event.data == null) {
event.popup = emptyPopup
return
}
node := entityTreeModel.toMeta(event.data)
event.popup = node.populateMenu(Menu())
}
eventHub.addListener(Events.newEntity) |Entity entity| {
node := entityTreeModel.toMeta(entity)
if (node.parent != null) {
node.refresh(tree)
node.expand(tree)
} else {
refreshAll
}
}
eventHub.addListener(Events.entitySaved) |Entity entity| {
node := entityTreeModel.toMeta(entity)
if (node.parent != null) {
node.refresh(tree)
node.expand(tree)
} else {
refreshAll
}
}
eventHub.addListener(Events.entityDeleted) |Entity entity| {
node := entityTreeModel.toMeta(entity)
if (node.parent != null) {
node.parentNode.refresh(tree)
node.parentNode.expand(tree)
} else {
refreshAll
}
}
eventHub.addListener(Events.refreshEverthing) |->| {
refreshAll
}
}
Bool entityContains(Entity entity, Str text) {
entity.typeof.fields.findAll { it.type.fits(Str#) }.any |field->Bool| {
Str str := field.get(entity) ?: ""
return str.lower.contains(text)
}
}
Void refreshAll() {
inCtor := (tree.model.typeof == TreeModel#)
entityTreeModel = registry.autobuild(EntityTreeModel#, [rootsFunc, filterFuncs])
tree.model = entityTreeModel
if (!inCtor) // don't refresh during ctor
tree.refreshAll
}
Void expand(Entity entity) {
entityTreeModel.toMeta(entity).expand(tree)
tree.select(entity)
}
Command collapseAllCommand() {
Command("Collapse All", icons.icoCollapseAll) |->| {
entityTreeModel.collapseAll(tree)
}
}
EntityMeta[] toMetas(Entity[] entities) {
entityTreeModel.toMetas(entities)
}
EntityMeta toMeta(Entity entity) {
entityTreeModel.toMeta(entity)
}
}
internal class EntityTreeModel : TreeModel {
@Inject
private EntityMetaSrc entityMetaSrc
private Entity:EntityMeta nodeMap := [:]
private |EntityMeta|[] filterFuncs
private |->Entity[]| rootsFunc
new make(|->Entity[]| rootsFunc, |EntityMeta|[] filterFuncs, |This|in) {
in(this)
this.rootsFunc = rootsFunc
this.filterFuncs = filterFuncs
}
override Obj[] roots() {
reFilter
return toMetas(rootsFunc.call).findAll { (it as EntityMeta).visible }.map { (it as EntityMeta).entity }
}
override Str text(Obj entity) {
toMeta(entity).text
}
override Image? image(Obj entity) {
toMeta(entity).icon
}
override Obj[] children(Obj entity) {
toMetas(toMeta(entity).children).findAll { (it as EntityMeta).visible }.map { (it as EntityMeta).entity }
}
Void reFilter() {
nodeMap.vals.each { it.visible = true }
filterFuncs.each |func| {
toMetas(rootsFunc.call).each { filter(func, it) }
}
}
Void walk(|EntityMeta| func, Entity[] treeFodder := rootsFunc.call) {
treeFodder.each |entity| {
node := toMeta(entity)
func.call(node)
walk(func, node.children)
}
}
Void filter(|EntityMeta| func, EntityMeta node) {
func.call(node)
if (node.visible)
node.childrenAsNodes.each { filter(func, it) }
}
EntityMeta[] toMetas(Entity[] entities) {
entities.map { toMeta(it) }
}
EntityMeta toMeta(Entity entity) {
nodeMap.getOrAdd(entity) {
entityMetaSrc.toMeta(entity, nodeMap)
}
}
Void collapseAll(Tree tree) {
nodeMap.keys.each { tree.setExpanded(it, false) }
}
}