0
# UI Widgets
1
2
The GWT backend provides web-specific UI components designed for browser integration and responsive behavior. These widgets handle common web deployment needs like progress indication, text input dialogs, and responsive layout management.
3
4
## Core Widget Interfaces
5
6
### ResizableWidget { .api }
7
8
```java
9
public interface ResizableWidget {
10
void resize(int width, int height);
11
}
12
```
13
14
### ResizableWidgetCollection { .api }
15
16
```java
17
public class ResizableWidgetCollection implements ResizeHandler, Iterable<ResizableWidget> {
18
// Widget management
19
public void add(ResizableWidget widget);
20
public boolean remove(ResizableWidget widget);
21
public void clear();
22
public int size();
23
24
// Resize handling
25
public void onResize(ResizeEvent event);
26
27
// Iteration support
28
public Iterator<ResizableWidget> iterator();
29
30
// Manual resize trigger
31
public void resize(int width, int height);
32
}
33
```
34
35
## Progress Display Widgets
36
37
### ProgressBar { .api }
38
39
```java
40
public class ProgressBar extends Widget implements ResizableWidget {
41
// Progress control
42
public void setValue(float value); // 0.0 to 1.0
43
public float getValue();
44
45
// Appearance
46
public void setVisible(boolean visible);
47
public boolean isVisible();
48
public void setAnimated(boolean animated);
49
public boolean isAnimated();
50
51
// Styling
52
public void setProgressColor(String color);
53
public void setBackgroundColor(String color);
54
public void setBorderColor(String color);
55
public void setHeight(int height);
56
57
// Text display
58
public void setShowText(boolean showText);
59
public boolean isShowText();
60
public void setTextFormat(String format); // e.g., "{0}%" for percentage
61
62
// ResizableWidget implementation
63
public void resize(int width, int height);
64
65
// Widget positioning
66
public void setPosition(int x, int y);
67
public void setSize(int width, int height);
68
69
// Constructors
70
public ProgressBar();
71
public ProgressBar(int width, int height);
72
}
73
```
74
75
## Text Input Widgets
76
77
### TextInputDialogBox { .api }
78
79
```java
80
public class TextInputDialogBox extends DialogBox {
81
// Dialog display
82
public void show(String title, String text, TextInputListener listener);
83
public void show(String title, String text, String hint, TextInputListener listener);
84
public void hide();
85
86
// Dialog configuration
87
public void setModal(boolean modal);
88
public boolean isModal();
89
public void setAnimationEnabled(boolean enabled);
90
public boolean isAnimationEnabled();
91
92
// Input validation
93
public void setValidator(InputValidator validator);
94
public InputValidator getValidator();
95
public void setMaxLength(int maxLength);
96
public int getMaxLength();
97
98
// Styling
99
public void setDialogStyle(String styleName);
100
public void setButtonText(String okText, String cancelText);
101
102
// Event handling
103
public interface TextInputListener {
104
void input(String text);
105
void canceled();
106
}
107
108
public interface InputValidator {
109
boolean isValid(String input);
110
String getErrorMessage();
111
}
112
113
// Constructors
114
public TextInputDialogBox();
115
public TextInputDialogBox(boolean autoHide);
116
}
117
```
118
119
### PlaceholderTextBox { .api }
120
121
```java
122
public class PlaceholderTextBox extends TextBox {
123
// Placeholder functionality
124
public void setPlaceholder(String placeholder);
125
public String getPlaceholder();
126
127
// Enhanced text input
128
public void selectAll();
129
public void setSelectionRange(int start, int length);
130
public String getSelectedText();
131
132
// Input filtering
133
public void setInputFilter(InputFilter filter);
134
public InputFilter getInputFilter();
135
136
// Event handling
137
public void addValueChangeHandler(ValueChangeHandler<String> handler);
138
public void addKeyUpHandler(KeyUpHandler handler);
139
public void addFocusHandler(FocusHandler handler);
140
public void addBlurHandler(BlurHandler handler);
141
142
// Styling enhancements
143
public void setErrorStyle(boolean error);
144
public boolean hasErrorStyle();
145
146
public interface InputFilter {
147
boolean accept(char character);
148
String filter(String input);
149
}
150
151
// Constructors
152
public PlaceholderTextBox();
153
public PlaceholderTextBox(String placeholder);
154
}
155
```
156
157
## Usage Examples
158
159
### Loading Screen with Progress Bar
160
161
```java
162
public class LoadingScreen implements Screen {
163
private ProgressBar progressBar;
164
private ResizableWidgetCollection widgets;
165
private Stage stage;
166
private Label statusLabel;
167
168
public LoadingScreen() {
169
stage = new Stage();
170
widgets = new ResizableWidgetCollection();
171
172
// Create progress bar
173
progressBar = new ProgressBar(400, 30);
174
progressBar.setProgressColor("#00ff00");
175
progressBar.setBackgroundColor("#333333");
176
progressBar.setBorderColor("#666666");
177
progressBar.setShowText(true);
178
progressBar.setTextFormat("{0}%");
179
progressBar.setAnimated(true);
180
181
// Add to widget collection for automatic resizing
182
widgets.add(progressBar);
183
184
// Create status label
185
statusLabel = new Label("Loading...", new Label.LabelStyle());
186
187
// Position widgets
188
layoutWidgets();
189
190
// Handle window resize
191
Gdx.graphics.setResizeCallback(new Graphics.ResizeCallback() {
192
@Override
193
public void onResize(int width, int height) {
194
widgets.resize(width, height);
195
layoutWidgets();
196
}
197
});
198
}
199
200
@Override
201
public void show() {
202
Gdx.input.setInputProcessor(stage);
203
}
204
205
@Override
206
public void render(float delta) {
207
// Update progress (example: based on asset loading)
208
updateProgress();
209
210
// Clear screen
211
Gdx.gl.glClearColor(0.1f, 0.1f, 0.1f, 1);
212
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
213
214
// Render UI
215
stage.act(delta);
216
stage.draw();
217
}
218
219
private void updateProgress() {
220
// Example: Update based on preloader state
221
if (MyGame.getPreloader() != null) {
222
Preloader.PreloaderState state = MyGame.getPreloader().update();
223
float progress = state.getProgress();
224
225
progressBar.setValue(progress);
226
227
// Update status text
228
if (progress < 0.3f) {
229
statusLabel.setText("Loading graphics...");
230
} else if (progress < 0.6f) {
231
statusLabel.setText("Loading audio...");
232
} else if (progress < 0.9f) {
233
statusLabel.setText("Loading data...");
234
} else if (progress < 1.0f) {
235
statusLabel.setText("Finalizing...");
236
} else {
237
statusLabel.setText("Complete!");
238
}
239
}
240
}
241
242
private void layoutWidgets() {
243
int screenWidth = Gdx.graphics.getWidth();
244
int screenHeight = Gdx.graphics.getHeight();
245
246
// Center progress bar
247
int barX = (screenWidth - 400) / 2;
248
int barY = (screenHeight - 30) / 2;
249
progressBar.setPosition(barX, barY);
250
251
// Position status label above progress bar
252
statusLabel.setPosition(barX, barY + 50);
253
}
254
255
// ... other Screen methods
256
}
257
```
258
259
### Text Input Dialog System
260
261
```java
262
public class TextInputManager {
263
private TextInputDialogBox currentDialog;
264
265
public void showNameInput(TextInputListener listener) {
266
if (currentDialog != null) {
267
currentDialog.hide();
268
}
269
270
currentDialog = new TextInputDialogBox();
271
currentDialog.setModal(true);
272
currentDialog.setAnimationEnabled(true);
273
currentDialog.setButtonText("OK", "Cancel");
274
275
// Set up validation
276
currentDialog.setValidator(new TextInputDialogBox.InputValidator() {
277
@Override
278
public boolean isValid(String input) {
279
return input != null && input.trim().length() >= 2 && input.length() <= 20;
280
}
281
282
@Override
283
public String getErrorMessage() {
284
return "Name must be 2-20 characters long";
285
}
286
});
287
288
currentDialog.show("Enter Your Name", "", "Your name here", new TextInputDialogBox.TextInputListener() {
289
@Override
290
public void input(String text) {
291
currentDialog = null;
292
if (listener != null) {
293
listener.input(text.trim());
294
}
295
}
296
297
@Override
298
public void canceled() {
299
currentDialog = null;
300
if (listener != null) {
301
listener.canceled();
302
}
303
}
304
});
305
}
306
307
public void showScoreInput(int currentHighScore, TextInputListener listener) {
308
if (currentDialog != null) {
309
currentDialog.hide();
310
}
311
312
currentDialog = new TextInputDialogBox();
313
currentDialog.setMaxLength(10);
314
315
// Numeric validation
316
currentDialog.setValidator(new TextInputDialogBox.InputValidator() {
317
@Override
318
public boolean isValid(String input) {
319
try {
320
int score = Integer.parseInt(input);
321
return score > currentHighScore;
322
} catch (NumberFormatException e) {
323
return false;
324
}
325
}
326
327
@Override
328
public String getErrorMessage() {
329
return "Enter a score higher than " + currentHighScore;
330
}
331
});
332
333
currentDialog.show("New High Score!", "", "Enter score", listener);
334
}
335
336
public void showCustomDialog(String title, String defaultText, String hint,
337
TextInputDialogBox.InputValidator validator,
338
TextInputListener listener) {
339
if (currentDialog != null) {
340
currentDialog.hide();
341
}
342
343
currentDialog = new TextInputDialogBox();
344
if (validator != null) {
345
currentDialog.setValidator(validator);
346
}
347
348
currentDialog.show(title, defaultText, hint, listener);
349
}
350
351
public void hideCurrentDialog() {
352
if (currentDialog != null) {
353
currentDialog.hide();
354
currentDialog = null;
355
}
356
}
357
358
public boolean isDialogShowing() {
359
return currentDialog != null;
360
}
361
}
362
```
363
364
### Enhanced Text Input with Filtering
365
366
```java
367
public class GameTextInput extends PlaceholderTextBox {
368
private InputType inputType;
369
370
public enum InputType {
371
ALPHA_ONLY,
372
NUMERIC_ONLY,
373
ALPHANUMERIC,
374
EMAIL,
375
PASSWORD,
376
CUSTOM
377
}
378
379
public GameTextInput(InputType type) {
380
super();
381
this.inputType = type;
382
setupInputFilter();
383
setupStyling();
384
}
385
386
public GameTextInput(String placeholder, InputType type) {
387
super(placeholder);
388
this.inputType = type;
389
setupInputFilter();
390
setupStyling();
391
}
392
393
private void setupInputFilter() {
394
switch (inputType) {
395
case ALPHA_ONLY:
396
setInputFilter(new InputFilter() {
397
@Override
398
public boolean accept(char c) {
399
return Character.isLetter(c) || Character.isWhitespace(c);
400
}
401
402
@Override
403
public String filter(String input) {
404
return input.replaceAll("[^a-zA-Z\\s]", "");
405
}
406
});
407
break;
408
409
case NUMERIC_ONLY:
410
setInputFilter(new InputFilter() {
411
@Override
412
public boolean accept(char c) {
413
return Character.isDigit(c) || c == '.' || c == '-';
414
}
415
416
@Override
417
public String filter(String input) {
418
return input.replaceAll("[^0-9.-]", "");
419
}
420
});
421
break;
422
423
case ALPHANUMERIC:
424
setInputFilter(new InputFilter() {
425
@Override
426
public boolean accept(char c) {
427
return Character.isLetterOrDigit(c) || Character.isWhitespace(c);
428
}
429
430
@Override
431
public String filter(String input) {
432
return input.replaceAll("[^a-zA-Z0-9\\s]", "");
433
}
434
});
435
break;
436
437
case EMAIL:
438
setInputFilter(new InputFilter() {
439
@Override
440
public boolean accept(char c) {
441
return Character.isLetterOrDigit(c) || c == '@' || c == '.' || c == '_' || c == '-';
442
}
443
444
@Override
445
public String filter(String input) {
446
return input.replaceAll("[^a-zA-Z0-9@._-]", "").toLowerCase();
447
}
448
});
449
break;
450
}
451
}
452
453
private void setupStyling() {
454
// Add focus/blur styling
455
addFocusHandler(new FocusHandler() {
456
@Override
457
public void onFocus(FocusEvent event) {
458
getElement().getStyle().setBorderColor("#4CAF50");
459
getElement().getStyle().setProperty("boxShadow", "0 0 5px rgba(76, 175, 80, 0.5)");
460
}
461
});
462
463
addBlurHandler(new BlurHandler() {
464
@Override
465
public void onBlur(BlurEvent event) {
466
getElement().getStyle().setBorderColor("#ccc");
467
getElement().getStyle().setProperty("boxShadow", "none");
468
469
// Validate input on blur
470
validateInput();
471
}
472
});
473
474
// Real-time validation
475
addValueChangeHandler(new ValueChangeHandler<String>() {
476
@Override
477
public void onValueChange(ValueChangeEvent<String> event) {
478
validateInput();
479
}
480
});
481
}
482
483
private void validateInput() {
484
String value = getText();
485
boolean isValid = true;
486
487
switch (inputType) {
488
case EMAIL:
489
isValid = value.matches("^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$");
490
break;
491
case NUMERIC_ONLY:
492
try {
493
Double.parseDouble(value);
494
} catch (NumberFormatException e) {
495
isValid = !value.isEmpty();
496
}
497
break;
498
}
499
500
setErrorStyle(!isValid);
501
}
502
503
public boolean isValidInput() {
504
validateInput();
505
return !hasErrorStyle();
506
}
507
508
public String getValidatedText() {
509
if (isValidInput()) {
510
return getText();
511
}
512
return null;
513
}
514
}
515
```
516
517
### Responsive Widget Layout Manager
518
519
```java
520
public class ResponsiveLayoutManager {
521
private ResizableWidgetCollection widgets;
522
private Map<ResizableWidget, LayoutConstraints> constraints;
523
524
public static class LayoutConstraints {
525
public float percentX, percentY; // Position as percentage of screen
526
public float percentWidth, percentHeight; // Size as percentage of screen
527
public int minWidth, minHeight; // Minimum pixel sizes
528
public int maxWidth, maxHeight; // Maximum pixel sizes
529
public Anchor anchor; // Anchor point for positioning
530
531
public enum Anchor {
532
TOP_LEFT, TOP_CENTER, TOP_RIGHT,
533
MIDDLE_LEFT, MIDDLE_CENTER, MIDDLE_RIGHT,
534
BOTTOM_LEFT, BOTTOM_CENTER, BOTTOM_RIGHT
535
}
536
}
537
538
public ResponsiveLayoutManager() {
539
widgets = new ResizableWidgetCollection();
540
constraints = new HashMap<>();
541
542
// Listen for window resize events
543
Window.addResizeHandler(new ResizeHandler() {
544
@Override
545
public void onResize(ResizeEvent event) {
546
layoutAllWidgets();
547
}
548
});
549
}
550
551
public void addWidget(ResizableWidget widget, LayoutConstraints layoutConstraints) {
552
widgets.add(widget);
553
constraints.put(widget, layoutConstraints);
554
layoutWidget(widget, layoutConstraints);
555
}
556
557
public void removeWidget(ResizableWidget widget) {
558
widgets.remove(widget);
559
constraints.remove(widget);
560
}
561
562
public void layoutAllWidgets() {
563
for (ResizableWidget widget : widgets) {
564
LayoutConstraints constraint = constraints.get(widget);
565
if (constraint != null) {
566
layoutWidget(widget, constraint);
567
}
568
}
569
}
570
571
private void layoutWidget(ResizableWidget widget, LayoutConstraints constraint) {
572
int screenWidth = Gdx.graphics.getWidth();
573
int screenHeight = Gdx.graphics.getHeight();
574
575
// Calculate size
576
int width = (int)(screenWidth * constraint.percentWidth);
577
int height = (int)(screenHeight * constraint.percentHeight);
578
579
// Apply size constraints
580
width = Math.max(constraint.minWidth, Math.min(constraint.maxWidth, width));
581
height = Math.max(constraint.minHeight, Math.min(constraint.maxHeight, height));
582
583
// Calculate position based on anchor
584
int x = calculateAnchoredX(screenWidth, width, constraint);
585
int y = calculateAnchoredY(screenHeight, height, constraint);
586
587
// Apply layout
588
widget.resize(width, height);
589
590
// Set position if widget supports it
591
if (widget instanceof Widget) {
592
((Widget) widget).setPixelSize(width, height);
593
// Position setting depends on widget implementation
594
}
595
}
596
597
private int calculateAnchoredX(int screenWidth, int widgetWidth, LayoutConstraints constraint) {
598
int baseX = (int)(screenWidth * constraint.percentX);
599
600
switch (constraint.anchor) {
601
case TOP_LEFT:
602
case MIDDLE_LEFT:
603
case BOTTOM_LEFT:
604
return baseX;
605
case TOP_CENTER:
606
case MIDDLE_CENTER:
607
case BOTTOM_CENTER:
608
return baseX - widgetWidth / 2;
609
case TOP_RIGHT:
610
case MIDDLE_RIGHT:
611
case BOTTOM_RIGHT:
612
return baseX - widgetWidth;
613
default:
614
return baseX;
615
}
616
}
617
618
private int calculateAnchoredY(int screenHeight, int widgetHeight, LayoutConstraints constraint) {
619
int baseY = (int)(screenHeight * constraint.percentY);
620
621
switch (constraint.anchor) {
622
case TOP_LEFT:
623
case TOP_CENTER:
624
case TOP_RIGHT:
625
return baseY;
626
case MIDDLE_LEFT:
627
case MIDDLE_CENTER:
628
case MIDDLE_RIGHT:
629
return baseY - widgetHeight / 2;
630
case BOTTOM_LEFT:
631
case BOTTOM_CENTER:
632
case BOTTOM_RIGHT:
633
return baseY - widgetHeight;
634
default:
635
return baseY;
636
}
637
}
638
639
// Predefined constraint builders
640
public static LayoutConstraints centerScreen(float percentWidth, float percentHeight) {
641
LayoutConstraints constraints = new LayoutConstraints();
642
constraints.percentX = 0.5f;
643
constraints.percentY = 0.5f;
644
constraints.percentWidth = percentWidth;
645
constraints.percentHeight = percentHeight;
646
constraints.anchor = LayoutConstraints.Anchor.MIDDLE_CENTER;
647
constraints.minWidth = 100;
648
constraints.minHeight = 50;
649
constraints.maxWidth = Integer.MAX_VALUE;
650
constraints.maxHeight = Integer.MAX_VALUE;
651
return constraints;
652
}
653
654
public static LayoutConstraints bottomCenter(float percentWidth, float height) {
655
LayoutConstraints constraints = new LayoutConstraints();
656
constraints.percentX = 0.5f;
657
constraints.percentY = 0.1f;
658
constraints.percentWidth = percentWidth;
659
constraints.percentHeight = 0; // Fixed height
660
constraints.anchor = LayoutConstraints.Anchor.BOTTOM_CENTER;
661
constraints.minWidth = 200;
662
constraints.minHeight = (int)height;
663
constraints.maxWidth = Integer.MAX_VALUE;
664
constraints.maxHeight = (int)height;
665
return constraints;
666
}
667
}
668
```
669
670
## Web-Specific Widget Considerations
671
672
### Browser Integration
673
674
```java
675
// Widgets are designed for web browser environments
676
public class BrowserIntegrationHelper {
677
678
public static void configureForMobile() {
679
// Optimize widgets for mobile browsers
680
ProgressBar.setDefaultHeight(40); // Larger for touch
681
TextInputDialogBox.setDefaultAnimationDuration(200); // Faster for mobile
682
683
// Handle virtual keyboard
684
PlaceholderTextBox.addGlobalFocusHandler(new FocusHandler() {
685
@Override
686
public void onFocus(FocusEvent event) {
687
// Scroll input into view when virtual keyboard appears
688
Element element = event.getSource().getElement();
689
element.scrollIntoView();
690
}
691
});
692
}
693
694
public static void configureForDesktop() {
695
// Optimize for desktop browsers
696
ProgressBar.setDefaultHeight(24); // Standard height
697
TextInputDialogBox.setDefaultAnimationDuration(300); // Smooth animations
698
699
// Enable advanced keyboard shortcuts
700
configureKeyboardShortcuts();
701
}
702
703
private static void configureKeyboardShortcuts() {
704
// Add Ctrl+A, Ctrl+C, Ctrl+V support for text inputs
705
// This is handled automatically by PlaceholderTextBox
706
}
707
}