-
Notifications
You must be signed in to change notification settings - Fork 77
Add documentation for the TreeWidget #736
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
base: master
Are you sure you want to change the base?
Conversation
Adds a documentation page describing how a custom TreeWidget can be implemented based on the Theia TreeWidget base class and collaborating classes. Highlights various features and how to use them. Signed-off-by: Stefan Winkler <stefan@winklerweb.net>
|
Signed-off-by: Stefan Winkler <stefan@winklerweb.net>
converted to draft - needs to be updated once eclipse-theia/generator-theia-extension#241 has been finalized. |
Signed-off-by: Stefan Winkler <stefan@winklerweb.net>
@martin-fleck-at I have adapted the docs to the final example code. So it is ready for review now. |
@xpomul Fantastic, thank you, I'll have a look at it today! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you very much for writing up this documentation @xpomul!
I think we already have a lot of information on this page but I do worry a bit about a clear distinction between the generic tree UI part vs the example that you use. I think we need to add a bit more generic information in the beginning about the tree, the services and the properties as well that for each of those things we do have default implementations.
Afterwards we need to make it very clear, that we will explain customization based on one particular example but that use cases can vary a lot and not all parts that are mentioned in the example need to be implemented by every adopter, e.g., checkboxes. I think readers may otherwise get overwhelmed and confused about what it is they actually need to do to get their own tree in the most basic version.
What do you think?
|
||
All of these are explained below in more detail. For demonstration purposes, we will create a `TreeviewExampleWidget` with some demo contents. | ||
|
||
### The `TreeWidget` widget |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: I'd avoid the code formatting in headers, it looks a bit off imho.
|
||
## Basic Building Blocks | ||
|
||
To create a basic `TreeWidget`, we need to implement: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To create a basic `TreeWidget`, we need to implement: | |
To create a basic `TreeWidget`, we the following parts: |
(cause we do not actually need to implement all of them, for some of them we could just take the default implementation and the label provider may not be needed at all)
]; | ||
``` | ||
|
||
This business model needs to be mapped to a hierarchy of tree nodes. These are objects that satisfy the `TreeNode` interface, which primarily provides the `id` and `parent` properties. Each node in the tree is required to have an `id` that is unique within the tree _(Note: expect strange effects if your `id`s are not unique!)._ The `parent` property can be `undefined` initially, and will be managed by utiltiy functions within the `CompoiteTreeNode` namespace, which are described below. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This business model needs to be mapped to a hierarchy of tree nodes. These are objects that satisfy the `TreeNode` interface, which primarily provides the `id` and `parent` properties. Each node in the tree is required to have an `id` that is unique within the tree _(Note: expect strange effects if your `id`s are not unique!)._ The `parent` property can be `undefined` initially, and will be managed by utiltiy functions within the `CompoiteTreeNode` namespace, which are described below. | |
This business model needs to be mapped to a hierarchy of tree nodes. These are objects that satisfy the `TreeNode` interface, which primarily provides the `id` and `parent` properties. Each node in the tree is required to have an `id` that is unique within the tree _(Note: expect strange effects if your `id`s are not unique!)._ The `parent` property can be `undefined` initially, and will be managed by utility functions within the `CompositeTreeNode` namespace, which are described below. |
} | ||
``` | ||
|
||
In this example, we initialize the tree in the `init()` method when the class is instantiated. This is not necessarily required, but it is the simplest way for our static model. In more complex scenarios, we could also call a concrete `initModel()` method from the Tree Widget implementation, for example, in the `onAfterAttach()` event handler, or we could implement the initialization asynchonously. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In this example, we initialize the tree in the `init()` method when the class is instantiated. This is not necessarily required, but it is the simplest way for our static model. In more complex scenarios, we could also call a concrete `initModel()` method from the Tree Widget implementation, for example, in the `onAfterAttach()` event handler, or we could implement the initialization asynchonously. | |
In this example, we initialize the tree in the `init()` method when the class is instantiated. This is not necessarily required, but it is the simplest way for our static model. In more complex scenarios, we could also call a concrete `initModel()` method from the Tree Widget implementation, for example, in the `onAfterAttach()` event handler, or we could implement the initialization asynchronously. |
* a label provider contribution - a subclass of `LabelProviderContribution` | ||
* the view contribution - a subclass of `AbstractViewContribution`, as is usual for all widgets/views | ||
* the widget factory, which is realized using an inversify child container | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are not mentioning the Tree
with the default TreeImpl
implementation.
To create a basic `TreeWidget`, we need to implement: | ||
|
||
* the actual widget - a subclass of `TreeWidget` | ||
* a model - a subclass of `TreeModelImpl` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd almost suggest to focus a bit more on the interfaces and mention that there are default implementations for each part already but that it can be customized.
} | ||
``` | ||
|
||
> **Note:** At the moment (Theia 1.60.x), there is an issue with the UI in which the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd remove the Theia 1.60.x version here because it might have already been present in previous versions, just referencing the issue below should be enough.
|
||
Note: The code snippets shown below are part of the full example available via the [Theia Extension Generator](https://github.com/eclipse-theia/generator-theia-extension/blob/master/README.md). To get started and play with the example code, generate the `TreeWidget View` example using the generator. | ||
|
||
## Basic Building Blocks |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I would like to see some more general explanation here (or in an Overview
section) about the tree UI in general, because at its core the tree widget is not really different than any widget that is created through widget factories. The main difference is, that due to the complexity of tree UI behavior, Theia provides a utility function createTreeContainer
to tie a set of services (selection, expansion, navigation, search, etc.) and behavioral properties (multi select, virtualization, etc.) together to create a pretty good default UI. So writing a tree UI consists mainly of making sure that the widget factory uses a tree container with all the necessary services and properties that define the behavior of the tree. So it is more in line with what you explain the section Putting it all together
. All that is left is for the user to plug in their own model.
Of course, the parts that you mention here are all valid and important but not all of them are strictly necessary ("basic"). I think having a more general picture of the tree first and then diving into a use case for customization (i.e., the Example tree) may give a clearer picture here, what do you think?
|
||
### The `TreeWidget` widget | ||
|
||
The `TreeWidget` is a specialized `ReactWidget` that already implements all of the logic required to render trees and let the user interact with them. So in its simplest form, we need to just implement the constructor to initialize the id, title, and caption |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not sure if we "need" to do it but we definitely want to do it. I'd be a bit more careful in what we describe as necessary in this article vs a common approach/recommendation.
} | ||
``` | ||
|
||
Under the hood, the tree expansion is handled by the `TreeExpansionServiceImpl` implementation which maintains the `expanded` state of `ExpandableTreeNode`, sends events related to expanding and collapsing subtrees, and also calls `Tree.refresh()` for the expanded node. The `refresh()` method in turn calls `resolveChildren()` which can be implemented to asyncronously provide the child nodes for the given parent. As a bonus, `Tree.refresh()` marks the node as `busy` until the promise is resolved, leading to a nice busy marker in the form of a spinning circle if the promise is not resolved within a certain amount of time (800ms). This can be observed in this demo code due to the `wait(2000)` call. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TreeExpansionServiceImpl
implementation -> TreeExpansionService
implementation (could be anything that is bound to that service)
Contributes a documentation page for the TreeWidget, describing how a custom Tree View can be implemented.
Highlights several features and customization possibilities.
Note: This PR references example code in the Theia Extension Generator repository which is not yet merged.
The respective PR is eclipse-theia/generator-theia-extension#241
That PR should be merged first before merging this one.