diff --git a/.vscode/extensions.json b/.vscode/extensions.json index bd072c293..aaeeb6311 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -3,5 +3,6 @@ "dbaeumer.vscode-eslint", "esbenp.prettier-vscode", "streetsidesoftware.code-spell-checker", - ], + "mermaidchart.vscode-mermaid-chart" + ] } diff --git a/packages/itwin/tree-widget/docs/CategoriesTreeVisibilityHandling.md b/packages/itwin/tree-widget/docs/CategoriesTreeVisibilityHandling.md new file mode 100644 index 000000000..fd91663ca --- /dev/null +++ b/packages/itwin/tree-widget/docs/CategoriesTreeVisibilityHandling.md @@ -0,0 +1,48 @@ + + +# Categories tree specific visibility handling + +This document explains visibility handling for categories tree specific cases. + +## Table of contents + +- [Getting visibility status](#getting-visibility-status) + - [getDefinitionContainersVisibilityStatus](#getdefinitioncontainersvisibilitystatus) + - [getCategoriesVisibilityStatus](./SharedVisibilityHandling.md#getcategoriesvisibilitystatus) + - [getSubCategoriesVisibilityStatus](./SharedVisibilityHandling.md#getsubcategoriesvisibilitystatus) + - [getElementsVisibilityStatus](./SharedVisibilityHandling.md#getelementsvisibilitystatus) + +## Getting visibility status + +### getDefinitionContainersVisibilityStatus + +To determine definition containers' visibility status, get their child categories from cache and call [getCategoriesVisibilityStatus](./SharedVisibilityHandling.md#getcategoriesvisibilitystatus). + +```mermaid +--- +config: + flowchart: + wrappingWidth: 750 + useMaxWidth: false +--- + +flowchart TD + RESULT_Partial[/partial/] + RESULT_Visible[/visible/] + RESULT_Hidden[/hidden/] + + %% Start + TITLE(["getDefinitionContainersVisibilityStatus"]) --> A["Get categories under props.definitionContainerIds from cache. These are categories whose modelId is the same as definition container or categories of child definition containers (can be nested)"] + + PROPS[\" + props +
- definitionContainerIds: **Id64Arg**
+ "\] + + A -- categoryIds --> B["getCategoriesVisibilityStatus({ categoryIds, modelId: undefined })"] + + %% Results + B -- partial --> RESULT_Partial + B -- visible --> RESULT_Visible + B -- hidden --> RESULT_Hidden +``` diff --git a/packages/itwin/tree-widget/docs/ClassificationsTreeVisibilityHandling.md b/packages/itwin/tree-widget/docs/ClassificationsTreeVisibilityHandling.md new file mode 100644 index 000000000..1fe340cfb --- /dev/null +++ b/packages/itwin/tree-widget/docs/ClassificationsTreeVisibilityHandling.md @@ -0,0 +1,82 @@ + + +# Classifications tree specific visibility handling + +This document explains visibility handling for classifications tree specific cases. + +## Table of contents + +- [Getting visibility status](#getting-visibility-status) + - [getClassificationTablesVisibilityStatus](#getclassificationtablesvisibilitystatus) + - [getClassificationsVisibilityStatus](#getclassificationsvisibilitystatus) + - [getCategoriesVisibilityStatus](./SharedVisibilityHandling.md#getcategoriesvisibilitystatus) + - [getElementsVisibilityStatus](./SharedVisibilityHandling.md#getelementsvisibilitystatus) + +## Getting visibility status + +### getClassificationTablesVisibilityStatus + +To determine classification tables' visibility status, get their child categories from cache and call [getCategoriesVisibilityStatus](./SharedVisibilityHandling.md#getcategoriesvisibilitystatus). + +```mermaid +--- +config: + flowchart: + wrappingWidth: 750 + useMaxWidth: false +--- + +flowchart TD + RESULT_Partial[/partial/] + RESULT_Visible[/visible/] + RESULT_Hidden[/hidden/] + + + %% Start + TITLE(["getClassificationTablesVisibilityStatus"]) --> A["Get categories under props.classificationTableIds from cache. These are categories of child classifications (can be nested)"] + + PROPS[\" + props +
- classificationTableIds: **Id64Arg**
+ "\] + + A -- categoryIds --> B["getCategoriesVisibilityStatus({ categoryIds, modelId: undefined })"] + + %% Results + B -- partial --> RESULT_Partial + B -- visible --> RESULT_Visible + B -- hidden --> RESULT_Hidden +``` + +### getClassificationsVisibilityStatus + +To determine classifications' visibility status, get their child categories from cache and call [getCategoriesVisibilityStatus](./SharedVisibilityHandling.md#getcategoriesvisibilitystatus). + +```mermaid +--- +config: + flowchart: + wrappingWidth: 750 + useMaxWidth: false +--- + +flowchart TD + RESULT_Partial[/partial/] + RESULT_Visible[/visible/] + RESULT_Hidden[/hidden/] + + %% Start + TITLE(["getClassificationsVisibilityStatus"]) --> A["Get categories under props.classificationIds from cache. These are related categories and categories of child classifications (can be nested)"] + + PROPS[\" + props +
- classificationIds: **Id64Arg**
+ "\] + + A -- categoryIds --> B["getCategoriesVisibilityStatus({ categoryIds, modelId: undefined })"] + + %% Results + B -- partial --> RESULT_Partial + B -- visible --> RESULT_Visible + B -- hidden --> RESULT_Hidden +``` diff --git a/packages/itwin/tree-widget/docs/ModelsTreeVisibilityHandling.md b/packages/itwin/tree-widget/docs/ModelsTreeVisibilityHandling.md new file mode 100644 index 000000000..d11b440ab --- /dev/null +++ b/packages/itwin/tree-widget/docs/ModelsTreeVisibilityHandling.md @@ -0,0 +1,47 @@ + + +# Models tree specific visibility handling + +This document explains visibility handling for models tree specific cases. + +## Table of contents + +- [Getting visibility status](#getting-visibility-status) + - [getSubjectsVisibilityStatus](#getsubjectsvisibilitystatus) + - [getModelsVisibilityStatus](./SharedVisibilityHandling.md#getmodelsvisibilitystatus) + - [getCategoriesVisibilityStatus](./SharedVisibilityHandling.md#getcategoriesvisibilitystatus) + - [getElementsVisibilityStatus](./SharedVisibilityHandling.md#getelementsvisibilitystatus) + +## Getting visibility status + +### getSubjectsVisibilityStatus + +To determine subjects' visibility status, get their child models from cache and call [getModelsVisibilityStatus](./SharedVisibilityHandling.md#getmodelsvisibilitystatus). + +```mermaid +--- +config: + flowchart: + wrappingWidth: 750 + useMaxWidth: false +--- + +flowchart TD + RESULT_Partial[/partial/] + RESULT_Visible[/visible/] + RESULT_Hidden[/hidden/] + + %% Start + TITLE("getSubjectsVisibilityStatus") --> A["Get models under props.subjectIds from cache. These are related models and models of child subjects (can be nested)"] + + PROPS[\"props +
- subjectIds: **Id64Arg**
+ "\] + + A -- modelIds --> B["getModelsVisibilityStatus({ modelIds })"] + + %% Results + B -- partial --> RESULT_Partial + B -- visible --> RESULT_Visible + B -- hidden --> RESULT_Hidden +``` diff --git a/packages/itwin/tree-widget/docs/SharedVisibilityHandling.md b/packages/itwin/tree-widget/docs/SharedVisibilityHandling.md new file mode 100644 index 000000000..9acf37590 --- /dev/null +++ b/packages/itwin/tree-widget/docs/SharedVisibilityHandling.md @@ -0,0 +1,442 @@ + + +# Shared visibility handling + +This document explains the shared parts of visibility handling in models, categories and classifications trees. Please read [how visibility is determined in the viewport](./Visibility.md#how-visibility-is-determined-in-the-viewport). + +## Table of contents + +- [Getting visibility status](#getting-visibility-status) + - [getSubCategoriesVisibilityStatus](#getsubcategoriesvisibilitystatus) + - [getModelsVisibilityStatus](#getmodelsvisibilitystatus) + - [getCategoriesVisibilityStatus](#getcategoriesvisibilitystatus) + - [getModelWithCategoryVisibilityStatus](#getmodelwithcategoryvisibilitystatus) + - [getElementsVisibilityStatus](#getelementsvisibilitystatus) + - [getAlwaysOrNeverDrawnVisibilityStatus](#getalwaysorneverdrawnvisibilitystatus) + +## Getting visibility status + +### getSubCategoriesVisibilityStatus + +Visibility of sub-category is `hidden` if its category is `hidden` **Or** the sub-category itself is hidden, otherwise it is `visible`. When determining visibility of multiple sub-categories, need to check if some are `visible` and some are `hidden`, in such case `partial` visibility is returned. + +```mermaid +--- +config: + flowchart: + wrappingWidth: 750 + useMaxWidth: false +--- + +flowchart TD + RESULT_Partial[/partial/] + RESULT_Visible[/visible/] + RESULT_Hidden[/hidden/] + + %% Start + TITLE(["getSubCategoriesVisibilityStatus"]) --> A{"viewport.viewsCategory(props.categoryId)"} + + PROPS[\" + props +
- categoryId: **Id64String**
- subCategoryIds: **Id64Arg**
+ "\] + + %% Branch No + A -- No --> RESULT_Hidden + + %% Branch Yes + A -- Yes --> B["Iterate through sub-categories"] + B -- subCategoryId --> C{"viewport.viewsSubCategory(subCategoryId)"} + C -- Yes --> D1[visible] + C -- No --> D2[hidden] + + %% Merge + D1 --> M[Merge visibility statuses] + D2 --> M + + M --> N[Some 'visible' && Some 'hidden'
**OR**
at least one is 'partial'] + + %% Results + N -- Yes --> RESULT_Partial + + N -- No --> O[All are 'visible'] + + O -- Yes --> RESULT_Visible + O -- No --> RESULT_Hidden +``` + +### getModelsVisibilityStatus + +Visibility of model is determined by merging visibility status of two parts: + +1. Model selector. If model is not hidden in selector, need to check categories of child elements (they are retrieved from cache) by calling [getCategoriesVisibilityStatus](#getcategoriesvisibilitystatus). +2. Child elements' which are sub-models (retrieved from cache). For such elements call [getModelsVisibilityStatus](#getmodelsvisibilitystatus). + +```mermaid +--- +config: + flowchart: + wrappingWidth: 750 + useMaxWidth: false +--- + +flowchart TD + RESULT_Partial[/partial/] + RESULT_Visible[/visible/] + RESULT_Hidden[/hidden/] + + %% Start + TITLE(["getModelsVisibilityStatus"]) --> A[Iterate through props.modelIds] + + PROPS[\" + props +
- modelIds: **Id64Arg**
+ "\] + + A -- modelId --> B{"viewport.viewsModel(modelId)"} + + %% Branch Yes + B -- Yes --> C1[Get categories of elements which exist under modelId] + C1 -- categoryIds --> D1["getCategoriesVisibilityStatus({ modelId, categoryIds })"] + + %% Branch No + B -- No --> C2[Get modelled elements under modelId] + C2 -- modelIds --> D2{"getModelsVisibilityStatus({ modelIds })
=== 'hidden'/empty"} + D2 -- Yes --> E1[hidden] + D2 -- No --> E2[partial] + + %% Merge + D1 --> M[Merge visibility statuses] + E1 --> M + E2 --> M + + M --> N[Some 'visible' && Some 'hidden'
**OR**
at least one is 'partial'] + + N -- Yes --> RESULT_Partial + + N -- No --> O[All are 'visible'] + + O -- Yes --> RESULT_Visible + O -- No --> RESULT_Hidden +``` + +### getCategoriesVisibilityStatus + +Allows getting category visibility under specific model (when modelId is defined in props) or to get generic category visibility. + +1. For category visibility under specific model, [getModelWithCategoryVisibilityStatus](#getmodelwithcategoryvisibilitystatus) is used. +2. For generic category visibility status, merge statuses from: + - Get sub-categories related to category (from cache), and call [getSubCategoriesVisibilityStatus](#getsubcategoriesvisibilitystatus). + - Get models of category elements (from cache), for each model call [getModelWithCategoryVisibilityStatus](#getmodelwithcategoryvisibilitystatus). + +```mermaid +--- +config: + flowchart: + wrappingWidth: 750 + useMaxWidth: false +--- + +flowchart TD + RESULT_Partial[/partial/] + RESULT_Visible[/visible/] + RESULT_Hidden[/hidden/] + + %% Start + TITLE(["getCategoriesVisibilityStatus"]) --> A{props.modelId
=== undefined} + + PROPS[\" + props +
- modelId: **Id64String | undefined**
- categoryIds: **Id64Arg**
+ "\] + + %% Branch Yes + A -- Yes --> B[Iterate through categories] + B -- categoryId --> C1[Get sub-categories for specified category from cache] + B -- categoryId --> C2[Get models for specified category from cache] + + C1 -- subCategoryIds --> D1["getSubCategoriesVisibilityStatus({ subCategoryIds, categoryId })"] + + C2 --> D2[Iterate through models] + D2 -- modelId --> F["getModelWithCategoryVisibilityStatus({ modelId, categoryId })"] + + %% Branch No + A -- No --> B2[Iterate through categories] + B2 -- categoryId --> F + + %% Merge + D1 --> M[Merge visibility statuses] + F --> M + + M --> N[Some 'visible' && Some 'hidden'
**OR**
at least one is 'partial'] + + %% Results + N -- Yes --> RESULT_Partial + + N -- No --> O[All are 'visible'] + + O -- Yes --> RESULT_Visible + O -- No --> RESULT_Hidden +``` + +### getModelWithCategoryVisibilityStatus + +Determines visibility status of category under model. It is done by merging visibility statuses of: + +- **Sub-models**: Model category elements which are sub-models (retrieved from cache) and calling [getModelsVisibilityStatus](#getmodelsvisibilitystatus). +- **Child elements**: determining child elements visibility is done by: + 1. Getting total count of elements under the category with model. + 2. Getting default child elements status based on per-model category override and category selector. + 3. Get `opposite set` to default status: default status === `visible` -> `alwaysDrawn`, `neverDrawn` otherwise. + 4. The `opposite set` can contain elements from any categories and models, need to query data of these elements and find the ones which are related to the desired category and model. + 5. Once all the above data (1-4) is known, visibility can be determined by comparing the total count, number of elements (related to specific model and category) in the opposite set, and default status. + + **Note**: All the checks are done only when [visibility rules](./Visibility.md#how-visibility-is-determined-in-the-viewport) that have higher priority do not interfere (e.g. if model is hidden in selector, then always/never drawn elements are **not checked** and `hidden` is returned for `Child Elements` visibility). + +```mermaid +--- +config: + flowchart: + wrappingWidth: 750 + useMaxWidth: false +--- + +flowchart TD + RESULT_Partial[/partial/] + RESULT_Visible[/visible/] + RESULT_Hidden[/hidden/] + + %% Start + TITLE(["getModelWithCategoryVisibilityStatus"]) --> A1[Get modelled elements under category with model] + TITLE(["getModelWithCategoryVisibilityStatus"]) --> A2{"viewport.viewsModel(props.modelId)"} + + PROPS[\" + props +
- modelId: **Id64String**
- categoryId: **Id64String**
+ "\] + + %% Branch A1 + A1 -- modelIds --> B["getModelsVisibilityStatus({ modelIds })"] + + %% Branch A2 + + %% Branch No + A2 -- No --> C[hidden] + + %% Branch Yes + + A2 -- Yes --> D{Is always drawn exclusive} + + %% Branch Yes + D -- Yes --> E1["**defaultStatus**: 'hidden'
**oppositeSet**: alwaysDrawn"] + + %% Branch No + D -- No --> E2{" +
+ Per model category override === 'show'
+ OR +
+ Per model category override === 'none'
&& viewport.viewsCategory(props.categoryId) +
+ "} + + + %% Branch No + E2 -- No --> E1 + + %% Branch Yes + E2 -- Yes --> E3["**defaultStatus**: 'visible'
**oppositeSet**: neverDrawn"] + + E1 -- Pass down --> F{"**oppositeSet**.size > 0"} + E3 -- Pass down --> F + + %% Branch No + F -- No --> G1[defaultStatus] + + %% Branch Yes + F -- Yes --> G2[From cache get total count of elements under category with model] + + F -- Yes --> G3["Props +
- For **oppositeSet** elements execute query (if set changed after last execution), to get their models, categories and parent elements path.
- Find always/never drawn child elements (nested as well) where queried data matches props.modelId & props.categoryId.
- Get count of elements under model with category in **oppositeSet**: numberOfElementsInOppositeSet
+ "] + + G2 -- totalCount --> H["getAlwaysOrNeverDrawnVisibilityStatus({ totalCount, numberOfElementsInOppositeSet, defaultStatus })"] + G3 -- Pass down --> H + + + %% Merge + B --> M[Merge visibility statuses] + C --> M + H --> M + G1 --> M + + M --> N[Some 'visible' && Some 'hidden'
**OR**
at least one is 'partial'] + + %% Results + N -- Yes --> RESULT_Partial + + N -- No --> O[All are 'visible'] + + O -- Yes --> RESULT_Visible + O -- No --> RESULT_Hidden +``` + +### getElementsVisibilityStatus + +Determines visibility status of elements. Structure is very similar to [getModelWithCategoryVisibilityStatus](#getmodelwithcategoryvisibilitystatus), except everything is done based on elements instead of model + category. + +```mermaid +--- +config: + flowchart: + wrappingWidth: 750 + useMaxWidth: false +--- + +flowchart TD + RESULT_Partial[/partial/] + RESULT_Visible[/visible/] + RESULT_Hidden[/hidden/] + + %% Start + TITLE(["getElementsVisibilityStatus"]) --> A1["
Get modelIds from cache:
1. props.elementIds which are sub-models
2. Children which are sub-models (nested as well)
"] + TITLE(["getElementsVisibilityStatus"]) --> A2{"viewport.viewsModel(props.modelId)"} + + PROPS[\" + props +
- elementIds: **Id64Arg**
- modelId: **Id64String**
- categoryId: **Id64String**
- categoryOfTopMostParentElement: **Id64String**
- parentElementIdsPath: **Array**
- childrenCount: **number | undefined**
+ "\] + + %% Branch A1 + A1 -- modelIds --> B["getModelsVisibilityStatus({ modelIds })"] + + %% Branch A2 + + %% Branch No + A2 -- No --> C[hidden] + + %% Branch Yes + + A2 -- Yes --> D{Is always drawn exclusive} + + %% Branch Yes + D -- Yes --> E1["**defaultStatus**: 'hidden'
**oppositeSet**: alwaysDrawn"] + + %% Branch No + D -- No --> E2{" +
+ Per model category override === 'show'
+ OR +
+ Per model category override === 'none'
&& viewport.viewsCategory(props.categoryId) +
+ "} + + %% Branch No + E2 -- No --> E1 + + %% Branch Yes + E2 -- Yes --> E3["**defaultStatus**: 'visible'
**oppositeSet**: neverDrawn"] + + E1 -- Pass down --> F{"**oppositeSet**.size > 0"} + E3 -- Pass down --> F + + %% Branch No + F -- No --> G1[defaultStatus] + + %% Branch Yes + F -- Yes --> G2{"props.childrenCount
=== 0 / undefined"} + + %% Branch Yes + G2 -- Yes --> H1[Children count in oppositeSet === 0] + + %% Branch No + G2 -- No --> H2["Props +
- For **oppositeSet** elements execute query (if set changed after last execution), to get their models, categories and parent elements path.
- Find always/never drawn child elements (nested as well) where queried data matches props.modelId & props.categoryId & props.parentElementIdsPath.
- Get count of children in **oppositeSet**: numberOfElementsInOppositeSet
+ "] + + + + H1 -- Pass down --> I["**numberOfElementsInOppositeSet**: props.elementIds in oppositeSet and children count in oppositeSet
**totalCount**: props.elementIds + props.childrenCount"] + H2 -- Pass down --> I + + I -- Pass down --> J["getAlwaysOrNeverDrawnVisibilityStatus({ totalCount, numberOfElementsInOppositeSet, defaultStatus })"] + + + %% Merge + B --> M[Merge visibility statuses] + C --> M + G1 --> M + J --> M + + M --> N[Some 'visible' && Some 'hidden'
**OR**
at least one is 'partial'] + + %% Results + N -- Yes --> RESULT_Partial + + N -- No --> O[All are 'visible'] + + O -- Yes --> RESULT_Visible + O -- No --> RESULT_Hidden +``` + +### getAlwaysOrNeverDrawnVisibilityStatus + +Helper function that is used by [getModelWithCategoryVisibilityStatus](#getmodelwithcategoryvisibilitystatus) and [getElementsVisibilityStatus](#getelementsvisibilitystatus). It determines visibility status of elements based on `totalCount`, `numberOfElementsInOppositeSet` and `defaultStatus`. + +```mermaid +--- +config: + flowchart: + wrappingWidth: 750 + useMaxWidth: false +--- + +flowchart TD + RESULT_Partial[/partial/] + RESULT_Visible[/visible/] + RESULT_Hidden[/hidden/] + + %% Start + TITLE(["getAlwaysOrNeverDrawnVisibilityStatus"]) --> A{"props.totalCount
=== 0
**OR**
props.numberOfElementsInOppositeSet
=== 0"} + + PROPS[\" + props +
+ - totalCount: **number** + %% need !important on color since to not take the config color + Number of elements that are under node
(includes node itself and its nested child elements) +
+ - numberOfElementsInOppositeSet: **number** + Number of elements in the set that is opposite
to default status. If default status 'visible', it's
always drawn, otherwise it's never drawn set +
+ - defaultStatus: **'visible' | 'hidden'** + Elements visibility status when they are not
in always/never drawn list +
+
+ "\] + + %% Branch Yes + A -- Yes --> B1{"props.defaultStatus
=== 'visible'"} + + %% Branch Yes + B1 -- Yes --> RESULT_Visible + + %% Branch No + B1 -- No --> RESULT_Hidden + + %% Branch No + A -- No --> B2{"props.numberOfElementsInOppositeSet
=== props.totalCount"} + + %% Branch No + B2 -- No --> RESULT_Partial + + %% Branch Yes + B2 -- Yes --> C{"props.defaultStatus
=== 'visible'"} + + %% Branch Yes + C -- Yes --> RESULT_Hidden + + %% Branch No + C -- No --> RESULT_Visible +``` diff --git a/packages/itwin/tree-widget/docs/Visibility.md b/packages/itwin/tree-widget/docs/Visibility.md new file mode 100644 index 000000000..a5316ed76 --- /dev/null +++ b/packages/itwin/tree-widget/docs/Visibility.md @@ -0,0 +1,79 @@ + + +# Visibility Handling in Tree Widget + +This document explains visibility handling across tree types (Models, Categories, and Classifications) and node types (models, categories, geometric elements, sub-categories, sub-models, classifications, classification tables and definition containers). + +## Key Internal APIs + +- [`useCachedVisibility`](../src/tree-widget-react/components/trees/common/internal/useTreeHooks/UseCachedVisibility.ts) — React hook that returns a tree-specific visibility handler. + - Uses [`VisibilityChangeEventListener`](../src/tree-widget-react/components/trees/common/internal/VisibilityChangeEventListener.ts) to allow `getVisibilityStatus()` calls to be cancelled and re-requested by [`useHierarchyVisibility`](../src/tree-widget-react/components/trees/common/UseHierarchyVisibility.ts) via `onVisibilityChange()`. + - Pauses event notifications while `changeVisibility()` is in progress to avoid re-requesting `getVisibilityStatus` before change finishes. + - Applies special handling when search paths are present (for nodes that are not search targets or have no search-target ancestors). + +- [`BaseVisibilityHelper`](../src/tree-widget-react/components/trees/common/internal/visibility/BaseVisibilityHelper.ts) — shared get/change operations for visibility status based on element/model/category ids. + - Uses [`BaseIdsCache`](../src/tree-widget-react/components/trees/common/internal/caches/BaseIdsCache.ts) to retrieve information about nodes. + - Examples: `getModelsVisibilityStatus()`, `getCategoriesVisibilityStatus()`, `changeModelsVisibilityStatus()`, `changeCategoriesVisibilityStatus()`. + +- Tree-specific visibility handlers [`CategoriesTreeVisibilityHandler`](../src/tree-widget-react/components/trees/categories-tree/internal/visibility/CategoriesTreeVisibilityHandler.ts), [`ClassificationsTreeVisibilityHandler`](../src/tree-widget-react/components/trees/classifications-tree/internal/visibility/ClassificationsTreeVisibilityHandler.ts), [`ModelsTreeVisibilityHandler`](../src/tree-widget-react/components/trees/models-tree/internal/visibility/ModelsTreeVisibilityHandler.ts): + - These handlers are aware of tree-specific hierarchy structure. + - Take tree nodes as input, determine node type via nodes' `extendedData` property, and use appropriate methods from visibility helpers. + - Expose get/change visibility status logic for search-target nodes. + +- Tree-specific visibility helpers ([`CategoriesTreeVisibilityHelper`](../src/tree-widget-react/components/trees/categories-tree/internal/visibility/CategoriesTreeVisibilityHelper.ts), [`ClassificationsTreeVisibilityHelper`](../src/tree-widget-react/components/trees/classifications-tree/internal/visibility/ClassificationsTreeVisibilityHelper.ts), [`ModelsTreeVisibilityHelper`](../src/tree-widget-react/components/trees/models-tree/internal/visibility/ModelsTreeVisibilityHelper.ts)): + - Cover tree-specific cases (e.g. definition containers exist only in the Categories tree, so `CategoriesTreeVisibilityHelper` implements get/change visibility methods for definition containers). + - All of them use [`BaseVisibilityHelper`](../src/tree-widget-react/components/trees/common/internal/visibility/BaseVisibilityHelper.ts) to get/change visibility for those tree-specific cases. + +- Search-results trees ([`BaseSearchResultsTree`](../src/tree-widget-react/components/trees/common/internal/visibility/BaseSearchResultsTree.ts) and tree-specific implementations: [Categories](../src/tree-widget-react/components/trees/categories-tree/internal/visibility/SearchResultsTree.ts), [Classifications](../src/tree-widget-react/components/trees/classifications-tree/internal/visibility/SearchResultsTree.ts), [Models](../src/tree-widget-react/components/trees/models-tree/internal/visibility/SearchResultsTree.ts)): + - Help get/change visibility of nodes which are not search targets and don't have search-target ancestors (since these nodes might have some children missing). They allow retrieving child search targets for such nodes and then getting/changing visibility is done based on search targets instead. + +- Caching: + - [`BaseIdsCache`](../src/tree-widget-react/components/trees/common/internal/caches/BaseIdsCache.ts) - stores data that is relevant to models/categories/classifications trees (e.g. model <-> category relationship). + - This cache is composed of other caches ([`ElementChildrenCache`](../src/tree-widget-react/components/trees/common/internal/caches/ElementChildrenCache.ts), [`SubCategoriesCache`](../src/tree-widget-react/components/trees/common/internal/caches/SubCategoriesCache.ts) and others). + - Data stored in this cache is requested only once, because it does not change. + - Tree-specific id caches ([`CategoriesTreeIdsCache`](../src/tree-widget-react/components/trees/categories-tree/internal/CategoriesTreeIdsCache.ts), [`ClassificationsTreeIdsCache`](../src/tree-widget-react/components/trees/classifications-tree/internal/ClassificationsTreeIdsCache.ts), [`ModelsTreeIdsCache`](../src/tree-widget-react/components/trees/models-tree/internal/ModelsTreeIdsCache.ts)): + - Store various tree-specific relationships, (e.g. models tree ids cache stores element's model <-> subject relationship). + - Extend `BaseIdsCacheImpl` so each tree-specific cache can be used in [`BaseVisibilityHelper`](../src/tree-widget-react/components/trees/common/internal/visibility/BaseVisibilityHelper.ts). + + - [`AlwaysAndNeverDrawnElementInfoCache`](../src/tree-widget-react/components/trees/common/internal/caches/AlwaysAndNeverDrawnElementInfoCache.ts) — caches extra data (like category) for always/never drawn elements. + - Always and never drawn caches are reset when always and never drawn sets change respectively. + - Child always and never drawn elements can be retrieved for models, categories and parent elements. + + - [`ElementChildrenCache`](../src/tree-widget-react/components/trees/common/internal/caches/ElementChildrenCache.ts) — cache for retrieving elements' children. + - When changing element or element grouping nodes' visibility, need to put all children (nested as well) into always/never drawn list. This cache is used to retrieve child nodes' ids in such cases. + - It is not used (and should not be used) when getting visibility status: + - Only total children counts (this is stored on nodes `extendedData` property) and child always/never drawn elements (these are retrieved from [`AlwaysAndNeverDrawnElementInfoCache`](../src/tree-widget-react/components/trees/common/internal/caches/AlwaysAndNeverDrawnElementInfoCache.ts)) are needed for determining visibility. + - Element might have hundreds of thousands of child elements. And retrieving this information for each element in the hierarchy would be very slow. + +## How visibility is determined in the viewport + +The viewport only renders elements. Element visibility is resolved in the following order (highest priority first): + +1. **Model selector**: if a model is hidden, its elements are never visible. +2. **Always/Never drawn sets**: elements in these sets are forced to be visible/hidden. +3. **Always drawn exclusive flag**: If flag is on, then only elements in the `alwaysDrawn` set are visible, otherwise rules below apply. +4. **Per model-category overrides**: a category can be overridden per model with `hide`, `show`, or `none`. + - `hide`: hides all elements of that category within the model. + - `show`: shows all elements of that category within the model. +5. **Category selector**: hidden categories hide their elements. +6. **Sub-categories**: hidden sub-categories hide their elements. + - **Note**: Determining element -> sub-category relationship is not supported at the moment. So sub-category checks are only performed when the Categories tree calls `getVisibilityStatus()` for categories or sub-categories. + +## Visibility logic + +- Getting visibility status + - [Models tree](./ModelsTreeVisibilityHandling.md) + - [getSubjectsVisibilityStatus](./ModelsTreeVisibilityHandling.md#getsubjectsvisibilitystatus) + - [getModelsVisibilityStatus](./SharedVisibilityHandling.md#getmodelsvisibilitystatus) + - [getCategoriesVisibilityStatus](./SharedVisibilityHandling.md#getcategoriesvisibilitystatus) + - [getElementsVisibilityStatus](./SharedVisibilityHandling.md#getelementsvisibilitystatus) + - [Categories tree](./CategoriesTreeVisibilityHandling.md) + - [getDefinitionContainersVisibilityStatus](./CategoriesTreeVisibilityHandling.md#getdefinitioncontainersvisibilitystatus) + - [getCategoriesVisibilityStatus](./SharedVisibilityHandling.md#getcategoriesvisibilitystatus) + - [getSubCategoriesVisibilityStatus](./SharedVisibilityHandling.md#getsubcategoriesvisibilitystatus) + - [getElementsVisibilityStatus](./SharedVisibilityHandling.md#getelementsvisibilitystatus) + - [Classifications tree](./ClassificationsTreeVisibilityHandling.md) + - [getClassificationTablesVisibilityStatus](./ClassificationsTreeVisibilityHandling.md#getclassificationtablesvisibilitystatus) + - [getClassificationsVisibilityStatus](./ClassificationsTreeVisibilityHandling.md#getclassificationsvisibilitystatus) + - [getCategoriesVisibilityStatus](./SharedVisibilityHandling.md#getcategoriesvisibilitystatus) + - [getElementsVisibilityStatus](./SharedVisibilityHandling.md#getelementsvisibilitystatus) diff --git a/packages/itwin/tree-widget/src/tree-widget-react/components/trees/common/TreeWidgetViewport.ts b/packages/itwin/tree-widget/src/tree-widget-react/components/trees/common/TreeWidgetViewport.ts index 65121008e..9f100be7a 100644 --- a/packages/itwin/tree-widget/src/tree-widget-react/components/trees/common/TreeWidgetViewport.ts +++ b/packages/itwin/tree-widget/src/tree-widget-react/components/trees/common/TreeWidgetViewport.ts @@ -95,10 +95,11 @@ export function createTreeWidgetViewport(viewport: Viewport): TreeWidgetViewport * 1. Model visibility - if model is not visible, elements from that model should never be displayed. * 2. `neverDrawn` set - elements in that set should never be displayed. * 3. `alwaysDrawn` set - elements in that set should always be displayed. - * 4. Per-model category visibility overrides: + * 4. Always drawn exclusive flag: if this flag is set to true, only elements in the `alwaysDrawn` set should be displayed. + * 5. Per-model category visibility overrides: * - if a per-model-category has `Hide` override, elements which have that category and model should not be displayed. * - if a per-model-category has `Show` override, elements which have that category and model should be displayed. - * 5. Category and sub-category visibility - if element's category or sub-category is turned off, it should not be displayed. + * 6. Category and sub-category visibility - if element's category or sub-category is turned off, it should not be displayed. * * Based on this order of precedence, element can only be displayed in these scenarios: * - Model is visible *AND* element is in `alwaysDrawn` set.