0
# Widgets and UI
1
2
Theia's widget system provides comprehensive UI components built on Phosphor/Lumino with React integration, layout management, and extensible widget types for building modern IDE interfaces.
3
4
## Capabilities
5
6
### Base Widget
7
8
Foundation widget class providing core widget functionality and lifecycle management.
9
10
```typescript { .api }
11
/**
12
* Base widget class for all UI components
13
*/
14
class Widget {
15
/** Unique widget identifier */
16
readonly id: string;
17
18
/** Widget title with label, icon, and other metadata */
19
readonly title: Title<Widget>;
20
21
/** Parent widget if this widget is attached */
22
readonly parent: Widget | null;
23
24
/** True if widget is attached to DOM */
25
readonly isAttached: boolean;
26
27
/** True if widget is visible */
28
readonly isVisible: boolean;
29
30
/** True if widget is disposed */
31
readonly isDisposed: boolean;
32
33
/**
34
* Show the widget
35
*/
36
show(): void;
37
38
/**
39
* Hide the widget
40
*/
41
hide(): void;
42
43
/**
44
* Close the widget
45
*/
46
close(): void;
47
48
/**
49
* Dispose the widget and clean up resources
50
*/
51
dispose(): void;
52
53
/**
54
* Activate the widget (give it focus)
55
*/
56
activate(): void;
57
58
/**
59
* Update the widget's appearance
60
*/
61
update(): void;
62
}
63
```
64
65
### React Widget
66
67
Widget base class for React-based components with lifecycle integration.
68
69
```typescript { .api }
70
/**
71
* Widget that renders React components
72
*/
73
class ReactWidget extends Widget {
74
/**
75
* Create React widget with optional props
76
* @param props - React component props
77
*/
78
constructor(props?: any);
79
80
/**
81
* Render the React component
82
* @returns React element to render
83
*/
84
protected render(): React.ReactNode;
85
86
/**
87
* Called after widget is attached to DOM
88
*/
89
protected onAfterAttach(): void;
90
91
/**
92
* Called before widget is detached from DOM
93
*/
94
protected onBeforeDetach(): void;
95
96
/**
97
* Called when widget is updated
98
*/
99
protected onUpdateRequest(): void;
100
101
/**
102
* Set React component state
103
* @param state - New state object
104
* @param callback - Optional callback after state update
105
*/
106
protected setState(state: any, callback?: () => void): void;
107
}
108
```
109
110
**Usage Example:**
111
112
```typescript
113
import React from "react";
114
import { ReactWidget } from "@theia/core/lib/browser";
115
116
interface MyWidgetState {
117
count: number;
118
}
119
120
export class MyCounterWidget extends ReactWidget {
121
protected state: MyWidgetState = { count: 0 };
122
123
constructor() {
124
super();
125
this.id = 'counter-widget';
126
this.title.label = 'Counter';
127
this.title.iconClass = 'fa fa-calculator';
128
}
129
130
protected render(): React.ReactNode {
131
return (
132
<div className="counter-widget">
133
<h3>Count: {this.state.count}</h3>
134
<button onClick={this.increment}>Increment</button>
135
<button onClick={this.decrement}>Decrement</button>
136
</div>
137
);
138
}
139
140
private increment = (): void => {
141
this.setState({ count: this.state.count + 1 });
142
};
143
144
private decrement = (): void => {
145
this.setState({ count: this.state.count - 1 });
146
};
147
}
148
```
149
150
### Application Shell
151
152
Main application container that manages widget layout and areas.
153
154
```typescript { .api }
155
/**
156
* Main application shell managing widget layout
157
*/
158
class ApplicationShell extends Widget {
159
/**
160
* Add widget to specified area
161
* @param widget - Widget to add
162
* @param area - Shell area to add to
163
* @param options - Optional layout options
164
*/
165
addWidget(widget: Widget, area?: ApplicationShell.Area, options?: ApplicationShell.WidgetOptions): void;
166
167
/**
168
* Activate a widget
169
* @param id - Widget ID to activate
170
*/
171
activateWidget(id: string): Widget | undefined;
172
173
/**
174
* Close all widgets in area
175
* @param area - Shell area to close
176
*/
177
closeAll(area?: ApplicationShell.Area): void;
178
179
/**
180
* Get all widgets in area
181
* @param area - Shell area
182
* @returns Array of widgets in area
183
*/
184
getWidgets(area: ApplicationShell.Area): Widget[];
185
}
186
187
namespace ApplicationShell {
188
/**
189
* Shell areas where widgets can be placed
190
*/
191
enum Area {
192
left = 'left',
193
right = 'right',
194
main = 'main',
195
bottom = 'bottom',
196
top = 'top'
197
}
198
199
/**
200
* Options for adding widgets to shell
201
*/
202
interface WidgetOptions {
203
/** Area to add widget to */
204
area?: Area;
205
/** Tab index for ordering */
206
rank?: number;
207
/** Reference widget for relative positioning */
208
ref?: Widget;
209
/** Insertion mode relative to reference */
210
mode?: 'tab-after' | 'tab-before' | 'split-top' | 'split-bottom' | 'split-left' | 'split-right';
211
}
212
}
213
214
/**
215
* Service token for ApplicationShell
216
*/
217
const ApplicationShell: symbol;
218
```
219
220
### Widget Manager
221
222
Manages widget lifecycle, creation, and disposal.
223
224
```typescript { .api }
225
/**
226
* Manages widget lifecycle and factories
227
*/
228
interface WidgetManager {
229
/**
230
* Get or create widget by ID
231
* @param id - Widget ID
232
* @returns Promise resolving to widget
233
*/
234
getOrCreateWidget<T extends Widget>(id: string): Promise<T>;
235
236
/**
237
* Try to get existing widget
238
* @param id - Widget ID
239
* @returns Widget if exists, undefined otherwise
240
*/
241
tryGetWidget<T extends Widget>(id: string): T | undefined;
242
243
/**
244
* Get all widgets of a specific type
245
* @param factoryId - Widget factory ID
246
* @returns Array of matching widgets
247
*/
248
getWidgets<T extends Widget>(factoryId: string): T[];
249
}
250
251
/**
252
* Factory for creating widgets
253
*/
254
interface WidgetFactory {
255
/** Unique factory identifier */
256
readonly id: string;
257
258
/**
259
* Create widget instance
260
* @param options - Creation options
261
* @returns Promise resolving to widget
262
*/
263
createWidget(options?: any): Promise<Widget>;
264
}
265
266
/**
267
* Service tokens
268
*/
269
const WidgetManager: symbol;
270
const WidgetFactory: symbol;
271
```
272
273
### Saveable Widget
274
275
Widget mixin for widgets that contain saveable content.
276
277
```typescript { .api }
278
/**
279
* Widget that contains saveable content
280
*/
281
interface SaveableWidget extends Widget {
282
/**
283
* Save the widget's content
284
* @returns Promise that resolves when save completes
285
*/
286
save(): Promise<void>;
287
288
/**
289
* True if widget has unsaved changes
290
*/
291
readonly dirty: boolean;
292
293
/**
294
* Event fired when dirty state changes
295
*/
296
readonly onDirtyChanged: Event<void>;
297
}
298
299
namespace SaveableWidget {
300
/**
301
* Type guard for saveable widgets
302
* @param widget - Widget to check
303
* @returns True if widget is saveable
304
*/
305
function is(widget: Widget): widget is SaveableWidget;
306
}
307
```
308
309
**Usage Example:**
310
311
```typescript
312
import { ReactWidget, SaveableWidget } from "@theia/core/lib/browser";
313
import { Emitter, Event } from "@theia/core";
314
315
export class MyEditorWidget extends ReactWidget implements SaveableWidget {
316
private readonly onDirtyChangedEmitter = new Emitter<void>();
317
readonly onDirtyChanged: Event<void> = this.onDirtyChangedEmitter.event;
318
319
private _dirty = false;
320
private content = '';
321
322
get dirty(): boolean {
323
return this._dirty;
324
}
325
326
private setDirty(dirty: boolean): void {
327
if (this._dirty !== dirty) {
328
this._dirty = dirty;
329
this.onDirtyChangedEmitter.fire();
330
this.title.className = dirty ? 'dirty' : '';
331
}
332
}
333
334
async save(): Promise<void> {
335
await this.saveContent(this.content);
336
this.setDirty(false);
337
}
338
339
private updateContent(newContent: string): void {
340
this.content = newContent;
341
this.setDirty(true);
342
this.update();
343
}
344
345
private async saveContent(content: string): Promise<void> {
346
// Save implementation
347
}
348
}
349
```
350
351
### Widget Extensions
352
353
Additional widget utilities and extensions for specialized use cases.
354
355
```typescript { .api }
356
/**
357
* Widget that can be extracted to separate window
358
*/
359
interface ExtractableWidget extends Widget {
360
/**
361
* True if widget can be extracted
362
*/
363
readonly isExtractable: boolean;
364
365
/**
366
* Extract widget to separate window
367
*/
368
extractWidget(): void;
369
}
370
371
/**
372
* Stateful widget that can save/restore state
373
*/
374
interface StatefulWidget extends Widget {
375
/**
376
* Store widget state
377
* @returns State object
378
*/
379
storeState(): any;
380
381
/**
382
* Restore widget state
383
* @param oldState - Previously stored state
384
*/
385
restoreState(oldState: any): void;
386
}
387
388
/**
389
* Widget with custom context menu
390
*/
391
interface ContextMenuWidget extends Widget {
392
/**
393
* Create context menu for widget
394
* @param event - Mouse event
395
* @returns Context menu items
396
*/
397
createContextMenu(event: MouseEvent): ContextMenu;
398
}
399
```
400
401
### Title and Tabs
402
403
Widget title management and tab behavior.
404
405
```typescript { .api }
406
/**
407
* Widget title containing label, icon, and metadata
408
*/
409
class Title<T> {
410
/** Display label */
411
label: string;
412
413
/** Icon CSS class */
414
iconClass: string;
415
416
/** Icon label for accessibility */
417
iconLabel: string;
418
419
/** Caption/tooltip text */
420
caption: string;
421
422
/** CSS class name */
423
className: string;
424
425
/** True if title can be closed */
426
closable: boolean;
427
428
/** Dataset for custom attributes */
429
dataset: Record<string, string>;
430
431
/** Owner widget */
432
readonly owner: T;
433
434
/** Event fired when title changes */
435
readonly changed: Event<void>;
436
}
437
```
438
439
## Layout and Styling
440
441
### CSS Classes
442
443
Common CSS classes used by Theia widgets:
444
445
```typescript { .api }
446
/**
447
* Standard CSS classes for widgets
448
*/
449
namespace WidgetCSS {
450
const WIDGET = 'theia-widget';
451
const PANEL = 'theia-panel';
452
const TOOLBAR = 'theia-toolbar';
453
const TAB_BAR = 'theia-tab-bar';
454
const DOCK_PANEL = 'theia-dock-panel';
455
const SPLIT_PANEL = 'theia-split-panel';
456
}
457
```
458
459
### Layout Options
460
461
```typescript { .api }
462
/**
463
* Layout configuration for widgets
464
*/
465
interface LayoutOptions {
466
/** Minimum width in pixels */
467
minWidth?: number;
468
469
/** Minimum height in pixels */
470
minHeight?: number;
471
472
/** Maximum width in pixels */
473
maxWidth?: number;
474
475
/** Maximum height in pixels */
476
maxHeight?: number;
477
478
/** Stretch factor for resizing */
479
stretch?: number;
480
}
481
```
482
483
## Types
484
485
```typescript { .api }
486
/**
487
* Widget-related type definitions
488
*/
489
type WidgetConstructor = new (...args: any[]) => Widget;
490
491
interface WidgetOptions {
492
id?: string;
493
title?: Partial<Title.Dataset>;
494
}
495
496
namespace Widget {
497
/**
498
* Widget message types
499
*/
500
enum MessageType {
501
BeforeAttach = 'before-attach',
502
AfterAttach = 'after-attach',
503
BeforeDetach = 'before-detach',
504
AfterDetach = 'after-detach',
505
BeforeShow = 'before-show',
506
AfterShow = 'after-show',
507
BeforeHide = 'before-hide',
508
AfterHide = 'after-hide',
509
CloseRequest = 'close-request',
510
Resize = 'resize',
511
UpdateRequest = 'update-request',
512
FitRequest = 'fit-request',
513
ActivateRequest = 'activate-request'
514
}
515
}
516
```