-
Notifications
You must be signed in to change notification settings - Fork 34
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature request: Tree view widget #181
Comments
I think that's a good fit for a built-in widget: it's not trivial to make with a Let's collect some more requirements before we start implementing: are there any other tools that display tree-like data that we could take inspiration from? For example, should the output be colored by default, do tree entries have descriptions etc. |
I don't have good answers for most of the questions posed and am not experienced enough with mordant to know if coloured by default is a good fit. I'll try to bring a few things to the discussion though: With regards to existing tools, Syntax wise, I'm thinking something like what Daisy UI does for their menu with submenu component would be simplest. Translated to DSL, perhaps something like tree {
leaf {
}
tree {
leaf {
}
}
} Personally I'd love multi-line support for "leaves". I think that's more uncommon when looking at other tree-like tools but it would be great for many use-cases. Being able to show other widgets, such as tables, inside a leaf would be dreamy but then again, perhaps not as hard to implement as one would think if multi-line leaves are supported. |
Also, terminology should probably be based on the tree data structure. |
I'm noticing a symmetry in the structure: nested nodes are drawn with exactly the same characters as the top level tree. So if each entry of the tree can be a widget, you wouldn't need to differentiate between leaf and middle nodes in a dsl, you could do something like class Entry(title: String, content: Widget? = null)
class Tree(vararg entries: Entry): Widget And the have the class definition like Tree(
Entry("bin", Tree(
Entry("parse-ansi-codes.rs"),
)),
Entry("Cargo.lock"),
Entry("Cargo.toml"),
Entry("README.md"),
Entry("src", Tree(
Entry("cursor.rs"),
Entry("lib.rs"),
Entry("style.rs"),
)),
Entry("target", Tree(
Entry("debug"),
)),
Entry("test"),
) with a dsl like tree {
entry("bin", tree {
entry("parse-ansi-codes.rs")
})
entry("Cargo.lock")
entry("Cargo.toml")
entry("README.md")
entry("src",
tree {
entry("cursor.rs")
entry("lib.rs")
entry("style.rs")
}
}
entry("target", tree {
entry("debug")
})
entry("test")
} |
I'm not sure I'm a fan of forcing a data class Widget(val text: String)
sealed interface TreeNode
data class Leaf(val content: Widget) : TreeNode
data class Tree(val content: Widget? = null, val entries: List<TreeNode>) : TreeNode {
constructor(content: Widget? = null, vararg entries: TreeNode) : this(content, entries.toList())
data class Builder(val content: Widget? = null, var entries: MutableList<TreeNode> = mutableListOf<TreeNode>()) {
fun tree(content: Widget? = null, init: Tree.Builder.() -> Unit) {
entries.add(
Tree.Builder(content).apply { init() }.build()
)
}
fun leaf(init: () -> Widget) {
entries.add(Leaf(init()))
}
fun build(): Tree = Tree(content, entries)
}
}
fun tree(content: Widget? = null, init: Tree.Builder.() -> Unit): Tree =
Tree.Builder(content).apply { init() }.build()
fun main() {
val normalTree = Tree(
content = Widget("Hotel"),
Tree(
content = Widget("Floor 1"),
Leaf(Widget("Room 100")),
Leaf(Widget("Room 101")),
Leaf(Widget("Room 102"))
),
Tree(
content = Widget("Floor 2"),
Leaf(Widget("Room 200")),
Leaf(Widget("Room 201")),
Leaf(Widget("Room 202"))
)
)
val dslTree = tree(Widget("Hotel")) {
tree(Widget("Floor 1")) {
leaf {
Widget("Room 100")
}
leaf {
Widget("Room 101")
}
leaf {
Widget("Room 102")
}
}
tree(Widget("Floor 2")) {
leaf {
Widget("Room 200")
}
leaf {
Widget("Room 201")
}
leaf {
Widget("Room 202")
}
}
}
println(normalTree)
println(dslTree)
println(normalTree == dslTree)
} Playground link: https://pl.kotl.in/o-95F5FZ1 Note that |
Thanks for all the feedback! I'll definitely have overloads for specifying entries as either a string or a widget, like I do for all the other widget builders. The reason I think that a list of entries is preferable than a split
And a single separate |
Not sure I see the problem - wouldn't that just be tree {
leaf { "Cargo.lock" }
leaf { "Cargo.toml" }
leaf { "test" }
} ? EDIT I.e.
or
I guess index 0 could be made "special" to mean the current node's content but I think it makes sense to use explicit syntax. EDIT2 Note that I imagine |
How do you mean a tree could have more than one root? |
I would like to render a tree view, similar to the linux
tree
command:The text was updated successfully, but these errors were encountered: