0
# Specialized Features
1
2
Advanced features for focus management, context menus, and other specialized functionality that enhance the user experience with sophisticated behaviors and interactions.
3
4
## Capabilities
5
6
### FocusTracker
7
8
A utility for tracking focus state within a collection of widgets, providing centralized focus management.
9
10
```typescript { .api }
11
/**
12
* A utility for tracking focus state within a collection of widgets.
13
* Monitors focus changes and maintains current/active widget state.
14
*/
15
class FocusTracker<T> implements IDisposable {
16
/**
17
* Construct a new focus tracker.
18
*/
19
constructor();
20
21
/** The currently focused widget, or null if no widget has focus */
22
readonly currentWidget: T | null;
23
24
/** The most recently active widget, or null if no widget has been active */
25
readonly activeWidget: T | null;
26
27
/** The widgets being tracked by the focus tracker (read-only) */
28
readonly widgets: ReadonlyArray<T>;
29
30
/** Whether the focus tracker has been disposed (read-only) */
31
readonly isDisposed: boolean;
32
33
/** A signal emitted when the current widget changes */
34
readonly currentChanged: ISignal<this, FocusTracker.ICurrentChangedArgs<T>>;
35
36
/** A signal emitted when the active widget changes */
37
readonly activeChanged: ISignal<this, FocusTracker.IActiveChangedArgs<T>>;
38
39
/** Dispose of the focus tracker and its resources */
40
dispose(): void;
41
42
/**
43
* Add a widget to the focus tracker.
44
* @param widget - The widget to add to the tracker
45
*/
46
add(widget: T): void;
47
48
/**
49
* Remove a widget from the focus tracker.
50
* @param widget - The widget to remove from the tracker
51
*/
52
remove(widget: T): void;
53
54
/**
55
* Test whether the focus tracker contains a specific widget.
56
* @param widget - The widget to test
57
* @returns Whether the widget is being tracked
58
*/
59
has(widget: T): boolean;
60
61
/**
62
* Get the focus number for a widget.
63
* @param widget - The widget of interest
64
* @returns The focus number for the widget
65
*/
66
focusNumber(widget: T): number;
67
68
/**
69
* Handle DOM events for the focus tracker.
70
* @param event - The DOM event to handle
71
*/
72
handleEvent(event: Event): void;
73
}
74
75
namespace FocusTracker {
76
/**
77
* The arguments object for the current changed signal.
78
*/
79
interface ICurrentChangedArgs<T> {
80
/** The old value for the current widget */
81
oldValue: T | null;
82
83
/** The new value for the current widget */
84
newValue: T | null;
85
}
86
87
/**
88
* The arguments object for the active changed signal.
89
*/
90
interface IActiveChangedArgs<T> {
91
/** The old value for the active widget */
92
oldValue: T | null;
93
94
/** The new value for the active widget */
95
newValue: T | null;
96
}
97
}
98
```
99
100
**Usage Examples:**
101
102
```typescript
103
import { Widget, FocusTracker } from "@lumino/widgets";
104
105
// Create focus tracker
106
const focusTracker = new FocusTracker<Widget>();
107
108
// Create widgets to track
109
const widget1 = new Widget();
110
const widget2 = new Widget();
111
const widget3 = new Widget();
112
113
// Add widgets to tracker
114
focusTracker.add(widget1);
115
focusTracker.add(widget2);
116
focusTracker.add(widget3);
117
118
// Listen for focus changes
119
focusTracker.currentChanged.connect((sender, args) => {
120
console.log('Current widget changed:', {
121
old: args.oldValue?.title.label,
122
new: args.newValue?.title.label
123
});
124
});
125
126
focusTracker.activeChanged.connect((sender, args) => {
127
console.log('Active widget changed:', {
128
old: args.oldValue?.title.label,
129
new: args.newValue?.title.label
130
});
131
});
132
133
// Check current state
134
console.log('Current widget:', focusTracker.currentWidget?.title.label);
135
console.log('Active widget:', focusTracker.activeWidget?.title.label);
136
137
// Clean up when done
138
focusTracker.dispose();
139
```
140
141
### ContextMenu
142
143
A context menu widget for displaying contextual actions based on the target element.
144
145
```typescript { .api }
146
/**
147
* A context menu widget for displaying contextual actions.
148
* Provides right-click menus with command integration and target-specific items.
149
*/
150
class ContextMenu {
151
/**
152
* Construct a new context menu.
153
* @param options - The options for initializing the context menu
154
*/
155
constructor(options: ContextMenu.IOptions);
156
157
/** The menu for the context menu (read-only) */
158
readonly menu: Menu;
159
160
/** The renderer for the context menu (read-only) */
161
readonly renderer: ContextMenu.IRenderer;
162
163
164
/**
165
* Add an item to the context menu.
166
* @param options - The options for creating the item
167
* @returns The context menu item that was added
168
*/
169
addItem(options: ContextMenu.IItemOptions): ContextMenu.IItem;
170
171
/**
172
* Remove an item from the context menu.
173
* @param item - The item to remove
174
*/
175
removeItem(item: ContextMenu.IItem): void;
176
177
/** Remove all items from the context menu */
178
clearItems(): void;
179
180
/**
181
* Open the context menu at the current mouse position.
182
* @param event - The mouse or keyboard event that triggered the menu
183
* @returns Whether the menu was opened successfully
184
*/
185
open(event: MouseEvent | KeyboardEvent): boolean;
186
}
187
188
namespace ContextMenu {
189
/**
190
* Options for initializing a context menu.
191
*/
192
interface IOptions {
193
/** The command registry to use for the context menu */
194
commands: CommandRegistry;
195
196
/** The renderer for creating context menu elements */
197
renderer?: IRenderer;
198
}
199
200
/**
201
* An object which represents an item in a context menu.
202
*/
203
interface IItem {
204
/** The rank for the item (lower ranks appear first) */
205
rank: number;
206
207
/** The CSS selector for matching target elements */
208
selector: string;
209
210
/** The command identifier for the item */
211
command: string;
212
213
/** The arguments for the command */
214
args?: any;
215
}
216
217
/**
218
* Options for creating a context menu item.
219
*/
220
interface IItemOptions {
221
/** The command identifier for the item */
222
command: string;
223
224
/** The CSS selector for matching target elements (default: '*') */
225
selector?: string;
226
227
/** The rank for the item (default: Infinity) */
228
rank?: number;
229
230
/** The arguments for the command (optional) */
231
args?: any;
232
}
233
234
/**
235
* An object which renders a context menu.
236
*/
237
interface IRenderer {
238
/** The renderer for the underlying menu */
239
readonly menu: Menu.IRenderer;
240
}
241
242
/**
243
* Default context menu renderer implementation.
244
*/
245
class Renderer implements IRenderer {
246
/** The menu renderer for the context menu */
247
readonly menu: Menu.IRenderer;
248
}
249
}
250
```
251
252
**Usage Examples:**
253
254
```typescript
255
import { ContextMenu } from "@lumino/widgets";
256
import { CommandRegistry } from "@lumino/commands";
257
258
// Create command registry
259
const commands = new CommandRegistry();
260
261
// Add commands
262
commands.addCommand('edit:cut', {
263
label: 'Cut',
264
iconClass: 'fa fa-cut',
265
execute: () => console.log('Cut executed')
266
});
267
268
commands.addCommand('edit:copy', {
269
label: 'Copy',
270
iconClass: 'fa fa-copy',
271
execute: () => console.log('Copy executed')
272
});
273
274
commands.addCommand('edit:paste', {
275
label: 'Paste',
276
iconClass: 'fa fa-paste',
277
execute: () => console.log('Paste executed')
278
});
279
280
// Create context menu
281
const contextMenu = new ContextMenu({ commands });
282
283
// Add items for text inputs
284
contextMenu.addItem({
285
command: 'edit:cut',
286
selector: 'input[type="text"], textarea',
287
rank: 1
288
});
289
290
contextMenu.addItem({
291
command: 'edit:copy',
292
selector: 'input[type="text"], textarea',
293
rank: 2
294
});
295
296
contextMenu.addItem({
297
command: 'edit:paste',
298
selector: 'input[type="text"], textarea',
299
rank: 3
300
});
301
302
// Add global items
303
contextMenu.addItem({
304
command: 'edit:copy',
305
selector: '*',
306
rank: 100
307
});
308
309
// Listen for context menu events
310
document.addEventListener('contextmenu', (event) => {
311
event.preventDefault();
312
contextMenu.open(event);
313
});
314
```
315
316
### Menu
317
318
A widget that displays menu items for user selection with full keyboard and mouse support.
319
320
```typescript { .api }
321
/**
322
* A widget that displays menu items for user selection.
323
* Provides hierarchical menus with keyboard navigation, mnemonics, and submenus.
324
*/
325
class Menu extends Widget {
326
/**
327
* Construct a new menu.
328
* @param options - The options for initializing the menu
329
*/
330
constructor(options: Menu.IOptions);
331
332
/** The command registry for the menu (read-only) */
333
readonly commands: CommandRegistry;
334
335
/** The renderer for the menu (read-only) */
336
readonly renderer: Menu.IRenderer;
337
338
/** The child menu for this menu (read-only) */
339
readonly childMenu: Menu | null;
340
341
/** The parent menu for this menu (read-only) */
342
readonly parentMenu: Menu | null;
343
344
/** The root menu for this menu (read-only) */
345
readonly rootMenu: Menu;
346
347
/** The leaf menu for this menu (read-only) */
348
readonly leafMenu: Menu;
349
350
/** The menu items (read-only) */
351
readonly items: ReadonlyArray<Menu.IItem>;
352
353
/** The active menu item, or null if no item is active */
354
activeItem: Menu.IItem | null;
355
356
/** The index of the active menu item, or -1 if no item is active */
357
activeIndex: number;
358
359
/** The content node for the menu (read-only) */
360
readonly contentNode: HTMLUListElement;
361
362
/** A signal emitted when the menu is about to close */
363
readonly aboutToClose: ISignal<this, void>;
364
365
/** A signal emitted when a menu is requested */
366
readonly menuRequested: ISignal<this, 'next' | 'previous'>;
367
368
/** Dispose of the menu and its resources */
369
dispose(): void;
370
371
/**
372
* Add an item to the end of the menu.
373
* @param options - The options for creating the item
374
* @returns The menu item that was added
375
*/
376
addItem(options: Menu.IItemOptions): Menu.IItem;
377
378
/**
379
* Insert an item at the specified index.
380
* @param index - The index at which to insert the item
381
* @param options - The options for creating the item
382
* @returns The menu item that was inserted
383
*/
384
insertItem(index: number, options: Menu.IItemOptions): Menu.IItem;
385
386
/**
387
* Remove an item from the menu.
388
* @param item - The item to remove
389
*/
390
removeItem(item: Menu.IItem): void;
391
392
/**
393
* Remove the item at the specified index.
394
* @param index - The index of the item to remove
395
*/
396
removeItemAt(index: number): void;
397
398
/** Remove all items from the menu */
399
clearItems(): void;
400
401
/**
402
* Open the menu at the specified location.
403
* @param x - The client X coordinate for the menu
404
* @param y - The client Y coordinate for the menu
405
* @param options - Additional options for opening the menu
406
* @returns Whether the menu was successfully opened
407
*/
408
open(x: number, y: number, options?: Menu.IOpenOptions): boolean;
409
410
/**
411
* Show the menu as a popup at the specified location.
412
* @param x - The client X coordinate for the menu
413
* @param y - The client Y coordinate for the menu
414
*/
415
popup(x: number, y: number): void;
416
417
/** Handle DOM events for the menu */
418
handleEvent(event: Event): void;
419
420
/** Activate the next menu item */
421
activateNextItem(): void;
422
423
/** Activate the previous menu item */
424
activatePreviousItem(): void;
425
426
/** Trigger the active menu item */
427
triggerActiveItem(): void;
428
}
429
430
namespace Menu {
431
/**
432
* Options for initializing a menu.
433
*/
434
interface IOptions extends Widget.IOptions {
435
/** The command registry to use for the menu */
436
commands: CommandRegistry;
437
438
/** The renderer for creating menu elements */
439
renderer?: IRenderer;
440
}
441
442
/**
443
* An object which represents an item in a menu.
444
*/
445
interface IItem {
446
/** The command identifier for the item */
447
readonly command: string;
448
449
/** The arguments for the command */
450
readonly args: any;
451
452
/** The submenu for the item, or null if no submenu */
453
readonly submenu: Menu | null;
454
455
/** The type of the menu item */
456
readonly type: ItemType;
457
458
/** The display label for the item */
459
readonly label: string;
460
461
/** The mnemonic index for the label */
462
readonly mnemonic: number;
463
464
/** The icon renderer for the item */
465
readonly icon: VirtualElement.IRenderer | undefined;
466
467
/** The icon class name for the item */
468
readonly iconClass: string;
469
470
/** The icon label for the item */
471
readonly iconLabel: string;
472
473
/** The caption for the item */
474
readonly caption: string;
475
476
/** The extra class name for the item */
477
readonly className: string;
478
479
/** The dataset for the item */
480
readonly dataset: { readonly [key: string]: string };
481
482
/** Whether the item is enabled */
483
readonly isEnabled: boolean;
484
485
/** Whether the item is toggled */
486
readonly isToggled: boolean;
487
488
/** Whether the item is visible */
489
readonly isVisible: boolean;
490
491
/** The key binding for the item, or null if no key binding */
492
readonly keyBinding: CommandRegistry.IKeyBinding | null;
493
}
494
495
/**
496
* Options for creating a menu item.
497
*/
498
interface IItemOptions {
499
/** The command identifier for the item (required for command items) */
500
command?: string;
501
502
/** The arguments for the command (optional) */
503
args?: any;
504
505
/** The submenu for the item (required for submenu items) */
506
submenu?: Menu;
507
508
/** The type of the menu item (default: 'command') */
509
type?: ItemType;
510
}
511
512
/**
513
* The available item types for menu items.
514
*/
515
type ItemType = 'command' | 'submenu' | 'separator';
516
517
/**
518
* Options for opening a menu.
519
*/
520
interface IOpenOptions {
521
/** Whether to force the X coordinate */
522
forceX?: boolean;
523
524
/** Whether to force the Y coordinate */
525
forceY?: boolean;
526
}
527
528
/**
529
* An object which renders the visual parts of a menu.
530
*/
531
interface IRenderer {
532
/**
533
* Create an item node for a menu item.
534
* @param data - The render data for the item
535
* @returns The item node for the menu item
536
*/
537
createItemNode(data: IRenderData): HTMLLIElement;
538
539
/**
540
* Update an existing item node to reflect current state.
541
* @param node - The item node to update
542
* @param data - The render data for the item
543
*/
544
updateItemNode(node: HTMLLIElement, data: IRenderData): void;
545
546
/**
547
* Create an icon node for a menu item.
548
* @param data - The render data for the item
549
* @returns The icon node for the menu item
550
*/
551
createIconNode(data: IRenderData): HTMLSpanElement;
552
553
/**
554
* Update an existing icon node to reflect current state.
555
* @param node - The icon node to update
556
* @param data - The render data for the item
557
*/
558
updateIconNode(node: HTMLSpanElement, data: IRenderData): void;
559
560
/**
561
* Create a label node for a menu item.
562
* @param data - The render data for the item
563
* @returns The label node for the menu item
564
*/
565
createLabelNode(data: IRenderData): HTMLSpanElement;
566
567
/**
568
* Update an existing label node to reflect current state.
569
* @param node - The label node to update
570
* @param data - The render data for the item
571
*/
572
updateLabelNode(node: HTMLSpanElement, data: IRenderData): void;
573
574
/**
575
* Create a submenu icon node for a menu item.
576
* @param data - The render data for the item
577
* @returns The submenu icon node for the menu item
578
*/
579
createSubmenuIconNode(data: IRenderData): HTMLSpanElement;
580
581
/**
582
* Update an existing submenu icon node to reflect current state.
583
* @param node - The submenu icon node to update
584
* @param data - The render data for the item
585
*/
586
updateSubmenuIconNode(node: HTMLSpanElement, data: IRenderData): void;
587
588
/**
589
* Create a shortcut node for a menu item.
590
* @param data - The render data for the item
591
* @returns The shortcut node for the menu item
592
*/
593
createShortcutNode(data: IRenderData): HTMLSpanElement;
594
595
/**
596
* Update an existing shortcut node to reflect current state.
597
* @param node - The shortcut node to update
598
* @param data - The render data for the item
599
*/
600
updateShortcutNode(node: HTMLSpanElement, data: IRenderData): void;
601
}
602
603
/**
604
* The render data for a menu item.
605
*/
606
interface IRenderData {
607
/** The menu item being rendered */
608
item: IItem;
609
610
/** Whether the item is active */
611
active: boolean;
612
613
/** Whether the item is collapsed (for submenus) */
614
collapsed: boolean;
615
}
616
617
/**
618
* Default menu renderer implementation.
619
*/
620
class Renderer implements IRenderer {
621
createItemNode(data: IRenderData): HTMLLIElement;
622
updateItemNode(node: HTMLLIElement, data: IRenderData): void;
623
createIconNode(data: IRenderData): HTMLSpanElement;
624
updateIconNode(node: HTMLSpanElement, data: IRenderData): void;
625
createLabelNode(data: IRenderData): HTMLSpanElement;
626
updateLabelNode(node: HTMLSpanElement, data: IRenderData): void;
627
createSubmenuIconNode(data: IRenderData): HTMLSpanElement;
628
updateSubmenuIconNode(node: HTMLSpanElement, data: IRenderData): void;
629
createShortcutNode(data: IRenderData): HTMLSpanElement;
630
updateShortcutNode(node: HTMLSpanElement, data: IRenderData): void;
631
}
632
}
633
```
634
635
**Usage Examples:**
636
637
```typescript
638
import { Menu } from "@lumino/widgets";
639
import { CommandRegistry } from "@lumino/commands";
640
641
// Create command registry and commands
642
const commands = new CommandRegistry();
643
644
commands.addCommand('file:new', {
645
label: 'New File',
646
iconClass: 'fa fa-file',
647
execute: () => console.log('Creating new file')
648
});
649
650
commands.addCommand('file:open', {
651
label: 'Open File...',
652
iconClass: 'fa fa-folder-open',
653
execute: () => console.log('Opening file dialog')
654
});
655
656
commands.addCommand('edit:undo', {
657
label: 'Undo',
658
iconClass: 'fa fa-undo',
659
execute: () => console.log('Undoing last action')
660
});
661
662
// Create main menu
663
const fileMenu = new Menu({ commands });
664
665
// Add menu items
666
fileMenu.addItem({ command: 'file:new' });
667
fileMenu.addItem({ command: 'file:open' });
668
fileMenu.addItem({ type: 'separator' });
669
670
// Create submenu
671
const recentMenu = new Menu({ commands });
672
recentMenu.addItem({ command: 'file:open', args: { path: '/path/to/file1.txt' } });
673
recentMenu.addItem({ command: 'file:open', args: { path: '/path/to/file2.txt' } });
674
675
// Add submenu to main menu
676
fileMenu.addItem({
677
type: 'submenu',
678
submenu: recentMenu
679
});
680
recentMenu.title.label = 'Open Recent';
681
682
// Open menu at specific coordinates
683
fileMenu.open(100, 200);
684
685
// Or show as popup (centers on screen)
686
fileMenu.popup(event.clientX, event.clientY);
687
```