-
Notifications
You must be signed in to change notification settings - Fork 6
/
BaseTool.ts
186 lines (158 loc) · 4.93 KB
/
BaseTool.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
import { EditorNotifier, EditorEventType } from '../types';
import { WheelEvt, PointerEvt, KeyPressEvent, KeyUpEvent, PasteEvent, CopyEvent, InputEvt, InputEvtType, GestureCancelEvt, PointerDownEvt, PointerMoveEvt, PointerUpEvt } from '../inputEvents';
import ToolEnabledGroup from './ToolEnabledGroup';
import InputMapper, { InputEventListener } from './InputFilter/InputMapper';
import { MutableReactiveValue, ReactiveValue } from '../util/ReactiveValue';
import { DispatcherEventListener } from '../EventDispatcher';
export default abstract class BaseTool implements InputEventListener {
#enabled: MutableReactiveValue<boolean>;
#group: ToolEnabledGroup|null = null;
#inputMapper: InputMapper|null = null;
#readOnlyEditorChangeListener: DispatcherEventListener|null = null;
protected constructor(private notifier: EditorNotifier, public readonly description: string) {
this.#enabled = ReactiveValue.fromInitialValue(true);
this.#enabled.onUpdate(enabled => {
// Ensure that at most one tool in the group is enabled.
if (enabled) {
this.#group?.notifyEnabled(this);
this.notifier.dispatch(EditorEventType.ToolEnabled, {
kind: EditorEventType.ToolEnabled,
tool: this,
});
} else {
this.notifier.dispatch(EditorEventType.ToolDisabled, {
kind: EditorEventType.ToolDisabled,
tool: this,
});
}
});
}
/** Override this to allow this tool to be enabled in a read-only editor */
public canReceiveInputInReadOnlyEditor() {
return false;
}
public setInputMapper(mapper: InputMapper|null) {
this.#inputMapper = mapper;
if (mapper) {
mapper.setEmitListener(event => this.dispatchEventToCallback(event));
}
}
public getInputMapper() {
return this.#inputMapper;
}
private dispatchEventToCallback(event: InputEvt) {
let exhaustivenessCheck: never;
switch (event.kind) {
case InputEvtType.PointerDownEvt:
return this.onPointerDown(event);
case InputEvtType.PointerMoveEvt:
this.onPointerMove(event);
break;
case InputEvtType.PointerUpEvt:
return this.onPointerUp(event) ?? false;
case InputEvtType.GestureCancelEvt:
this.onGestureCancel(event);
break;
case InputEvtType.WheelEvt:
return this.onWheel(event);
case InputEvtType.KeyPressEvent:
return this.onKeyPress(event);
case InputEvtType.KeyUpEvent:
return this.onKeyUp(event);
case InputEvtType.CopyEvent:
return this.onCopy(event);
case InputEvtType.PasteEvent:
return this.onPaste(event);
default:
exhaustivenessCheck = event;
return exhaustivenessCheck;
}
return true;
}
// @internal
public onEvent(event: InputEvt): boolean {
if (this.#inputMapper) {
return this.#inputMapper.onEvent(event);
}
return this.dispatchEventToCallback(event);
}
/**
* Returns true iff the tool handled the event and thus should receive additional
* events.
*/
public onPointerDown(_event: PointerDownEvt): boolean { return false; }
public onPointerMove(_event: PointerMoveEvt) { }
/**
* Returns true iff there are additional pointers down and the tool should
* remain active to handle the additional events.
*
* For most purposes, this should return `false` or nothing.
*/
public onPointerUp(_event: PointerUpEvt): boolean|void { }
public onGestureCancel(_event: GestureCancelEvt) { }
public onWheel(_event: WheelEvt): boolean {
return false;
}
public onCopy(_event: CopyEvent): boolean {
return false;
}
public onPaste(_event: PasteEvent): boolean {
return false;
}
public onKeyPress(_event: KeyPressEvent): boolean {
return false;
}
public onKeyUp(_event: KeyUpEvent): boolean {
return false;
}
/**
* Return true if, while this tool is active, `_event` can be delivered to
* another tool that is higher priority than this.
* @internal May be renamed
*/
public eventCanBeDeliveredToNonActiveTool(_event: PointerEvt) {
return true;
}
public setEnabled(enabled: boolean) {
this.#enabled.set(enabled);
}
public isEnabled(): boolean {
return this.#enabled.get();
}
/**
* Returns a {@link ReactiveValue} that updates based on whether this tool is
* enabled.
*
* @example
* ```ts
* const tool = new SomeTool();
*
* // Watch for changes in enabled status
* tool.enabledValue().onUpdate(enabled => doSomething(enabled));
* ```
*/
public enabledValue(): ReactiveValue<boolean> {
return this.#enabled;
}
// Connect this tool to a set of other tools, ensuring that at most one
// of the tools in the group is enabled.
public setToolGroup(group: ToolEnabledGroup) {
if (this.isEnabled()) {
group.notifyEnabled(this);
}
this.#group = group;
}
public getToolGroup(): ToolEnabledGroup|null {
if (this.#group) {
return this.#group;
}
return null;
}
// Called when the tool is removed/when the editor is destroyed.
// Subclasses that override this method **must call super.onDestroy()**.
public onDestroy() {
this.#readOnlyEditorChangeListener?.remove();
this.#readOnlyEditorChangeListener = null;
this.#group = null;
}
}