1 | // *****************************************************************************
|
2 | // Copyright (C) 2017 TypeFox and others.
|
3 | //
|
4 | // This program and the accompanying materials are made available under the
|
5 | // terms of the Eclipse Public License v. 2.0 which is available at
|
6 | // http://www.eclipse.org/legal/epl-2.0.
|
7 | //
|
8 | // This Source Code may also be made available under the following Secondary
|
9 | // Licenses when the conditions for such availability set forth in the Eclipse
|
10 | // Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
11 | // with the GNU Classpath Exception which is available at
|
12 | // https://www.gnu.org/software/classpath/license.html.
|
13 | //
|
14 | // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
|
15 | // *****************************************************************************
|
16 |
|
17 | import { TreeNode } from './tree';
|
18 | import { Event, Disposable, isObject, SelectionProvider } from '../../common';
|
19 |
|
20 | /**
|
21 | * The tree selection service.
|
22 | */
|
23 | export const TreeSelectionService = Symbol('TreeSelectionService');
|
24 | export interface TreeSelectionService extends Disposable, SelectionProvider<ReadonlyArray<Readonly<SelectableTreeNode>>> {
|
25 |
|
26 | /**
|
27 | * The tree selection, representing the selected nodes from the tree. If nothing is selected, the
|
28 | * result will be empty.
|
29 | */
|
30 | readonly selectedNodes: ReadonlyArray<Readonly<SelectableTreeNode>>;
|
31 |
|
32 | /**
|
33 | * Emitted when the selection has changed in the tree.
|
34 | */
|
35 | readonly onSelectionChanged: Event<ReadonlyArray<Readonly<SelectableTreeNode>>>;
|
36 |
|
37 | /**
|
38 | * Registers the given selection into the tree selection service. If the selection state changes after adding the
|
39 | * `selectionOrTreeNode` argument, a selection changed event will be fired. If the argument is a tree node,
|
40 | * a it will be treated as a tree selection with the default selection type.
|
41 | */
|
42 | addSelection(selectionOrTreeNode: TreeSelection | Readonly<SelectableTreeNode>): void;
|
43 |
|
44 | /**
|
45 | * Clears all selected nodes
|
46 | */
|
47 | clearSelection(): void;
|
48 |
|
49 | /**
|
50 | * Store selection state.
|
51 | */
|
52 | storeState(): object;
|
53 |
|
54 | /**
|
55 | * Restore selection state.
|
56 | */
|
57 | restoreState(state: object): void;
|
58 |
|
59 | }
|
60 |
|
61 | /**
|
62 | * Representation of a tree selection.
|
63 | */
|
64 | export interface TreeSelection {
|
65 |
|
66 | /**
|
67 | * The actual item that has been selected.
|
68 | */
|
69 | readonly node: Readonly<SelectableTreeNode>;
|
70 |
|
71 | /**
|
72 | * The optional tree selection type. Defaults to `SelectionType.DEFAULT`;
|
73 | */
|
74 | readonly type?: TreeSelection.SelectionType;
|
75 |
|
76 | }
|
77 |
|
78 | export namespace TreeSelection {
|
79 |
|
80 | /**
|
81 | * Enumeration of selection types.
|
82 | */
|
83 | export enum SelectionType {
|
84 | DEFAULT,
|
85 | TOGGLE,
|
86 | RANGE
|
87 | }
|
88 |
|
89 | export function is(arg: unknown): arg is TreeSelection {
|
90 | return isObject(arg) && 'node' in arg;
|
91 | }
|
92 |
|
93 | export function isRange(arg: TreeSelection | SelectionType | undefined): boolean {
|
94 | return isSelectionTypeOf(arg, SelectionType.RANGE);
|
95 | }
|
96 |
|
97 | export function isToggle(arg: TreeSelection | SelectionType | undefined): boolean {
|
98 | return isSelectionTypeOf(arg, SelectionType.TOGGLE);
|
99 | }
|
100 |
|
101 | function isSelectionTypeOf(arg: TreeSelection | SelectionType | undefined, expected: SelectionType): boolean {
|
102 | if (arg === undefined) {
|
103 | return false;
|
104 | }
|
105 | const type = typeof arg === 'number' ? arg : arg.type;
|
106 | return type === expected;
|
107 | }
|
108 |
|
109 | }
|
110 |
|
111 | /**
|
112 | * A selectable tree node.
|
113 | */
|
114 | export interface SelectableTreeNode extends TreeNode {
|
115 |
|
116 | /**
|
117 | * `true` if the tree node is selected. Otherwise, `false`.
|
118 | */
|
119 | selected: boolean;
|
120 |
|
121 | /**
|
122 | * @deprecated @since 1.27.0. Use TreeFocusService to track the focused node.
|
123 | *
|
124 | * `true` if the tree node has the focus. Otherwise, `false`. Defaults to `false`.
|
125 | */
|
126 | focus?: boolean;
|
127 |
|
128 | }
|
129 |
|
130 | export namespace SelectableTreeNode {
|
131 |
|
132 | export function is(node: unknown): node is SelectableTreeNode {
|
133 | return TreeNode.is(node) && 'selected' in node;
|
134 | }
|
135 |
|
136 | export function isSelected(node: unknown): node is SelectableTreeNode {
|
137 | return is(node) && node.selected;
|
138 | }
|
139 |
|
140 | /**
|
141 | * @deprecated @since 1.27.0. Use TreeFocusService to track the focused node.
|
142 | */
|
143 | export function hasFocus(node: TreeNode | undefined): boolean {
|
144 | return is(node) && node.focus === true;
|
145 | }
|
146 |
|
147 | export function isVisible(node: TreeNode | undefined): node is SelectableTreeNode {
|
148 | return is(node) && TreeNode.isVisible(node);
|
149 | }
|
150 |
|
151 | export function getVisibleParent(node: TreeNode | undefined): SelectableTreeNode | undefined {
|
152 | if (node) {
|
153 | if (isVisible(node.parent)) {
|
154 | return node.parent;
|
155 | }
|
156 | return getVisibleParent(node.parent);
|
157 | }
|
158 | }
|
159 | }
|